diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/tdi/isnp/nb | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/tdi/isnp/nb')
35 files changed, 30887 insertions, 0 deletions
diff --git a/private/ntos/tdi/isnp/nb/action.c b/private/ntos/tdi/isnp/nb/action.c new file mode 100644 index 000000000..9ff843a76 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/action.c @@ -0,0 +1,221 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + action.c + +Abstract: + + This module contains code which implements the TDI action + dispatch routines. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +typedef struct _NB_ACTION_GET_COUNTS { + USHORT MaximumNicId; // returns maximum NIC ID + USHORT NicIdCounts[32]; // session counts for first 32 NIC IDs +} NB_ACTION_GET_COUNTS, *PNB_ACTION_GET_COUNTS; + + +NTSTATUS +NbiTdiAction( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine handles action requests. + +Arguments: + + Device - The netbios device. + + Request - The request describing the action. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + NTSTATUS Status; + PADDRESS_FILE AddressFile; + PCONNECTION Connection; + UINT BufferLength; + UINT DataLength; + PNDIS_BUFFER NdisBuffer; + CTELockHandle LockHandle; + union { + PNB_ACTION_GET_COUNTS GetCounts; + } u; // BUGBUG: Make these unaligned?? + PNWLINK_ACTION NwlinkAction; + UINT i; + static UCHAR BogusId[4] = { 0x01, 0x00, 0x00, 0x00 }; // old nwrdr uses this + + + // + // To maintain some compatibility with the NWLINK streams- + // based transport, we use the streams header format for + // our actions. The old transport expected the action header + // to be in InputBuffer and the output to go in OutputBuffer. + // We follow the TDI spec, which states that OutputBuffer + // is used for both input and output. Since IOCTL_TDI_ACTION + // is method out direct, this means that the output buffer + // is mapped by the MDL chain; for action the chain will + // only have one piece so we use it for input and output. + // + + NdisBuffer = REQUEST_NDIS_BUFFER(Request); + if (NdisBuffer == NULL) { + return STATUS_INVALID_PARAMETER; + } + + NdisQueryBuffer (REQUEST_NDIS_BUFFER(Request), (PVOID *)&NwlinkAction, &BufferLength); + + if ((!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MISN", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "MIPX", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), "XPIM", 4)) && + (!RtlEqualMemory ((PVOID)(&NwlinkAction->Header.TransportId), BogusId, 4))) { + + return STATUS_NOT_SUPPORTED; + } + + + // + // Make sure we have enough room for just the header not + // including the data. + // + + if (BufferLength < (UINT)(FIELD_OFFSET(NWLINK_ACTION, Data[0]))) { + NB_DEBUG (QUERY, ("Nwlink action failed, buffer too small\n")); + return STATUS_BUFFER_TOO_SMALL; + } + + DataLength = BufferLength - FIELD_OFFSET(NWLINK_ACTION, Data[0]); + + + // + // Make sure that the correct file object is being used. + // + + if (NwlinkAction->OptionType == NWLINK_OPTION_ADDRESS) { + + if (REQUEST_OPEN_TYPE(Request) != (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + NB_DEBUG (QUERY, ("Nwlink action failed, not address file\n")); + return STATUS_INVALID_HANDLE; + } + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + if ((AddressFile->Size != sizeof (ADDRESS_FILE)) || + (AddressFile->Type != NB_ADDRESSFILE_SIGNATURE)) { + + NB_DEBUG (QUERY, ("Nwlink action failed, bad address file\n")); + return STATUS_INVALID_HANDLE; + } + + } else if (NwlinkAction->OptionType != NWLINK_OPTION_CONTROL) { + + NB_DEBUG (QUERY, ("Nwlink action failed, option type %d\n", NwlinkAction->OptionType)); + return STATUS_NOT_SUPPORTED; + } + + + // + // Handle the requests based on the action code. For these + // requests ActionHeader->ActionCode is 0, we use the + // Option field in the streams header instead. + // + + + Status = STATUS_SUCCESS; + + switch (NwlinkAction->Option) { + + case (I_MIPX | 351): + + // + // A request for details on every binding. + // + + if (DataLength < sizeof(NB_ACTION_GET_COUNTS)) { + return STATUS_BUFFER_TOO_SMALL; + } + + u.GetCounts = (PNB_ACTION_GET_COUNTS)(NwlinkAction->Data); + + u.GetCounts->MaximumNicId = NbiDevice->MaximumNicId; + + for (i = 0; i < 32 ; i++) { + u.GetCounts->NicIdCounts[i] = 0; + } + + for (i = 0; i < CONNECTION_HASH_COUNT; i++) { + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Connection = Device->ConnectionHash[i].Connections; + + while (Connection != NULL) { +#if defined(_PNP_POWER) + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->LocalTarget.NicHandle.NicId < 32)) { + + ++u.GetCounts->NicIdCounts[Connection->LocalTarget.NicHandle.NicId]; + } +#else + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->LocalTarget.NicId < 32)) { + + ++u.GetCounts->NicIdCounts[Connection->LocalTarget.NicId]; + } +#endif _PNP_POWER + Connection = Connection->NextConnection; + } + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + break; + + // + // The Option was not supported, so fail. + // + + default: + + Status = STATUS_NOT_SUPPORTED; + break; + + + } // end of the long switch on NwlinkAction->Option + + +#if DBG + if (!NT_SUCCESS(Status)) { + NB_DEBUG (QUERY, ("Nwlink action %lx failed, status %lx\n", NwlinkAction->Option, Status)); + } +#endif + + return Status; + +} /* NbiTdiAction */ + diff --git a/private/ntos/tdi/isnp/nb/address.c b/private/ntos/tdi/isnp/nb/address.c new file mode 100644 index 000000000..2eb882b80 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/address.c @@ -0,0 +1,2406 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + address.c + +Abstract: + + This module contains code which implements the ADDRESS object. + Routines are provided to create, destroy, reference, and dereference, + transport address objects. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Map all generic accesses to the same one. +// + +static GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + + + +TDI_ADDRESS_NETBIOS UNALIGNED * +NbiParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN BOOLEAN BroadcastAddressOk + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, looking for an address + of type TDI_ADDRESS_TYPE_NETBIOS. + +Arguments: + + Transport - The generic TDI address. + + BroadcastAddressOk - TRUE if we should return the broadcast + address if found. If so, a value of (PVOID)-1 indicates + the broadcast address. + +Return Value: + + A pointer to the Netbios address, or NULL if none is found, + or (PVOID)-1 if the broadcast address is found. + +--*/ + +{ + TA_ADDRESS UNALIGNED * addressName; + INT i; + + addressName = &TransportAddress->Address[0]; + + // + // The name can be passed with multiple entries; we'll take and use only + // the Netbios one. + // + + for (i=0;i<TransportAddress->TAAddressCount;i++) { + if (addressName->AddressType == TDI_ADDRESS_TYPE_NETBIOS) { + if ((addressName->AddressLength == 0) && + BroadcastAddressOk) { + return (PVOID)-1; + } else if (addressName->AddressLength == sizeof(TDI_ADDRESS_NETBIOS)) { + return ((TDI_ADDRESS_NETBIOS UNALIGNED *)(addressName->Address)); + } + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + return NULL; + +} /* NbiParseTdiAddress */ + + +BOOLEAN +NbiValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, verifying that the + components of the address do not extend past the specified + length. + +Arguments: + + TransportAddress - The generic TDI address. + + TransportAddressLength - The specific length of TransportAddress. + +Return Value: + + TRUE if the address is valid, FALSE otherwise. + +--*/ + +{ + PUCHAR AddressEnd = ((PUCHAR)TransportAddress) + TransportAddressLength; + TA_ADDRESS UNALIGNED * addressName; + INT i; + + if (TransportAddressLength < sizeof(TransportAddress->TAAddressCount)) { + NbiPrint0 ("NbfValidateTdiAddress: runt address\n"); + return FALSE; + } + + addressName = &TransportAddress->Address[0]; + + for (i=0;i<TransportAddress->TAAddressCount;i++) { + if (addressName->Address > AddressEnd) { + NbiPrint0 ("NbiValidateTdiAddress: address too short\n"); + return FALSE; + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + + if ((PUCHAR)addressName > AddressEnd) { + NbiPrint0 ("NbiValidateTdiAddress: address too short\n"); + return FALSE; + } + return TRUE; + +} /* NbiValidateTdiAddress */ + + +NTSTATUS +NbiOpenAddress( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine opens a file that points to an existing address object, or, if + the object doesn't exist, creates it (note that creation of the address + object includes registering the address, and may take many seconds to + complete, depending upon system configuration). + + If the address already exists, and it has an ACL associated with it, the + ACL is checked for access rights before allowing creation of the address. + +Arguments: + + Device - pointer to the device describing the Netbios transport. + + Request - a pointer to the request used for the creation of the address. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PADDRESS Address; + PADDRESS_FILE AddressFile; + PFILE_FULL_EA_INFORMATION ea; + TRANSPORT_ADDRESS UNALIGNED *name; + TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress; + ULONG DesiredShareAccess; + CTELockHandle LockHandle; + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + BOOLEAN found = FALSE; +#ifdef ISN_NT + PIRP Irp = (PIRP)Request; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif +#if 0 + TA_NETBIOS_ADDRESS FakeAddress; +#endif + + + // + // The network name is in the EA, passed in the request. + // + + ea = OPEN_REQUEST_EA_INFORMATION(Request); + if (ea == NULL) { + NbiPrint1("OpenAddress: REQUEST %lx has no EA\n", Request); + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + // + // this may be a valid name; parse the name from the EA and use it if OK. + // + + name = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1]; +#if 0 + TdiBuildNetbiosAddress( + "ADAMBA67 ", + FALSE, + &FakeAddress); + name = (PTRANSPORT_ADDRESS)&FakeAddress; +#endif + + // + // The name can be passed with multiple entries; we'll take and use only + // the first one of type Netbios. This call returns (PVOID)-1 if the + // address is the broadcast address. + // + + NetbiosAddress = NbiParseTdiAddress (name, TRUE); + + if (NetbiosAddress == NULL) { + NbiPrint1("OpenAddress: REQUEST %lx has no Netbios Address\n", Request); + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + // + // get an address file structure to represent this address. + // + + AddressFile = NbiCreateAddressFile (Device); + + if (AddressFile == (PADDRESS_FILE)NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // See if this address is already established. This call automatically + // increments the reference count on the address so that it won't disappear + // from underneath us after this call but before we have a chance to use it. + // + // To ensure that we don't create two address objects for the + // same address, we hold the device context addressResource until + // we have found the address or created a new one. + // + + ExAcquireResourceExclusive (&Device->AddressResource, TRUE); + +#if defined(_PNP_POWER) + + Address = NbiFindAddress ( + Device, + ( NetbiosAddress == (PVOID)-1 ) ? (PVOID)-1 : NetbiosAddress->NetbiosName + ); + + if (Address == NULL) { + +#else + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Address = NbiLookupAddress (Device, NetbiosAddress); + + if (Address == NULL) { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + +#endif _PNP_POWER + + // + // This address doesn't exist. Create it. + // This initializes the address with a ref + // of type ADDRESS_FILE, so if we fail here + // we need to remove that. + // + + Address = NbiCreateAddress ( + Device, + NetbiosAddress); + + if (Address != (PADDRESS)NULL) { + + // + // Set this now in case we have to deref. + // + + AddressFile->AddressLock = &Address->Lock; + +#ifdef ISN_NT + + // + // Initialize the shared access now. We use read access + // to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + IoSetShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &Address->u.ShareAccess); + + + // + // Assign the security descriptor (need to do this with + // the spinlock released because the descriptor is not + // mapped). + // + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + status = SeAssignSecurity( + NULL, // parent descriptor + AccessState->SecurityDescriptor, + &Address->SecurityDescriptor, + FALSE, // is directory + &AccessState->SubjectSecurityContext, + &AddressGenericMapping, + NonPagedPool); + + if (!NT_SUCCESS(status)) { + + // + // Error, return status. + // + + IoRemoveShareAccess (IrpSp->FileObject, &Address->u.ShareAccess); + ExReleaseResource (&Device->AddressResource); + NbiDereferenceAddress (Address, AREF_ADDRESS_FILE); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + return status; + + } + +#endif + + ExReleaseResource (&Device->AddressResource); + + // + // if the adapter isn't ready, we can't do any of this; get out + // +#if defined(_PNP_POWER) + if (Device->State != DEVICE_STATE_OPEN) { +#else + if (Device->State == DEVICE_STATE_STOPPING) { +#endif _PNP_POWER + NbiDereferenceAddress (Address, AREF_ADDRESS_FILE); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + status = STATUS_DEVICE_NOT_READY; + + } else { + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)AddressFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; +#ifdef ISN_NT + AddressFile->FileObject = IrpSp->FileObject; +#endif + AddressFile->Address = Address; + + NB_INSERT_TAIL_LIST( + &Address->AddressFileDatabase, + &AddressFile->Linkage, + &Address->Lock); + + if (NetbiosAddress == (PVOID)-1) { + + AddressFile->OpenRequest = NULL; + AddressFile->State = ADDRESSFILE_STATE_OPEN; + status = STATUS_SUCCESS; + + } else { + + AddressFile->OpenRequest = Request; + AddressFile->State = ADDRESSFILE_STATE_OPENING; + status = STATUS_PENDING; + + NbiStartRegistration (Address); + } + + } + + } else { + + ExReleaseResource (&Device->AddressResource); + + // + // If the address could not be created, and is not in the + // process of being created, then we can't open up an address. + // Since we can't use the AddressLock to deref, we just destroy + // the address file. + // + + NbiDestroyAddressFile (AddressFile); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + +#if !defined(_PNP_POWER) + NB_FREE_LOCK (&Device->Lock, LockHandle); + +#endif !_PNP_POWER + NB_DEBUG2 (ADDRESS, ("Add to address %lx\n", Address)); + + // + // Set this now in case we have to deref. + // + + AddressFile->AddressLock = &Address->Lock; + + // + // Make sure the types do not conflict. + // + + if ((NetbiosAddress != (PVOID)-1) && + (NetbiosAddress->NetbiosNameType != Address->NetbiosAddress.NetbiosNameType)) { + + NB_DEBUG (ADDRESS, ("Address types conflict %lx\n", Address)); + ExReleaseResource (&Device->AddressResource); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + status = STATUS_DUPLICATE_NAME; + + } else { + + // + // The address already exists. Check the ACL and see if we + // can access it. If so, simply use this address as our address. + // + +#ifdef ISN_NT + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + AccessAllowed = SeAccessCheck( + Address->SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, // tokens locked + IrpSp->Parameters.Create.SecurityContext->DesiredAccess, + (ACCESS_MASK)0, // previously granted + NULL, // privileges + &AddressGenericMapping, + Irp->RequestorMode, + &GrantedAccess, + &status); + +#else // ISN_NT + + AccessAllowed = TRUE; + +#endif // ISN_NT + + if (!AccessAllowed) { + + NB_DEBUG (ADDRESS, ("Address access not allowed %lx\n", Address)); + ExReleaseResource (&Device->AddressResource); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + +#ifdef ISN_NT + + // + // Now check that we can obtain the desired share + // access. We use read access to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + status = IoCheckShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &Address->u.ShareAccess, + TRUE); + +#else // ISN_NT + + status = STATUS_SUCCESS; + +#endif // ISN_NT + + if (!NT_SUCCESS (status)) { + + NB_DEBUG (ADDRESS, ("Address share access wrong %lx\n", Address)); + ExReleaseResource (&Device->AddressResource); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + + ExReleaseResource (&Device->AddressResource); + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + // + // Insert the address file on the address + // list; we will pend this open if the address + // is still registering. If the address has + // already failed as duplicate, then we + // fail the open. + // + + if (Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) { + + NB_DEBUG (ADDRESS, ("Address duplicated %lx\n", Address)); + NB_FREE_LOCK (&Address->Lock, LockHandle); + + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + status = STATUS_DUPLICATE_NAME; + + } else { + + InsertTailList ( + &Address->AddressFileDatabase, + &AddressFile->Linkage); + + // + // Start registration unless it is registered or + // it is the broadcast address. + // + + if ((Address->State == ADDRESS_STATE_REGISTERING) && + (NetbiosAddress != (PVOID)-1)) { + + AddressFile->OpenRequest = Request; + AddressFile->State = ADDRESSFILE_STATE_OPENING; + status = STATUS_PENDING; + + } else { + + AddressFile->OpenRequest = NULL; + AddressFile->State = ADDRESSFILE_STATE_OPEN; + status = STATUS_SUCCESS; + } + + AddressFile->Address = Address; +#ifdef ISN_NT + AddressFile->FileObject = IrpSp->FileObject; +#endif + + NbiReferenceAddress (Address, AREF_ADDRESS_FILE); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)AddressFile; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + } + + } + } + } + + // + // Remove the reference from NbiLookupAddress. + // + + NbiDereferenceAddress (Address, AREF_LOOKUP); + } + + return status; + +} /* NbiOpenAddress */ + + +VOID +NbiStartRegistration( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine starts the registration process for a netbios name + by sending out the first add name packet and starting the timer + so that NbiRegistrationTimeout is called after the correct timeout. + +Arguments: + + Address - The address which is to be registered. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + NB_DEBUG2 (ADDRESS, ("StartRegistration of %lx\n", Address)); + + // + // First send out an add name packet. + // + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | NB_NAME_USED), + NB_CMD_ADD_NAME, + NULL, + NULL); + + Address->RegistrationCount = 0; + + // + // Now start the timer. + // + + NbiReferenceAddress (Address, AREF_TIMER); + + CTEInitTimer (&Address->RegistrationTimer); + CTEStartTimer( + &Address->RegistrationTimer, + Address->Device->BroadcastTimeout, + NbiRegistrationTimeout, + (PVOID)Address); + +} /* NbiStartRegistration */ + + +VOID +NbiRegistrationTimeout( + IN CTEEvent * Event, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the address registration + timer expires. It sends another add name if needed, or + checks the result if the correct number have been sent. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the address pointer. + +Return Value: + + None. + +--*/ + +{ + PADDRESS Address = (PADDRESS)Context; + CTELockHandle LockHandle; + PADDRESS_FILE AddressFile, ReferencedAddressFile; + PLIST_ENTRY p; + + ++Address->RegistrationCount; + + if ((Address->RegistrationCount < Address->Device->BroadcastCount) && + ((Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) == 0)) { + + NB_DEBUG2 (ADDRESS, ("Send add name %d for %lx\n", Address->RegistrationCount+1, Address)); + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | NB_NAME_USED), + NB_CMD_ADD_NAME, + NULL, + NULL); + + CTEStartTimer( + &Address->RegistrationTimer, + Address->Device->BroadcastTimeout, + NbiRegistrationTimeout, + (PVOID)Address); + + } else { + + // + // The correct number of frames have been sent, see what + // happened. + // + + NB_DEBUG2 (ADDRESS, ("Done with add names for %lx\n", Address)); + + ReferencedAddressFile = NULL; + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if ((Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) == 0) { + Address->State = ADDRESS_STATE_OPEN; + } else { + Address->State = ADDRESS_STATE_STOPPING; + } + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage); + CTEAssert (AddressFile->State == ADDRESSFILE_STATE_OPENING); + CTEAssert (AddressFile->OpenRequest != NULL); + + NbiReferenceAddressFileLock (AddressFile, AFREF_TIMEOUT); + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_TIMEOUT); + } + + // + // Now see what to do with this address file. + // + + REQUEST_INFORMATION(AddressFile->OpenRequest) = 0; + + if (Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) { + + NB_DEBUG (ADDRESS, ("Open of address file %lx failed, duplicate\n", AddressFile)); + REQUEST_STATUS(AddressFile->OpenRequest) = STATUS_DUPLICATE_NAME; + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + + } else { + + NB_DEBUG2 (ADDRESS, ("Complete open of address file %lx\n", AddressFile)); + REQUEST_STATUS(AddressFile->OpenRequest) = STATUS_SUCCESS; + AddressFile->State = ADDRESSFILE_STATE_OPEN; + + } + + NbiCompleteRequest (AddressFile->OpenRequest); + NbiFreeRequest (Address->Device, AddressFile->OpenRequest); + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + ReferencedAddressFile = AddressFile; + + } + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_TIMEOUT); + } + + NbiDereferenceAddress (Address, AREF_TIMER); + + } + +} /* NbiRegistrationTimeout */ + + +VOID +NbiProcessFindName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_FIND_NAME frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PADDRESS Address; + NB_CONNECTIONLESS UNALIGNED * NbConnectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + PDEVICE Device = NbiDevice; + + if (PacketSize != sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)) { + return; + } + + // + // Quick check for any names starting with this character. + // + + if (Device->AddressCounts[NbConnectionless->NameFrame.Name[0]] == 0) { + return; + } + + // + // Always respond to broadcast requests. + // +#if defined(_PNP_POWER) + if (RtlEqualMemory (NetbiosBroadcastName, NbConnectionless->NameFrame.Name, 16)) { + + NbiSendNameFrame( + NULL, + NB_NAME_DUPLICATED, // this is what Novell machines use + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + NbConnectionless); + + } else if (Address = NbiFindAddress(Device, (PUCHAR)NbConnectionless->NameFrame.Name)) { + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | NB_NAME_USED | NB_NAME_REGISTERED), + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + NbConnectionless); + + NbiDereferenceAddress (Address, AREF_FIND); + + } else if ( NbiFindAdapterAddress( NbConnectionless->NameFrame.Name, LOCK_NOT_ACQUIRED ) ) { + + NbiSendNameFrame( + NULL, + (UCHAR)(NB_NAME_UNIQUE | NB_NAME_USED | NB_NAME_REGISTERED), + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + NbConnectionless); + } +#else + if (RtlEqualMemory (NetbiosBroadcastName, NbConnectionless->NameFrame.Name, 16)) { + + NbiSendNameFrame( + NULL, + NB_NAME_DUPLICATED, // this is what Novell machines use + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + (PTDI_ADDRESS_IPX)(NbConnectionless->IpxHeader.SourceNetwork)); + + } else if (Address = NbiFindAddress(Device, (PUCHAR)NbConnectionless->NameFrame.Name)) { + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | NB_NAME_USED | NB_NAME_REGISTERED), + NB_CMD_NAME_RECOGNIZED, + RemoteAddress, + (PTDI_ADDRESS_IPX)(NbConnectionless->IpxHeader.SourceNetwork)); + + NbiDereferenceAddress (Address, AREF_FIND); + + } +#endif _PNP_POWER +} /* NbiProcessFindName */ + + +VOID +NbiProcessAddName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_ADD_NAME frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PADDRESS Address; + NB_CONNECTIONLESS UNALIGNED * NbConnectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; + BOOLEAN LocalFrame; + + + if (PacketSize != sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)) { + return; + } + + // + // Ignore any frame that came from us, except for the purpose + // of updating the cache. + // + + if ((Device->Bind.QueryHandler)( + IPX_QUERY_IS_ADDRESS_LOCAL, +#if defined(_PNP_POWER) + &RemoteAddress->NicHandle, +#else + RemoteAddress->NicId, +#endif _PNP_POWER + NbConnectionless->IpxHeader.SourceNetwork, + sizeof(TDI_ADDRESS_IPX), + NULL) == STATUS_SUCCESS) { + + LocalFrame = TRUE; + + } else { + + LocalFrame = FALSE; + + } + + if (!LocalFrame) { + + if ((Device->AddressCounts[NbConnectionless->NameFrame.Name[0]] != 0) && + (Address = NbiFindAddress(Device, (PUCHAR)NbConnectionless->NameFrame.Name))) { + + if (NB_NODE_BROADCAST(NbConnectionless->IpxHeader.DestinationNode)) { + + // + // If this frame is an add name (identified because it is a + // broadcast frame) then respond if we have it registered + // unique, or we have it group and someone is trying to add + // it unique. + // + + if ((Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) || + ((Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_GROUP) && + ((NbConnectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0))) { + + // + // According to GeorgeJ's doc, on a name in use we just + // echo back the name type flags from the request. + // + + NbiSendNameFrame( + Address, + NbConnectionless->NameFrame.NameTypeFlag, + NB_CMD_NAME_IN_USE, + RemoteAddress, +#if defined(_PNP_POWER) + NbConnectionless); +#else + (PTDI_ADDRESS_IPX)(NbConnectionless->IpxHeader.SourceNetwork)); +#endif _PNP_POWER + } + + } else if ((*(UNALIGNED ULONG *)NbConnectionless->IpxHeader.DestinationNetwork == + *(UNALIGNED ULONG *)Device->Bind.Network) && + NB_NODE_EQUAL(NbConnectionless->IpxHeader.DestinationNode, Device->Bind.Node)) { + + // + // If this is an add name response (which will be sent + // directly to us) then we need to mark the address + // as such. + // + + NB_GET_LOCK (&Address->Lock, &LockHandle); + Address->Flags |= ADDRESS_FLAGS_DUPLICATE_NAME; + NB_FREE_LOCK (&Address->Lock, LockHandle); + } + + NbiDereferenceAddress (Address, AREF_FIND); + + } + + } + + + // + // Pass this frame over to the netbios cache management + // routines to check if they need to update their cache. + // + + CacheUpdateFromAddName (RemoteAddress, NbConnectionless, LocalFrame); + +} /* NbiProcessAddName */ + + +PADDRESS +NbiCreateAddress( + IN PDEVICE Device, + IN TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress + ) + +/*++ + +Routine Description: + + This routine creates a transport address and associates it with + the specified transport device context. The reference count in the + address is automatically set to 1, and the reference count of the + device context is incremented. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + NetbiosAddress - The name to assign to this address, or -1 if it + is the broadcast address. + +Return Value: + + The newly created address, or NULL if none can be allocated. + +--*/ + +{ + PADDRESS Address; + + Address = (PADDRESS)NbiAllocateMemory (sizeof(ADDRESS), MEMORY_ADDRESS, "Address"); + if (Address == NULL) { + NB_DEBUG (ADDRESS, ("Create address %.16s failed\n", + (NetbiosAddress == (PVOID)-1) ? "<broadcast>" : NetbiosAddress->NetbiosName)); + return NULL; + } + + NB_DEBUG2 (ADDRESS, ("Create address %lx (%.16s)\n", Address, + (NetbiosAddress == (PVOID)-1) ? "<broadcast>" : NetbiosAddress->NetbiosName)); + RtlZeroMemory (Address, sizeof(ADDRESS)); + + Address->Type = NB_ADDRESS_SIGNATURE; + Address->Size = sizeof (ADDRESS); + Address->State = ADDRESS_STATE_REGISTERING; + Address->Flags = 0; + + Address->Device = Device; + Address->DeviceLock = &Device->Lock; + CTEInitLock (&Address->Lock.Lock); + + InitializeListHead (&Address->AddressFileDatabase); + + Address->ReferenceCount = 1; +#if DBG + Address->RefTypes[AREF_ADDRESS_FILE] = 1; +#endif + + if (NetbiosAddress == (PVOID)-1) { + Address->NetbiosAddress.Broadcast = TRUE; + } else { + Address->NetbiosAddress.Broadcast = FALSE; + Address->NetbiosAddress.NetbiosNameType = NetbiosAddress->NetbiosNameType; + RtlCopyMemory (Address->NetbiosAddress.NetbiosName, NetbiosAddress->NetbiosName, 16); + ++Device->AddressCounts[NetbiosAddress->NetbiosName[0]]; + } + + if (Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) { + Address->NameTypeFlag = NB_NAME_UNIQUE; + } else { + Address->NameTypeFlag = NB_NAME_GROUP; + } + + // + // Now link this address into the specified device context's + // address database. To do this, we need to acquire the spin lock + // on the device context. + // + + InsertTailList (&Device->AddressDatabase, &Address->Linkage); + ++Device->AddressCount; + + NbiReferenceDevice (Device, DREF_ADDRESS); + + return Address; + +} /* NbiCreateAddress */ + + +NTSTATUS +NbiVerifyAddressFile ( +#if defined(_PNP_POWER) + IN PADDRESS_FILE AddressFile, + IN BOOLEAN ConflictIsOk +#else + IN PADDRESS_FILE AddressFile +#endif _PNP_POWER + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + AddressFile - potential pointer to a ADDRESS_FILE object + + ConflictIsOk - TRUE if we should succeed the verify even if the + corresponding address is in CONFLICT. ( For Close and + cleanup we return STATUS_SUCCESS even if we are in conflict + so that the addressfile can be destroyed) + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + PADDRESS Address; + BOOLEAN LockHeld = FALSE; + + // + // try to verify the address file signature. If the signature is valid, + // verify the address pointed to by it and get the address spinlock. + // check the address's state, and increment the reference count if it's + // ok to use it. Note that the only time we return an error for state is + // if the address is closing. + // + + try { + + if ((AddressFile->Size == sizeof (ADDRESS_FILE)) && + (AddressFile->Type == NB_ADDRESSFILE_SIGNATURE) ) { +// (AddressFile->State != ADDRESSFILE_STATE_CLOSING) ) { + + Address = AddressFile->Address; + + if ((Address->Size == sizeof (ADDRESS)) && + (Address->Type == NB_ADDRESS_SIGNATURE) ) { + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + LockHeld = TRUE; + +#if defined(_PNP_POWER) + if (Address->State != ADDRESS_STATE_STOPPING && + ( ConflictIsOk || ( !(Address->Flags & ADDRESS_FLAGS_CONFLICT) )) ) { +#else + if (Address->State != ADDRESS_STATE_STOPPING) { +#endif _PNP_POWER + + NbiReferenceAddressFileLock (AddressFile, AFREF_VERIFY); + + } else { + + NbiPrint1("NbiVerifyAddressFile: A %lx closing\n", Address); + status = STATUS_INVALID_ADDRESS; + } + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + } else { + + NbiPrint1("NbiVerifyAddressFile: A %lx bad signature\n", Address); + status = STATUS_INVALID_ADDRESS; + } + + } else { + + NbiPrint1("NbiVerifyAddressFile: AF %lx bad signature\n", AddressFile); + status = STATUS_INVALID_ADDRESS; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + NbiPrint1("NbiVerifyAddressFile: AF %lx exception\n", Address); + if (LockHeld) { + NB_FREE_LOCK (&Address->Lock, LockHandle); + } + return GetExceptionCode(); + } + + return status; + +} /* NbiVerifyAddressFile */ + + +VOID +NbiDestroyAddress( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine destroys a transport address and removes all references + made by it to other objects in the transport. The address structure + is returned to nonpaged system pool. It is assumed + that the caller has already removed all addressfile structures associated + with this address. + + It is called from a worker thread queue by NbiDerefAddress when + the reference count goes to 0. + + This thread is only queued by NbiDerefAddress. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same address object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + Address - Pointer to a transport address structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PADDRESS Address = (PADDRESS)Parameter; + PDEVICE Device = Address->Device; + CTELockHandle LockHandle; + + NB_DEBUG2 (ADDRESS, ("Destroy address %lx <%.16s>\n", Address, + Address->NetbiosAddress.Broadcast ? "<broadcast>" : Address->NetbiosAddress.NetbiosName)); + + SeDeassignSecurity (&Address->SecurityDescriptor); + + // + // Delink this address from its associated device context's address + // database. To do this we must spin lock on the device context object, + // not on the address. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if (!Address->NetbiosAddress.Broadcast) { + --Device->AddressCounts[Address->NetbiosAddress.NetbiosName[0]]; + } + --Device->AddressCount; + RemoveEntryList (&Address->Linkage); + NB_FREE_LOCK (&Device->Lock, LockHandle); + + NbiFreeMemory (Address, sizeof(ADDRESS), MEMORY_ADDRESS, "Address"); + + NbiDereferenceDevice (Device, DREF_ADDRESS); + +} /* NbiDestroyAddress */ + + +#if DBG +VOID +NbiRefAddress( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->ReferenceCount > 0); // not perfect, but... + + InterlockedIncrement( &Address->ReferenceCount ); +} /* NbiRefAddress */ + + +VOID +NbiRefAddressLock( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address + when the device lock is already held. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (Address->ReferenceCount > 0); // not perfect, but... + + InterlockedIncrement( &Address->ReferenceCount ); + +} /* NbiRefAddressLock */ +#endif + + +VOID +NbiDerefAddress( + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbiDestroyAddress to remove it from the system. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + ULONG newvalue; + + newvalue = InterlockedDecrement( &Address->ReferenceCount ); + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (newvalue >= 0); + + if (newvalue == 0) { + +#if ISN_NT + ExInitializeWorkItem( + &Address->u.DestroyAddressQueueItem, + NbiDestroyAddress, + (PVOID)Address); + ExQueueWorkItem(&Address->u.DestroyAddressQueueItem, DelayedWorkQueue); +#else + NbiDestroyAddress(Address); +#endif + + } + +} /* NbiDerefAddress */ + + +PADDRESS_FILE +NbiCreateAddressFile( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine creates an address file from the pool of ther + specified device context. The reference count in the + address is automatically set to 1. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + +Return Value: + + The allocate address file or NULL. + +--*/ + +{ + CTELockHandle LockHandle; + PADDRESS_FILE AddressFile; + UINT i; + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + AddressFile = (PADDRESS_FILE)NbiAllocateMemory (sizeof(ADDRESS_FILE), MEMORY_ADDRESS, "AddressFile"); + if (AddressFile == NULL) { + NB_DEBUG (ADDRESS, ("Create address file failed\n")); + NB_FREE_LOCK (&Device->Lock, LockHandle); + return NULL; + } + + NB_DEBUG2 (ADDRESS, ("Create address file %lx\n", AddressFile)); + + RtlZeroMemory (AddressFile, sizeof(ADDRESS_FILE)); + + AddressFile->Type = NB_ADDRESSFILE_SIGNATURE; + AddressFile->Size = sizeof (ADDRESS_FILE); + + InitializeListHead (&AddressFile->ReceiveDatagramQueue); + InitializeListHead (&AddressFile->ConnectionDatabase); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + AddressFile->Address = NULL; +#ifdef ISN_NT + AddressFile->FileObject = NULL; +#endif + AddressFile->Device = Device; + AddressFile->State = ADDRESSFILE_STATE_OPENING; + AddressFile->ReferenceCount = 1; +#if DBG + AddressFile->RefTypes[AFREF_CREATE] = 1; +#endif + AddressFile->CloseRequest = (PREQUEST)NULL; + + // + // Initialize the request handlers. + // + + for (i = 0; i < 6; i++) { + AddressFile->RegisteredHandler[i] = FALSE; + AddressFile->HandlerContexts[i] = NULL; + AddressFile->Handlers[i] = TdiDefaultHandlers[i]; + } + + CTEAssert (AddressFile->ConnectionHandler == TdiDefaultConnectHandler); + CTEAssert (AddressFile->DisconnectHandler == TdiDefaultDisconnectHandler); + CTEAssert (AddressFile->ErrorHandler == TdiDefaultErrorHandler); + CTEAssert (AddressFile->ReceiveHandler == TdiDefaultReceiveHandler); + CTEAssert (AddressFile->ReceiveDatagramHandler == TdiDefaultRcvDatagramHandler); + CTEAssert (AddressFile->ExpeditedDataHandler == TdiDefaultRcvExpeditedHandler); + + return AddressFile; + +} /* NbiCreateAddressFile */ + + +NTSTATUS +NbiDestroyAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine destroys an address file and removes all references + made by it to other objects in the transport. + + This routine is only called by NbiDereferenceAddressFile. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address file object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + AddressFile Pointer to a transport address file structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + CTELockHandle LockHandle, LockHandle1; + PADDRESS Address; + PDEVICE Device; + PREQUEST CloseRequest; + BOOLEAN StopAddress; + + NB_DEBUG2 (ADDRESS, ("Destroy address file %lx\n", AddressFile)); + + Address = AddressFile->Address; + Device = AddressFile->Device; + + if (Address) { + + // + // This addressfile was associated with an address. + // + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + // + // remove this addressfile from the address list and disassociate it from + // the file handle. + // + + RemoveEntryList (&AddressFile->Linkage); + InitializeListHead (&AddressFile->Linkage); + + if (Address->AddressFileDatabase.Flink == &Address->AddressFileDatabase) { + + // + // This is the last open of this address, it will close + // due to normal dereferencing but we have to set the + // CLOSING flag too to stop further references. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle1); + Address->State = ADDRESS_STATE_STOPPING; + NB_FREE_LOCK (&Device->Lock, LockHandle1); + + StopAddress = TRUE; + + } else { + + StopAddress = FALSE; + } + + AddressFile->Address = NULL; + +#ifdef ISN_NT + AddressFile->FileObject->FsContext = NULL; + AddressFile->FileObject->FsContext2 = NULL; +#endif + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + // + // We will already have been removed from the ShareAccess + // of the owning address. + // + + if (StopAddress && (!Address->NetbiosAddress.Broadcast)) { + + NbiSendNameFrame( + Address, + (UCHAR)(Address->NameTypeFlag | + NB_NAME_USED | NB_NAME_REGISTERED | NB_NAME_DEREGISTERED), + NB_CMD_DELETE_NAME, + NULL, + NULL); + } + + // + // Now dereference the owning address. + // + + NbiDereferenceAddress (Address, AREF_ADDRESS_FILE); + + } + + // + // Save this for later completion. + // + + CloseRequest = AddressFile->CloseRequest; + + // + // return the addressFile to the pool of address files + // + + NbiFreeMemory (AddressFile, sizeof(ADDRESS_FILE), MEMORY_ADDRESS, "AddressFile"); + + if (CloseRequest != (PREQUEST)NULL) { + REQUEST_INFORMATION(CloseRequest) = 0; + REQUEST_STATUS(CloseRequest) = STATUS_SUCCESS; + NbiCompleteRequest (CloseRequest); + NbiFreeRequest (Device, CloseRequest); + } + + return STATUS_SUCCESS; + +} /* NbiDestroyAddressFile */ + + +#if DBG +VOID +NbiRefAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + + InterlockedIncrement( &AddressFile->ReferenceCount ); +} /* NbiRefAddressFile */ + + +VOID +NbiRefAddressFileLock( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + IT IS CALLED WITH THE ADDRESS LOCK HELD. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + CTEAssert (AddressFile->ReferenceCount > 0); // not perfect, but... + + + InterlockedIncrement( &AddressFile->ReferenceCount ); + +} /* NbiRefAddressFileLock */ + +#endif + + +VOID +NbiDerefAddressFile( + IN PADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbiDestroyAddressFile to remove it from the system. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + ULONG newvalue; + + newvalue = InterlockedDecrement( &AddressFile->ReferenceCount ); + + // + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + CTEAssert (newvalue >= 0); + + if (newvalue == 0) { + NbiDestroyAddressFile (AddressFile); + } + +} /* NbiDerefAddressFile */ + +#if !defined(_PNP_POWER) + +PADDRESS +NbiLookupAddress( + IN PDEVICE Device, + IN TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NETWORK + NAME values. If an exact match is found, then a pointer to the + ADDRESS object is returned, and as a side effect, the reference + count to the address object is incremented. If the address is not + found, then NULL is returned. + + NOTE: This routine must be called with the Device + spinlock held. + +Arguments: + + Device - Pointer to the device object and its extension. + + NetbiosAddress - The name to look up, or -1 if the broadcast + address is being searched for. + +Return Value: + + Pointer to the ADDRESS object found, or NULL if not found. + +--*/ + +{ + PADDRESS Address; + PLIST_ENTRY p; + + p = Device->AddressDatabase.Flink; + + for (p = Device->AddressDatabase.Flink; + p != &Device->AddressDatabase; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + if (Address->State == ADDRESS_STATE_STOPPING) { + continue; + } + + if (Address->NetbiosAddress.Broadcast) { + + // + // This address is the broadcast one, so no match + // unless we are looking for that. + // + + if (NetbiosAddress != (PVOID)-1) { + continue; + } + + } else { + + // + // This address is not the broadcast, so if we are + // looking for that then no match, else compare the + // two names. + // + + if (NetbiosAddress == (PVOID)-1) { + continue; + } + + if (!RtlEqualMemory( + Address->NetbiosAddress.NetbiosName, + NetbiosAddress->NetbiosName, + 16)) { + continue; + } + } + + // + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + // + + NbiReferenceAddressLock (Address, AREF_LOOKUP); + return Address; + + } /* for */ + + // + // The specified address was not found. + // + + return NULL; + +} /* NbiLookupAddress */ +#endif !_PNP_POWER + + +PADDRESS +NbiFindAddress( + IN PDEVICE Device, + IN PUCHAR NetbiosName + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NetbiosName + values. If a match is found, the address is referenced and the + pointer is returned. + + We ignore any addresses which are either STOPPING or are under + CONFLICT state. + + A name in CONFLICT is dead for all practical purposes + except Close. This routine is called by various name service, + datagram and session sevice routines. We hide any names in CONFLICT + from these routines. + + This routine is also called by NbiTdiOpenAddress(). + A name could have been marked in CONFLICT ages ago(but is not closed + yet). We must allow another open of the same name as that might + succeed now. + +Arguments: + + Device - Pointer to the device object and its extension. + + NetbiosName - The name to look up, or -1 for the broadcast name. + +Return Value: + + Pointer to the ADDRESS object found, or NULL if not found. + +--*/ + +{ + PADDRESS Address; + PLIST_ENTRY p; + CTELockHandle LockHandle; + + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + p = Device->AddressDatabase.Flink; + + for (p = Device->AddressDatabase.Flink; + p != &Device->AddressDatabase; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + +#if defined(_PNP_POWER) + if ( ( Address->State == ADDRESS_STATE_STOPPING ) || + ( Address->Flags & ADDRESS_FLAGS_CONFLICT ) ) { +#else + if (Address->State == ADDRESS_STATE_STOPPING) { +#endif _PNP_POWER + continue; + } + + if (Address->NetbiosAddress.Broadcast) { + + // + // This address is the broadcast one, so no match + // unless we are looking for that. + // + + if (NetbiosName != (PVOID)-1) { + continue; + } + + } else { + + // + // This address is not the broadcast, so if we are + // looking for that then no match, else compare the + // two names. + // + + if ((NetbiosName == (PVOID)-1) || + (!RtlEqualMemory( + Address->NetbiosAddress.NetbiosName, + NetbiosName, + 16))) { + continue; + } + } + + + // + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + // + + NbiReferenceAddressLock (Address, AREF_FIND); + NB_FREE_LOCK (&Device->Lock, LockHandle); + return Address; + + } /* for */ + + // + // The specified address was not found. + // + + NB_FREE_LOCK (&Device->Lock, LockHandle); + return NULL; + +} /* NbiFindAddress */ + + +NTSTATUS +NbiStopAddressFile( + IN PADDRESS_FILE AddressFile, + IN PADDRESS Address + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an AddressFile and + destroy the object. We remove every connection and datagram associated + with this addressfile from the address database and terminate their + activity. Then, if there are no other outstanding addressfiles open on + this address, the address will go away. + +Arguments: + + AddressFile - pointer to the addressFile to be stopped + + Address - the owning address for this addressFile (we do not depend upon + the pointer in the addressFile because we want this routine to be safe) + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the request + is not for a real address. + +--*/ + +{ + PLIST_ENTRY p; + PCONNECTION Connection; + PREQUEST Request; + PDEVICE Device = Address->Device; + CTELockHandle LockHandle1, LockHandle2; + LIST_ENTRY SendDatagramList; + PNB_SEND_RESERVED Reserved; + PREQUEST DatagramRequest; + NB_DEFINE_LOCK_HANDLE (LockHandle3) + CTELockHandle CancelLH; + NB_DEFINE_SYNC_CONTEXT (SyncContext) + LIST_ENTRY DatagramQ; + + + + NB_GET_LOCK (&Address->Lock, &LockHandle1); + + if (AddressFile->State == ADDRESSFILE_STATE_CLOSING) { + NB_FREE_LOCK (&Address->Lock, LockHandle1); + return STATUS_SUCCESS; + } + + + // + // This prevents anybody else from being put on the + // ConnectionDatabase. + // + + AddressFile->State = ADDRESSFILE_STATE_CLOSING; + + while (!IsListEmpty (&AddressFile->ConnectionDatabase)) { + + p = RemoveHeadList (&AddressFile->ConnectionDatabase); + Connection = CONTAINING_RECORD (p, CONNECTION, AddressFileLinkage); + + CTEAssert (Connection->AddressFile == AddressFile); + Connection->AddressFileLinked = FALSE; + + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + if (Connection->ReferenceCount == 0) { + + // + // The refcount is already 0, so we can just + // NULL out this field to complete the disassociate. + // + + Connection->AddressFile = NULL; + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + + } else { + + // + // Mark this so we know to disassociate when the + // count goes to 0, but that there is no specific + // request pending on it. We also stop the connection + // to shut it down. + // + + Connection->DisassociatePending = (PVOID)-1; + NbiReferenceConnectionLock (Connection, CREF_DISASSOC); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + NB_BEGIN_SYNC (&SyncContext); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle3); + + // + // This call frees the connection lock. + // + + NbiStopConnection( + Connection, + STATUS_INVALID_ADDRESS + NB_LOCK_HANDLE_ARG (LockHandle3)); + + NB_END_SYNC (&SyncContext); + + NbiDereferenceConnection (Connection, CREF_DISASSOC); + + } + + NB_GET_LOCK (&Address->Lock, &LockHandle1); + } + + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + + // + // Abort all pending send datagrams. + // + // BUGBUG: Also make them cancellable. + // + + InitializeListHead (&SendDatagramList); + + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + p = Device->WaitingDatagrams.Flink; + + while (p != &Device->WaitingDatagrams) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + p = p->Flink; + + if (Reserved->u.SR_DG.AddressFile == AddressFile) { + + RemoveEntryList (&Reserved->WaitLinkage); + InsertTailList (&SendDatagramList, &Reserved->WaitLinkage); + + } + + } + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + for (p = SendDatagramList.Flink; p != &SendDatagramList; ) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + p = p->Flink; + + DatagramRequest = Reserved->u.SR_DG.DatagramRequest; + + NB_DEBUG2 (DATAGRAM, ("Aborting datagram %lx on %lx\n", DatagramRequest, AddressFile)); + + REQUEST_STATUS(DatagramRequest) = STATUS_SUCCESS; + + NbiCompleteRequest(DatagramRequest); + NbiFreeRequest (Device, DatagramRequest); + + NbiDereferenceAddressFile (AddressFile, AFREF_SEND_DGRAM); + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + } + + + // + // Abort all pending receive datagrams. + // + + InitializeListHead( &DatagramQ ); + + NB_GET_CANCEL_LOCK(&CancelLH); + NB_GET_LOCK (&Address->Lock, &LockHandle1); + + while (!IsListEmpty(&AddressFile->ReceiveDatagramQueue)) { + + p = RemoveHeadList (&AddressFile->ReceiveDatagramQueue); + Request = LIST_ENTRY_TO_REQUEST (p); + + // Insert it on a private Q, so it can be completed later. + InsertTailList( &DatagramQ, p); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_NETWORK_NAME_DELETED; + + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + + + + NbiDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + } + + NB_FREE_LOCK (&Address->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK(CancelLH); + + for( p = DatagramQ.Flink; p != &DatagramQ; ) { + Request = LIST_ENTRY_TO_REQUEST ( p ); + + p = p->Flink; + + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + + } + + + return STATUS_SUCCESS; + +} /* NbiStopAddressFile */ + + +NTSTATUS +NbiCloseAddressFile( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to close the addressfile pointed to by a file + object. If there is any activity to be run down, we will run it down + before we terminate the addressfile. We remove every connection and + datagram associated with this addressfile from the address database + and terminate their activity. Then, if there are no other outstanding + addressfiles open on this address, the address will go away. + +Arguments: + + Request - the close request. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the + request does not point to a real address. + +--*/ + +{ + PADDRESS Address; + PADDRESS_FILE AddressFile; + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + AddressFile->CloseRequest = Request; + + // + // We assume that addressFile has already been verified + // at this point. + // + + Address = AddressFile->Address; + CTEAssert (Address); + + // + // Remove us from the access info for this address. + // + + ExAcquireResourceExclusive (&Device->AddressResource, TRUE); +#ifdef ISN_NT + IoRemoveShareAccess (AddressFile->FileObject, &Address->u.ShareAccess); +#endif + ExReleaseResource (&Device->AddressResource); + + NbiStopAddressFile (AddressFile, Address); + NbiDereferenceAddressFile (AddressFile, AFREF_CREATE); + + return STATUS_PENDING; + +} /* NbiCloseAddressFile */ + +#if defined(_PNP_POWER) + + +PADAPTER_ADDRESS +NbiCreateAdapterAddress( + IN PCHAR AdapterMacAddress + ) + +/*++ + +Routine Description: + + This routine creates an adapter address sttuctures which stores + the netbios name of an adapter. the netbios name has 12 0's + followed by the mac address of the adapter. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + AdapterMacAddress - pointer to the adapter mac address given to us + by IPX. + +Return Value: + + The newly created address, or NULL if none can be allocated. + THIS ROUTINE MUST BE CALLED WITH THE DEVICE LOCK HELD. + +--*/ + +{ + PADAPTER_ADDRESS AdapterAddress; + CTELockHandle LockHandle; + PDEVICE Device = NbiDevice; + + AdapterAddress = (PADAPTER_ADDRESS)NbiAllocateMemory (sizeof(ADAPTER_ADDRESS), MEMORY_ADAPTER_ADDRESS, "Adapter Address"); + if (AdapterAddress == NULL) { + NB_DEBUG (ADDRESS, ("Create Adapter Address %<2.2x><2.2x><2.2x><2.2x><2.2x><2.2x> failed\n", + AdapterMacAddress[0], + AdapterMacAddress[1], + AdapterMacAddress[2], + AdapterMacAddress[3], + AdapterMacAddress[4], + AdapterMacAddress[5] + )); + return NULL; + } + + AdapterAddress->Type = NB_ADAPTER_ADDRESS_SIGNATURE; + AdapterAddress->Size = sizeof (ADDRESS); + + RtlZeroMemory(AdapterAddress->NetbiosName, 10); + RtlCopyMemory(&AdapterAddress->NetbiosName[10], AdapterMacAddress, 6); + + + InsertTailList (&Device->AdapterAddressDatabase, &AdapterAddress->Linkage); + ++Device->AddressCounts[AdapterAddress->NetbiosName[0]]; + + return AdapterAddress; + +} /* NbiCreateAdapterAddress */ + + +NTSTATUS +NbiDestroyAdapterAddress( + IN PADAPTER_ADDRESS AdapterAddress OPTIONAL, + IN PCHAR AdapterMacAddress OPTIONAL + ) + +/*++ + +Routine Description: + + This routine destroys the adapter address structure and removes it + from the list. + +Arguments: + + AdapterAddress - Pointer to an adapter address structure to be destroyed + NULL if AdapterMacAddress is given. + + AdapterMacAddress - Mac Address of the adapter which just got deleted. so find + the corresponding adapter address structure and remove it. + NULL if AdapterAddress is supplied. + +Return Value: + + STATUS_SUCCESS or STATUS_UNSUCCESSFUL if address not found. + + THIS ROUTINE ASSUMES THE THE DEVICE IS LOCK IS HELD BY THE CALLER + +--*/ + +{ + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; + UCHAR NetbiosName[NB_NETBIOS_NAME_SIZE]; + + + // + + CTEAssert( AdapterAddress || AdapterMacAddress ); + if ( !AdapterAddress ) { + RtlZeroMemory( NetbiosName, 10); + RtlCopyMemory( &NetbiosName[10], AdapterMacAddress, 6 ); + + AdapterAddress = NbiFindAdapterAddress( NetbiosName, LOCK_ACQUIRED ); + + if ( !AdapterAddress ) { + return STATUS_UNSUCCESSFUL; + } + } + + NB_DEBUG2 (ADDRESS, ("Destroy Adapter address %lx <%.16s>\n", AdapterAddress,AdapterAddress->NetbiosName)); + RemoveEntryList (&AdapterAddress->Linkage); + ++Device->AddressCounts[AdapterAddress->NetbiosName[0]]; + + NbiFreeMemory (AdapterAddress, sizeof(ADAPTER_ADDRESS), MEMORY_ADAPTER_ADDRESS, "AdapterAddress"); + + return STATUS_SUCCESS; +} /* NbiDestroyAdapterAddress */ + + +PADAPTER_ADDRESS +NbiFindAdapterAddress( + IN PCHAR NetbiosName, + IN BOOLEAN LockHeld + ) + +/*++ + +Routine Description: + + This routine finds an adapter address ( netbios name ) for the given + AdapterMacAddress and returns a pointer to it. Note that no reference + is done on this address, so if this routine is called without the device + lock, the caller must not use this pointer directly. + +Arguments: + + NetbiosName - NetbiosName to be found. + + LockHeld - is device lock already held or not. + +Return Value: + + Pointer to the adapter address if found, NULL otherwise. + +--*/ + +{ + + PLIST_ENTRY p; + CTELockHandle LockHandle; + PADAPTER_ADDRESS AdapterAddress; + PDEVICE Device = NbiDevice; + + + if ( !LockHeld ) { + NB_GET_LOCK( &Device->Lock, &LockHandle ); + } + for ( p = Device->AdapterAddressDatabase.Flink; + p != &Device->AdapterAddressDatabase; + p = p->Flink ) { + + AdapterAddress = CONTAINING_RECORD( p, ADAPTER_ADDRESS, Linkage ); + if ( RtlEqualMemory( + NetbiosName, + AdapterAddress->NetbiosName, + NB_NETBIOS_NAME_SIZE ) ) { + break; + } + } + + + if ( !LockHeld ) { + NB_FREE_LOCK( &Device->Lock, LockHandle ); + } + + if ( p == &Device->AdapterAddressDatabase ) { + return NULL; + } else { + return AdapterAddress; + } + +} /* NbiFindAdapterAddress */ + +#endif _PNP_POWER diff --git a/private/ntos/tdi/isnp/nb/autodial.c b/private/ntos/tdi/isnp/nb/autodial.c new file mode 100644 index 000000000..ec56e2351 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/autodial.c @@ -0,0 +1,526 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + autodial.c + +Abstract: + + NT specific routines for interfacing with the + RAS AutoDial driver (rasacd.sys). + +Author: + + Anthony Discolo (adiscolo) Aug 30, 1995 + +Revision History: + + Who When What + -------- -------- ---------------------------------------------- + adiscolo 08-30-95 created + +Notes: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef RASAUTODIAL + +#include <acd.h> +#include <acdapi.h> + +// +// Global variables +// +BOOLEAN fAcdLoadedG; +ACD_DRIVER AcdDriverG; +ULONG ulDriverIdG = 'Nbi '; + + + +VOID +NbiRetryTdiConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ) + +/*++ + +Routine Description: + + This routine is called indirectly by the automatic + connection driver to continue the connection process + after an automatic connection has been made. + +Arguments: + + fSuccess - TRUE if the connection attempt was successful. + + pArgs - a pointer to the argument vector + +Return Value: + + None. + +--*/ + +{ + NTSTATUS status; + PDEVICE pDevice = pArgs[0]; + PCONNECTION pConnection = pArgs[1]; + PREQUEST pRequest = pArgs[2]; + CTELockHandle ConnectionLH, DeviceLH; + CTELockHandle CancelLH; + BOOLEAN bLockFreed = FALSE; + + // + // Check that the connection is valid. This references + // the connection. + // +#if notdef // DBG + DbgPrint("NbiRetryTdiConnect: fSuccess=%d, pConnection=0x%x\n", fSuccess, pConnection); +#endif + + status = NbiVerifyConnection(pConnection); + if (!NT_SUCCESS(status)) { + DbgPrint( + "NbiRetryTdiConnect: NbiVerifyConnection failed on connection 0x%x (status=0x%x)\n", + pConnection, + status); + return; + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_GET_LOCK (&pConnection->Lock, &ConnectionLH); + NB_GET_LOCK (&pDevice->Lock, &DeviceLH); + +#if notdef // DBG + DbgPrint( + "NbiRetryTdiConnect: AddressFile=0x%x, DisassociatePending=0x%x, ClosePending=0x%x\n", + pConnection->AddressFile, + pConnection->DisassociatePending, + pConnection->ClosePending); +#endif + + if ((pConnection->AddressFile != NULL) && + (pConnection->AddressFile != (PVOID)-1) && + (pConnection->DisassociatePending == NULL) && + (pConnection->ClosePending == NULL)) + { + NbiReferenceConnectionLock(pConnection, CREF_CONNECT); + // + // Clear the AUTOCONNECTING flag since we + // done with the automatic connection attempt. + // Set the AUTOCONNECTED flag to prevent us + // from attempting an automatic connection + // for this connection again. + // + pConnection->Flags &= ~CONNECTION_FLAGS_AUTOCONNECTING; + pConnection->Flags |= CONNECTION_FLAGS_AUTOCONNECTED; + + pConnection->State = CONNECTION_STATE_CONNECTING; + pConnection->Retries = pDevice->ConnectionCount; + status = NbiTdiConnectFindName( + pDevice, + pRequest, + pConnection, + CancelLH, + ConnectionLH, + DeviceLH, + &bLockFreed); + } + else { + DbgPrint("NbiRetryTdiConnect: Connect on invalid connection 0x%x\n", pConnection); + + pConnection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + NB_FREE_LOCK (&pDevice->Lock, DeviceLH); + status = STATUS_INVALID_CONNECTION; + } + if (!bLockFreed) { + NB_FREE_LOCK (&pConnection->Lock, ConnectionLH); + NB_FREE_CANCEL_LOCK(CancelLH); + } + // + // Complete the irp if necessary. + // + if (status != STATUS_PENDING) { + REQUEST_INFORMATION(pRequest) = 0; + REQUEST_STATUS(pRequest) = status; + + NbiCompleteRequest(pRequest); + NbiFreeRequest(pDevice, pRequest); + } + NbiDereferenceConnection(pConnection, CREF_VERIFY); +} /* NbiRetryTdiConnect */ + + + +BOOLEAN +NbiCancelAutoDialRequest( + IN PVOID pArg, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN USHORT nArgs, + IN PVOID *pArgs + ) +{ +#if notdef // DBG + DbgPrint("NbiCancelAutodialRequest: pArg=0x%x\n", pArg); +#endif + if (nArgs != 2) + return FALSE; + + return (pArgs[1] == pArg); +} // NbiCancelAutoDialRequest + + + +BOOLEAN +NbiCancelTdiConnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest, + IN PCONNECTION pConnection + ) + +/*++ + +DESCRIPTION + This routine is called by the I/O system to cancel a connection + when we are attempting to restore an automatic connection. + +ARGUMENTS + pDevice: a pointer to the device object for this driver + + pRequest: a pointer to the irp to be cancelled + + pConnection: a pointer to the connnection to be cancelled + +RETURN VALUE + TRUE if the request was canceled; FALSE otherwise. + +--*/ + +{ + ACD_ADDR addr; + + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->RemoteName, 16); +#ifdef notdef // DBG + DbgPrint( + "NbiCancelTdiConnect: pIrp=0x%x, RemoteName=%-15.15s, pConnection=0x%x\n", + pRequest, + addr.cNetbios, + pConnection); +#endif + // + // Cancel the autodial request. + // + return (*AcdDriverG.lpfnCancelConnection)( + ulDriverIdG, + &addr, + NbiCancelAutoDialRequest, + pConnection); +} // NbiCancelTdiConnect + + + +BOOLEAN +NbiAttemptAutoDial( + IN PDEVICE pDevice, + IN PCONNECTION pConnection, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN PREQUEST pRequest + ) + +/*++ + +Routine Description: + + Call the automatic connection driver to attempt an + automatic connection. + +Arguments: + + pDevice - a pointer to the DEVICE structure for this connection + + pConnection - a pointer to the CONNECTION block for this connection + + ulFlags - connection flags to pass to the automatic + connection driver + + pProc - a callback procedure when the automatic connection completes + + pRequest - a pointer to the request irp + +Return Value: + + TRUE if the automatic connection was started successfully, + FALSE otherwise. + +--*/ + +{ + ACD_ADDR addr; + PVOID pArgs[3]; + BOOLEAN bSuccess; + + // + // If we've already attempted an automatic connection + // on this connection, don't try it again. + // + if (pConnection->Flags & CONNECTION_FLAGS_AUTOCONNECTED) + return FALSE; + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->RemoteName, 16); +#ifdef notdef // DBG + DbgPrint("NbiAttemptAutoDial: szAddr=%15.15s\n", addr.cNetbios); +#endif + // + // Attempt to start the connection. + // NbiRetryTdiConnect() will be called + // when the connection process has completed. + // + pArgs[0] = pDevice; + pArgs[1] = pConnection; + pArgs[2] = pRequest; + bSuccess = (*AcdDriverG.lpfnStartConnection)( + ulDriverIdG, + &addr, + ulFlags, + pProc, + 3, + pArgs); + if (bSuccess) { + // + // Set the AUTOCONNECTING flag so we know + // to also cancel the connection in the + // automatic connection driver if this + // request gets canceled. + // + pConnection->Flags |= CONNECTION_FLAGS_AUTOCONNECTING; + } +} // NbiAttemptAutoDial + + + +VOID +NbiNoteNewConnection( + IN PCONNECTION pConnection + ) +{ + NTSTATUS status; + ACD_ADDR addr; + ACD_ADAPTER adapter; + ULONG i; + TDI_ADDRESS_IPX tdiIpxAddress; + + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->RemoteName, 16); + // + // Determine the mac address of the adapter + // over which the connection has been made. + // + status = (pConnection->Device->Bind.QueryHandler)( + IPX_QUERY_IPX_ADDRESS, +#if defined(_PNP_POWER) + &pConnection->LocalTarget.NicHandle, +#else + pConnection->LocalTarget.NicId, +#endif _PNP_POWER + &tdiIpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); + if (status != STATUS_SUCCESS) { +#if notdef // DBG + DbgPrint("NbiNoteNewConnection: QueryHandler(IPX_QUERY_IPX_ADDRESS) failed (status=0x%x)\n", status); + return; +#endif + } + // + // Copy the source mac address to identify + // the adapter. + // + adapter.fType = ACD_ADAPTER_MAC; + for (i = 0; i < 6; i++) + adapter.cMac[i] = tdiIpxAddress.NodeAddress[i]; +#if notdef // DBG + DbgPrint( + "NbiNoteNewConnection: address=%-15.15s, remote mac=%02x:%02x:%02x:%02x:%02x:%02x\n", + addr.cNetbios, + adapter.cMac[0], + adapter.cMac[1], + adapter.cMac[2], + adapter.cMac[3], + adapter.cMac[4], + adapter.cMac[5]); +#endif + // + // Simply notify the automatic connection driver + // that a successful connection has been made. + // + (*AcdDriverG.lpfnNewConnection)( + &addr, + &adapter); +} // NbiNoteNewConnection + + + +VOID +NbiAcdBind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Initialize our part of the ACD_DRIVER + // structure. + // + KeInitializeSpinLock(&AcdDriverG.SpinLock); + AcdDriverG.ulDriverId = ulDriverIdG; + AcdDriverG.fEnabled = FALSE; + // + // Build a request to get the automatic + // connection driver entry points. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_BIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + fAcdLoadedG = (status == STATUS_SUCCESS); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // NbiAcdBind + + + +VOID +NbiAcdUnbind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Don't bother to unbind if we + // didn't successfully bind in the + // first place. + // + if (!fAcdLoadedG) + return; + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Build a request to unbind from + // the automatic connection driver. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_UNBIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // NbiAcdUnbind + +#endif // RASAUTODIAL diff --git a/private/ntos/tdi/isnp/nb/bind.c b/private/ntos/tdi/isnp/nb/bind.c new file mode 100644 index 000000000..7dc20d0d5 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/bind.c @@ -0,0 +1,593 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + driver.c + +Abstract: + + This module contains the DriverEntry and other initialization + code for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NbiBind) +#endif + +#if defined(_PNP_POWER) +// +// local functions. +// +VOID +NbiPnPNotification( + IN IPX_PNP_OPCODE OpCode, + IN PVOID PnPData + ); +#endif _PNP_POWER + + +NTSTATUS +NbiBind( + IN PDEVICE Device, + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine binds the Netbios module of ISN to the IPX + module, which provides the NDIS binding services. + +Arguments: + + Device - Pointer to the Netbios device. + + Config - Pointer to the configuration information. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; +/* union { + IPX_INTERNAL_BIND_INPUT Input; + IPX_INTERNAL_BIND_OUTPUT Output; + } Bind; +*/ + InitializeObjectAttributes( + &ObjectAttributes, + &Config->BindName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = ZwCreateFile( + &Device->BindHandle, + SYNCHRONIZE | GENERIC_READ, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0L); + + if (!NT_SUCCESS(Status)) { + + NB_DEBUG (BIND, ("Could not open IPX (%ws) %lx\n", + Config->BindName.Buffer, Status)); + NbiWriteGeneralErrorLog( + Device, + EVENT_TRANSPORT_ADAPTER_NOT_FOUND, + 1, + Status, + Config->BindName.Buffer, + 0, + NULL); + return Status; + } + + // + // Fill in our bind data. + // + +#if defined(_PNP_POWER) + Device->BindInput.Version = ISN_VERSION; +#else + Device->BindInput.Version = 1; +#endif _PNP_POWER + Device->BindInput.Identifier = IDENTIFIER_NB; + Device->BindInput.BroadcastEnable = TRUE; + Device->BindInput.LookaheadRequired = 192; + Device->BindInput.ProtocolOptions = 0; + Device->BindInput.ReceiveHandler = NbiReceive; + Device->BindInput.ReceiveCompleteHandler = NbiReceiveComplete; + Device->BindInput.StatusHandler = NbiStatus; + Device->BindInput.SendCompleteHandler = NbiSendComplete; + Device->BindInput.TransferDataCompleteHandler = NbiTransferDataComplete; + Device->BindInput.FindRouteCompleteHandler = NbiFindRouteComplete; + Device->BindInput.LineUpHandler = NbiLineUp; + Device->BindInput.LineDownHandler = NbiLineDown; + Device->BindInput.ScheduleRouteHandler = NULL; +#if defined(_PNP_POWER) + Device->BindInput.PnPHandler = NbiPnPNotification; +#endif _PNP_POWER + + + Status = ZwDeviceIoControlFile( + Device->BindHandle, // HANDLE to File + NULL, // HANDLE to Event + NULL, // ApcRoutine + NULL, // ApcContext + &IoStatusBlock, // IO_STATUS_BLOCK + IOCTL_IPX_INTERNAL_BIND, // IoControlCode + &Device->BindInput, // Input Buffer + sizeof(Device->BindInput), // Input Buffer Length + &Device->Bind, // OutputBuffer + sizeof(Device->Bind)); // OutputBufferLength + + // + // We open synchronous, so this shouldn't happen. + // + + CTEAssert (Status != STATUS_PENDING); + + // + // Save the bind data. + // + + if (Status == STATUS_SUCCESS) { + + NB_DEBUG2 (BIND, ("Successfully bound to IPX (%ws)\n", + Config->BindName.Buffer)); +// RtlCopyMemory (&Device->Bind, &Bind.Output, sizeof(IPX_INTERNAL_BIND_OUTPUT)); + +#if !defined(_PNP_POWER) + RtlZeroMemory (Device->ReservedNetbiosName, 16); + RtlCopyMemory (&Device->ReservedNetbiosName[10], Device->Bind.Node, 6); + + Status = (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_MAXIMUM_NIC_ID, + (USHORT)0, + &Device->MaximumNicId, + sizeof(Device->MaximumNicId), + NULL); + CTEAssert (Status == STATUS_SUCCESS); +#endif !_PNP_POWER + + } else { + + NB_DEBUG (BIND, ("Could not bind to IPX (%ws) %lx\n", + Config->BindName.Buffer, Status)); + NbiWriteGeneralErrorLog( + Device, + EVENT_TRANSPORT_BINDING_FAILED, + 1, + Status, + Config->BindName.Buffer, + 0, + NULL); + ZwClose(Device->BindHandle); + } + + return Status; + +} /* NbiBind */ + + +VOID +NbiUnbind( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This function closes the binding between the Netbios over + IPX module and the IPX module previously established by + NbiBind. + +Arguments: + + Device - The netbios device object. + +Return Value: + + None. + +--*/ + +{ + ZwClose (Device->BindHandle); + +} /* NbiUnbind */ + + +VOID +NbiStatus( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength + ) + +/*++ + +Routine Description: + + This function receives a status indication from IPX, + corresponding to a status indication from an underlying + NDIS driver. + +Arguments: + + NicId - The NIC ID of the underlying adapter. + + GeneralStatus - The general status code. + + StatusBuffer - The status buffer. + + StatusBufferLength - The length of the status buffer. + +Return Value: + + None. + +--*/ + +{ + +} /* NbiStatus */ + + +VOID +NbiLineUp( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData + ) + + +/*++ + +Routine Description: + + This function receives line up indications from IPX, + indicating that the specified adapter is now up with + the characteristics shown. + +Arguments: + + NicId - The NIC ID of the underlying adapter. + + LineInfo - Information about the adapter's medium. + + DeviceType - The type of the adapter. + + ConfigurationData - IPX-specific configuration data. + +Return Value: + + None. + +--*/ + +{ + PIPXCP_CONFIGURATION Configuration = (PIPXCP_CONFIGURATION)ConfigurationData; + + // + // Update queries have NULL as the ConfigurationData. These + // only indicate changes in LineInfo. BUGBUG Ignore these + // for the moment. + // + + if (Configuration == NULL) { + return; + } + +#if !defined(_PNP_POWER) + // + // Since Netbios outgoing queries only go out on network 1, + // we ignore this (BUGBUG for the moment) unless that is + // the NIC it is on. + // + + if (NicId == 1) { + + RtlCopyMemory(NbiDevice->ConnectionlessHeader.SourceNetwork, Configuration->Network, 4); + RtlCopyMemory(NbiDevice->ConnectionlessHeader.SourceNode, Configuration->LocalNode, 6); + + } +#endif !_PNP_POWER +} /* NbiLineUp */ + + +VOID +NbiLineDown( + IN USHORT NicId + ) + + +/*++ + +Routine Description: + + This function receives line down indications from IPX, + indicating that the specified adapter is no longer + up. + +Arguments: + + NicId - The NIC ID of the underlying adapter. + +Return Value: + + None. + +--*/ + +{ + +} /* NbiLineDown */ + +#if defined(_PNP_POWER) + +VOID +NbiPnPNotification( + IN IPX_PNP_OPCODE OpCode, + IN PVOID PnPData + ) + +/*++ + +Routine Description: + + This function receives the notification about PnP events from IPX. + +Arguments: + + OpCode - Type of the PnP event + + PnPData - Data associated with this event. + +Return Value: + + None. + +--*/ + +{ + + PDEVICE Device = NbiDevice; + USHORT MaximumNicId = 0; + CTELockHandle LockHandle; + UCHAR PrevReservedName[NB_NETBIOS_NAME_SIZE]; + UNICODE_STRING UnicodeDeviceName; + + + NB_DEBUG2( DEVICE, ("Received a pnp notification, opcode %d\n",OpCode )); + + switch( OpCode ) { + case IPX_PNP_ADD_DEVICE : { + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + BOOLEAN ReallocReceiveBuffers = FALSE; + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + + if ( PnPInfo->NewReservedAddress ) { + + *(UNALIGNED ULONG *)Device->Bind.Network = PnPInfo->NetworkAddress; + RtlCopyMemory( Device->Bind.Node, PnPInfo->NodeAddress, 6); + +// RtlZeroMemory(Device->ReservedNetbiosName, NB_NETBIOS_NAME_SIZE); +// RtlCopyMemory(&Device->ReservedNetbiosName[10], Device->Bind.Node, 6); + + *(UNALIGNED ULONG *)Device->ConnectionlessHeader.SourceNetwork = *(UNALIGNED ULONG *)Device->Bind.Network; + RtlCopyMemory(Device->ConnectionlessHeader.SourceNode, Device->Bind.Node, 6); + } + + if ( PnPInfo->FirstORLastDevice ) { + CTEAssert( PnPInfo->NewReservedAddress ); + CTEAssert( Device->State != DEVICE_STATE_OPEN ); + + + // + // we must do this while we still have the device lock. + // + if ( !Device->LongTimerRunning ) { + Device->LongTimerRunning = TRUE; + NbiReferenceDevice (Device, DREF_LONG_TIMER); + + CTEStartTimer( + &Device->LongTimer, + LONG_TIMER_DELTA, + NbiLongTimeout, + (PVOID)Device); + + } + + Device->State = DEVICE_STATE_OPEN; + + CTEAssert( !Device->MaximumNicId ); + + Device->Bind.LineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + Device->Bind.LineInfo.MaximumPacketSize = PnPInfo->LineInfo.MaximumSendSize; + ReallocReceiveBuffers = TRUE; + } else { + if ( PnPInfo->LineInfo.MaximumPacketSize > Device->CurMaxReceiveBufferSize ) { + ReallocReceiveBuffers = TRUE; + } + // + // MaxSendSize could become smaller. + // + Device->Bind.LineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + } + + Device->MaximumNicId++; + + + // + // + NbiCreateAdapterAddress( PnPInfo->NodeAddress ); + + // + // And finally remove all the failed cache entries since we might + // find those routes using this new adapter + // + FlushFailedNetbiosCacheEntries(Device->NameCache); + + NB_FREE_LOCK( &Device->Lock, LockHandle ); + + + if ( ReallocReceiveBuffers ) { + PWORK_QUEUE_ITEM WorkItem; + + WorkItem = NbiAllocateMemory( sizeof(WORK_QUEUE_ITEM), MEMORY_WORK_ITEM, "Alloc Rcv Buffer work item"); + + if ( WorkItem ) { + ExInitializeWorkItem( WorkItem, NbiReAllocateReceiveBufferPool, (PVOID) WorkItem ); + ExQueueWorkItem( WorkItem, DelayedWorkQueue ); + } else { + NB_DEBUG( DEVICE, ("Cannt schdule work item to realloc receive buffer pool\n")); + } + } + // + // Notify the TDI clients about the device creation + // + if ( PnPInfo->FirstORLastDevice ) { + UnicodeDeviceName.Buffer = Device->DeviceName; + UnicodeDeviceName.MaximumLength = Device->DeviceNameLength; + UnicodeDeviceName.Length = Device->DeviceNameLength - sizeof(WCHAR); + + if ( !NT_SUCCESS( TdiRegisterDeviceObject( + &UnicodeDeviceName, + &Device->TdiRegistrationHandle ) )) { + NB_DEBUG( DEVICE, ("Failed to register nwlnknb with TDI\n")); + } + } + + break; + } + case IPX_PNP_DELETE_DEVICE : { + + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + USHORT i,j,NetworksRemoved; + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + + CTEAssert( Device->MaximumNicId ); + Device->MaximumNicId--; + + if ( PnPInfo->FirstORLastDevice ) { + Device->State = DEVICE_STATE_LOADED; + Device->MaximumNicId = 0; + + } + + + // + // MaximumSendSize could change if the card with the smallest send size just + // got removed. MaximumPacketSize could only become smaller and we ignore that + // since we dont need to(want to) realloc ReceiveBuffers. + // + + Device->Bind.LineInfo.MaximumSendSize = PnPInfo->LineInfo.MaximumSendSize; + + // + // Flush all the cache entries that are using this NicId in the local + // target. + // + RemoveInvalidRoutesFromNetbiosCacheTable( Device->NameCache, &PnPInfo->NicHandle ); + + NbiDestroyAdapterAddress( NULL, PnPInfo->NodeAddress ); + + NB_FREE_LOCK( &Device->Lock, LockHandle ); + +/* // + // Now mark the previous reserved name in conflict if it has + // been registered by any of our client + // + if ( Address = NbiFindAddress( Device, PrevReservedName ) ) { + NB_GET_LOCK( &Address->Lock, &LockHandle ); + Address->Flags |= ADDRESS_FLAGS_CONFLICT; + NB_FREE_LOCK( &Address->Lock, LockHandle ); + + NB_DEBUG( ADDRESS, ("Reserved Address %lx<%.16s> is marked CONFLICT\n",Address,Address->NetbiosAddress.NetbiosName)); + // + // nbifindaddress added a reference, so deref + // + NbiDereferenceAddress( Address, AREF_FIND ); + } +*/ + + // + // inform tdi clients about the device deletion + // + if ( PnPInfo->FirstORLastDevice ) { + if ( !NT_SUCCESS( TdiDeregisterDeviceObject( + Device->TdiRegistrationHandle ) )) { + NB_DEBUG( DEVICE, ("Failed to Deregister nwlnknb with TDI\n")); + } + } + + break; + } + case IPX_PNP_ADDRESS_CHANGE: { + IPX_PNP_INFO UNALIGNED *PnPInfo = (IPX_PNP_INFO UNALIGNED *)PnPData; + PADDRESS Address; + BOOLEAN ReservedNameClosing = FALSE; + + CTEAssert( PnPInfo->NewReservedAddress ); + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + *(UNALIGNED ULONG *)Device->Bind.Network = PnPInfo->NetworkAddress; + RtlCopyMemory( Device->Bind.Node, PnPInfo->NodeAddress, 6); + + *(UNALIGNED ULONG *)Device->ConnectionlessHeader.SourceNetwork = *(UNALIGNED ULONG *)Device->Bind.Network; + RtlCopyMemory(Device->ConnectionlessHeader.SourceNode, Device->Bind.Node, 6); + + NB_FREE_LOCK( &Device->Lock, LockHandle ); + + + break; + } + case IPX_PNP_TRANSLATE_DEVICE: + break; + case IPX_PNP_TRANSLATE_ADDRESS: + break; + default: + CTEAssert( FALSE ); + } +} /* NbiPnPNotification */ + +#endif _PNP_POWER diff --git a/private/ntos/tdi/isnp/nb/cache.c b/private/ntos/tdi/isnp/nb/cache.c new file mode 100644 index 000000000..cbd27ad67 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/cache.c @@ -0,0 +1,2746 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + cache.c + +Abstract: + + This module contains the name cache routines for the Netbios + module of the ISN transport. + +Author: + + Adam Barr (adamba) 20-December-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef RASAUTODIAL +#include <acd.h> +#include <acdapi.h> + +extern BOOLEAN fAcdLoadedG; +extern ACD_DRIVER AcdDriverG; + +BOOLEAN +NbiAttemptAutoDial( + IN PDEVICE pDevice, + IN PCONNECTION pConnection, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN PREQUEST pRequest + ); + +VOID +NbiRetryTdiConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ); +#endif // RASAUTODIAL + +// +// BUGBUG: We should change to monitor add name packets better, +// so if we get an add for a different place we attempt to determine +// if it is real or bogus and update if possible. +// + + +NTSTATUS +CacheFindName( + IN PDEVICE Device, + IN FIND_NAME_TYPE Type, + IN PUCHAR RemoteName OPTIONAL, + OUT PNETBIOS_CACHE * CacheName +) + +/*++ + +Routine Description: + + This routine looks up a particular remote name in the + Netbios name cache. If it cannot find it, a find name + request is queued up. + + THIS REQUEST IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The netbios device. + + Type - Defines the type. The effect this has is: + FindNameConnect - On connects we will ignore an existing + cache entry if it got no response before. + FindNameNetbiosFindName - For these we ignore an existing + cache entry if it is for a group name -- this is + because the find name wants the address of every + machine, not just the network list. + FindNameOther - Normal handling is done. + + RemoteName - The name to be discovered -- will be NULL if it + is the broadcast address. + + CacheName - Returns the cache entry that was discovered. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PNETBIOS_CACHE FoundCacheName; + PNB_SEND_RESERVED Reserved; + PUCHAR RealRemoteName; // RemoteName or NetbiosBroadcastName + + // + // First scan the netbios name cache to see if we know + // about this remote. + // + + if (RemoteName) { + RealRemoteName = RemoteName; + } else { + RealRemoteName = NetbiosBroadcastName; + } + + if ( FindInNetbiosCacheTable ( Device->NameCache, + RealRemoteName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + // + // If this is a netbios find name, we only can use unique + // names in the cache; for the group ones we need to requery + // because the cache only lists networks, not individual machines. + // For connect requests, if we find an empty cache entry we + // remove it and requery. + // + + if ( FoundCacheName->Unique || (Type != FindNameNetbiosFindName) ) { + + if (FoundCacheName->NetworksUsed > 0) { + + *CacheName = FoundCacheName; + NB_DEBUG2 (CACHE, ("Found cache name <%.16s>\n", RemoteName ? RemoteName : "<broadcast>")); + return STATUS_SUCCESS; + + } else { + + if (Type != FindNameConnect) { + + if (FoundCacheName->FailedOnDownWan) { + NB_DEBUG2 (CACHE, ("Found cache name, but down wan <%.16s>\n", RemoteName ? RemoteName : "<broadcast>")); + return STATUS_DEVICE_DOES_NOT_EXIST; + } else { + NB_DEBUG2 (CACHE, ("Found cache name, but no nets <%.16s>\n", RemoteName ? RemoteName : "<broadcast>")); + return STATUS_BAD_NETWORK_PATH; + } + + } else { + + // + // This is a connect and the current cache entry + // has zero names; delete it. + // + + RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName ); + CTEAssert (FoundCacheName->ReferenceCount == 1); + if (--FoundCacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free unneeded empty cache entry %lx\n", FoundCacheName)); + NbiFreeMemory( + FoundCacheName, + sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free due to replacement"); + } + + } + } + } + } + + + // + // There was no suitable cache entry for this network, first see + // if there is one pending. + // + + for (p = Device->WaitingFindNames.Flink; + p != &Device->WaitingFindNames; + p = p->Flink) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + // + // For this purpose we ignore a packet if a route + // has been found and it was for a unique name. This + // is because the cache information has already been + // inserted for this name. Otherwise if the name has + // since been deleted from the cache, the request + // that is looking for this name will starve because + // FindNameTimeout will just destroy the packet. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + continue; + } + + if (RtlEqualMemory( + Reserved->u.SR_FN.NetbiosName, + RealRemoteName, 16)) { + + NB_DEBUG2 (CACHE, ("Cache name already pending <%.16s>\n", RemoteName ? RemoteName : "<broadcast>")); + + // + // There is already one pending. If it is for a group + // name and this is a netbios find name, we make sure + // the retry count is such that at least one more + // query will be sent, so the netbios find name + // buffer can be filled with the responses from this. + // + + if ((Type == FindNameNetbiosFindName) && + (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) && + (Reserved->u.SR_FN.RetryCount == Device->BroadcastCount)) { + + --Reserved->u.SR_FN.RetryCount; + } + + return STATUS_PENDING; + } + } + + s = NbiPopSendPacket(Device, TRUE); + + if (s == NULL) { + NB_DEBUG (CACHE, ("Couldn't get packet to find <%.16s>\n", RemoteName ? RemoteName : "<broadcast>")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + + // + // We have the packet, fill it in for this request. + // + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = FALSE; + Reserved->Type = SEND_TYPE_FIND_NAME; + RtlCopyMemory (Reserved->u.SR_FN.NetbiosName, RealRemoteName, 16); + Reserved->u.SR_FN.StatusAndSentOnUpLine = FNStatusNoResponse; // SentOnUpLine is FALSE + Reserved->u.SR_FN.RetryCount = 0; + Reserved->u.SR_FN.NewCache = NULL; + Reserved->u.SR_FN.SendTime = Device->FindNameTime; +#if !defined(_PNP_POWER) + Reserved->u.SR_FN.CurrentNicId = 1; + + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_MAX_TYPE_20_NIC_ID, + (USHORT)0, + &Reserved->u.SR_FN.MaximumNicId, + sizeof(USHORT), + NULL); + + if (Reserved->u.SR_FN.MaximumNicId == 0) { + Reserved->u.SR_FN.MaximumNicId = 1; // code assumes at least one + } +#endif !_PNP_POWER + NB_DEBUG2 (CACHE, ("Queued FIND_NAME %lx for <%.16s>\n", + Reserved, RemoteName ? RemoteName : "<broadcast>")); + + + InsertHeadList( + &Device->WaitingFindNames, + &Reserved->WaitLinkage); + + ++Device->FindNamePacketCount; + + if (!Device->FindNameTimerActive) { + + Device->FindNameTimerActive = TRUE; + NbiReferenceDevice (Device, DREF_FN_TIMER); + + CTEStartTimer( + &Device->FindNameTimer, + 1, // 1 ms, i.e. expire immediately + FindNameTimeout, + (PVOID)Device); + } + + NbiReferenceDevice (Device, DREF_FIND_NAME); + + return STATUS_PENDING; + +} /* CacheFindName */ + + +VOID +FindNameTimeout( + CTEEvent * Event, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the find name timer expires. + It is called every FIND_NAME_GRANULARITY milliseconds unless there + is nothing to do. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the device pointer. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PLIST_ENTRY p, q; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + PNETBIOS_CACHE FoundCacheName; + NDIS_STATUS NdisStatus; +#if !defined(_PNP_POWER) + static IPX_LOCAL_TARGET BroadcastTarget = { 0, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; +#endif !_PNP_POWER + NB_DEFINE_LOCK_HANDLE (LockHandle) + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + ++Device->FindNameTime; + + if (Device->FindNamePacketCount == 0) { + + NB_DEBUG2 (CACHE, ("FindNameTimeout exiting\n")); + + Device->FindNameTimerActive = FALSE; + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + NbiDereferenceDevice (Device, DREF_FN_TIMER); + + return; + } + + // + // Check what is on the queue; this is set up as a + // loop but in fact it rarely does (under no + // circumstances can we send more than one packet + // each time this function executes). + // +#if defined(_PNP_POWER) + while (TRUE) { + + p = Device->WaitingFindNames.Flink; + if (p == &Device->WaitingFindNames) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + if (Reserved->SendInProgress) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + // + // This was a find name for a unique name which got a + // response but was not freed at the time (because + // SendInProgress was still TRUE) so we free it now. + // + + (VOID)RemoveHeadList (&Device->WaitingFindNames); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + --Device->FindNamePacketCount; + + // + // It is OK to do this with the lock held because + // it won't be the last one (we have the RIP_TIMER ref). + // + + NbiDereferenceDevice (Device, DREF_FIND_NAME); + continue; + } + + if (((SHORT)(Device->FindNameTime - Reserved->u.SR_FN.SendTime)) < 0) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + (VOID)RemoveHeadList (&Device->WaitingFindNames); + + + // + // Increment the counter and see if we have sent + // all the frames we need to (we will age out + // here if we got no response for a unique query, + // or if we are doing a global name or broadcast + // search). We also kill the query right now if + // we have not found anything but down wan lines + // to send it on. + // + + if ((++Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) || + ((Reserved->u.SR_FN.RetryCount > 1) && (!NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved)))) { + +#if DBG + if (Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) { + NB_DEBUG2 (CACHE, ("FindNameTimeout aging out %lx\n", Reserved)); + } else { + NB_DEBUG2 (CACHE, ("FindNameTimeout no active nets %lx\n", Reserved)); + } +#endif + + // + // This packet is stale, clean it up and continue. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) { + + CTEAssert (Reserved->u.SR_FN.NewCache != NULL); + + // + // If this was a group name and we have a new + // cache entry that we have been building for it, + // then insert that in the queue and use it + // to succeed any pending connects. Because + // netbios find name requests can cause cache + // requests for group names to be queued even + // if we already have on in the database, we + // first scan for old ones and remove them. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Reserved->u.SR_FN.NetbiosName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + NB_DEBUG2 (CACHE, ("Found old group cache name <%.16s>\n", FoundCacheName->NetbiosName)); + + RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName ); + + if (--FoundCacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", FoundCacheName)); + NbiFreeMemory( + FoundCacheName, + sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free due to replacement"); + + } + + } + + Reserved->u.SR_FN.NewCache->TimeStamp = Device->CacheTimeStamp; + + InsertInNetbiosCacheTable( + Device->NameCache, + Reserved->u.SR_FN.NewCache); + + // + // Reference it for the moment since CacheHandlePending + // uses it after releasing the lock. CacheHandlePending + // will dereference it. + // + + ++Reserved->u.SR_FN.NewCache->ReferenceCount; + + // + // This call releases the locks + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NetbiosNameFound, + Reserved->u.SR_FN.NewCache + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + CTEAssert (Reserved->u.SR_FN.NewCache == NULL); + + // + // Allocate an empty cache entry to record the + // fact that we could not find this name, unless + // there is already an entry for this name. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Reserved->u.SR_FN.NetbiosName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + + NB_DEBUG2 (CACHE, ("Don't replace old group cache name with empty <%.16s>\n", FoundCacheName->NetbiosName)); + } else { + + PNETBIOS_CACHE EmptyCache; + + // + // Nothing found. + // + + EmptyCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); + if (EmptyCache != NULL) { + + RtlZeroMemory (EmptyCache, sizeof(NETBIOS_CACHE)); + + NB_DEBUG2 (CACHE, ("Allocate new empty cache %lx for <%.16s>\n", + EmptyCache, Reserved->u.SR_FN.NetbiosName)); + + RtlCopyMemory (EmptyCache->NetbiosName, Reserved->u.SR_FN.NetbiosName, 16); + EmptyCache->Unique = TRUE; // so we'll delete it if we see an add name + EmptyCache->ReferenceCount = 1; + EmptyCache->NetworksAllocated = 1; + EmptyCache->TimeStamp = Device->CacheTimeStamp; + EmptyCache->NetworksUsed = 0; + EmptyCache->FailedOnDownWan = (BOOLEAN) + !NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved); + + InsertInNetbiosCacheTable ( + Device->NameCache, + EmptyCache); + } + } + + // + // Fail all datagrams, etc. that were waiting for + // this route. This call releases the lock. + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved) ? + NetbiosNameNotFoundNormal : + NetbiosNameNotFoundWanDown, + NULL + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + --Device->FindNamePacketCount; + NbiDereferenceDevice (Device, DREF_FIND_NAME); + continue; + } + + + + + // + // Send the packet out again. We first set the time so + // it won't be sent again until the appropriate timeout. + // + + Reserved->u.SR_FN.SendTime = (USHORT)(Device->FindNameTime + Device->FindNameTimeout); + + InsertTailList (&Device->WaitingFindNames, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_NB); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + // + // If this is the first retry, we need to initialize the packet + // + if ( Reserved->u.SR_FN.RetryCount == 1 ) { + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address, which is + // what we want. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *)(&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = NB_CMD_FIND_NAME; + Header->NameFrame.DataStreamType2 = NB_CMD_FIND_NAME; + Header->NameFrame.NameTypeFlag = 0x00; + + RtlCopyMemory( + Header->NameFrame.Name, + Reserved->u.SR_FN.NetbiosName, + 16); + + + } + // + // Now submit the packet to IPX. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + NB_DEBUG2 (CACHE, ("FindNameTimeout sending %lx\n", Reserved)); + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + + sizeof(NB_NAME_FRAME)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &BroadcastTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + + + break; + + } +#else + while (TRUE) { + + p = Device->WaitingFindNames.Flink; + if (p == &Device->WaitingFindNames) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + if (Reserved->SendInProgress) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + // + // This was a find name for a unique name which got a + // response but was not freed at the time (because + // SendInProgress was still TRUE) so we free it now. + // + + (VOID)RemoveHeadList (&Device->WaitingFindNames); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + --Device->FindNamePacketCount; + + // + // It is OK to do this with the lock held because + // it won't be the last one (we have the RIP_TIMER ref). + // + + NbiDereferenceDevice (Device, DREF_FIND_NAME); + continue; + } + + if (((SHORT)(Device->FindNameTime - Reserved->u.SR_FN.SendTime)) < 0) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + (VOID)RemoveHeadList (&Device->WaitingFindNames); + + // + // Save this now, then change it if needed. + // + + BroadcastTarget.NicId = Reserved->u.SR_FN.CurrentNicId; + + if (Reserved->u.SR_FN.CurrentNicId == 1) { + + // + // Increment the counter and see if we have sent + // all the frames we need to (we will age out + // here if we got no response for a unique query, + // or if we are doing a global name or broadcast + // search). We also kill the query right now if + // we have not found anything but down wan lines + // to send it on. + // + + if ((++Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) || + ((Reserved->u.SR_FN.RetryCount > 1) && (!NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved)))) { + +#if DBG + if (Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) { + NB_DEBUG2 (CACHE, ("FindNameTimeout aging out %lx\n", Reserved)); + } else { + NB_DEBUG2 (CACHE, ("FindNameTimeout no active nets %lx\n", Reserved)); + } +#endif + + // + // This packet is stale, clean it up and continue. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) { + + CTEAssert (Reserved->u.SR_FN.NewCache != NULL); + + // + // If this was a group name and we have a new + // cache entry that we have been building for it, + // then insert that in the queue and use it + // to succeed any pending connects. Because + // netbios find name requests can cause cache + // requests for group names to be queued even + // if we already have on in the database, we + // first scan for old ones and remove them. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Reserved->u.SR_FN.NetbiosName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + NB_DEBUG2 (CACHE, ("Found old group cache name <%.16s>\n", FoundCacheName->NetbiosName)); + + RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName ); + + if (--FoundCacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", FoundCacheName)); + NbiFreeMemory( + FoundCacheName, + sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free due to replacement"); + + } + } + + Reserved->u.SR_FN.NewCache->TimeStamp = Device->CacheTimeStamp; + + InsertInNetbiosCacheTable( + Device->NameCache, + Reserved->u.SR_FN.NewCache); + + // + // Reference it for the moment since CacheHandlePending + // uses it after releasing the lock. CacheHandlePending + // will dereference it. + // + + ++Reserved->u.SR_FN.NewCache->ReferenceCount; + + // + // This call releases the locks + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NetbiosNameFound, + Reserved->u.SR_FN.NewCache + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + CTEAssert (Reserved->u.SR_FN.NewCache == NULL); + + // + // Allocate an empty cache entry to record the + // fact that we could not find this name, unless + // there is already an entry for this name. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Reserved->u.SR_FN.NetbiosName, + &FoundCacheName ) == STATUS_SUCCESS ) { + + + NB_DEBUG2 (CACHE, ("Don't replace old group cache name with empty <%.16s>\n", FoundCacheName->NetbiosName)); + + } else { + + PNETBIOS_CACHE EmptyCache; + + // + // Nothing found. + // + + EmptyCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); + if (EmptyCache != NULL) { + + RtlZeroMemory (EmptyCache, sizeof(NETBIOS_CACHE)); + + NB_DEBUG2 (CACHE, ("Allocate new empty cache %lx for <%.16s>\n", + EmptyCache, Reserved->u.SR_FN.NetbiosName)); + + RtlCopyMemory (EmptyCache->NetbiosName, Reserved->u.SR_FN.NetbiosName, 16); + EmptyCache->Unique = TRUE; // so we'll delete it if we see an add name + EmptyCache->ReferenceCount = 1; + EmptyCache->NetworksAllocated = 1; + EmptyCache->TimeStamp = Device->CacheTimeStamp; + EmptyCache->NetworksUsed = 0; + EmptyCache->FailedOnDownWan = (BOOLEAN) + !NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved); + + InsertInNetbiosCacheTable ( + Device->NameCache, + EmptyCache); + } + } + + // + // Fail all datagrams, etc. that were waiting for + // this route. This call releases the lock. + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved) ? + NetbiosNameNotFoundNormal : + NetbiosNameNotFoundWanDown, + NULL + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + --Device->FindNamePacketCount; + NbiDereferenceDevice (Device, DREF_FIND_NAME); + continue; + } + + } + + + // + // Increment the current NIC ID for next time. + // + + if (Reserved->u.SR_FN.CurrentNicId >= Reserved->u.SR_FN.MaximumNicId) { + Reserved->u.SR_FN.CurrentNicId = 1; + } else { + ++Reserved->u.SR_FN.CurrentNicId; + } + + + // + // Send the packet out again. We first set the time so + // it won't be sent again until the appropriate timeout. + // If we are going to wrap around the maximum NIC ID + // after sending this packet we wait the full configured + // amount, otherwise we wait almost the minimum (but still + // insert ourselves at the back of the queue to be fair). + // + + if (Reserved->u.SR_FN.CurrentNicId == 1) { + Reserved->u.SR_FN.SendTime = (USHORT)(Device->FindNameTime + Device->FindNameTimeout); + } else { + Reserved->u.SR_FN.SendTime = (USHORT)(Device->FindNameTime + 2); + } + InsertTailList (&Device->WaitingFindNames, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_NB); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address, which is + // what we want. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *)(&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = NB_CMD_FIND_NAME; + Header->NameFrame.DataStreamType2 = NB_CMD_FIND_NAME; + Header->NameFrame.NameTypeFlag = 0x00; + + RtlCopyMemory( + Header->NameFrame.Name, + Reserved->u.SR_FN.NetbiosName, + 16); + + // + // Now submit the packet to IPX. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + NB_DEBUG2 (CACHE, ("FindNameTimeout sending %lx to %d\n", Reserved, BroadcastTarget.NicId)); + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + + sizeof(NB_NAME_FRAME)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &BroadcastTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + + if (NdisStatus == STATUS_DEVICE_DOES_NOT_EXIST) { + + // + // This send was done on a down wan line. To avoid + // extensive delays sending find names on systems + // with a lot of wan lines, we loop around immediately + // and do the next send. We put this packet back on + // the head of the list and change its SendTime so + // it will be resent immediately. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + NB_DEBUG2 (CACHE, ("FindNameTimeout resending %lx, wan send failed\n", Reserved)); + + RemoveEntryList (&Reserved->WaitLinkage); + InsertHeadList (&Device->WaitingFindNames, &Reserved->WaitLinkage); + Reserved->u.SR_FN.SendTime = Device->FindNameTime; + + continue; + + } else { + + // + // We keep track of when it finds a net that isn't + // a down wan line so that we can tell when datagram + // sends should fail (otherwise we succeed them, so + // the browser won't think this is a down wan line). + // + + NB_SET_SR_FN_SENT_ON_UP_LINE (Reserved, TRUE); + } + + + break; + + } +#endif _PNP_POWER + + // + // Since we did something this time, we restart the timer. + // + + CTEStartTimer( + &Device->FindNameTimer, + FIND_NAME_GRANULARITY, + FindNameTimeout, + (PVOID)Device); + +} /* FindNameTimeout */ + + +VOID +CacheHandlePending( + IN PDEVICE Device, + IN PUCHAR RemoteName, + IN NETBIOS_NAME_RESULT Result, + IN PNETBIOS_CACHE CacheName OPTIONAL + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine cleans up pending datagrams and connects + that were waiting for a route to be discovered to a + given Netbios NAME. THIS ROUTINE IS CALLED WITH + DEVICE->LOCK ACQUIRED AND RETURNS WITH IT RELEASED. + +Arguments: + + Device - The device. + + RemoteName - The netbios name that was being searched for. + + Result - Indicates if the name was found, or not found due + to no response or wan lines being down. + + CacheName - If Result is NetbiosNameFound, the cache entry for this name. + This entry has been referenced and this routine will deref it. + + LockHandle - The handle used to acquire the lock. + +Return Value: + + None. + +--*/ + +{ + + LIST_ENTRY DatagramList; + LIST_ENTRY ConnectList; + LIST_ENTRY AdapterStatusList; + LIST_ENTRY NetbiosFindNameList; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + PLIST_ENTRY p; + PREQUEST ConnectRequest, DatagramRequest, AdapterStatusRequest, NetbiosFindNameRequest; + PCONNECTION Connection; + PADDRESS_FILE AddressFile; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteAddress; + CTELockHandle CancelLH; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + + InitializeListHead (&DatagramList); + InitializeListHead (&ConnectList); + InitializeListHead (&AdapterStatusList); + InitializeListHead (&NetbiosFindNameList); + + // + // Put all connect requests on ConnectList. They will + // be continued or failed later. + // + + p = Device->WaitingConnects.Flink; + + while (p != &Device->WaitingConnects) { + + ConnectRequest = LIST_ENTRY_TO_REQUEST(p); + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(ConnectRequest); + p = p->Flink; + + if (RtlEqualMemory (Connection->RemoteName, RemoteName, 16)) { + + RemoveEntryList (REQUEST_LINKAGE(ConnectRequest)); + InsertTailList (&ConnectList, REQUEST_LINKAGE(ConnectRequest)); + + Connection->SubState = CONNECTION_SUBSTATE_C_W_ACK; + } + + } + + + // + // Put all the datagrams on Datagram list. They will be + // sent or failed later. + // + + p = Device->WaitingDatagrams.Flink; + + while (p != &Device->WaitingDatagrams) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + p = p->Flink; + + // + // Check differently based on whether we were looking for + // the broadcast address or not. + // + + if (Reserved->u.SR_DG.RemoteName == (PVOID)-1) { + if (!RtlEqualMemory (RemoteName, NetbiosBroadcastName, 16)) { + continue; + } + } else { + + if (!RtlEqualMemory (RemoteName, Reserved->u.SR_DG.RemoteName->NetbiosName, 16)) { + continue; + } + } + + RemoveEntryList (&Reserved->WaitLinkage); + InsertTailList (&DatagramList, &Reserved->WaitLinkage); + + // + // Reference this here with the lock held. + // + + if (Result == NetbiosNameFound) { + ++CacheName->ReferenceCount; + } + + } + + + // + // Put all the adapter status requests on AdapterStatus + // list. They will be sent or failed later. + // + + p = Device->WaitingAdapterStatus.Flink; + + while (p != &Device->WaitingAdapterStatus) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + + p = p->Flink; + + RemoteAddress = (TDI_ADDRESS_NETBIOS UNALIGNED *)REQUEST_INFORMATION(AdapterStatusRequest); + + if (!RtlEqualMemory( + RemoteName, + RemoteAddress->NetbiosName, + 16)) { + continue; + } + + RemoveEntryList (REQUEST_LINKAGE(AdapterStatusRequest)); + InsertTailList (&AdapterStatusList, REQUEST_LINKAGE(AdapterStatusRequest)); + + // + // Reference this here with the lock held. + // + + if (Result == NetbiosNameFound) { + ++CacheName->ReferenceCount; + } + + } + + + // + // Put all the netbios find name requests on NetbiosFindName + // list. They will be completed later. + // + + p = Device->WaitingNetbiosFindName.Flink; + + while (p != &Device->WaitingNetbiosFindName) { + + NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p); + + p = p->Flink; + + RemoteAddress = (TDI_ADDRESS_NETBIOS UNALIGNED *)REQUEST_INFORMATION(NetbiosFindNameRequest); + + if (!RtlEqualMemory( + RemoteName, + RemoteAddress->NetbiosName, + 16)) { + continue; + } + + RemoveEntryList (REQUEST_LINKAGE(NetbiosFindNameRequest)); + InsertTailList (&NetbiosFindNameList, REQUEST_LINKAGE(NetbiosFindNameRequest)); + + } + + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + + // + // Now that the lock is free, process all the packets on + // the various lists. + // + + for (p = ConnectList.Flink; p != &ConnectList; ) { + + ConnectRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(ConnectRequest); + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN)) { + + if (Result == NetbiosNameFound) { + + NB_DEBUG2 (CONNECTION, ("Found queued connect %lx on %lx\n", ConnectRequest, Connection)); + + // + // Continue with the connection sequence. + // + + Connection->SubState = CONNECTION_SUBSTATE_C_W_ROUTE; + + + if (!ConnectRequest->Cancel) { + + IoSetCancelRoutine (ConnectRequest, NbiCancelConnectWaitResponse); + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle1 ); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + Connection->LocalTarget = CacheName->Networks[0].LocalTarget; + RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, &CacheName->FirstResponse, 12); + NbiReferenceConnectionSync (Connection, CREF_FIND_ROUTE); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = CacheName->FirstResponse.NetworkAddress; + RtlCopyMemory(Connection->FindRouteRequest.Node,CacheName->FirstResponse.NodeAddress,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_RIP_IF_NEEDED; + + // + // When this completes, we will send the session init. + // We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // + + if (CacheName->FirstResponse.NetworkAddress != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelling connect %lx on %lx\n", ConnectRequest, Connection)); + + goto AbortConnect; + + // + // Jumps down into the else below. + // + + } + + } else { + BOOLEAN bAutodialAttempt = FALSE; + + NB_DEBUG2 (CONNECTION, ("Timing out connect %lx on %lx\n", ConnectRequest, Connection)); +AbortConnect: + + ASSERT (Connection->ConnectRequest == ConnectRequest); + +#ifdef RASAUTODIAL + if (fAcdLoadedG) { + CTELockHandle adirql; + BOOLEAN fEnabled; + + // + // See if the automatic connection driver knows + // about this address before we search the + // network. If it does, we return STATUS_PENDING, + // and we will come back here via NbfRetryTdiConnect(). + // + CTEGetLock(&AcdDriverG.SpinLock, &adirql); + fEnabled = AcdDriverG.fEnabled; + CTEFreeLock(&AcdDriverG.SpinLock, adirql); + if (fEnabled && NbiAttemptAutoDial( + Device, + Connection, + 0, + NbiRetryTdiConnect, + ConnectRequest)) + { + NB_SYNC_FREE_LOCK(&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK(CancelLH); + + bAutodialAttempt = TRUE; + } + } +#endif // RASAUTODIAL + + if (!bAutodialAttempt) { + Connection->ConnectRequest = NULL; + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + IoSetCancelRoutine( ConnectRequest, (PDRIVER_CANCEL)NULL ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + REQUEST_STATUS(ConnectRequest) = STATUS_BAD_NETWORK_PATH; + + NbiCompleteRequest(ConnectRequest); + NbiFreeRequest (Device, ConnectRequest); + } + + NbiDereferenceConnection (Connection, CREF_CONNECT); + + } + + } else { + + // BUGBUG What happens to the IRP? Who completes it? + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + } + + NbiDereferenceConnection (Connection, CREF_WAIT_CACHE); + + } + + + for (p = DatagramList.Flink; p != &DatagramList; ) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + p = p->Flink; + + if (Result == NetbiosNameFound) { + + NB_DEBUG2 (DATAGRAM, ("Found queued datagram %lx on %lx\n", Reserved->u.SR_DG.DatagramRequest, Reserved->u.SR_DG.AddressFile)); + + Reserved->u.SR_DG.Cache = CacheName; + Reserved->u.SR_DG.CurrentNetwork = 0; + + // + // CacheName was referenced above. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + if ( REQUEST_NDIS_BUFFER( Reserved->u.SR_DG.DatagramRequest )) { + NdisChainBufferAtBack (Packet, REQUEST_NDIS_BUFFER(Reserved->u.SR_DG.DatagramRequest)); + } + + NbiTransmitDatagram (Reserved); + + } else { + + // + // BETABUGBUG: Should we send it once as a broadcast + // on net 0, just in case?? + // + + AddressFile = Reserved->u.SR_DG.AddressFile; + DatagramRequest = Reserved->u.SR_DG.DatagramRequest; + + NB_DEBUG2 (DATAGRAM, ("Timing out datagram %lx on %lx\n", DatagramRequest, AddressFile)); + + // + // If the failure was due to a down wan line indicate + // that, otherwise return success (so the browser won't + // confuse this with a down wan line). + // + + if (Result == NetbiosNameNotFoundWanDown) { + REQUEST_STATUS(DatagramRequest) = STATUS_DEVICE_DOES_NOT_EXIST; + REQUEST_INFORMATION(DatagramRequest) = 0; + } else { + REQUEST_STATUS(DatagramRequest) = STATUS_SUCCESS; + } + + NbiCompleteRequest(DatagramRequest); + NbiFreeRequest (Device, DatagramRequest); + + NbiDereferenceAddressFile (AddressFile, AFREF_SEND_DGRAM); + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + } + + } + + + for (p = AdapterStatusList.Flink; p != &AdapterStatusList; ) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + if (Result == NetbiosNameFound) { + + NB_DEBUG2 (QUERY, ("Found queued AdapterStatus %lx\n", AdapterStatusRequest)); + + // + // Continue with the AdapterStatus sequence. We put + // it in ActiveAdapterStatus, it will either get + // completed when a response is received or timed + // out by the long timeout. + // + + REQUEST_STATUS(AdapterStatusRequest) = (NTSTATUS)CacheName; + + // + // CacheName was referenced above. + // + + REQUEST_INFORMATION (AdapterStatusRequest) = 0; + + NB_INSERT_TAIL_LIST( + &Device->ActiveAdapterStatus, + REQUEST_LINKAGE (AdapterStatusRequest), + &Device->Lock); + + NbiSendStatusQuery (AdapterStatusRequest); + + } else { + + NB_DEBUG2 (QUERY, ("Timing out AdapterStatus %lx\n", AdapterStatusRequest)); + + REQUEST_STATUS(AdapterStatusRequest) = STATUS_IO_TIMEOUT; + + NbiCompleteRequest(AdapterStatusRequest); + NbiFreeRequest (Device, AdapterStatusRequest); + + NbiDereferenceDevice (Device, DREF_STATUS_QUERY); + + } + + } + + + for (p = NetbiosFindNameList.Flink; p != &NetbiosFindNameList; ) { + + NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + // + // In fact there is not much difference between success or + // failure, since in the successful case the information + // will already have been written to the buffer. Just + // complete the request with the appropriate status, + // which will already be stored in the request. + // + + if (Result == NetbiosNameFound) { + + if (CacheName->Unique) { + + NB_DEBUG2 (QUERY, ("Found queued unique NetbiosFindName %lx\n", NetbiosFindNameRequest)); + + } else { + + NB_DEBUG2 (QUERY, ("Found queued group NetbiosFindName %lx\n", NetbiosFindNameRequest)); + + } + + } else { + + CTEAssert (REQUEST_STATUS(NetbiosFindNameRequest) == STATUS_IO_TIMEOUT); + NB_DEBUG2 (QUERY, ("Timed out NetbiosFindName %lx\n", NetbiosFindNameRequest)); + + } + + // + // This sets REQUEST_INFORMATION(Request) to the correct value. + // + + NbiSetNetbiosFindNameInformation (NetbiosFindNameRequest); + + NbiCompleteRequest(NetbiosFindNameRequest); + NbiFreeRequest (Device, NetbiosFindNameRequest); + + NbiDereferenceDevice (Device, DREF_NB_FIND_NAME); + + } + + + // + // We referenced this temporarily so we could use it in here, + // deref and check if we need to delete it. + // + + if (Result == NetbiosNameFound) { + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle1); + + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free newly allocated cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free in CacheHandlePending"); + + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle1); + + } + +} /* CacheHandlePending */ + + +VOID +NbiProcessNameRecognized( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_NAME_RECOGNIZED frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PDEVICE Device = NbiDevice; + PNETBIOS_CACHE NameCache; + PREQUEST NetbiosFindNameRequest; + PNB_SEND_RESERVED Reserved; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteNetbiosAddress; + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + NB_DEFINE_LOCK_HANDLE(LockHandle) + + +#if 0 + // + // BETABUGBUG: We should handle responses from network 0 + // differently -- if they are for a group name, we should + // keep them around but only until we get a non-zero + // response from the same card. + // + + if (*(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork) == 0) { + return; + } +#endif + + + // + // We need to scan our queue of pending find name packets + // to see if someone is waiting for this name. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + for (p = Device->WaitingFindNames.Flink; + p != &Device->WaitingFindNames; + p = p->Flink) { + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + + // + // Find names which have already found unique names are + // "dead", waiting for FindNameTimeout to remove them, + // and should be ignored when scanning the list. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + continue; + } + + if (RtlEqualMemory (Reserved->u.SR_FN.NetbiosName, Connectionless->NameFrame.Name, 16)) { + break; + } + } + + if (p == &Device->WaitingFindNames) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + + // + // Scan for any netbios find name requests on the queue, and + // inform them about this remote. We need to do this on every + // response because group names need every computer recorded, + // but the normal cache only includes one entry per network. + // + + for (p = Device->WaitingNetbiosFindName.Flink; + p != &Device->WaitingNetbiosFindName; + p = p->Flink) { + + NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p); + + RemoteNetbiosAddress = (TDI_ADDRESS_NETBIOS UNALIGNED *)REQUEST_INFORMATION(NetbiosFindNameRequest); + + if (!RtlEqualMemory( + Connectionless->NameFrame.Name, + RemoteNetbiosAddress->NetbiosName, + 16)) { + continue; + } + + // + // This will update the request status if needed. + // + + NbiUpdateNetbiosFindName( + NetbiosFindNameRequest, +#if defined(_PNP_POWER) + &RemoteAddress->NicHandle, +#else + RemoteAddress->NicId, +#endif _PNP_POWER + (TDI_ADDRESS_IPX UNALIGNED *)Connectionless->IpxHeader.SourceNetwork, + (BOOLEAN)((Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0)); + + } + + + // + // See what is up with this pending find name packet. + // + + if (Reserved->u.SR_FN.NewCache == NULL) { + + // + // This is the first response we have received, so we + // allocate the initial entry with room for a single + // entry. + // + + NameCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); + if (NameCache == NULL) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NB_DEBUG2 (CACHE, ("Alloc new cache %lx for <%.16s>, net %lx\n", + NameCache, Reserved->u.SR_FN.NetbiosName, + *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork))); + + RtlCopyMemory (NameCache->NetbiosName, Connectionless->NameFrame.Name, 16); + NameCache->Unique = (BOOLEAN)((Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0); + NameCache->ReferenceCount = 1; + RtlCopyMemory (&NameCache->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12); + NameCache->NetworksAllocated = 1; + NameCache->NetworksUsed = 1; + NameCache->Networks[0].Network = *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork); + + if (RtlEqualMemory (Connectionless->NameFrame.Name, NetbiosBroadcastName, 16)) { + + NB_SET_SR_FN_STATUS (Reserved, FNStatusResponseGroup); + NameCache->Unique = FALSE; + + } else { + + NB_SET_SR_FN_STATUS( + Reserved, + NameCache->Unique ? FNStatusResponseUnique : FNStatusResponseGroup); + + } + + Reserved->u.SR_FN.NewCache = NameCache; + + // + // If this packet was not routed to us and is for a group name, + // rather than use whatever local target it happened to come + // from we set it up so that it is broadcast on that net. + // + + if ((RtlEqualMemory (RemoteAddress->MacAddress, Connectionless->IpxHeader.SourceNode, 6)) && + (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup)) { +#if defined(_PNP_POWER) + NameCache->Networks[0].LocalTarget.NicHandle = RemoteAddress->NicHandle; +#else + NameCache->Networks[0].LocalTarget.NicId = RemoteAddress->NicId; +#endif _PNP_POWER + RtlCopyMemory (NameCache->Networks[0].LocalTarget.MacAddress, BroadcastAddress, 6); + RtlCopyMemory (NameCache->FirstResponse.NodeAddress, BroadcastAddress, 6); + } else { + NameCache->Networks[0].LocalTarget = *RemoteAddress; + } + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + // + // Complete pending requests now, since it is a unique + // name we have all the information we will get. + // + + NameCache->TimeStamp = Device->CacheTimeStamp; + + InsertInNetbiosCacheTable( + Device->NameCache, + NameCache); + + // + // Reference it since CacheHandlePending uses it + // with the lock released. CacheHandlePending + // will dereference it. + // + + ++NameCache->ReferenceCount; + + // + // This call releases the lock. + // + + CacheHandlePending( + Device, + Reserved->u.SR_FN.NetbiosName, + NetbiosNameFound, + NameCache + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } + + } else { + + // + // We already have a response to this frame. + // + + if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { + + // + // BUGBUG: Should we check that the response is also + // unique? Not much to do since I don't know of an + // equivalent to the netbeui NAME_IN_CONFLICT. + // + + } else { + + // + // This is a group name. + // + + if (Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) { + + // + // Update our information about this network if needed. + // This may free the existing cache and allocate a new one. + // + + Reserved->u.SR_FN.NewCache = + CacheUpdateNameCache( + Reserved->u.SR_FN.NewCache, + RemoteAddress, + (TDI_ADDRESS_IPX UNALIGNED *) + Connectionless->IpxHeader.SourceNetwork, + FALSE); + + } else { + + // + // BUGBUG: This respondent thinks it is a unique name + // but we think it is group, should we do something? + // + + } + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } + +} /* NbiProcessNameRecognized */ + + +PNETBIOS_CACHE +CacheUpdateNameCache( + IN PNETBIOS_CACHE NameCache, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress, + IN BOOLEAN ModifyQueue + ) + +/*++ + +Routine Description: + + This routine is called to update a netbios cache entry + with a new network, if it is does not already contain + information about the network. It is called when a frame + is received advertising the appropriate cache entry, which + is either a group name or the broadcast name. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH IT HELD. + +Arguments: + + NameCache - The name cache entry to update. + + RemoteAddress - The remote address on which a frame was received. + + IpxAddress - The source IPX address of the frame. + + ModifyQueue - TRUE if we should update the queue which this + cache entry is in, if we reallocate it. + +Return Value: + + The netbios cache entry, either the original or a reallocated one. + +--*/ + +{ + + PDEVICE Device = NbiDevice; + USHORT NewNetworks; + PNETBIOS_CACHE NewNameCache; + PLIST_ENTRY OldPrevious; + UINT i; + + // + // See if we already know about this network. + // + + for (i = 0; i < NameCache->NetworksUsed; i++) { + if (NameCache->Networks[i].Network == SourceAddress->NetworkAddress) { + return NameCache; + } + } + + // + // We need to add information about this network + // to the name cache entry. If we have to allocate + // a new one we do that. + // + + NB_DEBUG2 (CACHE, ("Got new net %lx for <%.16s>\n", + SourceAddress->NetworkAddress, + NameCache->NetbiosName)); + + if (NameCache->NetworksUsed == NameCache->NetworksAllocated) { + + // + // We double the number of entries allocated until + // we hit 16, then add 8 at a time. + // + + if (NameCache->NetworksAllocated < 16) { + NewNetworks = NameCache->NetworksAllocated * 2; + } else { + NewNetworks = NameCache->NetworksAllocated + 8; + } + + + NewNameCache = NbiAllocateMemory( + sizeof(NETBIOS_CACHE) + ((NewNetworks-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Enlarge cache entry"); + + if (NewNameCache == NULL) { + return NameCache; + } + + NB_DEBUG2 (CACHE, ("Expand cache %lx to %lx for <%.16s>\n", + NameCache, NewNameCache, NameCache->NetbiosName)); + + // + // Copy the new current data to the new one. + // + + RtlCopyMemory( + NewNameCache, + NameCache, + sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK))); + + NewNameCache->NetworksAllocated = NewNetworks; + NewNameCache->ReferenceCount = 1; + + if (ModifyQueue) { + + // + // Insert at the same place as the old one. The time + // stamp is the same as the old one. + // + + + ReinsertInNetbiosCacheTable( Device->NameCache, NameCache, NewNameCache ); + + } + + if (--NameCache->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", NameCache)); + NbiFreeMemory( + NameCache, + sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Enlarge existing"); + + } + + NameCache = NewNameCache; + + } + + NameCache->Networks[NameCache->NetworksUsed].Network = + SourceAddress->NetworkAddress; + + // + // If this packet was not routed to us, then store the local + // target for a correct broadcast. + // + + if (RtlEqualMemory (RemoteAddress->MacAddress, SourceAddress->NodeAddress, 6)) { +#if defined(_PNP_POWER) + NameCache->Networks[NameCache->NetworksUsed].LocalTarget.NicHandle = RemoteAddress->NicHandle; +#else + NameCache->Networks[NameCache->NetworksUsed].LocalTarget.NicId = RemoteAddress->NicId; +#endif _PNP_POWER + RtlCopyMemory (NameCache->Networks[NameCache->NetworksUsed].LocalTarget.MacAddress, BroadcastAddress, 6); + } else { + NameCache->Networks[NameCache->NetworksUsed].LocalTarget = *RemoteAddress; + } + + ++NameCache->NetworksUsed; + + return NameCache; + +} /* CacheUpdateNameCache */ + + +VOID +CacheUpdateFromAddName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN NB_CONNECTIONLESS UNALIGNED * Connectionless, + IN BOOLEAN LocalFrame + ) + +/*++ + +Routine Description: + + This routine is called when an add name frame is received. + If it is for a group name it checks if our cache entry for + that group name needs to be updated to include a new network; + for all frames it checks if our broadcast cache entry needs + to be updated to include a new network. + +Arguments: + + RemoteAddress - The address the frame was received from. + + Connectionless - The header of the received add name. + + LocalFrame - TRUE if the frame was sent locally. + +Return Value: + + None. + +--*/ + +{ + PUCHAR NetbiosName; + PNETBIOS_CACHE NameCache; + PLIST_ENTRY p; + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + + NetbiosName = (PUCHAR)Connectionless->NameFrame.Name; + + // + // First look up the broadcast name. + // + // BUGBUG: We should cache a pointer to the cache name + // for the broadcast entry, if there is one. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + if (!LocalFrame) { + + if ( FindInNetbiosCacheTable( Device->NameCache, + NetbiosBroadcastName, + &NameCache ) == STATUS_SUCCESS ) { + // + // This will reallocate a cache entry and update the + // queue if necessary. + // + + (VOID)CacheUpdateNameCache( + NameCache, + RemoteAddress, + (TDI_ADDRESS_IPX UNALIGNED *)(Connectionless->IpxHeader.SourceNetwork), + TRUE); + } + + } + + + // + // Now see if our database needs to be updated based on this. + // + + if ( FindInNetbiosCacheTable( Device->NameCache, + Connectionless->NameFrame.Name, + &NameCache ) == STATUS_SUCCESS ) { + + + if (!NameCache->Unique) { + + if (!LocalFrame) { + + // + // This will reallocate a cache entry and update the + // queue if necessary. + // + + (VOID)CacheUpdateNameCache( + NameCache, + RemoteAddress, + (TDI_ADDRESS_IPX UNALIGNED *)(Connectionless->IpxHeader.SourceNetwork), + TRUE); + + } + + } else { + + // + // To be safe, delete any unique names we get add + // names for (we will requery next time we need it). + // BUGBUG: Update the database instead -- but then + // we may not get the best route?? + // + + RemoveFromNetbiosCacheTable ( Device->NameCache, NameCache ); + + if (--NameCache->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free add named cache entry %lx\n", NameCache)); + NbiFreeMemory( + NameCache, + sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Enlarge existing"); + + } + + } + + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + +} /* CacheUpdateFromAddName */ + + +VOID +NbiProcessDeleteName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_DELETE_NAME frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + PUCHAR NetbiosName; + PNETBIOS_CACHE CacheName; + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + + if (PacketSize != sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)) { + return; + } + + // + // We want to update our netbios cache to reflect the + // fact that this name is no longer valid. + // + + NetbiosName = (PUCHAR)Connectionless->NameFrame.Name; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + if ( FindInNetbiosCacheTable( Device->NameCache, + NetbiosName, + &CacheName ) == STATUS_SUCCESS ) { + + // + // We don't track group names since we don't know if + // this is the last person that owns it. We also drop + // the frame if does not come from the person we think + // owns this name. + // + + if ((!CacheName->Unique) || + (CacheName->NetworksUsed == 0) || + (!RtlEqualMemory (&CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12))) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NB_DEBUG2 (CACHE, ("Found cache name to delete <%.16s>\n", NetbiosName)); + + }else { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + + // + // We have a cache entry, take it out of the list. If no + // one else is using it, delete it; if not, they will delete + // it when they are done. + // + + + RemoveFromNetbiosCacheTable ( Device->NameCache, CacheName); + + if (--CacheName->ReferenceCount == 0) { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + NB_DEBUG2 (CACHE, ("Free delete name cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Name deleted"); + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } + +} /* NbiProcessDeleteName */ + +VOID +InsertInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE CacheEntry + ) + +/*++ + +Routine Description: + + This routine inserts a new cache entry in the hash table + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + + +Arguments: + + CacheTable - The pointer of the Hash Table. + + CacheEntry - Entry to be inserted. + +Return Value: + + None + +--*/ + +{ + USHORT HashIndex; + + // + // Keep a threshold of how many entries do we keep in the table. + // If it crosses the threshold, just remove the oldest entry + // + if ( CacheTable->CurrentEntries >= CacheTable->MaxHashIndex * NB_MAX_AVG_CACHE_ENTRIES_PER_BUCKET ) { + PNETBIOS_CACHE OldestCacheEntry = NULL; + PNETBIOS_CACHE NextEntry; + PLIST_ENTRY p; + + for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { + if ( (p = CacheTable->Bucket[ HashIndex ].Blink ) != &CacheTable->Bucket[ HashIndex ] ) { + NextEntry = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + + if ( OldestCacheEntry ) { + if ( NextEntry->TimeStamp < OldestCacheEntry->TimeStamp ) { + OldestCacheEntry = NextEntry; + } + } else { + OldestCacheEntry = NextEntry; + } + } + } + + CTEAssert( OldestCacheEntry ); + + NB_DEBUG2 (CACHE, ("Threshold exceeded, removing oldest cache entry %lx\n", OldestCacheEntry)); + RemoveEntryList (&OldestCacheEntry->Linkage); + CacheTable->CurrentEntries--; + + if (--OldestCacheEntry->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Freed cache entry %lx\n", OldestCacheEntry)); + + NbiFreeMemory( + OldestCacheEntry, + sizeof(NETBIOS_CACHE) + ((OldestCacheEntry->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Aged out"); + + } + + } + HashIndex = ( ( CacheEntry->NetbiosName[0] & 0x0f ) << 4 ) + ( CacheEntry->NetbiosName[1] & 0x0f ); + HashIndex = HashIndex % CacheTable->MaxHashIndex; + + InsertHeadList( &CacheTable->Bucket[HashIndex], &CacheEntry->Linkage ); + CacheTable->CurrentEntries++; +} /* InsertInNetbiosCacheTable */ + + +__inline +VOID +ReinsertInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE OldEntry, + IN PNETBIOS_CACHE NewEntry + ) + +/*++ + +Routine Description: + + This routine inserts a new cache entry at the same place where + the old entry was. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + + +Arguments: + + CacheTable - The pointer of the Hash Table. + + CacheEntry - Entry to be inserted. + +Return Value: + + None + +--*/ + +{ + PLIST_ENTRY OldPrevious; + + OldPrevious = OldEntry->Linkage.Blink; + RemoveEntryList (&OldEntry->Linkage); + InsertHeadList (OldPrevious, &NewEntry->Linkage); +} /* ReinsertInNetbiosCacheTable */ + +__inline +VOID +RemoveFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE CacheEntry + ) + +/*++ + +Routine Description: + + This routine removes an entry from the cache table. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + CacheEntry - Entry to be removed. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Return Value: + + None. +--*/ + +{ + RemoveEntryList( &CacheEntry->Linkage ); + CacheTable->CurrentEntries--; +} /* RemoveFromNetbiosCacheTable */ + + + +VOID +FlushOldFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN USHORT AgeLimit + ) + +/*++ + +Routine Description: + + This routine removes all the old entries from the hash table. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + AgeLimit - All the entries older than AgeLimit will be removed. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Return Value: + + None. +--*/ + +{ + USHORT HashIndex; + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + + // + // run the hash table looking for old entries. Since new entries + // are stored at the head and all entries are time stamped when + // they are inserted, we scan backwards and stop once we find + // an entry which does not need to be aged. + // we repeat this for each bucket. + + for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { + for (p = CacheTable->Bucket[ HashIndex ].Blink; + p != &CacheTable->Bucket[ HashIndex ]; + ) { + + CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + p = p->Blink; + + // + // see if any entries have been around for more than agelimit + // + + if ((USHORT)(NbiDevice->CacheTimeStamp - CacheName->TimeStamp) >= AgeLimit ) { + + RemoveEntryList (&CacheName->Linkage); + CacheTable->CurrentEntries--; + + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Aging out name cache entry %lx\n", CacheName)); + + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Aged out"); + + } + + } else { + + break; + + } + } // for loop + } // for loop +} /* FlushOldFromNetbiosCacheTable */ + +VOID +FlushFailedNetbiosCacheEntries( + IN PNETBIOS_CACHE_TABLE CacheTable + ) + +/*++ + +Routine Description: + + This routine removes all the failed entries from the hash table. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Return Value: + + None. +--*/ + +{ + USHORT HashIndex; + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + + // + // run the hash table looking for old entries. Since new entries + // are stored at the head and all entries are time stamped when + // they are inserted, we scan backwards and stop once we find + // an entry which does not need to be aged. + // we repeat this for each bucket. + + for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { + for (p = CacheTable->Bucket[ HashIndex ].Blink; + p != &CacheTable->Bucket[ HashIndex ]; + ) { + + CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + p = p->Blink; + + // + // flush all the failed cache entries. + // We do this when a new adapter appears, and there's a possiblity that + // the failed entries might succeed now on the new adapter. + // + + if (CacheName->NetworksUsed == 0) { + RemoveEntryList (&CacheName->Linkage); + CacheTable->CurrentEntries--; + CTEAssert( CacheName->ReferenceCount == 1 ); + CTEAssert( CacheName->NetworksAllocated == 1 ); + + NB_DEBUG2 (CACHE, ("Flushing out failed name cache entry %lx\n", CacheName)); + + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE), + MEMORY_CACHE, + "Aged out"); + + } + } // for loop + } // for loop +} /* FlushFailedNetbiosCacheEntries */ + +VOID +RemoveInvalidRoutesFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN NIC_HANDLE UNALIGNED *InvalidNicHandle + ) + +/*++ + +Routine Description: + + This routine removes all invalid route entries from the hash table. + Routes become invalid when the binding is deleted in Ipx due to PnP + event. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + InvalidRouteNicId - NicId of the invalid routes. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Return Value: + + None. +--*/ + +{ + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + USHORT i,j,NetworksRemoved; + USHORT HashIndex; + PDEVICE Device = NbiDevice; + + // + // Flush all the cache entries that are using this NicId in the local + // target. + // + + for ( HashIndex = 0; HashIndex < Device->NameCache->MaxHashIndex; HashIndex++) { + for (p = Device->NameCache->Bucket[ HashIndex ].Flink; + p != &Device->NameCache->Bucket[ HashIndex ]; + ) { + + CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + p = p->Flink; + + + // + // Remove each of those routes which is using this NicId. + // if no routes left, then flush the cache entry also. + // ( unique names have only one route anyways ) + // + for ( i = 0, NetworksRemoved = 0; i < CacheName->NetworksUsed; i++ ) { + if ( CacheName->Networks[i].LocalTarget.NicHandle.NicId == InvalidNicHandle->NicId ) { + CTEAssert( RtlEqualMemory( &CacheName->Networks[i].LocalTarget.NicHandle, InvalidNicHandle, sizeof(NIC_HANDLE))); + for ( j = i+1; j < CacheName->NetworksUsed; j++ ) { + CacheName->Networks[j-1] = CacheName->Networks[j]; + } + NetworksRemoved++; + } else if ( CacheName->Networks[i].LocalTarget.NicHandle.NicId > InvalidNicHandle->NicId ) { + CacheName->Networks[i].LocalTarget.NicHandle.NicId--; + } + } + CTEAssert( NetworksRemoved <= CacheName->NetworksUsed ); + if ( ! ( CacheName->NetworksUsed -= NetworksRemoved ) ) { + RemoveEntryList (&CacheName->Linkage); + CacheTable->CurrentEntries--; + + NB_DEBUG2 (CACHE, ("Removed cache entry %lx bcoz route(NicId %d) deleted\n", CacheName, InvalidNicHandle->NicId )); + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Freed name cache entry %lx\n", CacheName)); + + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Aged out"); + + } + } + } // for loop + } // for loop +} /* RemoveInvalidRoutesFromNetbiosCacheTable */ + + +NTSTATUS +FindInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PUCHAR NameToBeFound, + OUT PNETBIOS_CACHE *CacheEntry + ) + +/*++ + +Routine Description: + + This routine finds a netbios name in the Hash Table and returns + the corresponding cache entry. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Arguments: + + CacheTable - The pointer of the Hash Table. + + CacheEntry - Pointer to the netbios cache entry if found. + +Return Value: + + STATUS_SUCCESS - if successful. + + STATUS_UNSUCCESSFUL - otherwise. + +--*/ + +{ + USHORT HashIndex; + PLIST_ENTRY p; + PNETBIOS_CACHE FoundCacheName; + + + HashIndex = ( ( NameToBeFound[0] & 0x0f ) << 4 ) + ( NameToBeFound[1] & 0x0f ); + HashIndex = HashIndex % CacheTable->MaxHashIndex; + + for (p = ( CacheTable->Bucket[ HashIndex ] ).Flink; + p != &CacheTable->Bucket[ HashIndex ]; + p = p->Flink) { + + FoundCacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + + // + // See if this entry is for the same name we are looking for. + + if ( RtlEqualMemory (FoundCacheName->NetbiosName, NameToBeFound, 16) ) { + *CacheEntry = FoundCacheName; + return STATUS_SUCCESS; + } + } + + return STATUS_UNSUCCESSFUL; +} /* FindInNetbiosCacheTable */ + +NTSTATUS +CreateNetbiosCacheTable( + IN OUT PNETBIOS_CACHE_TABLE *NewTable, + IN USHORT MaxHashIndex + ) + +/*++ + +Routine Description: + + This routine creates a new hash table for netbios cache + and initializes it. + + THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS + WITH THE LOCK HELD. + +Arguments: + + NewTable - The pointer of the table to be created. + + MaxHashIndex - Number of buckets in the hash table. + +Return Value: + + STATUS_SUCCESS - if successful. + + STATUS_INSUFFICIENT_RESOURCES - If cannot allocate memory. + +--*/ + +{ + USHORT i; + + *NewTable = NbiAllocateMemory (sizeof(NETBIOS_CACHE_TABLE) + sizeof(LIST_ENTRY) * ( MaxHashIndex - 1) , + MEMORY_CACHE, "Cache Table"); + + if ( *NewTable ) { + for ( i = 0; i < MaxHashIndex; i++ ) { + InitializeListHead(& (*NewTable)->Bucket[i] ); + } + + (*NewTable)->MaxHashIndex = MaxHashIndex; + (*NewTable)->CurrentEntries = 0; + return STATUS_SUCCESS; + } + else { + NB_DEBUG( CACHE, ("Cannot create Netbios Cache Table\n") ); + return STATUS_INSUFFICIENT_RESOURCES; + } + +} /* CreateNetbiosCacheTable */ + + +VOID +DestroyNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable + ) + +/*++ + +Routine Description: + + This routine removes all entries from the hash table. + and free up the hash table. + +Arguments: + + CacheTable - The pointer of the Hash Table. + +Return Value: + + None. +--*/ + +{ + USHORT HashIndex; + PLIST_ENTRY p; + PNETBIOS_CACHE CacheName; + + + for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { + while (!IsListEmpty ( &( CacheTable->Bucket[ HashIndex ] ) ) ) { + + p = RemoveHeadList ( &( CacheTable->Bucket[ HashIndex ] )); + CacheTable->CurrentEntries--; + CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); + + NB_DEBUG2 (CACHE, ("Free cache entry %lx\n", CacheName)); + + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free entries"); + + } + } // for loop + + CTEAssert( CacheTable->CurrentEntries == 0 ); + + NbiFreeMemory (CacheTable, sizeof(NETBIOS_CACHE_TABLE) + sizeof(LIST_ENTRY) * ( CacheTable->MaxHashIndex - 1) , + MEMORY_CACHE, "Free Cache Table"); + +} /* DestroyNetbiosCacheTable */ + + diff --git a/private/ntos/tdi/isnp/nb/config.c b/private/ntos/tdi/isnp/nb/config.c new file mode 100644 index 000000000..8689c5d20 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/config.c @@ -0,0 +1,661 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + config.c + +Abstract: + + This contains all routines necessary for the support of the dynamic + configuration of the ISN Netbios module. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Local functions used to access the registry. +// + +NTSTATUS +NbiGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +NbiAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +NbiAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +NbiReadLinkageInformation( + IN PCONFIG Config + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NbiGetConfiguration) +#pragma alloc_text(INIT,NbiFreeConfiguration) +#pragma alloc_text(INIT,NbiGetConfigValue) +#pragma alloc_text(INIT,NbiAddBind) +#pragma alloc_text(INIT,NbiAddExport) +#pragma alloc_text(INIT,NbiReadLinkageInformation) +#endif + + + +NTSTATUS +NbiGetConfiguration ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ) + +/*++ + +Routine Description: + + This routine is called by Netbios to get information from the configuration + management routines. We read the registry, starting at RegistryPath, + to get the parameters. If they don't exist, we use the defaults + set in ipxcnfg.h file. A list of adapters to bind to is chained + on to the config information. + +Arguments: + + DriverObject - Used for logging errors. + + RegistryPath - The name of Netbios' node in the registry. + + ConfigPtr - Returns the configuration information. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ +{ + PWSTR RegistryPathBuffer; + PCONFIG Config; + RTL_QUERY_REGISTRY_TABLE QueryTable[CONFIG_PARAMETERS+2]; + NTSTATUS Status; + ULONG One = 1; + ULONG Two = 2; + ULONG Three = 3; + ULONG Four = 4; + ULONG Five = 5; + ULONG Eight = 8; + ULONG FortyEight = 48; + ULONG Sixty = 60; + ULONG TwoFifty = 250; + ULONG FiveHundred = 500; + ULONG MaxMTU = 0xffffffff; + + PWSTR Parameters = L"Parameters"; + struct { + PWSTR KeyName; + PULONG DefaultValue; + } ParameterValues[CONFIG_PARAMETERS] = { + { L"AckDelayTime", &TwoFifty } , // milliseconds + { L"AckWindow", &Two } , + { L"AckWindowThreshold", &FiveHundred } , // milliseconds + { L"EnablePiggyBackAck", &One } , + { L"Extensions", &One } , + { L"RcvWindowMax", &Four } , + { L"BroadcastCount", &Three } , + { L"BroadcastTimeout", &One } , // half-seconds + { L"ConnectionCount", &Five } , + { L"ConnectionTimeout", &Two } , // half-seconds + { L"InitPackets", &Eight } , + { L"MaxPackets", &FortyEight } , + { L"InitialRetransmissionTime", &FiveHundred } , // milliseconds + { L"Internet", &One } , + { L"KeepAliveCount", &Eight } , + { L"KeepAliveTimeout", &Sixty } , // half-seconds + { L"RetransmitMax", &Eight } , + { L"RouterMTU", &MaxMTU } }; + UINT i; + + + // + // Allocate memory for the main config structure. + // + + Config = NbiAllocateMemory (sizeof(CONFIG), MEMORY_CONFIG, "Config"); + if (Config == NULL) { + NbiWriteResourceErrorLog ((PVOID)DriverObject, sizeof(CONFIG), MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Config->DeviceName.Buffer = NULL; + Config->BindName.Buffer = NULL; + Config->DriverObject = DriverObject; // save this to log errors + + // + // Read in the NDIS binding information (if none is present + // the array will be filled with all known drivers). + // + // NbiReadLinkageInformation expects a null-terminated path, + // so we have to create one from the UNICODE_STRING. + // + + RegistryPathBuffer = (PWSTR)NbiAllocateMemory(RegistryPath->Length + sizeof(WCHAR), + MEMORY_CONFIG, "RegistryPathBuffer"); + if (RegistryPathBuffer == NULL) { + NbiWriteResourceErrorLog ((PVOID)DriverObject, RegistryPath->Length + sizeof(WCHAR), MEMORY_CONFIG); + NbiFreeConfiguration(Config); + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlCopyMemory (RegistryPathBuffer, RegistryPath->Buffer, RegistryPath->Length); + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + Config->RegistryPathBuffer = RegistryPathBuffer; + + + // + // Determine what name to export and who to bind to. + // + + Status = NbiReadLinkageInformation (Config); + + if (Status != STATUS_SUCCESS) { + + // + // If it failed it logged an error. + // + + NbiFreeConfiguration(Config); + return Status; + } + + + // + // Read the per-transport (as opposed to per-binding) + // parameters. + // + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Parameters key below Netbios + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Parameters; + + // + // 2-18) Call NbiSetBindingValue for each of the keys we + // care about. + // + + for (i = 0; i < CONFIG_PARAMETERS; i++) { + + QueryTable[i+1].QueryRoutine = NbiGetConfigValue; + QueryTable[i+1].Flags = 0; + QueryTable[i+1].Name = ParameterValues[i].KeyName; + QueryTable[i+1].EntryContext = (PVOID)i; + QueryTable[i+1].DefaultType = REG_DWORD; + QueryTable[i+1].DefaultData = (PVOID)(ParameterValues[i].DefaultValue); + QueryTable[i+1].DefaultLength = sizeof(ULONG); + + } + + // + // 19) Stop + // + + QueryTable[CONFIG_PARAMETERS+1].QueryRoutine = NULL; + QueryTable[CONFIG_PARAMETERS+1].Flags = 0; + QueryTable[CONFIG_PARAMETERS+1].Name = NULL; + + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if (Status != STATUS_SUCCESS) { + + NbiFreeConfiguration(Config); + NbiWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 701, + Status, + Parameters, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + NbiFreeMemory (RegistryPathBuffer, RegistryPath->Length + sizeof(WCHAR), MEMORY_CONFIG, "RegistryPathBuffer"); + *ConfigPtr = Config; + + return STATUS_SUCCESS; + +} /* NbiGetConfiguration */ + + +VOID +NbiFreeConfiguration ( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by Netbios to get free any storage that was allocated + by NbiGetConfiguration in producing the specified CONFIG structure. + +Arguments: + + Config - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ + if (Config->BindName.Buffer) { + NbiFreeMemory (Config->BindName.Buffer, Config->BindName.MaximumLength, MEMORY_CONFIG, "BindName"); + } + + if (Config->DeviceName.Buffer) { + NbiFreeMemory (Config->DeviceName.Buffer, Config->DeviceName.MaximumLength, MEMORY_CONFIG, "DeviceName"); + } + + NbiFreeMemory (Config, sizeof(CONFIG), MEMORY_CONFIG, "Config"); + +} /* NbiFreeConfig */ + + +NTSTATUS +NbiGetConfigValue( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each entry in the Parameters + node to set the config values. The table is set up + so that this function will be called with correct default + values for keys that are not present. + +Arguments: + + ValueName - The name of the value (ignored). + + ValueType - The type of the value (REG_DWORD -- ignored). + + ValueData - The data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the CONFIG structure. + + EntryContext - The index in Config->Parameters to save the value. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + ULONG Data = *(UNALIGNED ULONG *)ValueData; + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + if ((ValueType != REG_DWORD) || (ValueLength != sizeof(ULONG))) { + return STATUS_INVALID_PARAMETER; + } + + + switch ( (ULONG) EntryContext ) { + case CONFIG_ROUTER_MTU: + if ( ( Data - sizeof(NB_CONNECTION) - sizeof(IPX_HEADER) ) <= 0 ) { + Config->Parameters[CONFIG_ROUTER_MTU] = 0xffffffff; + NbiWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 704, + STATUS_INVALID_PARAMETER, + ValueName, + 0, + NULL); + return STATUS_SUCCESS; + } + break; + default: + break; + } + + NB_DEBUG2 (CONFIG, ("Config parameter %d, value %lx\n", + (ULONG)EntryContext, Data)); + Config->Parameters[(ULONG)EntryContext] = Data; + + return STATUS_SUCCESS; + +} /* NbiGetConfigValue */ + + +NTSTATUS +NbiAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Bind" multi-string and + saves the information in a Config structure. + +Arguments: + + ValueName - The name of the value ("Bind" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the Config structure. + + EntryContext - A pointer to a count of binds that is incremented. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + PULONG ValueReadOk = ((PULONG)EntryContext); + PWCHAR NameBuffer; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + + if (*ValueReadOk == 0) { + + NB_DEBUG2 (CONFIG, ("Read bind value %ws\n", ValueData)); + + NameBuffer = (PWCHAR)NbiAllocateMemory (ValueLength, MEMORY_CONFIG, "BindName"); + if (NameBuffer == NULL) { + NbiWriteResourceErrorLog ((PVOID)Config->DriverObject, ValueLength, MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory (NameBuffer, ValueData, ValueLength); + Config->BindName.Buffer = NameBuffer; + Config->BindName.Length = (USHORT)(ValueLength - sizeof(WCHAR)); + Config->BindName.MaximumLength = (USHORT)ValueLength; + + // + // Set this to ignore any other callbacks and let the + // caller know we read something. + // + + *ValueReadOk = 1; + + } + + return STATUS_SUCCESS; + +} /* NbiAddBind */ + + +NTSTATUS +NbiAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Export" multi-string. It + saves the first callback string in the Config structure. + +Arguments: + + ValueName - The name of the value ("Export" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData. + + Context - A pointer to the Config structure. + + EntryContext - A pointer to a ULONG that goes to 1 after the + first call to this routine (so we know to ignore other ones). + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG Config = (PCONFIG)Context; + PULONG ValueReadOk = ((PULONG)EntryContext); + PWCHAR NameBuffer; + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + + if (*ValueReadOk == 0) { + + NB_DEBUG2 (CONFIG, ("Read export value %ws\n", ValueData)); + + NameBuffer = (PWCHAR)NbiAllocateMemory (ValueLength, MEMORY_CONFIG, "DeviceName"); + if (NameBuffer == NULL) { + NbiWriteResourceErrorLog ((PVOID)Config->DriverObject, ValueLength, MEMORY_CONFIG); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory (NameBuffer, ValueData, ValueLength); + Config->DeviceName.Buffer = NameBuffer; + Config->DeviceName.Length = (USHORT)(ValueLength - sizeof(WCHAR)); + Config->DeviceName.MaximumLength = (USHORT)ValueLength; + + // + // Set this to ignore any other callbacks and let the + // caller know we read something. + // + + *ValueReadOk = 1; + + } + + return STATUS_SUCCESS; + +} /* NbiAddExport */ + + +NTSTATUS +NbiReadLinkageInformation( + IN PCONFIG Config + ) + +/*++ + +Routine Description: + + This routine is called by Netbios to read its linkage information + from the registry. + +Arguments: + + Config - The config structure which will have per-binding information + linked on to it. + +Return Value: + + The status of the operation. + +--*/ + +{ + + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[3]; + PWSTR Subkey = L"Linkage"; + PWSTR Bind = L"Bind"; + PWSTR Export = L"Export"; + ULONG ValueReadOk; // set to TRUE when a value is read correctly + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Linkage key below Netbios + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 1) Call NbiAddExport for each string in "Export" + // + + QueryTable[1].QueryRoutine = NbiAddExport; + QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[1].Name = Export; + QueryTable[1].EntryContext = (PVOID)&ValueReadOk; + QueryTable[1].DefaultType = REG_NONE; + + // + // 2) Stop + // + + QueryTable[2].QueryRoutine = NULL; + QueryTable[2].Flags = 0; + QueryTable[2].Name = NULL; + + + ValueReadOk = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if ((Status != STATUS_SUCCESS) || (ValueReadOk == 0)) { + + NbiWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 702, + Status, + Export, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + + // + // 1) Change to call NbiAddBind for each string in "Bind" + // + + QueryTable[1].QueryRoutine = NbiAddBind; + QueryTable[1].Flags = 0; // not required + QueryTable[1].Name = Bind; + QueryTable[1].EntryContext = (PVOID)&ValueReadOk; + QueryTable[1].DefaultType = REG_NONE; + + ValueReadOk = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Config->RegistryPathBuffer, + QueryTable, + (PVOID)Config, + NULL); + + if ((Status != STATUS_SUCCESS) || (ValueReadOk == 0)) { + + NbiWriteGeneralErrorLog( + (PVOID)Config->DriverObject, + EVENT_IPX_ILLEGAL_CONFIG, + 703, + Status, + Bind, + 0, + NULL); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + return STATUS_SUCCESS; + +} /* NbiReadLinkageInformation */ + diff --git a/private/ntos/tdi/isnp/nb/config.h b/private/ntos/tdi/isnp/nb/config.h new file mode 100644 index 000000000..99b6c6357 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/config.h @@ -0,0 +1,70 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + config.h + +Abstract: + + Private include file for the ISN Netbios module. + file defines all constants and structures necessary for support of + the dynamic configuration of ST. + +Revision History: + +--*/ + + +// +// These are used to index into the Parameters array in CONFIG. +// + +#define CONFIG_ACK_DELAY_TIME 0 +#define CONFIG_ACK_WINDOW 1 +#define CONFIG_ACK_WINDOW_THRESHOLD 2 +#define CONFIG_ENABLE_PIGGYBACK_ACK 3 +#define CONFIG_EXTENSIONS 4 +#define CONFIG_RCV_WINDOW_MAX 5 +#define CONFIG_BROADCAST_COUNT 6 +#define CONFIG_BROADCAST_TIMEOUT 7 +#define CONFIG_CONNECTION_COUNT 8 +#define CONFIG_CONNECTION_TIMEOUT 9 +#define CONFIG_INIT_PACKETS 10 +#define CONFIG_MAX_PACKETS 11 +#define CONFIG_INIT_RETRANSMIT_TIME 12 +#define CONFIG_INTERNET 13 +#define CONFIG_KEEP_ALIVE_COUNT 14 +#define CONFIG_KEEP_ALIVE_TIMEOUT 15 +#define CONFIG_RETRANSMIT_MAX 16 +#define CONFIG_ROUTER_MTU 17 +#define CONFIG_PARAMETERS 18 + +// +// Main configuration structure. +// + +typedef struct _CONFIG { + + ULONG Parameters[CONFIG_PARAMETERS]; // index defined above + NDIS_STRING DeviceName; // device name exported + NDIS_STRING BindName; // device to bind to + PWSTR RegistryPathBuffer; // path to config info + PDRIVER_OBJECT DriverObject; // used for logging errors + +} CONFIG, * PCONFIG; + + +NTSTATUS +NbiGetConfiguration ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + OUT PCONFIG * ConfigPtr + ); + +VOID +NbiFreeConfiguration ( + IN PCONFIG Config + ); + diff --git a/private/ntos/tdi/isnp/nb/connect.c b/private/ntos/tdi/isnp/nb/connect.c new file mode 100644 index 000000000..7ee7204b5 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/connect.c @@ -0,0 +1,3628 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + connect.c + +Abstract: + + This routine contains the code to handle connect requests + for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 22-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +#ifdef RASAUTODIAL +#include <acd.h> +#include <acdapi.h> + +BOOLEAN +NbiCancelTdiConnect( + IN PDEVICE pDevice, + IN PREQUEST pRequest, + IN PCONNECTION pConnection + ); +#endif // RASAUTODIAL + + + +VOID +NbiFindRouteComplete( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute + ) + +/*++ + +Routine Description: + + This routine is called when a find route request + previously issued to IPX completes. + +Arguments: + + FindRouteRequest - The find route request that was issued. + + FoundRoute - TRUE if the route was found. + +Return Value: + + None. + +--*/ + +{ + PCONNECTION Connection; + PDEVICE Device = NbiDevice; + UINT i; + BOOLEAN LocalRoute; + USHORT TickCount; + PREQUEST RequestToComplete; + PUSHORT Counts; + CTELockHandle LockHandle1, LockHandle2; + CTELockHandle CancelLH; + + Connection = CONTAINING_RECORD (FindRouteRequest, CONNECTION, FindRouteRequest); + + NB_GET_CANCEL_LOCK(&CancelLH); + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + Connection->FindRouteInProgress = FALSE; + + if (FoundRoute) { + + // + // See if the route is local or not (for local routes + // we use the real MAC address in the local target, but + // the NIC ID may not be what we expect. + // + + LocalRoute = TRUE; + + for (i = 0; i < 6; i++) { + if (FindRouteRequest->LocalTarget.MacAddress[i] != 0x00) { + LocalRoute = FALSE; + } + } + + if (LocalRoute) { + +#if defined(_PNP_POWER) + Connection->LocalTarget.NicHandle = FindRouteRequest->LocalTarget.NicHandle; +#else + Connection->LocalTarget.NicId = FindRouteRequest->LocalTarget.NicId; +#endif _PNP_POWER + + } else { + + Connection->LocalTarget = FindRouteRequest->LocalTarget; + + } + + Counts = (PUSHORT)(&FindRouteRequest->Reserved2); + TickCount = Counts[0]; + + if (TickCount > 1) { + + // + // Each tick is 55 ms, and for our timeout we use 10 ticks + // worth (this makes tick count of 1 be about 500 ms, the + // default). + // + // We get 55 milliseconds from + // + // 1 second * 1000 milliseconds 55 ms + // -------- ----------------- = ----- + // 18.21 ticks 1 second tick + // + + Connection->TickCount = TickCount; + Connection->BaseRetransmitTimeout = (TickCount * 550) / SHORT_TIMER_DELTA; + if (Connection->State != CONNECTION_STATE_ACTIVE) { + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + } + } + + Connection->HopCount = Counts[1]; + + } + + // + // If the call failed we just use whatever route we had before + // (on a connect it will be from the name query response, on + // a listen from whatever the incoming connect frame had). + // + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState == CONNECTION_SUBSTATE_C_W_ROUTE)) { + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SWAP_IRQL( CancelLH, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + // + // Continue on with the session init frame. + // + + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_LINE_INFO, + +#if defined(_PNP_POWER) + &Connection->LocalTarget.NicHandle, +#else + Connection->LocalTarget.NicId, +#endif _PNP_POWER + &Connection->LineInfo, + sizeof(IPX_LINE_INFO), + NULL); + + // Maximum packet size is the lower of RouterMtu and MaximumSendSize. + Connection->MaximumPacketSize = NB_MIN( Device->RouterMtu - sizeof(IPX_HEADER) , Connection->LineInfo.MaximumSendSize ) - sizeof(NB_CONNECTION) ; + + Connection->ReceiveWindowSize = 6; + Connection->SendWindowSize = 2; + Connection->MaxSendWindowSize = 6; // BUGBUG: Base on what he sent + + // + // Don't set RcvSequenceMax yet because we don't know + // if the connection is old or new netbios. + // + + Connection->SubState = CONNECTION_SUBSTATE_C_W_ACK; + + // + // We found a route, we need to start the connect + // process by sending out the session initialize + // frame. We start the timer to handle retries. + // + // CTEStartTimer doesn't deal with changing the + // expiration time of a running timer, so we have + // to stop it first. If we succeed in stopping the + // timer, then the CREF_TIMER reference from the + // previous starting of the timer remains, so we + // don't need to reference the connection again. + // + + if (!CTEStopTimer (&Connection->Timer)) { + NbiReferenceConnectionLock (Connection, CREF_TIMER); + } + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + CTEStartTimer( + &Connection->Timer, + Device->ConnectionTimeout, + NbiConnectionTimeout, + (PVOID)Connection); + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + + NbiSendSessionInitialize (Connection); + + } else if ((Connection->State == CONNECTION_STATE_LISTENING) && + (Connection->SubState == CONNECTION_SUBSTATE_L_W_ROUTE)) { + + if (Connection->ListenRequest != NULL) { + + NbiTransferReferenceConnection (Connection, CREF_LISTEN, CREF_ACTIVE); + RequestToComplete = Connection->ListenRequest; + Connection->ListenRequest = NULL; + IoSetCancelRoutine (RequestToComplete, (PDRIVER_CANCEL)NULL); + + } else if (Connection->AcceptRequest != NULL) { + + NbiTransferReferenceConnection (Connection, CREF_ACCEPT, CREF_ACTIVE); + RequestToComplete = Connection->AcceptRequest; + Connection->AcceptRequest = NULL; + + } else { + + CTEAssert (FALSE); + RequestToComplete = NULL; + + } + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SWAP_IRQL( CancelLH, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_LINE_INFO, +#if defined(_PNP_POWER) + &Connection->LocalTarget.NicHandle, +#else + Connection->LocalTarget.NicId, +#endif _PNP_POWER + &Connection->LineInfo, + sizeof(IPX_LINE_INFO), + NULL); + + + // Take the lowest of MaximumPacketSize ( set from the sessionInit + // frame ), MaximumSendSize and RouterMtu. + + if (Connection->MaximumPacketSize > Connection->LineInfo.MaximumSendSize - sizeof(NB_CONNECTION)) { + + Connection->MaximumPacketSize = NB_MIN( Device->RouterMtu - sizeof(IPX_HEADER), Connection->LineInfo.MaximumSendSize ) - sizeof(NB_CONNECTION); + + } else { + + // Connection->MaximumPacketSize is what was set by the sender so already + // accounts for the header. + Connection->MaximumPacketSize = NB_MIN( Device->RouterMtu - sizeof(NB_CONNECTION) - sizeof(IPX_HEADER), Connection->MaximumPacketSize ) ; + + } + + Connection->ReceiveWindowSize = 6; + Connection->SendWindowSize = 2; + Connection->MaxSendWindowSize = 6; // BUGBUG: Base on what he sent + + if (Connection->NewNetbios) { + CTEAssert (Connection->LocalRcvSequenceMax == 4); // should have been set + Connection->LocalRcvSequenceMax = Connection->ReceiveWindowSize; + } + + Connection->State = CONNECTION_STATE_ACTIVE; + Connection->SubState = CONNECTION_SUBSTATE_A_IDLE; + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + + ++Device->Statistics.OpenConnections; + + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + // + // StartWatchdog acquires TimerLock, so we have to + // free Lock first. + // + + + NbiStartWatchdog (Connection); + + // + // This releases the connection lock, so that SessionInitAckData + // can't be freed before it is copied. + // + + NbiSendSessionInitAck( + Connection, + Connection->SessionInitAckData, + Connection->SessionInitAckDataLength, + &LockHandle1); + + if (RequestToComplete != NULL) { + + REQUEST_STATUS(RequestToComplete) = STATUS_SUCCESS; + + NbiCompleteRequest (RequestToComplete); + NbiFreeRequest (Device, RequestToComplete); + + } + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + } + + NbiDereferenceConnection (Connection, CREF_FIND_ROUTE); + +} /* NbiFindRouteComplete */ + + +NTSTATUS +NbiOpenConnection( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to open a connection. Note that the connection that + is open is of little use until associated with an address; until then, + the only thing that can be done with it is close it. + +Arguments: + + Device - Pointer to the device for this driver. + + Request - Pointer to the request representing the open. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PCONNECTION Connection; + PFILE_FULL_EA_INFORMATION ea; +#ifdef ISN_NT + PIRP Irp = (PIRP)Request; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); +#endif + + // + // First, try to make a connection object to represent this pending + // connection. Then fill in the relevant fields. + // In addition to the creation, if successful NbfCreateConnection + // will create a second reference which is removed once the request + // references the connection, or if the function exits before that. + + if (!(Connection = NbiCreateConnection (Device))) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // set the connection context so we can connect the user to this data + // structure + // + + ea = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + RtlCopyMemory ( + &Connection->Context, + &ea->EaName[ea->EaNameLength+1], + sizeof (PVOID)); + + // + // let file object point at connection and connection at file object + // + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)Connection; + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_CONNECTION_FILE; +#ifdef ISN_NT + Connection->FileObject = IrpSp->FileObject; +#endif + + return STATUS_SUCCESS; + +} /* NbiOpenConnection */ + + +VOID +NbiStopConnection( + IN PCONNECTION Connection, + IN NTSTATUS DisconnectStatus + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is called to stop an active connection. + + THIS ROUTINE IS CALLED WITH THE CONNECTION LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection to be stopped. + + DisconnectStatus - The reason for the disconnect. One of: + STATUS_LINK_FAILED: We timed out trying to probe the remote. + STATUS_REMOTE_DISCONNECT: The remote sent a session end. + STATUS_LOCAL_DISCONNECT: The local side disconnected. + STATUS_CANCELLED: A send or receive on this connection was cancelled. + STATUS_INVALID_CONNECTION: The local side closed the connection. + STATUS_INVALID_ADDRESS: The local side closed the address. + + LockHandle - The handle which the connection lock was acquired with. + +Return Value: + + None. + +--*/ + +{ + PREQUEST ListenRequest, AcceptRequest, SendRequest, ReceiveRequest, + DisconnectWaitRequest, ConnectRequest; + PREQUEST Request, TmpRequest; + BOOLEAN DerefForPacketize; + BOOLEAN DerefForWaitPacket; + BOOLEAN DerefForActive; + BOOLEAN DerefForWaitCache; + BOOLEAN SendSessionEnd; + BOOLEAN ActiveReceive; + BOOLEAN IndicateToClient; + BOOLEAN ConnectionWasActive; + PDEVICE Device = NbiDevice; + PADDRESS_FILE AddressFile; + NB_DEFINE_LOCK_HANDLE (LockHandle2) + NB_DEFINE_LOCK_HANDLE (LockHandle3) + CTELockHandle CancelLH; + + + NB_DEBUG2 (CONNECTION, ("Stop connection %lx (%lx)\n", Connection, DisconnectStatus)); + + // + // These flags control our actions after we set the state to + // DISCONNECT. + // + + DerefForPacketize = FALSE; + DerefForWaitPacket = FALSE; + DerefForActive = FALSE; + DerefForWaitCache = FALSE; + SendSessionEnd = FALSE; + ActiveReceive = FALSE; + IndicateToClient = FALSE; + ConnectionWasActive = FALSE; + + // + // These contain requests or queues of request to complete. + // + + ListenRequest = NULL; + AcceptRequest = NULL; + SendRequest = NULL; + ReceiveRequest = NULL; + DisconnectWaitRequest = NULL; + ConnectRequest = NULL; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + --Device->Statistics.OpenConnections; + + ConnectionWasActive = TRUE; + + Connection->Status = DisconnectStatus; + + if ((DisconnectStatus == STATUS_LINK_FAILED) || + (DisconnectStatus == STATUS_LOCAL_DISCONNECT)) { + + // + // Send out session end frames, but fewer if + // we timed out. + // + // BUGBUG: What about STATUS_CANCELLED? + // + + Connection->Retries = (DisconnectStatus == STATUS_LOCAL_DISCONNECT) ? + Device->ConnectionCount : + (Device->ConnectionCount / 2); + + SendSessionEnd = TRUE; + Connection->SubState = CONNECTION_SUBSTATE_D_W_ACK; + + // + // CTEStartTimer doesn't deal with changing the + // expiration time of a running timer, so we have + // to stop it first. If we succeed in stopping the + // timer, then the CREF_TIMER reference from the + // previous starting of the timer remains, so we + // don't need to reference the connection again. + // + + if (!CTEStopTimer (&Connection->Timer)) { + NbiReferenceConnectionLock (Connection, CREF_TIMER); + } + + CTEStartTimer( + &Connection->Timer, + Device->ConnectionTimeout, + NbiConnectionTimeout, + (PVOID)Connection); + + } + + if (Connection->ReceiveState == CONNECTION_RECEIVE_TRANSFER) { + ActiveReceive = TRUE; + } + + Connection->State = CONNECTION_STATE_DISCONNECT; + DerefForActive = TRUE; + + if (Connection->DisconnectWaitRequest != NULL) { + DisconnectWaitRequest = Connection->DisconnectWaitRequest; + Connection->DisconnectWaitRequest = NULL; + } + + if ((DisconnectStatus == STATUS_LINK_FAILED) || + (DisconnectStatus == STATUS_REMOTE_DISCONNECT) || + (DisconnectStatus == STATUS_CANCELLED)) { + + IndicateToClient = TRUE; + + } + + // + // If we are inside NbiAssignSequenceAndSend, add + // a reference so the connection won't go away during it. + // + + if (Connection->NdisSendsInProgress > 0) { + *(Connection->NdisSendReference) = TRUE; + NB_DEBUG2 (SEND, ("Adding CREF_NDIS_SEND to %lx\n", Connection)); + NbiReferenceConnectionLock (Connection, CREF_NDIS_SEND); + } + + // + // Clean up some other stuff. + // + + Connection->ReceiveUnaccepted = 0; + Connection->CurrentIndicateOffset = 0; + + // + // Update our counters. BUGBUG: Some of these we + // never use. + // + + switch (DisconnectStatus) { + + case STATUS_LOCAL_DISCONNECT: + ++Device->Statistics.LocalDisconnects; + break; + case STATUS_REMOTE_DISCONNECT: + ++Device->Statistics.RemoteDisconnects; + break; + case STATUS_LINK_FAILED: + ++Device->Statistics.LinkFailures; + break; + case STATUS_IO_TIMEOUT: + ++Device->Statistics.SessionTimeouts; + break; + case STATUS_CANCELLED: + ++Device->Statistics.CancelledConnections; + break; + case STATUS_REMOTE_RESOURCES: + ++Device->Statistics.RemoteResourceFailures; + break; + case STATUS_INVALID_CONNECTION: + case STATUS_INVALID_ADDRESS: + case STATUS_INSUFFICIENT_RESOURCES: + ++Device->Statistics.LocalResourceFailures; + break; + case STATUS_BAD_NETWORK_PATH: + case STATUS_REMOTE_NOT_LISTENING: + ++Device->Statistics.NotFoundFailures; + break; + default: + CTEAssert(FALSE); + break; + } + + } else if (Connection->State == CONNECTION_STATE_CONNECTING) { + + // + // There is a connect in progress. We have to find ourselves + // in the pending connect queue if we are there. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_C_FIND_NAME) { + RemoveEntryList (REQUEST_LINKAGE(Connection->ConnectRequest)); + DerefForWaitCache = TRUE; + } + + if (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN) { + + ConnectRequest = Connection->ConnectRequest; + Connection->ConnectRequest = NULL; + + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + } + + } + + + // + // If we allocated this memory, free it. + // + + if (Connection->SessionInitAckDataLength > 0) { + + NbiFreeMemory( + Connection->SessionInitAckData, + Connection->SessionInitAckDataLength, + MEMORY_CONNECTION, + "SessionInitAckData"); + Connection->SessionInitAckData = NULL; + Connection->SessionInitAckDataLength = 0; + + } + + + if (Connection->ListenRequest != NULL) { + + ListenRequest = Connection->ListenRequest; + Connection->ListenRequest = NULL; + RemoveEntryList (REQUEST_LINKAGE(ListenRequest)); // take out of Device->ListenQueue + + } + + if (Connection->AcceptRequest != NULL) { + + AcceptRequest = Connection->AcceptRequest; + Connection->AcceptRequest = NULL; + + } + + + // + // BUGBUG: Do we need to stop the connection timer? + // I don't think so. + // + + + + // + // A lot of this we only have to tear down if we were + // active before this, because once we are stopping nothing + // new will get started. BUGBUG: Some of the other stuff + // can be put inside this if also. + // + + if (ConnectionWasActive) { + + // + // Stop any receives. If there is one that is actively + // transferring we leave it and just run down the rest + // of the queue. If not, we queue the rest of the + // queue on the back of the current one and run + // down them all. + // + + if (ActiveReceive) { + + ReceiveRequest = Connection->ReceiveQueue.Head; + + // + // Connection->ReceiveRequest will get set to NULL + // when the transfer completes. + // + + } else { + + ReceiveRequest = Connection->ReceiveRequest; + if (ReceiveRequest) { + REQUEST_SINGLE_LINKAGE (ReceiveRequest) = Connection->ReceiveQueue.Head; + } else { + ReceiveRequest = Connection->ReceiveQueue.Head; + } + Connection->ReceiveRequest = NULL; + + } + + Connection->ReceiveQueue.Head = NULL; + + + if ((Request = Connection->FirstMessageRequest) != NULL) { + + // + // If the current request has some sends outstanding, then + // we dequeue it from the queue to let it complete when + // the sends complete. In that case we set SendRequest + // to be the rest of the queue, which will be aborted. + // If the current request has no sends, then we put + // queue everything to SendRequest to be aborted below. + // + +#if DBG + if (REQUEST_REFCOUNT(Request) > 100) { + DbgPrint ("Request %lx (%lx) has high refcount\n", + Connection, Request); + DbgBreakPoint(); + } +#endif + if (--REQUEST_REFCOUNT(Request) == 0) { + + // + // NOTE: If this is a multi-request message, then + // the linkage of Request will already point to the + // send queue head, but we don't bother checking. + // + + SendRequest = Request; + REQUEST_SINGLE_LINKAGE (Request) = Connection->SendQueue.Head; + + } else { + + if (Connection->FirstMessageRequest == Connection->LastMessageRequest) { + + REQUEST_SINGLE_LINKAGE (Request) = NULL; + + } else { + + Connection->SendQueue.Head = REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest); + REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest) = NULL; + + } + + SendRequest = Connection->SendQueue.Head; + + } + + Connection->FirstMessageRequest = NULL; + + } else { + + // + // This may happen if we were sending a probe when a + // send was submitted, and the probe timed out. + // + + SendRequest = Connection->SendQueue.Head; + + } + + Connection->SendQueue.Head = NULL; + + } + + + if (Connection->OnWaitPacketQueue) { + Connection->OnWaitPacketQueue = FALSE; + RemoveEntryList (&Connection->WaitPacketLinkage); + DerefForWaitPacket = TRUE; + } + + if (Connection->OnPacketizeQueue) { + Connection->OnPacketizeQueue = FALSE; + RemoveEntryList (&Connection->PacketizeLinkage); + DerefForPacketize = TRUE; + } + + // + // BUGBUG: Should we check if DataAckPending is TRUE and + // send an ack?? + // + + Connection->DataAckPending = FALSE; + Connection->PiggybackAckTimeout = FALSE; + Connection->ReceivesWithoutAck = 0; + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + // + // We can't acquire TimerLock with Lock held, since + // we sometimes call ReferenceConnection (which does an + // interlocked add using Lock) with TimerLock held. + // + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle3); + + if (Connection->OnShortList) { + Connection->OnShortList = FALSE; + RemoveEntryList (&Connection->ShortList); + } + + if (Connection->OnLongList) { + Connection->OnLongList = FALSE; + RemoveEntryList (&Connection->LongList); + } + + if (Connection->OnDataAckQueue) { + Connection->OnDataAckQueue = FALSE; + RemoveEntryList (&Connection->DataAckLinkage); + Device->DataAckQueueChanged = TRUE; + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle3); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + + if (IndicateToClient) { + + AddressFile = Connection->AddressFile; + + if (AddressFile->RegisteredHandler[TDI_EVENT_DISCONNECT]) { + + NB_DEBUG2 (CONNECTION, ("Session end indicated on connection %lx\n", Connection)); + + (*AddressFile->DisconnectHandler)( + AddressFile->HandlerContexts[TDI_EVENT_DISCONNECT], + Connection->Context, + 0, // DisconnectData + NULL, + 0, // DisconnectInformation + NULL, + TDI_DISCONNECT_RELEASE); // DisconnectReason. BUGBUG: Clean it up? + + } + + } + + + if (DisconnectWaitRequest != NULL) { + + // + // Make the TDI tester happy by returning CONNECTION_RESET + // here. + // + + if (DisconnectStatus == STATUS_REMOTE_DISCONNECT) { + REQUEST_STATUS(DisconnectWaitRequest) = STATUS_CONNECTION_RESET; + } else { + REQUEST_STATUS(DisconnectWaitRequest) = DisconnectStatus; + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (DisconnectWaitRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest (DisconnectWaitRequest); + NbiFreeRequest (Device, DisconnectWaitRequest); + + } + + if (ConnectRequest != NULL) { + + REQUEST_STATUS (ConnectRequest) = STATUS_LOCAL_DISCONNECT; + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (ConnectRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest(ConnectRequest); + NbiFreeRequest (Device, ConnectRequest); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + + } + + if (ListenRequest != NULL) { + + REQUEST_INFORMATION(ListenRequest) = 0; + REQUEST_STATUS(ListenRequest) = STATUS_LOCAL_DISCONNECT; + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (ListenRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest (ListenRequest); + NbiFreeRequest(Device, ListenRequest); + + NbiDereferenceConnection (Connection, CREF_LISTEN); + + } + + if (AcceptRequest != NULL) { + + REQUEST_INFORMATION(AcceptRequest) = 0; + REQUEST_STATUS(AcceptRequest) = STATUS_LOCAL_DISCONNECT; + + NbiCompleteRequest (AcceptRequest); + NbiFreeRequest(Device, AcceptRequest); + + NbiDereferenceConnection (Connection, CREF_ACCEPT); + + } + + while (ReceiveRequest != NULL) { + + TmpRequest = REQUEST_SINGLE_LINKAGE (ReceiveRequest); + + REQUEST_STATUS (ReceiveRequest) = DisconnectStatus; + REQUEST_INFORMATION (ReceiveRequest) = 0; + + NB_DEBUG2 (RECEIVE, ("StopConnection aborting receive %lx\n", ReceiveRequest)); + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (ReceiveRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest (ReceiveRequest); + NbiFreeRequest (Device, ReceiveRequest); + + ++Connection->ConnectionInfo.ReceiveErrors; + + ReceiveRequest = TmpRequest; + + NbiDereferenceConnection (Connection, CREF_RECEIVE); + + } + + while (SendRequest != NULL) { + + TmpRequest = REQUEST_SINGLE_LINKAGE (SendRequest); + + REQUEST_STATUS (SendRequest) = DisconnectStatus; + REQUEST_INFORMATION (SendRequest) = 0; + + NB_DEBUG2 (SEND, ("StopConnection aborting send %lx\n", SendRequest)); + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (SendRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiCompleteRequest (SendRequest); + NbiFreeRequest (Device, SendRequest); + + ++Connection->ConnectionInfo.TransmissionErrors; + + SendRequest = TmpRequest; + + NbiDereferenceConnection (Connection, CREF_SEND); + + } + + if (SendSessionEnd) { + NbiSendSessionEnd (Connection); + } + + if (DerefForWaitCache) { + NbiDereferenceConnection (Connection, CREF_WAIT_CACHE); + } + + if (DerefForPacketize) { + NbiDereferenceConnection (Connection, CREF_PACKETIZE); + } + + if (DerefForWaitPacket) { + NbiDereferenceConnection (Connection, CREF_W_PACKET); + } + + if (DerefForActive) { + NbiDereferenceConnection (Connection, CREF_ACTIVE); + } + +} /* NbiStopConnection */ + + +NTSTATUS +NbiCloseConnection( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine is called to close a connection. + +Arguments: + + Device - Pointer to the device for this driver. + + Request - Pointer to the request representing the open. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + PADDRESS_FILE AddressFile; + PADDRESS Address; + CTELockHandle LockHandle; + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_DEBUG2 (CONNECTION, ("Close connection %lx\n", Connection)); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if (Connection->ReferenceCount == 0) { + + // + // If we are associated with an address, we need + // to simulate a disassociate at this point. + // + + if ((Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1)) { + + AddressFile = Connection->AddressFile; + Connection->AddressFile = (PVOID)-1; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Take this connection out of the address file's list. + // + + Address = AddressFile->Address; + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFileLinked) { + Connection->AddressFileLinked = FALSE; + RemoveEntryList (&Connection->AddressFileLinkage); + } + + // + // We are done. + // + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + Connection->AddressFile = NULL; + + // + // Clean up the reference counts and complete any + // disassociate requests that pended. + // + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + } + + // + // Even if the ref count is zero and some thread has already done cleanup, + // we can not destroy the connection bcoz some other thread might still be + // in HandleConnectionZero routine. This could happen when 2 threads call into + // HandleConnectionZero, one thread runs thru completion, close comes along + // and the other thread is still in HandleConnectionZero routine. + // + + if ( Connection->CanBeDestroyed && ( Connection->ThreadsInHandleConnectionZero == 0 ) ) { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + NbiDestroyConnection(Connection); + Status = STATUS_SUCCESS; + + } else { + + Connection->ClosePending = Request; + NB_FREE_LOCK (&Device->Lock, LockHandle); + Status = STATUS_PENDING; + + } + + } else { + + Connection->ClosePending = Request; + NB_FREE_LOCK (&Device->Lock, LockHandle); + Status = STATUS_PENDING; + + } + + return Status; + +} /* NbiCloseConnection */ + + +NTSTATUS +NbiTdiAssociateAddress( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the association of the connection and + the address for the user. + +Arguments: + + Device - The netbios device. + + Request - The request describing the associate. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; +#ifdef ISN_NT + PFILE_OBJECT FileObject; +#endif + PADDRESS_FILE AddressFile; + PADDRESS Address; + PTDI_REQUEST_KERNEL_ASSOCIATE Parameters; + CTELockHandle LockHandle; + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + + // + // The request request parameters hold + // get a pointer to the address FileObject, which points us to the + // transport's address object, which is where we want to put the + // connection. + // + + Parameters = (PTDI_REQUEST_KERNEL_ASSOCIATE)REQUEST_PARAMETERS(Request); + +#ifdef ISN_NT + + Status = ObReferenceObjectByHandle ( + Parameters->AddressHandle, + 0L, + 0, + KernelMode, + (PVOID *)&FileObject, + NULL); + + if (!NT_SUCCESS(Status)) { + NbiDereferenceConnection (Connection, CREF_VERIFY); + return Status; + } + + AddressFile = (PADDRESS_FILE)(FileObject->FsContext); + +#else + + // + // I don't know how this works in a VxD. + // + + AddressFile = (PADDRESS_FILE)(Parameters->AddressHandle); + +#endif + + // + // Make sure the address file is valid, and reference it. + // + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS(Status)) { + +#ifdef ISN_NT + ObDereferenceObject (FileObject); +#endif + NbiDereferenceConnection (Connection, CREF_VERIFY); + return Status; + } + + NB_DEBUG2 (CONNECTION, ("Associate connection %lx with address file %lx\n", + Connection, AddressFile)); + + + // + // Now insert the connection into the database of the address. + // + + Address = AddressFile->Address; + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFile != NULL) { + + // + // The connection is already associated with + // an address file. + // + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + Status = STATUS_INVALID_CONNECTION; + + } else { + + if (AddressFile->State == ADDRESSFILE_STATE_OPEN) { + + Connection->AddressFile = AddressFile; + Connection->AddressFileLinked = TRUE; + InsertHeadList (&AddressFile->ConnectionDatabase, &Connection->AddressFileLinkage); + NB_FREE_LOCK (&Address->Lock, LockHandle); + + NbiTransferReferenceAddressFile (AddressFile, AFREF_VERIFY, AFREF_CONNECTION); + Status = STATUS_SUCCESS; + + } else { + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + Status = STATUS_INVALID_ADDRESS; + } + + } + +#ifdef ISN_NT + + // + // We don't need the reference to the file object, we just + // used it to get from the handle to the object. + // + + ObDereferenceObject (FileObject); + +#endif + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiAssociateAddress */ + + +NTSTATUS +NbiTdiDisassociateAddress( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the disassociation of the connection + and the address for the user. + +Arguments: + + Device - The netbios device. + + Request - The request describing the associate. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PCONNECTION Connection; + NTSTATUS Status; + PADDRESS_FILE AddressFile; + PADDRESS Address; + CTELockHandle LockHandle; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_DEBUG2 (CONNECTION, ("Disassociate connection %lx\n", Connection)); + + + // + // First check if the connection is still active. + // + + NB_BEGIN_SYNC (&SyncContext); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + if (Connection->State != CONNECTION_STATE_INACTIVE) { + + // + // This releases the lock. + // + + NbiStopConnection( + Connection, + STATUS_INVALID_ADDRESS + NB_LOCK_HANDLE_ARG (LockHandle1)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + } + + // + // BUGBUG: Keep the sync through the function?? + // + + NB_END_SYNC (&SyncContext); + + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + // + // Make sure the connection is associated and is not in the + // middle of disassociating. + // + + if ((Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1) && + (Connection->DisassociatePending == NULL)) { + + if (Connection->ReferenceCount == 0) { + + // + // Because the connection still has a reference to + // the address file, we know it is still valid. We + // set the connection address file to the temporary + // value of -1, which prevents somebody else from + // disassociating it and also prevents a new association. + // + + AddressFile = Connection->AddressFile; + Connection->AddressFile = (PVOID)-1; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + Address = AddressFile->Address; + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFileLinked) { + Connection->AddressFileLinked = FALSE; + RemoveEntryList (&Connection->AddressFileLinkage); + } + NB_FREE_LOCK (&Address->Lock, LockHandle); + + Connection->AddressFile = NULL; + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + Status = STATUS_SUCCESS; + + } else { + + // + // Set this so when the count goes to 0 it will + // be disassociated and the request completed. + // + + Connection->DisassociatePending = Request; + NB_FREE_LOCK (&Device->Lock, LockHandle); + Status = STATUS_PENDING; + + } + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + Status = STATUS_INVALID_CONNECTION; + + } + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiDisassociateAddress */ + + +NTSTATUS +NbiTdiListen( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine posts a listen on a connection. + +Arguments: + + Device - The netbios device. + + Request - The request describing the listen. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + CTELockHandle CancelLH; + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + // + // The connection must be inactive, but associated and + // with no disassociate or close pending. + // + + if ((Connection->State == CONNECTION_STATE_INACTIVE) && + (Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1) && + (Connection->DisassociatePending == NULL) && + (Connection->ClosePending == NULL)) { + + Connection->State = CONNECTION_STATE_LISTENING; + Connection->SubState = CONNECTION_SUBSTATE_L_WAITING; + + (VOID)NbiAssignConnectionId (Device, Connection); // BUGBUG: Check return code. + + + if (!Request->Cancel) { + + NB_DEBUG2 (CONNECTION, ("Queued listen %lx on %lx\n", Request, Connection)); + InsertTailList (&Device->ListenQueue, REQUEST_LINKAGE(Request)); + IoSetCancelRoutine (Request, NbiCancelListen); + Connection->ListenRequest = Request; + NbiReferenceConnectionLock (Connection, CREF_LISTEN); + Status = STATUS_PENDING; + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelled listen %lx on %lx\n", Request, Connection)); + Connection->State = CONNECTION_STATE_INACTIVE; + Status = STATUS_CANCELLED; + } + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + Status = STATUS_INVALID_CONNECTION; + + } + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiListen */ + + +NTSTATUS +NbiTdiAccept( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine accepts a connection to a remote machine. The + connection must previously have completed a listen with + the TDI_QUERY_ACCEPT flag on. + +Arguments: + + Device - The netbios device. + + Request - The request describing the accept. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + if ((Connection->State == CONNECTION_STATE_LISTENING) && + (Connection->SubState == CONNECTION_SUBSTATE_L_W_ACCEPT)) { + + Connection->SubState = CONNECTION_SUBSTATE_L_W_ROUTE; + + NbiTransferReferenceConnection (Connection, CREF_W_ACCEPT, CREF_ACCEPT); + Connection->AcceptRequest = Request; + + NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + Connection->Retries = NbiDevice->KeepAliveCount; + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = + *(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork; + RtlCopyMemory(Connection->FindRouteRequest.Node,Connection->RemoteHeader.DestinationNode,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_NO_RIP; + + // + // When this completes, we will send the session init + // ack. We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // The accept is completed when this completes. + // + + if (*(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + NB_DEBUG2 (CONNECTION, ("Accept received on %lx\n", Connection)); + + Status = STATUS_PENDING; + + } else { + + NB_DEBUG (CONNECTION, ("Accept received on invalid connection %lx\n", Connection)); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + Status = STATUS_INVALID_CONNECTION; + + } + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiAccept */ + + +NTSTATUS +NbiTdiConnect( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine connects to a remote machine. + +Arguments: + + Device - The netbios device. + + Request - The request describing the connect. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteName; + PTDI_REQUEST_KERNEL_CONNECT Parameters; +#if 0 + PLARGE_INTEGER RequestedTimeout; + LARGE_INTEGER RealTimeout; +#endif + PNETBIOS_CACHE CacheName; + CTELockHandle LockHandle1, LockHandle2; + CTELockHandle CancelLH; + BOOLEAN bLockFreed = FALSE; + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + // + // The connection must be inactive, but associated and + // with no disassociate or close pending. + // + + if ((Connection->State == CONNECTION_STATE_INACTIVE) && + (Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1) && + (Connection->DisassociatePending == NULL) && + (Connection->ClosePending == NULL)) { + + Parameters = (PTDI_REQUEST_KERNEL_CONNECT)REQUEST_PARAMETERS(Request); + RemoteName = NbiParseTdiAddress((PTRANSPORT_ADDRESS)(Parameters->RequestConnectionInformation->RemoteAddress), FALSE); + + if (RemoteName == NULL) { + + // + // There is no netbios remote address specified. + // + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + Status = STATUS_BAD_NETWORK_PATH; + + } else { + + NbiReferenceConnectionLock (Connection, CREF_CONNECT); + Connection->State = CONNECTION_STATE_CONNECTING; + RtlCopyMemory (Connection->RemoteName, RemoteName->NetbiosName, 16); + + Connection->Retries = Device->ConnectionCount; + + (VOID)NbiAssignConnectionId (Device, Connection); // BUGBUG: Check return code. + + Status = NbiTdiConnectFindName( + Device, + Request, + Connection, + CancelLH, + LockHandle1, + LockHandle2, + &bLockFreed); + + } + + } else { + + NB_DEBUG (CONNECTION, ("Connect on invalid connection %lx\n", Connection)); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + Status = STATUS_INVALID_CONNECTION; + + } + + if (!bLockFreed) { + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + } + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiConnect */ + + +NTSTATUS +NbiTdiConnectFindName( + IN PDEVICE Device, + IN PREQUEST Request, + IN PCONNECTION Connection, + IN CTELockHandle CancelLH, + IN CTELockHandle ConnectionLH, + IN CTELockHandle DeviceLH, + IN PBOOLEAN pbLockFreed + ) +{ + NTSTATUS Status; + PNETBIOS_CACHE CacheName; + + // + // See what is up with this Netbios name. + // + + Status = CacheFindName( + Device, + FindNameConnect, + Connection->RemoteName, + &CacheName); + + if (Status == STATUS_PENDING) { + + // + // A request for routes to this name has been + // sent out on the net, we queue up this connect + // request and processing will be resumed when + // we get a response. + // + + Connection->SubState = CONNECTION_SUBSTATE_C_FIND_NAME; + + + if (!Request->Cancel) { + + InsertTailList( &Device->WaitingConnects, REQUEST_LINKAGE(Request)); + IoSetCancelRoutine (Request, NbiCancelConnectFindName); + Connection->ConnectRequest = Request; + NbiReferenceConnectionLock (Connection, CREF_WAIT_CACHE); + NB_DEBUG2 (CONNECTION, ("Queueing up connect %lx on %lx\n", + Request, Connection)); + + NB_FREE_LOCK (&Device->Lock, DeviceLH); + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelled connect %lx on %lx\n", Request, Connection)); + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + NB_FREE_LOCK (&Device->Lock, DeviceLH); + NbiDereferenceConnection (Connection, CREF_CONNECT); + + Status = STATUS_CANCELLED; + } + + } else if (Status == STATUS_SUCCESS) { + + // + // We don't need to worry about referencing CacheName + // because we stop using it before we release the lock. + // + + Connection->SubState = CONNECTION_SUBSTATE_C_W_ROUTE; + + + if (!Request->Cancel) { + + IoSetCancelRoutine (Request, NbiCancelConnectWaitResponse); + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SWAP_IRQL( CancelLH, ConnectionLH); + NB_FREE_CANCEL_LOCK( CancelLH ); + + Connection->LocalTarget = CacheName->Networks[0].LocalTarget; + RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, &CacheName->FirstResponse, 12); + + Connection->ConnectRequest = Request; + NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE); + + NB_DEBUG2 (CONNECTION, ("Found connect cached %lx on %lx\n", + Request, Connection)); + + NB_FREE_LOCK (&Device->Lock, DeviceLH); + NB_FREE_LOCK (&Connection->Lock, ConnectionLH); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = CacheName->FirstResponse.NetworkAddress; + RtlCopyMemory(Connection->FindRouteRequest.Node,CacheName->FirstResponse.NodeAddress,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_RIP_IF_NEEDED; + + // + // When this completes, we will send the session init. + // We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // + + if (CacheName->FirstResponse.NetworkAddress != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + Status = STATUS_PENDING; + + // + // This jump is like falling out of the if, except + // it skips over freeing the connection lock since + // we just did that. + // + + *pbLockFreed = TRUE; + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelled connect %lx on %lx\n", Request, Connection)); + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + NB_FREE_LOCK (&Device->Lock, DeviceLH); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + + Status = STATUS_CANCELLED; + } + + } else { + + // + // We could not find or queue a request for + // this remote, fail it. When the refcount + // drops the state will go to INACTIVE and + // the connection ID will be deassigned. + // + + if (Status == STATUS_DEVICE_DOES_NOT_EXIST) { + Status = STATUS_BAD_NETWORK_PATH; + } + + NB_FREE_LOCK (&Device->Lock, DeviceLH); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + } + + return Status; +} /* NbiTdiConnectFindName */ + + +NTSTATUS +NbiTdiDisconnect( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine connects to a remote machine. + +Arguments: + + Device - The netbios device. + + Request - The request describing the connect. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PCONNECTION Connection; + BOOLEAN DisconnectWait; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_LOCK_HANDLE (LockHandle2) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + CTELockHandle CancelLH; + + + // + // Check that the connection is valid. This references + // the connection. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + if (!NT_SUCCESS (Status)) { + return Status; + } + + DisconnectWait = (BOOLEAN) + ((((PTDI_REQUEST_KERNEL_DISCONNECT)(REQUEST_PARAMETERS(Request)))->RequestFlags & + TDI_DISCONNECT_WAIT) != 0); + + NB_GET_CANCEL_LOCK( &CancelLH ); + + // + // We need to be inside a sync because NbiStopConnection + // expects that. + // + + NB_BEGIN_SYNC (&SyncContext); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2); + + if (DisconnectWait) { + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + // + // This disconnect wait will get completed by + // NbiStopConnection. + // + + if (Connection->DisconnectWaitRequest == NULL) { + + + if (!Request->Cancel) { + + IoSetCancelRoutine (Request, NbiCancelDisconnectWait); + NB_DEBUG2 (CONNECTION, ("Disconnect wait queued on connection %lx\n", Connection)); + Connection->DisconnectWaitRequest = Request; + Status = STATUS_PENDING; + + } else { + + NB_DEBUG2 (CONNECTION, ("Cancelled disconnect wait on connection %lx\n", Connection)); + Status = STATUS_CANCELLED; + } + + } else { + + // + // We got a second disconnect request and we already + // have one pending. + // + + NB_DEBUG (CONNECTION, ("Disconnect wait failed, already queued on connection %lx\n", Connection)); + Status = STATUS_INVALID_CONNECTION; + + } + + } else if (Connection->State == CONNECTION_STATE_DISCONNECT) { + + NB_DEBUG (CONNECTION, ("Disconnect wait submitted on disconnected connection %lx\n", Connection)); + Status = Connection->Status; + + } else { + + NB_DEBUG (CONNECTION, ("Disconnect wait failed, bad state on connection %lx\n", Connection)); + Status = STATUS_INVALID_CONNECTION; + + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + } else { + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + Connection->DisconnectRequest = Request; + Status = STATUS_PENDING; + + NB_DEBUG2 (CONNECTION, ("Disconnect of active connection %lx\n", Connection)); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + + // + // This call releases the connection lock, sets + // the state to DISCONNECTING, and sends out + // the first session end. + // + + NbiStopConnection( + Connection, + STATUS_LOCAL_DISCONNECT + NB_LOCK_HANDLE_ARG (LockHandle1)); + + } else if (Connection->State == CONNECTION_STATE_DISCONNECT) { + + // + // There is already a disconnect pending. Queue + // this one up so it completes when the refcount + // goes to zero. + // + + NB_DEBUG2 (CONNECTION, ("Disconnect of disconnecting connection %lx\n", Connection)); + + if (Connection->DisconnectRequest == NULL) { + Connection->DisconnectRequest = Request; + Status = STATUS_PENDING; + } else { + Status = STATUS_SUCCESS; + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + } else if ((Connection->State == CONNECTION_STATE_LISTENING) && + (Connection->SubState == CONNECTION_SUBSTATE_L_W_ACCEPT)) { + + // + // We were waiting for an accept, but instead we got + // a disconnect. Remove the reference and the teardown + // will proceed. The disconnect will complete when the + // refcount goes to zero. + // + + NB_DEBUG2 (CONNECTION, ("Disconnect of accept pending connection %lx\n", Connection)); + + if (Connection->DisconnectRequest == NULL) { + Connection->DisconnectRequest = Request; + Status = STATUS_PENDING; + } else { + Status = STATUS_SUCCESS; + } + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK ( CancelLH ); + + NbiDereferenceConnection (Connection, CREF_W_ACCEPT); + + } else if (Connection->State == CONNECTION_STATE_CONNECTING) { + + // we dont need to hold CancelSpinLock so release it, + // since we are releasing the locks out of order, we must + // swap the irql to get the priorities right. + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + // + // We are connecting, and got a disconnect. We call + // NbiStopConnection which will handle this case + // and abort the connect. + // + + NB_DEBUG2 (CONNECTION, ("Disconnect of connecting connection %lx\n", Connection)); + + if (Connection->DisconnectRequest == NULL) { + Connection->DisconnectRequest = Request; + Status = STATUS_PENDING; + } else { + Status = STATUS_SUCCESS; + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + // + // This call releases the connection lock and + // aborts the connect request. + // + + NbiStopConnection( + Connection, + STATUS_LOCAL_DISCONNECT + NB_LOCK_HANDLE_ARG (LockHandle1)); + + } else { + + NB_DEBUG2 (CONNECTION, ("Disconnect of invalid connection (%d) %lx\n", + Connection->State, Connection)); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + NB_FREE_CANCEL_LOCK( CancelLH ); + + Status = STATUS_INVALID_CONNECTION; + + } + + } + + NB_END_SYNC (&SyncContext); + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + return Status; + +} /* NbiTdiDisconnect */ + + +BOOLEAN +NbiAssignConnectionId( + IN PDEVICE Device, + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to assign a connection ID. It picks + one whose hash table has the fewest entries. + + THIS ROUTINE IS CALLED WITH THE LOCK HELD AND RETURNS WITH + IT HELD. THE CONNECTION IS INSERTED INTO THE CORRECT HASH + ENTRY BY THIS CALL. + +Arguments: + + Device - The netbios device. + + Connection - The connection that needs an ID assigned. + +Return Value: + + TRUE if it could be successfully assigned. + +--*/ + +{ + UINT Hash; + UINT i; + USHORT ConnectionId, HashId; + PCONNECTION CurConnection; + + + CTEAssert (Connection->LocalConnectionId == 0xffff); + + // + // Find the hash bucket with the fewest entries. + // + + Hash = 0; + for (i = 1; i < CONNECTION_HASH_COUNT; i++) { + if (Device->ConnectionHash[i].ConnectionCount < Device->ConnectionHash[Hash].ConnectionCount) { + Hash = i; + } + } + + + // + // Now find a valid connection ID within that bucket. + // + + ConnectionId = Device->ConnectionHash[Hash].NextConnectionId; + + while (TRUE) { + + // + // Scan through the list to see if this ID is in use. + // + + HashId = (USHORT)(ConnectionId | (Hash << CONNECTION_HASH_SHIFT)); + + CurConnection = Device->ConnectionHash[Hash].Connections; + + while (CurConnection != NULL) { + if (CurConnection->LocalConnectionId != HashId) { + CurConnection = CurConnection->NextConnection; + } else { + break; + } + } + + if (CurConnection == NULL) { + break; + } + + if (ConnectionId >= CONNECTION_MAXIMUM_ID) { + ConnectionId = 1; + } else { + ++ConnectionId; + } + + // + // BUGBUG: What if we have 64K-1 sessions and loop forever? + // + } + + if (Device->ConnectionHash[Hash].NextConnectionId >= CONNECTION_MAXIMUM_ID) { + Device->ConnectionHash[Hash].NextConnectionId = 1; + } else { + ++Device->ConnectionHash[Hash].NextConnectionId; + } + + Connection->LocalConnectionId = HashId; + Connection->RemoteConnectionId = 0xffff; + NB_DEBUG2 (CONNECTION, ("Assigned ID %lx to %x\n", Connection->LocalConnectionId, Connection)); + + Connection->NextConnection = Device->ConnectionHash[Hash].Connections; + Device->ConnectionHash[Hash].Connections = Connection; + ++Device->ConnectionHash[Hash].ConnectionCount; + + return TRUE; + +} /* NbiAssignConnectionId */ + + +VOID +NbiDeassignConnectionId( + IN PDEVICE Device, + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to deassign a connection ID. It removes + the connection from the hash bucket for its ID. + + THIS ROUTINE IS CALLED WITH THE LOCK HELD AND RETURNS WITH + IT HELD. + +Arguments: + + Device - The netbios device. + + Connection - The connection that needs an ID assigned. + +Return Value: + + None. + +--*/ + +{ + UINT Hash; + PCONNECTION CurConnection; + PCONNECTION * PrevConnection; + + // + // Make sure the connection has a valid ID. + // + + CTEAssert (Connection->LocalConnectionId != 0xffff); + + Hash = (Connection->LocalConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + CurConnection = Device->ConnectionHash[Hash].Connections; + PrevConnection = &Device->ConnectionHash[Hash].Connections; + + while (TRUE) { + + CTEAssert (CurConnection != NULL); + + // + // We can loop until we find it because it should be + // on here. + // + + if (CurConnection == Connection) { + *PrevConnection = Connection->NextConnection; + --Device->ConnectionHash[Hash].ConnectionCount; + break; + } + + PrevConnection = &CurConnection->NextConnection; + CurConnection = CurConnection->NextConnection; + + } + + Connection->LocalConnectionId = 0xffff; + +} /* NbiDeassignConnectionId */ + + +VOID +NbiConnectionTimeout( + IN CTEEvent * Event, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the connection timer expires. + This is either because we need to send the next session + initialize, or because our listen has timed out. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the connection. + +Return Value: + + None. + +--*/ + +{ + PCONNECTION Connection = (PCONNECTION)Context; + PDEVICE Device = NbiDevice; + PREQUEST Request; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_LOCK_HANDLE (CancelLH) + + // + // Take the lock and see what we need to do. + // + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN)) { + + if (--Connection->Retries == 0) { + + NB_DEBUG2 (CONNECTION, ("Timing out session initializes on %lx\n", Connection)); + + // + // We have just timed out this connect, we fail the + // request. When the reference count goes to 0 we + // will set the state to INACTIVE and deassign + // the connection ID. + // + + Request = Connection->ConnectRequest; + Connection->ConnectRequest = NULL; + + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + REQUEST_STATUS (Request) = STATUS_BAD_NETWORK_PATH; + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + NbiDereferenceConnection (Connection, CREF_TIMER); + + } else { + + // + // Send the next session initialize. + // + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NbiSendSessionInitialize (Connection); + + CTEStartTimer( + &Connection->Timer, + Device->ConnectionTimeout, + NbiConnectionTimeout, + (PVOID)Connection); + + } + + } else if (Connection->State == CONNECTION_STATE_DISCONNECT) { + + if ((Connection->SubState != CONNECTION_SUBSTATE_D_W_ACK) || + (--Connection->Retries == 0)) { + + NB_DEBUG2 (CONNECTION, ("Timing out disconnect of %lx\n", Connection)); + + // + // Just dereference the connection, that will cause the + // disconnect to be completed, the state to be set + // to INACTIVE, and our connection ID deassigned. + // + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NbiDereferenceConnection (Connection, CREF_TIMER); + + } else { + + // + // Send the next session end. + // + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NbiSendSessionEnd(Connection); + + CTEStartTimer( + &Connection->Timer, + Device->ConnectionTimeout, + NbiConnectionTimeout, + (PVOID)Connection); + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_TIMER); + + } + +} /* NbiConnectionTimeout */ + + +VOID +NbiCancelListen( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a posted + listen. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request = (PREQUEST)Irp; + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_LISTEN)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + + if ((Connection->State == CONNECTION_STATE_LISTENING) && + (Connection->SubState == CONNECTION_SUBSTATE_L_WAITING) && + (Connection->ListenRequest == Request)) { + + // + // When the reference count goes to 0, we will set the + // state to INACTIVE and deassign the connection ID. + // + + NB_DEBUG2 (CONNECTION, ("Cancelled listen on %lx\n", Connection)); + + NB_GET_LOCK (&Device->Lock, &LockHandle2); + Connection->ListenRequest = NULL; + RemoveEntryList (REQUEST_LINKAGE(Request)); + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + NbiCompleteRequest (Request); + NbiFreeRequest(Device, Request); + + NbiDereferenceConnection (Connection, CREF_LISTEN); + + } else { + + NB_DEBUG (CONNECTION, ("Cancel listen on invalid connection %lx\n", Connection)); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + +} /* NbiCancelListen */ + + +VOID +NbiCancelConnectFindName( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a connect + request which is waiting for the name to be found. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request = (PREQUEST)Irp; + PLIST_ENTRY p; + BOOLEAN fCanceled = TRUE; + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_CONNECT)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState == CONNECTION_SUBSTATE_C_FIND_NAME) && + (Connection->ConnectRequest == Request)) { + + // + // Make sure the request is still on the queue + // before cancelling it. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + for (p = Device->WaitingConnects.Flink; + p != &Device->WaitingConnects; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + break; + } + } + + if (p != &Device->WaitingConnects) { + + NB_DEBUG2 (CONNECTION, ("Cancelled find name connect on %lx\n", Connection)); + + // + // When the reference count goes to 0, we will set the + // state to INACTIVE and deassign the connection ID. + // + + Connection->ConnectRequest = NULL; + RemoveEntryList (REQUEST_LINKAGE(Request)); + NB_FREE_LOCK (&Device->Lock, LockHandle2); + + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + REQUEST_STATUS(Request) = STATUS_CANCELLED; + +#ifdef RASAUTODIAL + if (Connection->Flags & CONNECTION_FLAGS_AUTOCONNECTING) + fCanceled = NbiCancelTdiConnect(Device, Request, Connection); +#endif // RASAUTODIAL + + if (fCanceled) { + NbiCompleteRequest (Request); + NbiFreeRequest(Device, Request); + } + + NbiDereferenceConnection (Connection, CREF_WAIT_CACHE); + NbiDereferenceConnection (Connection, CREF_CONNECT); + + } else { + + NB_DEBUG (CONNECTION, ("Cancel connect not found on queue %lx\n", Connection)); + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + + } else { + + NB_DEBUG (CONNECTION, ("Cancel connect on invalid connection %lx\n", Connection)); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + +} /* NbiCancelConnectFindName */ + + +VOID +NbiCancelConnectWaitResponse( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a connect + request which is waiting for a rip or session init response + from the remote. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PCONNECTION Connection; + CTELockHandle LockHandle1; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request = (PREQUEST)Irp; + BOOLEAN TimerWasStopped = FALSE; + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_CONNECT)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + + if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN) && + (Connection->ConnectRequest == Request)) { + + // + // When the reference count goes to 0, we will set the + // state to INACTIVE and deassign the connection ID. + // + + NB_DEBUG2 (CONNECTION, ("Cancelled wait response connect on %lx\n", Connection)); + + Connection->ConnectRequest = NULL; + Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN; + + if (CTEStopTimer (&Connection->Timer)) { + TimerWasStopped = TRUE; + } + + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + NbiCompleteRequest (Request); + NbiFreeRequest(Device, Request); + + NbiDereferenceConnection (Connection, CREF_CONNECT); + + if (TimerWasStopped) { + NbiDereferenceConnection (Connection, CREF_TIMER); + } + + } else { + + NB_DEBUG (CONNECTION, ("Cancel connect on invalid connection %lx\n", Connection)); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + +} /* NbiCancelConnectWaitResponse */ + + +VOID +NbiCancelDisconnectWait( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a posted + disconnect wait. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PCONNECTION Connection; + CTELockHandle LockHandle1, LockHandle2; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request = (PREQUEST)Irp; + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_DISCONNECT)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_GET_LOCK (&Device->Lock, &LockHandle2); + + if (Connection->DisconnectWaitRequest == Request) { + + Connection->DisconnectWaitRequest = NULL; + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + NbiCompleteRequest (Request); + NbiFreeRequest(Device, Request); + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle2); + NB_FREE_LOCK (&Connection->Lock, LockHandle1); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + } + +} /* NbiCancelDisconnectWait */ + + +PCONNECTION +NbiLookupConnectionByContext( + IN PADDRESS_FILE AddressFile, + IN CONNECTION_CONTEXT ConnectionContext + ) + +/*++ + +Routine Description: + + This routine looks up a connection based on the context. + The connection is assumed to be associated with the + specified address file. + +Arguments: + + AddressFile - Pointer to an address file. + + ConnectionContext - Connection context to find. + +Return Value: + + A pointer to the connection we found + +--*/ + +{ + CTELockHandle LockHandle1, LockHandle2; + PLIST_ENTRY p; + PADDRESS Address = AddressFile->Address; + PCONNECTION Connection; + + NB_GET_LOCK (&Address->Lock, &LockHandle1); + + for (p=AddressFile->ConnectionDatabase.Flink; + p != &AddressFile->ConnectionDatabase; + p=p->Flink) { + + Connection = CONTAINING_RECORD (p, CONNECTION, AddressFileLinkage); + + NB_GET_LOCK (&Connection->Lock, &LockHandle2); + + // + // BUGBUG: Does this spinlock ordering hurt us + // somewhere else? + // + + if (Connection->Context == ConnectionContext) { + + NbiReferenceConnection (Connection, CREF_BY_CONTEXT); + NB_FREE_LOCK (&Connection->Lock, LockHandle2); + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + return Connection; + } + + NB_FREE_LOCK (&Connection->Lock, LockHandle2); + + } + + NB_FREE_LOCK (&Address->Lock, LockHandle1); + + return NULL; + +} /* NbiLookupConnectionByContext */ + + +PCONNECTION +NbiCreateConnection( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine creates a transport connection and associates it with + the specified transport device context. The reference count in the + connection is automatically set to 1, and the reference count of the + device context is incremented. + +Arguments: + + Device - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + connection. + +Return Value: + + The newly created connection, or NULL if none can be allocated. + +--*/ + +{ + PCONNECTION Connection; + PNB_SEND_RESERVED SendReserved; + ULONG ConnectionSize; + ULONG HeaderLength; + NTSTATUS Status; + CTELockHandle LockHandle; + + HeaderLength = Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION); + ConnectionSize = FIELD_OFFSET (CONNECTION, SendPacketHeader[0]) + HeaderLength; + + Connection = (PCONNECTION)NbiAllocateMemory (ConnectionSize, MEMORY_CONNECTION, "Connection"); + if (Connection == NULL) { + NB_DEBUG (CONNECTION, ("Create connection failed\n")); + return NULL; + } + + NB_DEBUG2 (CONNECTION, ("Create connection %lx\n", Connection)); + RtlZeroMemory (Connection, ConnectionSize); + + +#if defined(NB_OWN_PACKETS) + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if (NbiInitializeSendPacket( + Device, + Connection->SendPacketPoolHandle, + &Connection->SendPacket, + Connection->SendPacketHeader, + HeaderLength) != STATUS_SUCCESS) { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + NB_DEBUG (CONNECTION, ("Could not initialize connection packet %lx\n", &Connection->SendPacket)); + Connection->SendPacketInUse = TRUE; + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + SendReserved = SEND_RESERVED(&Connection->SendPacket); + SendReserved->u.SR_CO.Connection = Connection; + SendReserved->OwnedByConnection = TRUE; +#ifdef NB_TRACK_POOL + SendReserved->Pool = NULL; +#endif + } + +#else // !NB_OWN_PACKETS + + // + // if we are using ndis packets, first create packet pool for 1 packet descriptor + // + NdisAllocatePacketPool( &Status, &Connection->SendPacketPoolHandle, 1, sizeof(NB_SEND_RESERVED)); + if (!NT_SUCCESS(Status)){ + NB_DEBUG (CONNECTION, ("Could not allocatee connection packet %lx\n", Status)); + Connection->SendPacketInUse = TRUE; + } else { + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if (NbiInitializeSendPacket( + Device, + Connection->SendPacketPoolHandle, + &Connection->SendPacket, + Connection->SendPacketHeader, + HeaderLength) != STATUS_SUCCESS) { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + NB_DEBUG (CONNECTION, ("Could not initialize connection packet %lx\n", &Connection->SendPacket)); + Connection->SendPacketInUse = TRUE; + + // + // Also free up the pool which we allocated above. + // + NdisFreePacketPool(Connection->SendPacketPoolHandle); + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + SendReserved = SEND_RESERVED(&Connection->SendPacket); + SendReserved->u.SR_CO.Connection = Connection; + SendReserved->OwnedByConnection = TRUE; +#ifdef NB_TRACK_POOL + SendReserved->Pool = NULL; +#endif + } + } + +#endif NB_OWN_PACKETS + + Connection->Type = NB_CONNECTION_SIGNATURE; + Connection->Size = (USHORT)ConnectionSize; + +#if 0 + Connection->AddressFileLinked = FALSE; + Connection->AddressFile = NULL; +#endif + + Connection->State = CONNECTION_STATE_INACTIVE; +#if 0 + Connection->SubState = 0; + Connection->ReferenceCount = 0; +#endif + + Connection->CanBeDestroyed = TRUE; + + Connection->TickCount = 1; + Connection->HopCount = 1; + + // + // Device->InitialRetransmissionTime is in milliseconds, as is + // SHORT_TIMER_DELTA. + // + + Connection->BaseRetransmitTimeout = Device->InitialRetransmissionTime / SHORT_TIMER_DELTA; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + // + // Device->KeepAliveTimeout is in half-seconds, while LONG_TIMER_DELTA + // is in milliseconds. + // + + Connection->WatchdogTimeout = (Device->KeepAliveTimeout * 500) / LONG_TIMER_DELTA; + + + Connection->LocalConnectionId = 0xffff; + + // + // When the connection becomes active we will replace the + // destination address of this header with the correct + // information. + // + + RtlCopyMemory(&Connection->RemoteHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + + Connection->Device = Device; + Connection->DeviceLock = &Device->Lock; + CTEInitLock (&Connection->Lock.Lock); + + CTEInitTimer (&Connection->Timer); + + InitializeListHead (&Connection->NdisSendQueue); +#if 0 + Connection->NdisSendsInProgress = 0; + Connection->DisassociatePending = NULL; + Connection->ClosePending = NULL; + Connection->SessionInitAckData = NULL; + Connection->SessionInitAckDataLength = 0; + Connection->PiggybackAckTimeout = FALSE; + Connection->ReceivesWithoutAck = 0; +#endif + Connection->Flags = 0; + + NbiReferenceDevice (Device, DREF_CONNECTION); + + return Connection; + +} /* NbiCreateConnection */ + + +NTSTATUS +NbiVerifyConnection ( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid connection object. We reference + it to keep it from disappearing while we use it. + +Arguments: + + Connection - potential pointer to a CONNECTION object + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise + +--*/ + +{ + CTELockHandle LockHandle; + NTSTATUS status = STATUS_SUCCESS; + PDEVICE Device = NbiDevice; + BOOLEAN LockHeld = FALSE; + + try { + + if ((Connection->Size == FIELD_OFFSET (CONNECTION, SendPacketHeader[0]) + + NbiDevice->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION)) && + (Connection->Type == NB_CONNECTION_SIGNATURE)) { + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + LockHeld = TRUE; + + if (Connection->State != CONNECTION_STATE_CLOSING) { + + NbiReferenceConnectionLock (Connection, CREF_VERIFY); + + } else { + + NbiPrint1("NbiVerifyConnection: C %lx closing\n", Connection); + status = STATUS_INVALID_CONNECTION; + } + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } else { + + NbiPrint1("NbiVerifyConnection: C %lx bad signature\n", Connection); + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + NbiPrint1("NbiVerifyConnection: C %lx exception\n", Connection); + if (LockHeld) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + } + return GetExceptionCode(); + } + + return status; + +} /* NbiVerifyConnection */ + + +VOID +NbiDestroyConnection( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine destroys a transport connection and removes all references + made by it to other objects in the transport. The connection structure + is returned to nonpaged system pool. + +Arguments: + + Connection - Pointer to a transport connection structure to be destroyed. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = Connection->Device; +#if 0 + CTELockHandle LockHandle; +#endif + + NB_DEBUG2 (CONNECTION, ("Destroy connection %lx\n", Connection)); + + if (!Connection->SendPacketInUse) { + NbiDeinitializeSendPacket (Device, &Connection->SendPacket, Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION)); +#if !defined(NB_OWN_PACKETS) + NdisFreePacketPool(Connection->SendPacketPoolHandle); +#endif + } + + NbiFreeMemory (Connection, (ULONG)Connection->Size, MEMORY_CONNECTION, "Connection"); + + NbiDereferenceDevice (Device, DREF_CONNECTION); + +} /* NbiDestroyConnection */ + + +#if DBG +VOID +NbiRefConnection( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport connection. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + + (VOID)ExInterlockedAddUlong ( + &Connection->ReferenceCount, + 1, + &Connection->DeviceLock->Lock); + + Connection->CanBeDestroyed = FALSE; + + CTEAssert (Connection->ReferenceCount > 0); + +} /* NbiRefConnection */ + + +VOID +NbiRefConnectionLock( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport connection + when the device lock is already held. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + + ++Connection->ReferenceCount; + Connection->CanBeDestroyed = FALSE; + + CTEAssert (Connection->ReferenceCount > 0); + +} /* NbiRefConnectionLock */ + + +VOID +NbiRefConnectionSync( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport connection + when we are in a sync routine. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + (VOID)NB_ADD_ULONG ( + &Connection->ReferenceCount, + 1, + Connection->DeviceLock); + + Connection->CanBeDestroyed = FALSE; + + CTEAssert (Connection->ReferenceCount > 0); + +} /* NbiRefConnectionSync */ + + +VOID +NbiDerefConnection( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine dereferences a transport connection by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbiHandleConnectionZero to complete any disconnect, disassociate, + or close requests that have pended on the connection. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + ULONG oldvalue; + CTELockHandle LockHandle; + + NB_GET_LOCK( Connection->DeviceLock, &LockHandle ); + CTEAssert( Connection->ReferenceCount ); + if ( !(--Connection->ReferenceCount) ) { + + Connection->ThreadsInHandleConnectionZero++; + + NB_FREE_LOCK( Connection->DeviceLock, LockHandle ); + + // + // If the refcount has dropped to 0, then the connection can + // become inactive. We reacquire the spinlock and if it has not + // jumped back up then we handle any disassociates and closes + // that have pended. + // + + NbiHandleConnectionZero (Connection); + } else { + + NB_FREE_LOCK( Connection->DeviceLock, LockHandle ); + } + + +} /* NbiDerefConnection */ + + +#endif + + +VOID +NbiHandleConnectionZero( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine handles a connection's refcount going to 0. + + BUGBUG: If two threads are in this at the same time and + the close has already come through, one of them might + destroy the connection while the other one is looking + at it. We minimize the chance of this by not derefing + the connection after calling CloseConnection. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + CTELockHandle LockHandle; + PDEVICE Device; + PADDRESS_FILE AddressFile; + PADDRESS Address; + PREQUEST DisconnectPending; + PREQUEST DisassociatePending; + PREQUEST ClosePending; + + + Device = Connection->Device; + + NB_GET_LOCK (&Device->Lock, &LockHandle); + +#if DBG + // + // Make sure if our reference count is zero, all the + // sub-reference counts are also zero. + // + + if (Connection->ReferenceCount == 0) { + + UINT i; + for (i = 0; i < CREF_TOTAL; i++) { + if (Connection->RefTypes[i] != 0) { + DbgPrint ("NBI: Connection reftype mismatch on %lx\n", Connection); + DbgBreakPoint(); + } + } + } +#endif + + // + // If the connection was assigned an ID, then remove it + // (it is assigned one when it leaves INACTIVE). + // + + if (Connection->LocalConnectionId != 0xffff) { + NbiDeassignConnectionId (Device, Connection); + } + + // + // Complete any pending disconnects. + // + + if (Connection->DisconnectRequest != NULL) { + + DisconnectPending = Connection->DisconnectRequest; + Connection->DisconnectRequest = NULL; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + REQUEST_STATUS(DisconnectPending) = STATUS_SUCCESS; + NbiCompleteRequest (DisconnectPending); + NbiFreeRequest (Device, DisconnectPending); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + } + + // + // This should have been completed by NbiStopConnection, + // or else not allowed to be queued. + // + + CTEAssert (Connection->DisconnectWaitRequest == NULL); + + + Connection->State = CONNECTION_STATE_INACTIVE; + + // + // BUGBUG: Make NbiInitializeConnection() to take care of all this. + // + + RtlZeroMemory (&Connection->ConnectionInfo, sizeof(TDI_CONNECTION_INFO)); + Connection->TickCount = 1; + Connection->HopCount = 1; + Connection->BaseRetransmitTimeout = Device->InitialRetransmissionTime / SHORT_TIMER_DELTA; + + Connection->ConnectionInfo.TransmittedTsdus = 0; + Connection->ConnectionInfo.TransmissionErrors = 0; + Connection->ConnectionInfo.ReceivedTsdus = 0; + Connection->ConnectionInfo.ReceiveErrors = 0; + + // + // See if we need to do a disassociate now. + // + + if ((Connection->ReferenceCount == 0) && + (Connection->DisassociatePending != NULL)) { + + // + // A disassociate pended, now we complete it. + // + + DisassociatePending = Connection->DisassociatePending; + Connection->DisassociatePending = NULL; + + // + // Set this so nobody else tries to disassociate. + // + + AddressFile = Connection->AddressFile; + Connection->AddressFile = (PVOID)-1; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Take this connection out of the address file's list. + // + + Address = AddressFile->Address; + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFileLinked) { + Connection->AddressFileLinked = FALSE; + RemoveEntryList (&Connection->AddressFileLinkage); + } + + // + // We are done. + // + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + Connection->AddressFile = NULL; + + // + // Clean up the reference counts and complete any + // disassociate requests that pended. + // + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + + if (DisassociatePending != (PVOID)-1) { + REQUEST_STATUS(DisassociatePending) = STATUS_SUCCESS; + NbiCompleteRequest (DisassociatePending); + NbiFreeRequest (Device, DisassociatePending); + } + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + + // + // If a close was pending, complete that. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + if ((Connection->ReferenceCount == 0) && + (Connection->ClosePending)) { + + ClosePending = Connection->ClosePending; + Connection->ClosePending = NULL; + + // + // If we are associated with an address, we need + // to simulate a disassociate at this point. + // + + if ((Connection->AddressFile != NULL) && + (Connection->AddressFile != (PVOID)-1)) { + + AddressFile = Connection->AddressFile; + Connection->AddressFile = (PVOID)-1; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Take this connection out of the address file's list. + // + + Address = AddressFile->Address; + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (Connection->AddressFileLinked) { + Connection->AddressFileLinked = FALSE; + RemoveEntryList (&Connection->AddressFileLinkage); + } + + // + // We are done. + // + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + Connection->AddressFile = NULL; + + // + // Clean up the reference counts and complete any + // disassociate requests that pended. + // + + NbiDereferenceAddressFile (AddressFile, AFREF_CONNECTION); + + } else { + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + // + // Even if the ref count is zero and we just cleaned up everything, + // we can not destroy the connection bcoz some other thread might still be + // in HandleConnectionZero routine. This could happen when 2 threads call into + // HandleConnectionZero, one thread runs thru completion, close comes along + // and the other thread is still in HandleConnectionZero routine. + // + + CTEAssert( Connection->ThreadsInHandleConnectionZero ); + if (ExInterlockedAddUlong ( &Connection->ThreadsInHandleConnectionZero, (ULONG)-1, &Device->Lock.Lock) == 1) { + NbiDestroyConnection(Connection); + } + + REQUEST_STATUS(ClosePending) = STATUS_SUCCESS; + NbiCompleteRequest (ClosePending); + NbiFreeRequest (Device, ClosePending); + + } else { + + if ( Connection->ReferenceCount == 0 ) { + Connection->CanBeDestroyed = TRUE; + } + + CTEAssert( Connection->ThreadsInHandleConnectionZero ); + Connection->ThreadsInHandleConnectionZero--; + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + +} /* NbiHandleConnectionZero */ + diff --git a/private/ntos/tdi/isnp/nb/datagram.c b/private/ntos/tdi/isnp/nb/datagram.c new file mode 100644 index 000000000..e81579d1b --- /dev/null +++ b/private/ntos/tdi/isnp/nb/datagram.c @@ -0,0 +1,1089 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + datagram.c + +Abstract: + + This module contains the code to handle datagram reception + for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 28-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + + +VOID +NbiProcessDatagram( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN BOOLEAN Broadcast + ) + +/*++ + +Routine Description: + + This routine handles datagram indications. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + + Broadcast - TRUE if the frame was a broadcast datagram. + +Return Value: + + None. + +--*/ + +{ + + PADDRESS Address; + NDIS_STATUS NdisStatus; + PUCHAR NetbiosName; + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)LookaheadBuffer; + PDEVICE Device = NbiDevice; + PSINGLE_LIST_ENTRY s; + PNB_RECEIVE_RESERVED ReceiveReserved; + PNB_RECEIVE_BUFFER ReceiveBuffer; + ULONG DataOffset; + UINT BytesTransferred; + PNDIS_PACKET Packet; + CTELockHandle LockHandle; + + + // + // See if there is an address that might want this. + // + + if (Broadcast) { + NetbiosName = (PVOID)-1; + } else { + NetbiosName = (PUCHAR)Connectionless->Datagram.DestinationName; + if (Device->AddressCounts[NetbiosName[0]] == 0) { + return; + } + } + + DataOffset = sizeof(IPX_HEADER) + sizeof(NB_DATAGRAM); + +#if defined(_PNP_POWER) + if ((PacketSize < DataOffset) || + (PacketSize > DataOffset + Device->CurMaxReceiveBufferSize)) { +#else + if ((PacketSize < DataOffset) || + (PacketSize > DataOffset + Device->Bind.LineInfo.MaximumPacketSize)) { +#endif _PNP_POWER + + NB_DEBUG (DATAGRAM, ("Datagram length %d discarded\n", PacketSize)); + return; + } + + Address = NbiFindAddress (Device, NetbiosName); + + if (Address == NULL) { + return; + } + + // + // We need to cache the remote name if the packet came across the router. + // This allows this machine to get back to the RAS client which might + // have sent this datagram. We currently dont allow broadcasts to go out + // on the dial-in line. + // Dont cache some of the widely used group names, that would be too much + // to store in cache. + // + +#if 0 + if ( Connectionless->IpxHeader.TransportControl && + !( (Address->NetbiosAddress.NetbiosName[15] == 0x0 ) && + (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) && + !( (Address->NetbiosAddress.NetbiosName[15] == 0x01 ) && + (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) && + !( (Address->NetbiosAddress.NetbiosName[15] == 0x1E ) && + (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) ) { +#endif + if ( Connectionless->IpxHeader.TransportControl && + ( (Address->NetbiosAddress.NetbiosName[15] == 0x1c ) && + (Address->NetbiosAddress.NetbiosNameType & TDI_ADDRESS_NETBIOS_TYPE_GROUP)) ) { + + PNETBIOS_CACHE CacheName; + + NB_GET_LOCK (&Device->Lock, &LockHandle); + if ( FindInNetbiosCacheTable ( Device->NameCache, + Connectionless->Datagram.SourceName, + &CacheName ) != STATUS_SUCCESS ) { + + CacheName = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); + if (CacheName ) { + RtlCopyMemory (CacheName->NetbiosName, Connectionless->Datagram.SourceName, 16); + CacheName->Unique = TRUE; + CacheName->ReferenceCount = 1; + RtlCopyMemory (&CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12); + CacheName->NetworksAllocated = 1; + CacheName->NetworksUsed = 1; + CacheName->Networks[0].Network = *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork); + CacheName->Networks[0].LocalTarget = *RemoteAddress; + NB_DEBUG2 (CACHE, ("Alloc new cache from Datagram %lx for <%.16s>\n", + CacheName, CacheName->NetbiosName)); + + CacheName->TimeStamp = Device->CacheTimeStamp; + + InsertInNetbiosCacheTable( + Device->NameCache, + CacheName); + + } + } else if ( CacheName->Unique ) { + // + // We already have an entry for this remote. We should update + // the address. This is so that if the ras client dials-out + // then dials-in again and gets a new address, we dont end up + // caching the old address. + // + if ( !RtlEqualMemory( &CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12) ) { + + RtlCopyMemory (&CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12); + CacheName->Networks[0].Network = *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork); + CacheName->Networks[0].LocalTarget = *RemoteAddress; + + } + } + NB_FREE_LOCK (&Device->Lock, LockHandle); + } + + // + // We need to allocate a packet and buffer for the transfer. + // + + s = NbiPopReceivePacket (Device); + if (s == NULL) { + NbiDereferenceAddress (Address, AREF_FIND); + return; + } + + ReceiveReserved = CONTAINING_RECORD (s, NB_RECEIVE_RESERVED, PoolLinkage); + + + s = NbiPopReceiveBuffer (Device); + if (s == NULL) { + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &ReceiveReserved->PoolLinkage, + &NbiGlobalPoolInterlock); + NbiDereferenceAddress (Address, AREF_FIND); + return; + } + + ReceiveBuffer = CONTAINING_RECORD (s, NB_RECEIVE_BUFFER, PoolLinkage); + + Packet = CONTAINING_RECORD (ReceiveReserved, NDIS_PACKET, ProtocolReserved[0]); + ReceiveReserved->u.RR_DG.ReceiveBuffer = ReceiveBuffer; + + + // + // Now that we have a packet and a buffer, set up the transfer. + // The indication to the TDI clients will happen at receive + // complete time. + // + + NdisChainBufferAtFront (Packet, ReceiveBuffer->NdisBuffer); + ReceiveBuffer->Address = Address; + + ReceiveReserved->Type = RECEIVE_TYPE_DATAGRAM; + CTEAssert (!ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = TRUE; + + TdiCopyLookaheadData( + &ReceiveBuffer->RemoteName, + Connectionless->Datagram.SourceName, + 16, + (MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) ? TDI_RECEIVE_COPY_LOOKAHEAD : 0); + + (*Device->Bind.TransferDataHandler) ( + &NdisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadBufferOffset + DataOffset, + PacketSize - DataOffset, + Packet, + &BytesTransferred); + + if (NdisStatus != NDIS_STATUS_PENDING) { +#if DBG + if (NdisStatus == STATUS_SUCCESS) { + CTEAssert (BytesTransferred == PacketSize - DataOffset); + } +#endif + + NbiTransferDataComplete( + Packet, + NdisStatus, + BytesTransferred); + + } + +} /* NbiProcessDatagram */ + + +VOID +NbiIndicateDatagram( + IN PADDRESS Address, + IN PUCHAR RemoteName, + IN PUCHAR Data, + IN ULONG DataLength + ) + +/*++ + +Routine Description: + + This routine indicates a datagram to clients on the specified + address. It is called from NbiReceiveComplete. + +Arguments: + + Address - The address the datagram was sent to. + + RemoteName - The source netbios address of the datagram. + + Data - The data. + + DataLength - The length of the data. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p, q; + PIRP Irp; + ULONG IndicateBytesCopied; + PREQUEST Request; + TA_NETBIOS_ADDRESS SourceName; + PTDI_CONNECTION_INFORMATION RemoteInformation; + PADDRESS_FILE AddressFile, ReferencedAddressFile; + PTDI_CONNECTION_INFORMATION DatagramInformation; + TDI_ADDRESS_NETBIOS UNALIGNED * DatagramAddress; + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + CTELockHandle CancelLH; + + // + // Update our statistics. + // + + ++Device->Statistics.DatagramsReceived; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DatagramBytesReceived, + DataLength); + + // + // Call the client's ReceiveDatagram indication handler. He may + // want to accept the datagram that way. + // + + TdiBuildNetbiosAddress (RemoteName, FALSE, &SourceName); + ReferencedAddressFile = NULL; + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + // + // Find the next open address file in the list. + // + + AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage); + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + continue; + } + + NbiReferenceAddressFileLock (AddressFile, AFREF_INDICATION); + + // + // do we have a datagram receive request outstanding? If so, we will + // satisfy it first. We run through the receive datagram queue + // until we find a datagram with no remote address or with + // this sender's address as its remote address. + // + + for (q = AddressFile->ReceiveDatagramQueue.Flink; + q != &AddressFile->ReceiveDatagramQueue; + q = q->Flink) { + + Request = LIST_ENTRY_TO_REQUEST (q); + DatagramInformation = ((PTDI_REQUEST_KERNEL_RECEIVEDG) + REQUEST_PARAMETERS(Request))->ReceiveDatagramInformation; + + if (DatagramInformation && + (DatagramInformation->RemoteAddress) && + (DatagramAddress = NbiParseTdiAddress(DatagramInformation->RemoteAddress, FALSE)) && + (!RtlEqualMemory( + RemoteName, + DatagramAddress->NetbiosName, + 16))) { + continue; + } + break; + } + + if (q != &AddressFile->ReceiveDatagramQueue) { + + RemoveEntryList (q); + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + ReferencedAddressFile = AddressFile; + + // + // Do this deref now, we hold another one so it + // will stick around. + // + + NbiDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + IndicateBytesCopied = 0; + + // + // Fall past the else to copy the data. + // + + } else { + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + ReferencedAddressFile = AddressFile; + + // + // No receive datagram requests; is there a kernel client? + // + + if (AddressFile->RegisteredHandler[TDI_EVENT_RECEIVE_DATAGRAM]) { + + IndicateBytesCopied = 0; + + if ((*AddressFile->ReceiveDatagramHandler)( + AddressFile->HandlerContexts[TDI_EVENT_RECEIVE_DATAGRAM], + sizeof (TA_NETBIOS_ADDRESS), + &SourceName, + 0, + NULL, + TDI_RECEIVE_COPY_LOOKAHEAD, + DataLength, // indicated + DataLength, // available + &IndicateBytesCopied, + Data, + &Irp) != STATUS_MORE_PROCESSING_REQUIRED) { + + // + // The client did not return a request, go to the + // next address file. + // + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } + + Request = NbiAllocateRequest (Device, Irp); + + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + continue; + } + + } else { + + // + // The client has nothing posted and no handler, + // go on to the next address file. + // + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + continue; + + } + + } + + // + // We have a request; copy the actual user data. + // + if ( REQUEST_NDIS_BUFFER (Request) ) { + + REQUEST_STATUS(Request) = + TdiCopyBufferToMdl ( + Data, + IndicateBytesCopied, + DataLength - IndicateBytesCopied, + REQUEST_NDIS_BUFFER (Request), + 0, + &REQUEST_INFORMATION (Request)); + + } else { + // + // No buffer specified in the request + // + REQUEST_INFORMATION (Request) = 0; + // + // If there was any data to be copied, return error o/w success + // + REQUEST_STATUS(Request) = ( (DataLength - IndicateBytesCopied) ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS ); + } + + // + // Copy the addressing information. + // + + RemoteInformation = ((PTDI_REQUEST_KERNEL_RECEIVEDG) + REQUEST_PARAMETERS(Request))->ReturnDatagramInformation; + + if (RemoteInformation != NULL) { + + RtlCopyMemory( + (PTA_NETBIOS_ADDRESS)RemoteInformation->RemoteAddress, + &SourceName, + (RemoteInformation->RemoteAddressLength < sizeof(TA_NETBIOS_ADDRESS)) ? + RemoteInformation->RemoteAddressLength : sizeof(TA_NETBIOS_ADDRESS)); + } + + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + + } // end of for loop through the address files + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); + + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + +} /* NbiIndicateDatagram */ + + +NTSTATUS +NbiTdiSendDatagram( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine sends a datagram on an address. + +Arguments: + + Device - The netbios device. + + Request - The request describing the datagram send. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PADDRESS_FILE AddressFile; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteName; + PTDI_REQUEST_KERNEL_SENDDG Parameters; + PSINGLE_LIST_ENTRY s; + PNETBIOS_CACHE CacheName; + CTELockHandle LockHandle; + NTSTATUS Status; + + // + // Make sure that the address is valid. + // + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (Status == STATUS_SUCCESS) { + + Parameters = (PTDI_REQUEST_KERNEL_SENDDG)REQUEST_PARAMETERS(Request); + RemoteName = NbiParseTdiAddress((PTRANSPORT_ADDRESS)(Parameters->SendDatagramInformation->RemoteAddress), TRUE); + + + // + // Check that datagram size is less than the maximum allowable + // by the adapters. In the worst case this would be + // 576 - 64 = 512. + // + +#if defined(_PNP_POWER) + if ( ( Parameters->SendLength + sizeof(NB_DATAGRAM) ) > Device->Bind.LineInfo.MaximumSendSize ) { + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + NB_DEBUG(DATAGRAM, ("Datagram too large %d, Max allowed %d\n", Parameters->SendLength + sizeof(NB_DATAGRAM), Device->Bind.LineInfo.MaximumSendSize )); + return STATUS_INVALID_PARAMETER; + } +#else + if ( ( Parameters->SendLength + sizeof(NB_DATAGRAM) ) > Device->Bind.LineInfo.MaximumPacketSize ) { + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + NB_DEBUG(DATAGRAM, ("Datagram too large %d, Max allowed %d\n", Parameters->SendLength + sizeof(NB_DATAGRAM), Device->Bind.LineInfo.MaximumPacketSize )); + return STATUS_INVALID_PARAMETER; + } +#endif _PNP_POWER + + if (RemoteName != NULL) { + + // + // Get a packet to use in this send. + // + + s = NbiPopSendPacket (Device, FALSE); + + if (s != NULL) { + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + // + // Check on the cache status of this name. + // + + Reserved->u.SR_DG.DatagramRequest = Request; + Reserved->u.SR_DG.AddressFile = AddressFile; + Reserved->u.SR_DG.RemoteName = RemoteName; + + REQUEST_INFORMATION (Request) = Parameters->SendLength; + + ++Device->Statistics.DatagramsSent; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DatagramBytesSent, + Parameters->SendLength); + + if (Device->Internet) { + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Status = CacheFindName( + Device, + FindNameOther, + (RemoteName == (PVOID)-1) ? NULL : (PUCHAR)RemoteName->NetbiosName, + &CacheName); + + if (Status == STATUS_PENDING) { + + // + // A request for routes to this name has been + // sent out on the net, we queue up this datagram + // request and processing will be resumed when + // we get a response. + // + + NB_DEBUG2 (CONNECTION, ("Queueing up datagram %lx on %lx\n", + Request, AddressFile)); + + NbiReferenceAddressFileLock (AddressFile, AFREF_SEND_DGRAM); + + InsertTailList( + &Device->WaitingDatagrams, + &Reserved->WaitLinkage); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } else if (Status == STATUS_SUCCESS) { + + NB_DEBUG2 (CONNECTION, ("Found datagram cached %lx on %lx\n", + Request, AddressFile)); + + // + // We reference the cache name entry so it won't + // go away while we are using it. + // + + Reserved->u.SR_DG.Cache = CacheName; + Reserved->u.SR_DG.CurrentNetwork = 0; + ++CacheName->ReferenceCount; + + NbiReferenceAddressFileLock (AddressFile, AFREF_SEND_DGRAM); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + if ( REQUEST_NDIS_BUFFER(Request) ) { + NdisChainBufferAtBack (Packet, REQUEST_NDIS_BUFFER(Request)); + } + + NbiTransmitDatagram( + Reserved); + + Status = STATUS_PENDING; + + } else { + + // + // Only this failure gets passed back up to + // the caller, to avoid confusing the browser. + // + + if (Status != STATUS_DEVICE_DOES_NOT_EXIST) { + + Status = STATUS_SUCCESS; + + } else { + + REQUEST_INFORMATION (Request) = 0; + } + NB_FREE_LOCK (&Device->Lock, LockHandle); + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + + + + } + + } else { + + // + // We are not in internet mode, so we do not + // need to do the name discovery. + // + + NB_DEBUG2 (CONNECTION, ("Sending datagram direct %lx on %lx\n", + Request, AddressFile)); + + Reserved->u.SR_DG.Cache = NULL; + + NbiReferenceAddressFileLock (AddressFile, AFREF_SEND_DGRAM); + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + if ( REQUEST_NDIS_BUFFER(Request) ) { + NdisChainBufferAtBack (Packet, REQUEST_NDIS_BUFFER(Request)); + } + NbiTransmitDatagram( + Reserved); + + Status = STATUS_PENDING; + + } + + } else { + + // + // Could not allocate a packet for the datagram. + // + + NB_DEBUG (DATAGRAM, ("Couldn't get packet to send DG %lx\n", Request)); + + Status = STATUS_INSUFFICIENT_RESOURCES; + + } + + } else { + + // + // There is no netbios remote address specified. + // + + NB_DEBUG (DATAGRAM, ("No netbios address in DG %lx\n", Request)); + Status = STATUS_BAD_NETWORK_PATH; + + } + + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } else { + + NB_DEBUG (DATAGRAM, ("Invalid address file for DG %lx\n", Request)); + + } + + return Status; + +} /* NbiTdiSendDatagram */ + + +VOID +NbiTransmitDatagram( + IN PNB_SEND_RESERVED Reserved + ) + +/*++ + +Routine Description: + + This routine sends a datagram to the next net in the + cache entry for the remote name. + +Arguments: + + Reserved - The reserved section of the packet that has + been allocated for this send. Reserved->u.SR_DG.Cache + will be NULL if Internet mode is off, otherwise it + will contain the cache entry to use when sending + this datagram. + +Return Value: + + None. + +--*/ + +{ + + PNDIS_PACKET Packet; + PNETBIOS_CACHE CacheName; + NB_CONNECTIONLESS UNALIGNED * Header; + ULONG HeaderLength; + ULONG PacketLength; + NDIS_STATUS NdisStatus; + IPX_LOCAL_TARGET TempLocalTarget; + PIPX_LOCAL_TARGET LocalTarget; + PDEVICE Device = NbiDevice; + + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_DATAGRAM; + + CacheName = Reserved->u.SR_DG.Cache; + + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address, so we modify + // that for the current netbios cache entry if needed. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + + if (CacheName == NULL) { + +#if defined(_PNP_POWER) + // + // IPX will send this on all the Nics. + // + TempLocalTarget.NicHandle.NicId = 0; +#else + TempLocalTarget.NicId = 1; +#endif _PNP_POWER + RtlCopyMemory (TempLocalTarget.MacAddress, BroadcastAddress, 6); + LocalTarget = &TempLocalTarget; + + } else { + + if (CacheName->Unique) { + RtlCopyMemory (Header->IpxHeader.DestinationNetwork, &CacheName->FirstResponse, 12); + } else { + *(UNALIGNED ULONG *)Header->IpxHeader.DestinationNetwork = CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].Network; + RtlCopyMemory (&Header->IpxHeader.DestinationNode, BroadcastAddress, 6); + } + + LocalTarget = &CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].LocalTarget; + + } + + HeaderLength = sizeof(IPX_HEADER) + sizeof(NB_DATAGRAM); + + PacketLength = HeaderLength + REQUEST_INFORMATION(Reserved->u.SR_DG.DatagramRequest); + + Header->IpxHeader.PacketLength[0] = (UCHAR)(PacketLength / 256); + Header->IpxHeader.PacketLength[1] = (UCHAR)(PacketLength % 256); + Header->IpxHeader.PacketType = 0x04; + + + // + // Now fill in the Netbios header. + // + + Header->Datagram.ConnectionControlFlag = 0x00; + RtlCopyMemory( + Header->Datagram.SourceName, + Reserved->u.SR_DG.AddressFile->Address->NetbiosAddress.NetbiosName, + 16); + + if (Reserved->u.SR_DG.RemoteName != (PVOID)-1) { + + // + // This is a directed, as opposed to broadcast, datagram. + // + + Header->Datagram.DataStreamType = NB_CMD_DATAGRAM; + RtlCopyMemory( + Header->Datagram.DestinationName, + Reserved->u.SR_DG.RemoteName->NetbiosName, + 16); + + } else { + + Header->Datagram.DataStreamType = NB_CMD_BROADCAST_DATAGRAM; + RtlZeroMemory( + Header->Datagram.DestinationName, + 16); + + } + + + // + // Now send the frame (IPX will adjust the length of the + // first buffer and the whole frame correctly). + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), HeaderLength); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + PacketLength, + HeaderLength)) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiTransmitDatagram */ + + +NTSTATUS +NbiTdiReceiveDatagram( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiReceiveDatagram request for the transport + provider. Receive datagrams just get queued up to an address, and are + completed when a DATAGRAM or DATAGRAM_BROADCAST frame is received at + the address. + +Arguments: + + Request - Describes this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + NTSTATUS Status; + PADDRESS Address; + PADDRESS_FILE AddressFile; + CTELockHandle LockHandle; + CTELockHandle CancelLH; + + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (Status != STATUS_SUCCESS) { + return Status; + } + + Address = AddressFile->Address; + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_GET_LOCK (&Address->Lock, &LockHandle); + + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + return STATUS_INVALID_HANDLE; + } + + + if (Request->Cancel) { + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + return STATUS_CANCELLED; + } + + InsertTailList (&AddressFile->ReceiveDatagramQueue, REQUEST_LINKAGE(Request)); + + IoSetCancelRoutine (Request, NbiCancelReceiveDatagram); + + NB_DEBUG2 (DATAGRAM, ("RDG posted on %lx\n", AddressFile)); + + NbiTransferReferenceAddressFile (AddressFile, AFREF_VERIFY, AFREF_RCV_DGRAM); + + NB_FREE_LOCK (&Address->Lock, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + + return STATUS_PENDING; + +} /* NbiTdiReceiveDatagram */ + + +VOID +NbiCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a receive + datagram. The datagram is found on the address file's receive + datagram queue. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + + PLIST_ENTRY p; + PADDRESS_FILE AddressFile; + PADDRESS Address; + PREQUEST Request = (PREQUEST)Irp; + BOOLEAN Found; + NB_DEFINE_LOCK_HANDLE(LockHandle) + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_RECEIVE_DATAGRAM)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_TRANSPORT_ADDRESS_FILE); + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + Address = AddressFile->Address; + + Found = FALSE; + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = AddressFile->ReceiveDatagramQueue.Flink; + p != &AddressFile->ReceiveDatagramQueue; + p = p->Flink) { + + if (LIST_ENTRY_TO_REQUEST(p) == Request) { + + RemoveEntryList (p); + Found = TRUE; + break; + } + } + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + if (Found) { + + NB_DEBUG (DATAGRAM, ("Cancelled datagram on %lx\n", AddressFile)); + + REQUEST_INFORMATION(Request) = 0; + REQUEST_STATUS(Request) = STATUS_CANCELLED; + + NbiCompleteRequest (Request); + NbiFreeRequest((PDEVICE)DeviceObject, Request); + + NbiDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM); + + } + +} /* NbiCancelReceiveDatagram */ + diff --git a/private/ntos/tdi/isnp/nb/device.c b/private/ntos/tdi/isnp/nb/device.c new file mode 100644 index 000000000..d1a9af781 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/device.c @@ -0,0 +1,461 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + device.c + +Abstract: + + This module contains code which implements the DEVICE object. + Routines are provided to reference, and dereference transport device + context objects. + + The transport device context object is a structure which contains a + system-defined DEVICE_OBJECT followed by information which is maintained + by the transport provider, called the context. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NbiCreateDevice) +#endif + + +VOID +NbiRefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a device context. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + CTEAssert (Device->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&Device->ReferenceCount); + +} /* NbiRefDevice */ + + +VOID +NbiDerefDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + Device - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Device->ReferenceCount); + + CTEAssert (result >= 0); + + if (result == 0) { + NbiDestroyDevice (Device); + } + +} /* NbiDerefDevice */ + + +NTSTATUS +NbiCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE *DevicePtr + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + Device - Pointer to a pointer to a transport device context object. + + DeviceName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + NTSTATUS status; + PDEVICE_OBJECT deviceObject; + PDEVICE Device; + ULONG DeviceSize; + UINT i; + + + // + // Create the device object for the sample transport, allowing + // room at the end for the device name to be stored (for use + // in logging errors) and the RIP fields. + // + + DeviceSize = sizeof(DEVICE) - sizeof(DEVICE_OBJECT) + + DeviceName->Length + sizeof(UNICODE_NULL); + + status = IoCreateDevice( + DriverObject, + DeviceSize, + DeviceName, + FILE_DEVICE_TRANSPORT, + 0, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + NB_DEBUG(DEVICE, ("Create device %ws failed %lx\n", DeviceName->Buffer, status)); + return status; + } + + deviceObject->Flags |= DO_DIRECT_IO; + + Device = (PDEVICE)deviceObject; + + NB_DEBUG2 (DEVICE, ("Create device %ws succeeded %lx\n", DeviceName->Buffer, Device)); + + // + // Initialize our part of the device context. + // + + RtlZeroMemory( + ((PUCHAR)Device) + sizeof(DEVICE_OBJECT), + sizeof(DEVICE) - sizeof(DEVICE_OBJECT)); + + // + // Copy over the device name. + // + + Device->DeviceNameLength = DeviceName->Length + sizeof(WCHAR); + Device->DeviceName = (PWCHAR)(Device+1); + RtlCopyMemory( + Device->DeviceName, + DeviceName->Buffer, + DeviceName->Length); + Device->DeviceName[DeviceName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + // + // Initialize the reference count. + // + + Device->ReferenceCount = 1; +#if DBG + Device->RefTypes[DREF_CREATE] = 1; +#endif + +#if DBG + RtlCopyMemory(Device->Signature1, "NDC1", 4); + RtlCopyMemory(Device->Signature2, "NDC2", 4); +#endif + + // + // BETABUGBUG: Clean this up a bit. + // + + Device->Information.Version = 0x0100; + Device->Information.MaxSendSize = 65535; + Device->Information.MaxConnectionUserData = 0; + Device->Information.MaxDatagramSize = 500; + Device->Information.ServiceFlags = + TDI_SERVICE_CONNECTION_MODE | TDI_SERVICE_ERROR_FREE_DELIVERY | + TDI_SERVICE_MULTICAST_SUPPORTED | TDI_SERVICE_BROADCAST_SUPPORTED | + TDI_SERVICE_DELAYED_ACCEPTANCE | TDI_SERVICE_CONNECTIONLESS_MODE | + TDI_SERVICE_MESSAGE_MODE; + Device->Information.MinimumLookaheadData = 128; + Device->Information.MaximumLookaheadData = 1500; + Device->Information.NumberOfResources = 0; + KeQuerySystemTime (&Device->Information.StartTime); + + Device->Statistics.Version = 0x0100; + Device->Statistics.MaximumSendWindow = 4; + Device->Statistics.AverageSendWindow = 4; + + // + // Set this so we won't ignore the broadcast name. + // + + Device->AddressCounts['*'] = 1; + + // + // Initialize the resource that guards address ACLs. + // + + ExInitializeResource (&Device->AddressResource); + + // + // initialize the various fields in the device context + // + + CTEInitLock (&Device->Interlock.Lock); + CTEInitLock (&Device->Lock.Lock); + + CTEInitTimer (&Device->FindNameTimer); + + Device->ControlChannelIdentifier = 1; + + InitializeListHead (&Device->GlobalSendPacketList); + InitializeListHead (&Device->GlobalReceivePacketList); + InitializeListHead (&Device->GlobalReceiveBufferList); + + InitializeListHead (&Device->AddressDatabase); +#if defined(_PNP_POWER) + InitializeListHead (&Device->AdapterAddressDatabase); +#endif _PNP_POWER + + InitializeListHead (&Device->WaitingFindNames); + + InitializeListHead (&Device->WaitingConnects); + InitializeListHead (&Device->WaitingDatagrams); + + InitializeListHead (&Device->WaitingAdapterStatus); + InitializeListHead (&Device->ActiveAdapterStatus); + + InitializeListHead (&Device->WaitingNetbiosFindName); + + InitializeListHead (&Device->ReceiveDatagrams); + InitializeListHead (&Device->ConnectIndicationInProgress); + + InitializeListHead (&Device->ListenQueue); + + InitializeListHead (&Device->ReceiveCompletionQueue); + + InitializeListHead (&Device->WaitPacketConnections); + InitializeListHead (&Device->PacketizeConnections); + InitializeListHead (&Device->DataAckConnections); + + Device->MemoryUsage = 0; + + InitializeListHead (&Device->SendPoolList); + InitializeListHead (&Device->ReceivePoolList); + InitializeListHead (&Device->ReceiveBufferPoolList); + + ExInitializeSListHead( &Device->SendPacketList ); + ExInitializeSListHead( &Device->ReceivePacketList ); + Device->ReceiveBufferList.Next = NULL; + + for (i = 0; i < CONNECTION_HASH_COUNT; i++) { + Device->ConnectionHash[i].Connections = NULL; + Device->ConnectionHash[i].ConnectionCount = 0; + Device->ConnectionHash[i].NextConnectionId = 1; + } + + KeQuerySystemTime (&Device->NbiStartTime); + + Device->State = DEVICE_STATE_CLOSED; + + Device->Type = NB_DEVICE_SIGNATURE; + Device->Size - sizeof (DEVICE); + + + *DevicePtr = Device; + return STATUS_SUCCESS; + +} /* NbiCreateDevice */ + + +VOID +NbiDestroyDevice( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + Device - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY p; + PNB_SEND_POOL SendPool; + PNB_SEND_PACKET SendPacket; + UINT SendPoolSize; + PNB_RECEIVE_POOL ReceivePool; + PNB_RECEIVE_PACKET ReceivePacket; + UINT ReceivePoolSize; + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PNB_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize; + ULONG HeaderLength; + UINT i; + + NB_DEBUG2 (DEVICE, ("Destroy device %lx\n", Device)); + + // + // Take all the connectionless packets out of its pools. + // + + HeaderLength = Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTIONLESS); + + SendPoolSize = FIELD_OFFSET (NB_SEND_POOL, Packets[0]) + + (sizeof(NB_SEND_PACKET) * Device->InitPackets) + + (HeaderLength * Device->InitPackets); + + while (!IsListEmpty (&Device->SendPoolList)) { + + p = RemoveHeadList (&Device->SendPoolList); + SendPool = CONTAINING_RECORD (p, NB_SEND_POOL, Linkage); + + for (i = 0; i < SendPool->PacketCount; i++) { + + SendPacket = &SendPool->Packets[i]; + NbiDeinitializeSendPacket (Device, SendPacket, HeaderLength); + + } + + NB_DEBUG2 (PACKET, ("Free packet pool %lx\n", SendPool)); + +#if !defined(NB_OWN_PACKETS) + NdisFreePacketPool(SendPool->PoolHandle); +#endif + + NbiFreeMemory (SendPool, SendPoolSize, MEMORY_PACKET, "SendPool"); + } + + + ReceivePoolSize = FIELD_OFFSET (NB_RECEIVE_POOL, Packets[0]) + + (sizeof(NB_RECEIVE_PACKET) * Device->InitPackets); + + while (!IsListEmpty (&Device->ReceivePoolList)) { + + p = RemoveHeadList (&Device->ReceivePoolList); + ReceivePool = CONTAINING_RECORD (p, NB_RECEIVE_POOL, Linkage); + + for (i = 0; i < ReceivePool->PacketCount; i++) { + + ReceivePacket = &ReceivePool->Packets[i]; + NbiDeinitializeReceivePacket (Device, ReceivePacket); + + } + + NB_DEBUG2 (PACKET, ("Free packet pool %lx\n", ReceivePool)); +#if !defined(NB_OWN_PACKETS) + NdisFreePacketPool(ReceivePool->PoolHandle); +#endif + NbiFreeMemory (ReceivePool, ReceivePoolSize, MEMORY_PACKET, "ReceivePool"); + } + +#if defined(_PNP_POWER) + NbiDestroyReceiveBufferPools( Device ); + + // + // Destroy adapter address list. + // + while(!IsListEmpty( &Device->AdapterAddressDatabase ) ){ + PADAPTER_ADDRESS AdapterAddress; + AdapterAddress = CONTAINING_RECORD( Device->AdapterAddressDatabase.Flink, ADAPTER_ADDRESS, Linkage ); + NbiDestroyAdapterAddress( AdapterAddress, NULL ); + } +#else + ReceiveBufferPoolSize = FIELD_OFFSET (NB_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(NB_RECEIVE_BUFFER) * Device->InitPackets) + + (Device->Bind.LineInfo.MaximumPacketSize * Device->InitPackets); + + while (!IsListEmpty (&Device->ReceiveBufferPoolList)) { + + p = RemoveHeadList (&Device->ReceiveBufferPoolList); + ReceiveBufferPool = CONTAINING_RECORD (p, NB_RECEIVE_BUFFER_POOL, Linkage); + + for (i = 0; i < ReceiveBufferPool->BufferCount; i++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[i]; + NbiDeinitializeReceiveBuffer (Device, ReceiveBuffer); + + } + + NB_DEBUG2 (PACKET, ("Free buffer pool %lx\n", ReceiveBufferPool)); + NbiFreeMemory (ReceiveBufferPool, ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + } +#endif _PNP_POWER + + NB_DEBUG (DEVICE, ("Final memory use is %d\n", Device->MemoryUsage)); + +#if DBG + for (i = 0; i < MEMORY_MAX; i++) { + if (NbiMemoryTag[i].BytesAllocated != 0) { + NB_DEBUG (DEVICE, ("Tag %d: %d bytes left\n", i, NbiMemoryTag[i].BytesAllocated)); + } + } +#endif + + // + // If we are being unloaded then someone is waiting for this + // event to finish the cleanup, since we may be at DISPATCH_LEVEL; + // otherwise it is during load and we can just kill ourselves here. + // + + if (Device->UnloadWaiting) { + + KeSetEvent( + &Device->UnloadEvent, + 0L, + FALSE); + + } else { + + CTEAssert (KeGetCurrentIrql() < DISPATCH_LEVEL); + ExDeleteResource (&Device->AddressResource); + IoDeleteDevice ((PDEVICE_OBJECT)Device); + } + +} /* NbiDestroyDevice */ + diff --git a/private/ntos/tdi/isnp/nb/dirs b/private/ntos/tdi/isnp/nb/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/isnp/nb/driver.c b/private/ntos/tdi/isnp/nb/driver.c new file mode 100644 index 000000000..c4df8cbe6 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/driver.c @@ -0,0 +1,1794 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + driver.c + +Abstract: + + This module contains the DriverEntry and other initialization + code for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + + +PDEVICE NbiDevice = NULL; +DEFINE_LOCK_STRUCTURE(NbiGlobalPoolInterlock); + +#ifdef RSRC_TIMEOUT_DBG + +ULONG NbiGlobalDebugResTimeout = 1; +LARGE_INTEGER NbiGlobalMaxResTimeout; + // the packet is allocated from ndis pool. +NB_SEND_PACKET NbiGlobalDeathPacket; // try to use this first for sends +UCHAR NbiGlobalDeathPacketHeader[100]; + +VOID +NbiInitDeathPacket() +{ + + NDIS_HANDLE PoolHandle; // poolhandle for sendpacket below when + NTSTATUS Status; + + // + // if we are using ndis packets, first create packet pool for 1 packet descriptor + // + NdisAllocatePacketPool( &Status, &PoolHandle, 1, sizeof(NB_SEND_RESERVED)); + if (!NT_SUCCESS(Status)){ + DbgPrint("Could not allocatee death packet %lx\n", Status); + NbiGlobalDebugResTimeout = 0; + } else { + + if (NbiInitializeSendPacket( + NbiDevice, + PoolHandle, + &NbiGlobalDeathPacket, + NbiGlobalDeathPacketHeader, + NbiDevice->Bind.MacHeaderNeeded + sizeof(NB_CONNECTION)) != STATUS_SUCCESS) { + + DbgPrint("Could not allocatee death packet %lx\n", Status); + NbiGlobalDebugResTimeout = 0; + + // + // Also free up the pool which we allocated above. + // + NdisFreePacketPool(PoolHandle); + } + } + +} +#endif //RSRC_TIMEOUT_DBG + +#if DBG + +ULONG NbiDebug = 0xffffffff; +ULONG NbiDebug2 = 0x00000000; +ULONG NbiMemoryDebug = 0x0002482c; + +UCHAR NbiTempDebugBuffer[150]; +UCHAR NbiDebugMemory[NB_MEMORY_LOG_SIZE][64]; +PUCHAR NbiDebugMemoryLoc = NbiDebugMemory[0]; +PUCHAR NbiDebugMemoryEnd = NbiDebugMemory[NB_MEMORY_LOG_SIZE]; +VOID +NbiDebugMemoryLog( + IN PUCHAR FormatString, + ... +) + +{ + INT ArgLen; + va_list ArgumentPointer; + + va_start(ArgumentPointer, FormatString); + + // + // To avoid any overflows, copy this in a temp buffer first. + RtlZeroMemory (NbiTempDebugBuffer, 150); + ArgLen = vsprintf(NbiTempDebugBuffer,FormatString, ArgumentPointer); + va_end(ArgumentPointer); + + if ( ArgLen > 64 ) { + CTEAssert( FALSE ); + } else { + RtlZeroMemory (NbiDebugMemoryLoc, 64); + RtlCopyMemory( NbiDebugMemoryLoc, NbiTempDebugBuffer, ArgLen ); + + NbiDebugMemoryLoc += 64; + if (NbiDebugMemoryLoc >= NbiDebugMemoryEnd) { + NbiDebugMemoryLoc = NbiDebugMemory[0]; + } + } + +} /* NbiDebugMemoryLog */ + + +DEFINE_LOCK_STRUCTURE(NbiMemoryInterlock); +MEMORY_TAG NbiMemoryTag[MEMORY_MAX]; + +#endif +// +// This is used only for CHK build. For +// tracking the refcount problem on connection, this +// is moved here for now. +// +DEFINE_LOCK_STRUCTURE(NbiGlobalInterlock); + + +#ifdef RASAUTODIAL +VOID +NbiAcdBind(); + +VOID +NbiAcdUnbind(); +#endif + +#ifdef NB_PACKET_LOG + +ULONG NbiPacketLogDebug = NB_PACKET_LOG_RCV_OTHER | NB_PACKET_LOG_SEND_OTHER; +USHORT NbiPacketLogSocket = 0; +DEFINE_LOCK_STRUCTURE(NbiPacketLogLock); +NB_PACKET_LOG_ENTRY NbiPacketLog[NB_PACKET_LOG_LENGTH]; +PNB_PACKET_LOG_ENTRY NbiPacketLogLoc = NbiPacketLog; +PNB_PACKET_LOG_ENTRY NbiPacketLogEnd = &NbiPacketLog[NB_PACKET_LOG_LENGTH]; + +VOID +NbiLogPacket( + IN BOOLEAN Send, + IN PUCHAR DestMac, + IN PUCHAR SrcMac, + IN USHORT Length, + IN PVOID NbiHeader, + IN PVOID Data + ) + +{ + + CTELockHandle LockHandle; + PNB_PACKET_LOG_ENTRY PacketLog; + LARGE_INTEGER TickCount; + ULONG DataLength; + + CTEGetLock (&NbiPacketLogLock, &LockHandle); + + PacketLog = NbiPacketLogLoc; + + ++NbiPacketLogLoc; + if (NbiPacketLogLoc >= NbiPacketLogEnd) { + NbiPacketLogLoc = NbiPacketLog; + } + *(UNALIGNED ULONG *)NbiPacketLogLoc->TimeStamp = 0x3e3d3d3d; // "===>" + + CTEFreeLock (&NbiPacketLogLock, LockHandle); + + RtlZeroMemory (PacketLog, sizeof(NB_PACKET_LOG_ENTRY)); + + PacketLog->SendReceive = Send ? '>' : '<'; + + KeQueryTickCount(&TickCount); + _itoa (TickCount.LowPart % 100000, PacketLog->TimeStamp, 10); + + RtlCopyMemory(PacketLog->DestMac, DestMac, 6); + RtlCopyMemory(PacketLog->SrcMac, SrcMac, 6); + PacketLog->Length[0] = Length / 256; + PacketLog->Length[1] = Length % 256; + + if (Length < sizeof(IPX_HEADER)) { + RtlCopyMemory(&PacketLog->NbiHeader, NbiHeader, Length); + } else { + RtlCopyMemory(&PacketLog->NbiHeader, NbiHeader, sizeof(IPX_HEADER)); + } + + DataLength = Length - sizeof(IPX_HEADER); + if (DataLength < 14) { + RtlCopyMemory(PacketLog->Data, Data, DataLength); + } else { + RtlCopyMemory(PacketLog->Data, Data, 14); + } + +} /* NbiLogPacket */ + +#endif // NB_PACKET_LOG + + +// +// Forward declaration of various routines used in this module. +// + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +VOID +NbiUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +NbiDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbiDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbiDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbiFreeResources ( + IN PVOID Adapter + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,DriverEntry) +#endif + +// +// This prevents us from having a bss section. +// + +ULONG _setjmpexused = 0; + + +// +// These two are used in various places in the driver. +// + +#if defined(_PNP_POWER) +IPX_LOCAL_TARGET BroadcastTarget = { {0}, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; +#endif _PNP_POWER + +UCHAR BroadcastAddress[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +UCHAR NetbiosBroadcastName[16] = { '*', 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; + +ULONG NbiFailLoad = FALSE; + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization of the Netbios ISN module. + It creates the device objects for the transport + provider and performs other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + + RegistryPath - The name of Netbios's node in the registry. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + static const NDIS_STRING ProtocolName = NDIS_STRING_CONST("Netbios/IPX Transport"); + PDEVICE Device; + PIPX_HEADER IpxHeader; + CTELockHandle LockHandle; + + PCONFIG Config = NULL; + +#if 0 + DbgPrint ("NBI: FailLoad at %lx\n", &NbiFailLoad); + DbgBreakPoint(); + + if (NbiFailLoad) { + return STATUS_UNSUCCESSFUL; + } +#endif + + // + // Initialize the Common Transport Environment. + // + + if (CTEInitialize() == 0) { + NB_DEBUG (DEVICE, ("CTEInitialize() failed\n")); + NbiWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_REGISTER_FAILED, + 101, + STATUS_UNSUCCESSFUL, + NULL, + 0, + NULL); + return STATUS_UNSUCCESSFUL; + } + +#if DBG + CTEInitLock (&NbiGlobalInterlock); + CTEInitLock (&NbiMemoryInterlock); + { + UINT i; + for (i = 0; i < MEMORY_MAX; i++) { + NbiMemoryTag[i].Tag = i; + NbiMemoryTag[i].BytesAllocated = 0; + } + } +#endif +#ifdef NB_PACKET_LOG + CTEInitLock (&NbiPacketLogLock); +#endif + +#if defined(NB_OWN_PACKETS) + CTEAssert (NDIS_PACKET_SIZE == FIELD_OFFSET(NDIS_PACKET, ProtocolReserved[0])); +#endif + + NB_DEBUG2 (DEVICE, ("ISN Netbios loaded\n")); + + // + // This allocates the CONFIG structure and returns + // it in Config. + // + + status = NbiGetConfiguration(DriverObject, RegistryPath, &Config); + + if (!NT_SUCCESS (status)) { + + // + // If it failed it logged an error. + // + + PANIC (" Failed to initialize transport, ISN Netbios initialization failed.\n"); + return status; + } + + + // + // Initialize the driver object with this driver's entry points. + // + + DriverObject->MajorFunction [IRP_MJ_CREATE] = NbiDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLOSE] = NbiDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLEANUP] = NbiDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] = NbiDispatchInternal; + DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = NbiDispatchDeviceControl; + + DriverObject->DriverUnload = NbiUnload; + + + // + // Create the device object which exports our name. + // + + status = NbiCreateDevice (DriverObject, &Config->DeviceName, &Device); + + if (!NT_SUCCESS (status)) { + + NbiWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_IPX_CREATE_DEVICE, + 801, + status, + NULL, + 0, + NULL); + + NbiFreeConfiguration(Config); + return status; + } + + NbiDevice = Device; + + // + // Initialize the global pool interlock + // + CTEInitLock (&NbiGlobalPoolInterlock); + + + // + // Save the relevant configuration parameters. + // + + Device->AckDelayTime = (Config->Parameters[CONFIG_ACK_DELAY_TIME] / SHORT_TIMER_DELTA) + 1; + Device->AckWindow = Config->Parameters[CONFIG_ACK_WINDOW]; + Device->AckWindowThreshold = Config->Parameters[CONFIG_ACK_WINDOW_THRESHOLD]; + Device->EnablePiggyBackAck = Config->Parameters[CONFIG_ENABLE_PIGGYBACK_ACK]; + Device->Extensions = Config->Parameters[CONFIG_EXTENSIONS]; + Device->RcvWindowMax = Config->Parameters[CONFIG_RCV_WINDOW_MAX]; + Device->BroadcastCount = Config->Parameters[CONFIG_BROADCAST_COUNT]; + Device->BroadcastTimeout = Config->Parameters[CONFIG_BROADCAST_TIMEOUT] * 500; + Device->ConnectionCount = Config->Parameters[CONFIG_CONNECTION_COUNT]; + Device->ConnectionTimeout = Config->Parameters[CONFIG_CONNECTION_TIMEOUT] * 500; + Device->InitPackets = Config->Parameters[CONFIG_INIT_PACKETS]; + Device->MaxPackets = Config->Parameters[CONFIG_MAX_PACKETS]; + Device->InitialRetransmissionTime = Config->Parameters[CONFIG_INIT_RETRANSMIT_TIME]; + Device->Internet = Config->Parameters[CONFIG_INTERNET]; + Device->KeepAliveCount = Config->Parameters[CONFIG_KEEP_ALIVE_COUNT]; + Device->KeepAliveTimeout = Config->Parameters[CONFIG_KEEP_ALIVE_TIMEOUT]; + Device->RetransmitMax = Config->Parameters[CONFIG_RETRANSMIT_MAX]; + Device->RouterMtu = Config->Parameters[CONFIG_ROUTER_MTU]; + + Device->FindNameTimeout = + ((Config->Parameters[CONFIG_BROADCAST_TIMEOUT] * 500) + (FIND_NAME_GRANULARITY/2)) / + FIND_NAME_GRANULARITY; + + Device->MaxReceiveBuffers = 20; // BUGBUG: Make it configurable? + +#if defined(_PNP_POWER) + // + // Make Tdi ready for pnp notifications before binding + // to IPX + // + TdiInitialize(); + + // Initialize the timer system. This should be done before + // binding to ipx because we should have timers intialized + // before ipx calls our pnp indications. + + NbiInitializeTimers (Device); +#endif _PNP_POWER + + // + // Now bind to IPX via the internal interface. + // + + status = NbiBind (Device, Config); + + if (!NT_SUCCESS (status)) { + + // + // If it failed it logged an error. + // + + NbiFreeConfiguration(Config); + NbiDereferenceDevice (Device, DREF_LOADED); + return status; + } + +#ifdef RSRC_TIMEOUT_DBG + NbiInitDeathPacket(); + // NbiGlobalMaxResTimeout.QuadPart = 50; // 1*1000*10000; + NbiGlobalMaxResTimeout.QuadPart = 20*60*1000; + NbiGlobalMaxResTimeout.QuadPart *= 10000; +#endif // RSRC_TIMEOUT_DBG + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + // + // Create Hash Table to store netbios cache entries + // For server create a big table, for workstation a small one + // + + if ( MmIsThisAnNtAsSystem() ) { + status = CreateNetbiosCacheTable( &Device->NameCache, NB_NETBIOS_CACHE_TABLE_LARGE ); + } else { + status = CreateNetbiosCacheTable( &Device->NameCache, NB_NETBIOS_CACHE_TABLE_SMALL ); + } + + if (!NT_SUCCESS (status)) { + + // + // If it failed it logged an error. + // + + NB_FREE_LOCK(&Device->Lock, LockHandle); + NbiFreeConfiguration(Config); + NbiDereferenceDevice (Device, DREF_LOADED); + return status; + } + + // + // Allocate our initial connectionless packet pool. + // + + NbiAllocateSendPool (Device); + + // + // Allocate our initial receive packet pool. + // + + NbiAllocateReceivePool (Device); + + // + // Allocate our initial receive buffer pool. + // + // +#if !defined(_PNP_POWER) + NbiAllocateReceiveBufferPool (Device); +#endif !_PNP_POWER + +#if defined(_PNP_POWER) + if ( DEVICE_STATE_CLOSED == Device->State ) { + Device->State = DEVICE_STATE_LOADED; + } +#endif _PNP_POWER + + NB_FREE_LOCK (&Device->Lock, LockHandle); + +#if !defined(_PNP_POWER) + // + // Start the timer system. + // + + NbiInitializeTimers (Device); +#endif !_PNP_POWER + + + // + // Fill in the default connnectionless header. + // + + IpxHeader = &Device->ConnectionlessHeader; + IpxHeader->CheckSum = 0xffff; + IpxHeader->PacketLength[0] = 0; + IpxHeader->PacketLength[1] = 0; + IpxHeader->TransportControl = 0; + IpxHeader->PacketType = 0; + *(UNALIGNED ULONG *)(IpxHeader->DestinationNetwork) = 0; + RtlCopyMemory(IpxHeader->DestinationNode, BroadcastAddress, 6); + IpxHeader->DestinationSocket = NB_SOCKET; + IpxHeader->SourceSocket = NB_SOCKET; +#if !defined(_PNP_POWER) + RtlCopyMemory(IpxHeader->SourceNetwork, Device->Bind.Network, 4); + RtlCopyMemory(IpxHeader->SourceNode, Device->Bind.Node, 6); + + Device->State = DEVICE_STATE_OPEN; +#endif !_PNP_POWER + + + NbiFreeConfiguration(Config); + +#ifdef RASAUTODIAL + // + // Get the automatic connection + // driver entry points. + // + NbiAcdBind(); +#endif + + return STATUS_SUCCESS; + +} /* DriverEntry */ + +VOID +NbiUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine unloads the sample transport driver. + It unbinds from any NDIS drivers that are open and frees all resources + associated with the transport. The I/O system will not call us until + nobody above has Netbios open. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None. When the function returns, the driver is unloaded. + +--*/ + +{ + PNETBIOS_CACHE CacheName; + PDEVICE Device = NbiDevice; + PLIST_ENTRY p; + + UNREFERENCED_PARAMETER (DriverObject); + + +#ifdef RASAUTODIAL + // + // Unbind from the + // automatic connection driver. + // + NbiAcdUnbind(); +#endif + + Device->State = DEVICE_STATE_STOPPING; + + // + // Free the cache of netbios names. + // + + DestroyNetbiosCacheTable( Device->NameCache ); + + // + // Cancel the long timer. + // + + if (CTEStopTimer (&Device->LongTimer)) { + NbiDereferenceDevice (Device, DREF_LONG_TIMER); + } + + // + // Unbind from the IPX driver. + // + + NbiUnbind (Device); + + // + // This event will get set when the reference count + // drops to 0. + // + + KeInitializeEvent( + &Device->UnloadEvent, + NotificationEvent, + FALSE); + Device->UnloadWaiting = TRUE; + + // + // Remove the reference for us being loaded. + // + + NbiDereferenceDevice (Device, DREF_LOADED); + + // + // Wait for our count to drop to zero. + // + + KeWaitForSingleObject( + &Device->UnloadEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + // + // Do the cleanup that has to happen at IRQL 0. + // + + ExDeleteResource (&Device->AddressResource); + IoDeleteDevice ((PDEVICE_OBJECT)Device); + +} /* NbiUnload */ + + +VOID +NbiFreeResources ( + IN PVOID Adapter + ) +/*++ + +Routine Description: + + This routine is called by Netbios to clean up the data structures associated + with a given Device. When this routine exits, the Device + should be deleted as it no longer has any assocaited resources. + +Arguments: + + Device - Pointer to the Device we wish to clean up. + +Return Value: + + None. + +--*/ +{ +#if 0 + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PTP_PACKET packet; + PNDIS_PACKET ndisPacket; + PBUFFER_TAG BufferTag; +#endif + + +#if 0 + // + // Clean up packet pool. + // + + while ( Device->PacketPool.Next != NULL ) { + s = PopEntryList( &Device->PacketPool ); + packet = CONTAINING_RECORD( s, TP_PACKET, Linkage ); + + NbiDeallocateSendPacket (Device, packet); + } + + // + // Clean up receive packet pool + // + + while ( Device->ReceivePacketPool.Next != NULL) { + s = PopEntryList (&Device->ReceivePacketPool); + + // + // HACK: This works because Linkage is the first field in + // ProtocolReserved for a receive packet. + // + + ndisPacket = CONTAINING_RECORD (s, NDIS_PACKET, ProtocolReserved[0]); + + NbiDeallocateReceivePacket (Device, ndisPacket); + } + + + // + // Clean up receive buffer pool. + // + + while ( Device->ReceiveBufferPool.Next != NULL ) { + s = PopEntryList( &Device->ReceiveBufferPool ); + BufferTag = CONTAINING_RECORD (s, BUFFER_TAG, Linkage ); + + NbiDeallocateReceiveBuffer (Device, BufferTag); + } + +#endif + +} /* NbiFreeResources */ + + +NTSTATUS +NbiDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the IPXNB device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + CTELockHandle LockHandle; + PDEVICE Device = (PDEVICE)DeviceObject; + NTSTATUS Status; + PFILE_FULL_EA_INFORMATION openType; + BOOLEAN found; + PADDRESS_FILE AddressFile; + PCONNECTION Connection; + PREQUEST Request; + UINT i; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + +#if !defined(_PNP_POWER) + if (Device->State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } +#endif !_PNP_POWER + + // + // Allocate a request to track this IRP. + // + + Request = NbiAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Make sure status information is consistent every time. + // + + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + // + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + // + + + switch (REQUEST_MAJOR_FUNCTION(Request)) { + + // + // The Create function opens a transport object (either address or + // connection). Access checking is performed on the specified + // address to ensure security of transport-layer addresses. + // + + case IRP_MJ_CREATE: + +#if defined(_PNP_POWER) + if (Device->State != DEVICE_STATE_OPEN) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + openType = OPEN_REQUEST_EA_INFORMATION(Request); + + if (openType != NULL) { + + found = TRUE; + + for (i=0;i<openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiTransportAddress[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = NbiOpenAddress (Device, Request); + break; + } + + // + // Connection? + // + + found = TRUE; + + for (i=0;i<openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiConnectionContext[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = NbiOpenConnection (Device, Request); + break; + } + + } else { + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + REQUEST_OPEN_CONTEXT(Request) = (PVOID)(Device->ControlChannelIdentifier); + ++Device->ControlChannelIdentifier; + if (Device->ControlChannelIdentifier == 0) { + Device->ControlChannelIdentifier = 1; + } + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + REQUEST_OPEN_TYPE(Request) = (PVOID)TDI_CONTROL_CHANNEL_FILE; + Status = STATUS_SUCCESS; + } + + break; + + case IRP_MJ_CLOSE: + +#if defined(_PNP_POWER) + if ( (Device->State != DEVICE_STATE_OPEN) && (Device->State != DEVICE_STATE_LOADED) ) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + // + // The Close function closes a transport endpoint, terminates + // all outstanding transport activity on the endpoint, and unbinds + // the endpoint from its transport address, if any. If this + // is the last transport endpoint bound to the address, then + // the address is removed from the provider. + // + + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) { + + case TDI_TRANSPORT_ADDRESS_FILE: + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + + // + // This creates a reference to AddressFile. + // + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile(AddressFile, CONFLICT_IS_OK); +#else + Status = NbiVerifyAddressFile(AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS (Status)) { + Status = STATUS_INVALID_HANDLE; + } else { + Status = NbiCloseAddressFile (Device, Request); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } + + break; + + case TDI_CONNECTION_FILE: + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + // + // We don't call VerifyConnection because the I/O + // system should only give us one close and the file + // object should be valid. This helps avoid a window + // where two threads call HandleConnectionZero at the + // same time. + // + + Status = NbiCloseConnection (Device, Request); + + break; + + case TDI_CONTROL_CHANNEL_FILE: + + // + // See if it is one of the upper driver's control channels. + // + + Status = STATUS_SUCCESS; + + break; + + default: + + Status = STATUS_INVALID_HANDLE; + + } + + break; + + case IRP_MJ_CLEANUP: + +#if defined(_PNP_POWER) + if ( (Device->State != DEVICE_STATE_OPEN) && (Device->State != DEVICE_STATE_LOADED) ) { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } +#endif _PNP_POWER + + // + // Handle the two stage IRP for a file close operation. When the first + // stage hits, run down all activity on the object of interest. This + // do everything to it but remove the creation hold. Then, when the + // CLOSE irp hits, actually close the object. + // + + switch ((ULONG)REQUEST_OPEN_TYPE(Request)) { + + case TDI_TRANSPORT_ADDRESS_FILE: + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile(AddressFile, CONFLICT_IS_OK); +#else + Status = NbiVerifyAddressFile(AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS (Status)) { + + Status = STATUS_INVALID_HANDLE; + + } else { + + NbiStopAddressFile (AddressFile, AddressFile->Address); + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + Status = STATUS_SUCCESS; + } + + break; + + case TDI_CONNECTION_FILE: + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection(Connection); + + if (!NT_SUCCESS (Status)) { + + Status = STATUS_INVALID_HANDLE; + + } else { + + NB_BEGIN_SYNC (&SyncContext); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + // + // This call releases the lock. + // + + NbiStopConnection( + Connection, + STATUS_INVALID_CONNECTION + NB_LOCK_HANDLE_ARG (LockHandle1)); + + NB_END_SYNC (&SyncContext); + + NbiDereferenceConnection (Connection, CREF_VERIFY); + Status = STATUS_SUCCESS; + } + + break; + + case TDI_CONTROL_CHANNEL_FILE: + + Status = STATUS_SUCCESS; + break; + + default: + + Status = STATUS_INVALID_HANDLE; + + } + + break; + + default: + + Status = STATUS_INVALID_DEVICE_REQUEST; + + } /* major function switch */ + + if (Status != STATUS_PENDING) { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + } + + // + // Return the immediate status code to the caller. + // + + return Status; + +} /* NbiDispatchOpenClose */ + + +NTSTATUS +NbiDispatchDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = (PDEVICE)DeviceObject; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp); + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + + default: + + // + // Convert the user call to the proper internal device call. + // + + Status = TdiMapUserRequest (DeviceObject, Irp, IrpSp); + + if (Status == STATUS_SUCCESS) { + + // + // If TdiMapUserRequest returns SUCCESS then the IRP + // has been converted into an IRP_MJ_INTERNAL_DEVICE_CONTROL + // IRP, so we dispatch it as usual. The IRP will + // be completed by this call. + // + + Status = NbiDispatchInternal (DeviceObject, Irp); + + } else { + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } + + break; + } + + return Status; + +} /* NbiDeviceControl */ + + +NB_TDI_DISPATCH_ROUTINE NbiDispatchInternalTable[] = { + NbiTdiAssociateAddress, + NbiTdiDisassociateAddress, + NbiTdiConnect, + NbiTdiListen, + NbiTdiAccept, + NbiTdiDisconnect, + NbiTdiSend, + NbiTdiReceive, + NbiTdiSendDatagram, + NbiTdiReceiveDatagram, + NbiTdiSetEventHandler, + NbiTdiQueryInformation, + NbiTdiSetInformation, + NbiTdiAction + }; + + +NTSTATUS +NbiDispatchInternal( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE Device = (PDEVICE)DeviceObject; + PREQUEST Request; + UCHAR MinorFunction; + + if (Device->State != DEVICE_STATE_OPEN) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // + // Allocate a request to track this IRP. + // + + Request = NbiAllocateRequest (Device, Irp); + IF_NOT_ALLOCATED(Request) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Make sure status information is consistent every time. + // + + MARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = STATUS_PENDING; + REQUEST_INFORMATION(Request) = 0; + + + // + // Branch to the appropriate request handler. + // + + MinorFunction = REQUEST_MINOR_FUNCTION(Request) - 1; + + if (MinorFunction <= (TDI_ACTION-1)) { + + Status = (*NbiDispatchInternalTable[MinorFunction]) ( + Device, + Request); + + } else { + + NB_DEBUG (DRIVER, ("Unsupported minor code %d\n", MinorFunction+1)); + if ((MinorFunction+1) == TDI_DISCONNECT) { + Status = STATUS_SUCCESS; + } else { + Status = STATUS_INVALID_DEVICE_REQUEST; + } + } + + if (Status != STATUS_PENDING) { + UNMARK_REQUEST_PENDING(Request); + REQUEST_STATUS(Request) = Status; + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + } + + // + // Return the immediate status code to the caller. + // + + return Status; + +} /* NbiDispatchInternal */ + + +PVOID +NbipAllocateMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN BOOLEAN ChargeDevice + ) + +/*++ + +Routine Description: + + This routine allocates memory, making sure it is within + the limit allowed by the device. + +Arguments: + + BytesNeeded - The number of bytes to allocated. + + ChargeDevice - TRUE if the device should be charged. + +Return Value: + + None. + +--*/ + +{ + PVOID Memory; + PDEVICE Device = NbiDevice; + + if (ChargeDevice) { + if ((Device->MemoryLimit != 0) && + (((LONG)(Device->MemoryUsage + BytesNeeded) > + Device->MemoryLimit))) { + + NbiPrint1 ("Nbi: Could not allocate %d: limit\n", BytesNeeded); + NbiWriteResourceErrorLog (Device, BytesNeeded, Tag); + return NULL; + } + } + +#if ISN_NT + Memory = ExAllocatePoolWithTag (NonPagedPool, BytesNeeded, ' IBN'); +#else + Memory = CTEAllocMem (BytesNeeded); +#endif + + if (Memory == NULL) { + + NbiPrint1("Nbi: Could not allocate %d: no pool\n", BytesNeeded); + + if (ChargeDevice) { + NbiWriteResourceErrorLog (Device, BytesNeeded, Tag); + } + + return NULL; + } + + if (ChargeDevice) { + Device->MemoryUsage += BytesNeeded; + } + + return Memory; + +} /* NbipAllocateMemory */ + + +VOID +NbipFreeMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN BOOLEAN ChargeDevice + ) + +/*++ + +Routine Description: + + This routine frees memory allocated with NbipAllocateMemory. + +Arguments: + + Memory - The memory allocated. + + BytesAllocated - The number of bytes to freed. + + ChargeDevice - TRUE if the device should be charged. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + +#if ISN_NT + ExFreePool (Memory); +#else + CTEFreeMem (Memory); +#endif + + if (ChargeDevice) { + Device->MemoryUsage -= BytesAllocated; + } + +} /* NbipFreeMemory */ + +#if DBG + + +PVOID +NbipAllocateTaggedMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN PUCHAR Description + ) + +/*++ + +Routine Description: + + This routine allocates memory, charging it to the device. + If it cannot allocate memory it uses the Tag and Descriptor + to log an error. + +Arguments: + + BytesNeeded - The number of bytes to allocated. + + Tag - A unique ID used in the error log. + + Description - A text description of the allocation. + +Return Value: + + None. + +--*/ + +{ + PVOID Memory; + + UNREFERENCED_PARAMETER(Description); + + Memory = NbipAllocateMemory(BytesNeeded, Tag, (BOOLEAN)(Tag != MEMORY_CONFIG)); + + if (Memory) { + ExInterlockedAddUlong( + &NbiMemoryTag[Tag].BytesAllocated, + BytesNeeded, + &NbiMemoryInterlock); + } + + return Memory; + +} /* NbipAllocateTaggedMemory */ + + +VOID +NbipFreeTaggedMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN ULONG Tag, + IN PUCHAR Description + ) + +/*++ + +Routine Description: + + This routine frees memory allocated with NbipAllocateTaggedMemory. + +Arguments: + + Memory - The memory allocated. + + BytesAllocated - The number of bytes to freed. + + Tag - A unique ID used in the error log. + + Description - A text description of the allocation. + +Return Value: + + None. + +--*/ + +{ + + UNREFERENCED_PARAMETER(Description); + + ExInterlockedAddUlong( + &NbiMemoryTag[Tag].BytesAllocated, + (ULONG)(-(LONG)BytesAllocated), + &NbiMemoryInterlock); + + NbipFreeMemory (Memory, BytesAllocated, (BOOLEAN)(Tag != MEMORY_CONFIG)); + +} /* NbipFreeTaggedMemory */ + +#endif + + +VOID +NbiWriteResourceErrorLog( + IN PDEVICE Device, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + an out of resources condition. + +Arguments: + + Device - Pointer to the device context. + + BytesNeeded - If applicable, the number of bytes that could not + be allocated. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + PUCHAR StringLoc; + ULONG TempUniqueError; + static WCHAR UniqueErrorBuffer[4] = L"000"; + INT i; + + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + Device->DeviceNameLength + + sizeof(UniqueErrorBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize + ); + + // + // Convert the error value into a buffer. + // + + TempUniqueError = UniqueErrorValue; + for (i=1; i>=0; i--) { + UniqueErrorBuffer[i] = (WCHAR)((TempUniqueError % 10) + L'0'); + TempUniqueError /= 10; + } + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = sizeof(ULONG); + errorLogEntry->NumberOfStrings = 2; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = EVENT_TRANSPORT_RESOURCE_POOL; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + errorLogEntry->DumpData[0] = BytesNeeded; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + + StringLoc += Device->DeviceNameLength; + RtlCopyMemory (StringLoc, UniqueErrorBuffer, sizeof(UniqueErrorBuffer)); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbiWriteResourceErrorLog */ + + +VOID +NbiWriteGeneralErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a general problem as indicated by the parameters. It handles + event codes REGISTER_FAILED, BINDING_FAILED, ADAPTER_NOT_FOUND, + TRANSFER_DATA, TOO_MANY_LINKS, and BAD_PROTOCOL. All these + events have messages with one or two strings in them. + +Arguments: + + Device - Pointer to the device context, or this may be + a driver object instead. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + SecondString - If not NULL, the string to use as the %3 + value in the error log packet. + + DumpDataCount - The number of ULONGs of dump data. + + DumpData - Dump data for the packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG SecondStringSize; + PUCHAR StringLoc; + static WCHAR DriverName[8] = L"NwlnkNb"; + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + (DumpDataCount * sizeof(ULONG)); + + if (Device->Type == IO_TYPE_DEVICE) { + EntrySize += (UCHAR)Device->DeviceNameLength; + } else { + EntrySize += sizeof(DriverName); + } + + if (SecondString) { + SecondStringSize = (wcslen(SecondString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize += (UCHAR)SecondStringSize; + } + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize + ); + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = (USHORT)(DumpDataCount * sizeof(ULONG)); + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 1 : 2; + errorLogEntry->StringOffset = + sizeof(IO_ERROR_LOG_PACKET) + ((DumpDataCount-1) * sizeof(ULONG)); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + if (DumpDataCount) { + RtlCopyMemory(errorLogEntry->DumpData, DumpData, DumpDataCount * sizeof(ULONG)); + } + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + if (Device->Type == IO_TYPE_DEVICE) { + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + StringLoc += Device->DeviceNameLength; + } else { + RtlCopyMemory (StringLoc, DriverName, sizeof(DriverName)); + StringLoc += sizeof(DriverName); + } + if (SecondString) { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbiWriteGeneralErrorLog */ + + +VOID +NbiWriteOidErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a problem querying or setting an OID on an adapter. It handles + event codes SET_OID_FAILED and QUERY_OID_FAILED. + +Arguments: + + Device - Pointer to the device context. + + ErrorCode - Used as the ErrorCode in the error log packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + AdapterString - The name of the adapter we were bound to. + + OidValue - The OID which could not be set or queried. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG AdapterStringSize; + PUCHAR StringLoc; + static WCHAR OidBuffer[9] = L"00000000"; + INT i; + UINT CurrentDigit; + + AdapterStringSize = (wcslen(AdapterString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize = sizeof(IO_ERROR_LOG_PACKET) - + sizeof(ULONG) + + Device->DeviceNameLength + + AdapterStringSize + + sizeof(OidBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)Device, + EntrySize + ); + + // + // Convert the OID into a buffer. + // + + for (i=7; i>=0; i--) { + CurrentDigit = OidValue & 0xf; + OidValue >>= 4; + if (CurrentDigit >= 0xa) { + OidBuffer[i] = (WCHAR)(CurrentDigit - 0xa + L'A'); + } else { + OidBuffer[i] = (WCHAR)(CurrentDigit + L'0'); + } + } + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = 0; + errorLogEntry->NumberOfStrings = 3; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) - sizeof(ULONG); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = 0; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, Device->DeviceName, Device->DeviceNameLength); + StringLoc += Device->DeviceNameLength; + + RtlCopyMemory (StringLoc, OidBuffer, sizeof(OidBuffer)); + StringLoc += sizeof(OidBuffer); + + RtlCopyMemory (StringLoc, AdapterString, AdapterStringSize); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbiWriteOidErrorLog */ + diff --git a/private/ntos/tdi/isnp/nb/event.c b/private/ntos/tdi/isnp/nb/event.c new file mode 100644 index 000000000..f6cff7105 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/event.c @@ -0,0 +1,117 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + event.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiSetEventHandler + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +PVOID TdiDefaultHandlers[6] = { + TdiDefaultConnectHandler, + TdiDefaultDisconnectHandler, + TdiDefaultErrorHandler, + TdiDefaultReceiveHandler, + TdiDefaultRcvDatagramHandler, + TdiDefaultRcvExpeditedHandler + }; + + +NTSTATUS +NbiTdiSetEventHandler( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetEventHandler request for the + transport provider. The caller (request dispatcher) verifies + that this routine will not be executed on behalf of a user-mode + client, as this request enables direct callouts at DISPATCH_LEVEL. + +Arguments: + + Device - The netbios device object. + + Request - Pointer to the request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + CTELockHandle LockHandle; + PTDI_REQUEST_KERNEL_SET_EVENT Parameters; + PADDRESS_FILE AddressFile; + UINT EventType; + + UNREFERENCED_PARAMETER (Device); + + // + // Get the Address this is associated with; if there is none, get out. + // + + AddressFile = REQUEST_OPEN_CONTEXT(Request); +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS (Status)) { + return Status; + } + + NB_GET_LOCK (&AddressFile->Address->Lock, &LockHandle); + + Parameters = (PTDI_REQUEST_KERNEL_SET_EVENT)REQUEST_PARAMETERS(Request); + EventType = (UINT)(Parameters->EventType); + + if (Parameters->EventType > TDI_EVENT_RECEIVE_EXPEDITED) { + + Status = STATUS_INVALID_PARAMETER; + + } else { + + if (Parameters->EventHandler == NULL) { + AddressFile->RegisteredHandler[EventType] = FALSE; + AddressFile->Handlers[EventType] = TdiDefaultHandlers[EventType]; + AddressFile->HandlerContexts[EventType] = NULL; + } else { + AddressFile->Handlers[EventType] = Parameters->EventHandler; + AddressFile->HandlerContexts[EventType] = Parameters->EventContext; + AddressFile->RegisteredHandler[EventType] = TRUE; + } + + } + + NB_FREE_LOCK (&AddressFile->Address->Lock, LockHandle); + + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + return Status; + +} /* NbiTdiSetEventHandler */ + diff --git a/private/ntos/tdi/isnp/nb/frame.c b/private/ntos/tdi/isnp/nb/frame.c new file mode 100644 index 000000000..bbc14fd56 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/frame.c @@ -0,0 +1,1095 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + frame.c + +Abstract: + + This module contains code which creates and sends various + types of frames. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#if defined(_PNP_POWER) + +VOID +NbiSendNameFrame( + IN PADDRESS Address OPTIONAL, + IN UCHAR NameTypeFlag, + IN UCHAR DataStreamType, + IN PIPX_LOCAL_TARGET LocalTarget OPTIONAL, + IN NB_CONNECTIONLESS UNALIGNED * ReqFrame OPTIONAL + ) + +/*++ + +Routine Description: + + This routine allocates and sends a name frame on the + specified address. It handles add name, name in use, and + delete name frames. + +Arguments: + + Address - The address on which the frame is sent. This will + be NULL if we are responding to a request to the + broadcast address. + + NameTypeFlag - The name type flag to use. + + DataStreamType - The type of the command. + + LocalTarget - If specified, the local target to use for the + send (if not, it will be broadcast). + + ReqFrame - If specified, the request frame for which this + response is being sent. The reqframe contains the + destination ipx address and the netbios name. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + NDIS_STATUS NdisStatus; + IPX_LOCAL_TARGET TempLocalTarget; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->u.SR_NF.Address = Address; // may be NULL + Reserved->Type = SEND_TYPE_NAME_FRAME; + + // + // Frame that are not sent to a specific address are + // sent to all valid NIC IDs. + // + + if (!ARGUMENT_PRESENT(LocalTarget)) { + Reserved->u.SR_NF.NameTypeFlag = NameTypeFlag; + Reserved->u.SR_NF.DataStreamType = DataStreamType; + } + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + if (ARGUMENT_PRESENT(ReqFrame)) { + RtlCopyMemory((PVOID)&Header->IpxHeader.DestinationNetwork, (PVOID)ReqFrame->IpxHeader.SourceNetwork, 12); + } + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + if (ARGUMENT_PRESENT(LocalTarget)) { + Header->IpxHeader.PacketType = 0x04; + } else { + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + } + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = DataStreamType; + Header->NameFrame.NameTypeFlag = NameTypeFlag; + + // + // DataStreamType2 is the same as DataStreamType except for + // name in use frames where it is set to the add name type. + // + + Header->NameFrame.DataStreamType2 = (UCHAR) + ((DataStreamType != NB_CMD_NAME_IN_USE) ? DataStreamType : NB_CMD_ADD_NAME); + + RtlCopyMemory( + Header->NameFrame.Name, + Address ? Address->NetbiosAddress.NetbiosName : ReqFrame->NameFrame.Name, + 16); + + if (Address) { + NbiReferenceAddress (Address, AREF_NAME_FRAME); + } else { + NbiReferenceDevice (Device, DREF_NAME_FRAME); + } + + // + // Now send the frame (because it is all in the first segment, + // IPX will adjust the length of the buffer correctly). + // + + if (!ARGUMENT_PRESENT(LocalTarget)) { + LocalTarget = &BroadcastTarget; + } + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + + sizeof(NB_NAME_FRAME)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendNameFrame */ +#else + +VOID +NbiSendNameFrame( + IN PADDRESS Address OPTIONAL, + IN UCHAR NameTypeFlag, + IN UCHAR DataStreamType, + IN PIPX_LOCAL_TARGET LocalTarget OPTIONAL, + IN TDI_ADDRESS_IPX UNALIGNED * DestAddress OPTIONAL + ) + +/*++ + +Routine Description: + + This routine allocates and sends a name frame on the + specified address. It handles add name, name in use, and + delete name frames. + +Arguments: + + Address - The address on which the frame is sent. This will + be NULL if we are responding to a request to the + broadcast address. + + NameTypeFlag - The name type flag to use. + + DataStreamType - The type of the command. + + LocalTarget - If specified, the local target to use for the + send (if not, it will be broadcast). + + DestAddress - If specified, the destination IPX address to + use for the send (if not, it will be broadcast on net 0). + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + NDIS_STATUS NdisStatus; + IPX_LOCAL_TARGET TempLocalTarget; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->u.SR_NF.Address = Address; // may be NULL + Reserved->Type = SEND_TYPE_NAME_FRAME; + + // + // Frame that are not sent to a specific address are + // sent to all valid NIC IDs. + // + + if (!ARGUMENT_PRESENT(LocalTarget)) { + Reserved->u.SR_NF.CurrentNicId = 1; + Reserved->u.SR_NF.NameTypeFlag = NameTypeFlag; + Reserved->u.SR_NF.DataStreamType = DataStreamType; + } else { + Reserved->u.SR_NF.CurrentNicId = 0; + } + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + if (ARGUMENT_PRESENT(DestAddress)) { + RtlCopyMemory((PVOID)&Header->IpxHeader.DestinationNetwork, (PVOID)DestAddress, 12); + } + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + if (ARGUMENT_PRESENT(LocalTarget)) { + Header->IpxHeader.PacketType = 0x04; + } else { + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + } + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = DataStreamType; + Header->NameFrame.NameTypeFlag = NameTypeFlag; + + // + // DataStreamType2 is the same as DataStreamType except for + // name in use frames where it is set to the add name type. + // + + Header->NameFrame.DataStreamType2 = (UCHAR) + ((DataStreamType != NB_CMD_NAME_IN_USE) ? DataStreamType : NB_CMD_ADD_NAME); + + RtlCopyMemory( + Header->NameFrame.Name, + Address ? Address->NetbiosAddress.NetbiosName : NetbiosBroadcastName, + 16); + + if (Address) { + NbiReferenceAddress (Address, AREF_NAME_FRAME); + } else { + NbiReferenceDevice (Device, DREF_NAME_FRAME); + } + + // + // Now send the frame (because it is all in the first segment, + // IPX will adjust the length of the buffer correctly). + // + + if (!ARGUMENT_PRESENT(LocalTarget)) { + TempLocalTarget.NicId = 1; // BUGBUG: What if 1 isn't valid? + RtlCopyMemory (TempLocalTarget.MacAddress, BroadcastAddress, 6); + LocalTarget = &TempLocalTarget; + } + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + + sizeof(NB_NAME_FRAME)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendNameFrame */ +#endif _PNP_POWER + + +VOID +NbiSendSessionInitialize( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine allocates and sends a session initialize + frame for the specified connection. + +Arguments: + + Connection - The connection on which the frame is sent. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + NDIS_STATUS NdisStatus; + PNB_SESSION_INIT SessionInitMemory; + PNDIS_BUFFER SessionInitBuffer; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + + // + // Allocate a buffer for the extra portion of the + // session initialize. + // + + SessionInitMemory = (PNB_SESSION_INIT)NbiAllocateMemory(sizeof(NB_SESSION_INIT), MEMORY_CONNECTION, "Session Initialize"); + if (!SessionInitMemory) { + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + // + // Allocate an NDIS buffer to map the extra buffer. + // + + NdisAllocateBuffer( + &NdisStatus, + &SessionInitBuffer, + Device->NdisBufferPoolHandle, + SessionInitMemory, + sizeof(NB_SESSION_INIT)); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbiFreeMemory (SessionInitMemory, sizeof(NB_SESSION_INIT), MEMORY_CONNECTION, "Session Initialize"); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_INIT; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)+sizeof(NB_SESSION_INIT)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)+sizeof(NB_SESSION_INIT)) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + if (Device->Extensions) { + Header->Session.ConnectionControlFlag = NB_CONTROL_SEND_ACK | NB_CONTROL_NEW_NB; + } else { + Header->Session.ConnectionControlFlag = NB_CONTROL_SEND_ACK; + } + Header->Session.DataStreamType = NB_CMD_SESSION_DATA; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = 0xffff; + Header->Session.SendSequence = 0; + Header->Session.TotalDataLength = sizeof(NB_SESSION_INIT); + Header->Session.Offset = 0; + Header->Session.DataLength = sizeof(NB_SESSION_INIT); + Header->Session.ReceiveSequence = 0; + if (Device->Extensions) { + Header->Session.ReceiveSequenceMax = 1; // low estimate for the moment + } else { + Header->Session.BytesReceived = 0; + } + + RtlCopyMemory (SessionInitMemory->SourceName, Connection->AddressFile->Address->NetbiosAddress.NetbiosName, 16); + RtlCopyMemory (SessionInitMemory->DestinationName, Connection->RemoteName, 16); + + // + // BUGBUG: What exactly should I put here? + // + + SessionInitMemory->MaximumDataSize = (USHORT)Connection->MaximumPacketSize; + SessionInitMemory->StartTripTime = (USHORT) + ((Device->InitialRetransmissionTime * (Device->KeepAliveCount+1)) / 500); + SessionInitMemory->MaximumPacketTime = SessionInitMemory->StartTripTime + 12; + + // + // BUGBUG: Should we ref the connection? It doesn't + // really matter which we do. + // + + NbiReferenceDevice (Device, DREF_SESSION_INIT); + + NdisChainBufferAtBack (Packet, SessionInitBuffer); + + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + sizeof(NB_CONNECTION) + sizeof(NB_SESSION_INIT), + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendSessionInitialize */ + + +VOID +NbiSendSessionInitAck( + IN PCONNECTION Connection, + IN PUCHAR ExtraData, + IN ULONG ExtraDataLength, + IN CTELockHandle * LockHandle OPTIONAL + ) + +/*++ + +Routine Description: + + This routine allocates and sends a session initialize ack + frame for the specified connection. If extra data was + specified in the session initialize frame it is echoed + back to the remote. + +Arguments: + + Connection - The connection on which the frame is sent. + + ExtraData - Any extra data (after the SESSION_INIT buffer) + in the frame. + + ExtraDataLength - THe length of the extra data. + + LockHandle - If specified, indicates the connection lock + is held and should be released. This is for cases + where the ExtraData is in memory which may be freed + once the connection lock is released. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + NDIS_STATUS NdisStatus; + ULONG SessionInitBufferLength; + PNB_SESSION_INIT SessionInitMemory; + PNDIS_BUFFER SessionInitBuffer; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + if (ARGUMENT_PRESENT(LockHandle)) { + NB_FREE_LOCK (&Connection->Lock, *LockHandle); + } + return; + } + + + // + // Allocate a buffer for the extra portion of the + // session initialize. + // + + SessionInitBufferLength = sizeof(NB_SESSION_INIT) + ExtraDataLength; + SessionInitMemory = (PNB_SESSION_INIT)NbiAllocateMemory(SessionInitBufferLength, MEMORY_CONNECTION, "Session Initialize"); + if (!SessionInitMemory) { + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + if (ARGUMENT_PRESENT(LockHandle)) { + NB_FREE_LOCK (&Connection->Lock, *LockHandle); + } + return; + } + + // + // Save the extra data, now we can free the lock. + // + + if (ExtraDataLength != 0) { + RtlCopyMemory (SessionInitMemory+1, ExtraData, ExtraDataLength); + } + if (ARGUMENT_PRESENT(LockHandle)) { + NB_FREE_LOCK (&Connection->Lock, *LockHandle); + } + + // + // Allocate an NDIS buffer to map the extra buffer. + // + + NdisAllocateBuffer( + &NdisStatus, + &SessionInitBuffer, + Device->NdisBufferPoolHandle, + SessionInitMemory, + SessionInitBufferLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbiFreeMemory (SessionInitMemory, SessionInitBufferLength, MEMORY_CONNECTION, "Session Initialize"); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_INIT; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)+SessionInitBufferLength) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)+SessionInitBufferLength) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + if (Connection->NewNetbios) { + Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM | NB_CONTROL_NEW_NB; + } else { + Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM; + } + CTEAssert (Connection->CurrentSend.SendSequence == 0); + CTEAssert (Connection->ReceiveSequence == 1); + Header->Session.DataStreamType = NB_CMD_SESSION_DATA; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = 0; + Header->Session.TotalDataLength = (USHORT)SessionInitBufferLength; + Header->Session.Offset = 0; + Header->Session.DataLength = (USHORT)SessionInitBufferLength; + Header->Session.ReceiveSequence = 1; + if (Connection->NewNetbios) { + Header->Session.ReceiveSequenceMax = Connection->LocalRcvSequenceMax; + } else { + Header->Session.BytesReceived = 0; + } + + RtlCopyMemory (SessionInitMemory->SourceName, Connection->AddressFile->Address->NetbiosAddress.NetbiosName, 16); + RtlCopyMemory (SessionInitMemory->DestinationName, Connection->RemoteName, 16); + + // + // BUGBUG: What exactly should I put here? + // + + SessionInitMemory->MaximumDataSize = (USHORT)Connection->MaximumPacketSize; + SessionInitMemory->StartTripTime = (USHORT) + ((Device->InitialRetransmissionTime * (Device->KeepAliveCount+1)) / 500); + SessionInitMemory->MaximumPacketTime = SessionInitMemory->StartTripTime + 12; + + // + // BUGBUG: Should we ref the connection? It doesn't + // really matter which we do. + // + + NbiReferenceDevice (Device, DREF_SESSION_INIT); + + NdisChainBufferAtBack (Packet, SessionInitBuffer); + + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + sizeof(NB_CONNECTION) + SessionInitBufferLength, + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendSessionInitAck */ + + +VOID +NbiSendDataAck( + IN PCONNECTION Connection, + IN NB_ACK_TYPE AckType + IN NB_LOCK_HANDLE_PARAM (LockHandle) + ) + +/*++ + +Routine Description: + + This routine allocates and sends a data ack frame. + + THIS ROUTINE IS CALLED WITH THE LOCK HANDLE HELD AND + RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection on which the frame is sent. + + AckType - Indicates if this is a query to the remote, + a response to a received probe, or a request to resend. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, try for the connection + // packet. If that's not available, that's OK since data + // acks are connectionless anyway. + // + + if (s == NULL) { + + if (!Connection->SendPacketInUse) { + + Connection->SendPacketInUse = TRUE; + Packet = PACKET(&Connection->SendPacket); + Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + } + + } else { + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_NO_DATA; + Reserved->u.SR_CO.Connection = Connection; + Reserved->u.SR_CO.PacketLength = sizeof(NB_CONNECTION); + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = sizeof(NB_CONNECTION) / 256; + Header->IpxHeader.PacketLength[1] = sizeof(NB_CONNECTION) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + switch (AckType) { + case NbiAckQuery: Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM | NB_CONTROL_SEND_ACK; break; + case NbiAckResponse: Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM; break; + case NbiAckResend: Header->Session.ConnectionControlFlag = NB_CONTROL_SYSTEM | NB_CONTROL_RESEND; break; + } + Header->Session.DataStreamType = NB_CMD_SESSION_DATA; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = Connection->CurrentSend.SendSequence; + Header->Session.TotalDataLength = (USHORT)Connection->CurrentSend.MessageOffset; + Header->Session.Offset = 0; + Header->Session.DataLength = 0; + +#if 0 + // + // These are set by NbiAssignSequenceAndSend. + // + + Header->Session.ReceiveSequence = Connection->ReceiveSequence; + Header->Session.BytesReceived = (USHORT)Connection->CurrentReceive.MessageOffset; +#endif + + NbiReferenceConnectionSync(Connection, CREF_FRAME); + + // + // Set this so we will accept a probe from a remote without + // the send ack bit on. However if we receive such a request + // we turn this flag off until we get something else from the + // remote. + // + + Connection->IgnoreNextDosProbe = FALSE; + + Connection->ReceivesWithoutAck = 0; + + // + // This frees the lock. IPX will adjust the length of + // the first buffer correctly. + // + + NbiAssignSequenceAndSend( + Connection, + Packet + NB_LOCK_HANDLE_ARG(LockHandle)); + +} /* NbiSendDataAck */ + + +VOID +NbiSendSessionEnd( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine allocates and sends a session end + frame for the specified connection. + +Arguments: + + Connection - The connection on which the frame is sent. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + NDIS_STATUS NdisStatus; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_NO_DATA; + Reserved->u.SR_CO.Connection = Connection; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = sizeof(NB_CONNECTION) / 256; + Header->IpxHeader.PacketLength[1] = sizeof(NB_CONNECTION) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. We don't advance the + // send pointer, since it is the last frame of the session + // and we want it to stay the same in the case of resends. + // + + Header->Session.ConnectionControlFlag = NB_CONTROL_SEND_ACK; + Header->Session.DataStreamType = NB_CMD_SESSION_END; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = Connection->CurrentSend.SendSequence; + Header->Session.TotalDataLength = 0; + Header->Session.Offset = 0; + Header->Session.DataLength = 0; + Header->Session.ReceiveSequence = Connection->ReceiveSequence; + if (Connection->NewNetbios) { + Header->Session.ReceiveSequenceMax = Connection->LocalRcvSequenceMax; + } else { + Header->Session.BytesReceived = 0; + } + + NbiReferenceConnection (Connection, CREF_FRAME); + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + sizeof(NB_CONNECTION), + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendSessionEnd */ + + +VOID +NbiSendSessionEndAck( + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PIPX_LOCAL_TARGET LocalTarget, + IN NB_SESSION UNALIGNED * SessionEnd + ) + +/*++ + +Routine Description: + + This routine allocates and sends a session end + frame. Generally it is sent on a connection but we + are not tied to that, to allow us to respond to + session ends from unknown remotes. + +Arguments: + + RemoteAddress - The remote IPX address. + + LocalTarget - The local target of the remote. + + SessionEnd - The received session end frame. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTION UNALIGNED * Header; + NDIS_STATUS NdisStatus; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + // + // If we can't allocate a frame, that is OK, since + // it is connectionless anyway. + // + + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_NO_DATA; + Reserved->u.SR_CO.Connection = NULL; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + RtlCopyMemory(&Header->IpxHeader.DestinationNetwork, (PVOID)RemoteAddress, 12); + + Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + Header->Session.ConnectionControlFlag = 0x00; + Header->Session.DataStreamType = NB_CMD_SESSION_END_ACK; + Header->Session.SourceConnectionId = SessionEnd->DestConnectionId; + Header->Session.DestConnectionId = SessionEnd->SourceConnectionId; + Header->Session.SendSequence = SessionEnd->ReceiveSequence; + Header->Session.TotalDataLength = 0; + Header->Session.Offset = 0; + Header->Session.DataLength = 0; + if (SessionEnd->BytesReceived != 0) { // BUGBUG: Will this detect new netbios? + Header->Session.ReceiveSequence = SessionEnd->SendSequence + 1; + Header->Session.ReceiveSequenceMax = SessionEnd->SendSequence + 3; + } else { + Header->Session.ReceiveSequence = SessionEnd->SendSequence; + Header->Session.BytesReceived = 0; + } + + NbiReferenceDevice (Device, DREF_FRAME); + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + sizeof(NB_CONNECTION), + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiSendSessionEndAck */ + diff --git a/private/ntos/tdi/isnp/nb/isnnb.h b/private/ntos/tdi/isnp/nb/isnnb.h new file mode 100644 index 000000000..2d142e346 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/isnnb.h @@ -0,0 +1,787 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + isnnb.h + +Abstract: + + This module contains definitions specific to the + Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 2-September-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +#define NB_MAXIMUM_MAC 40 + +#define NB_SOCKET 0x5504 + +#if defined(_PNP_POWER) +#define NB_NETBIOS_NAME_SIZE 16 + +#define LOCK_ACQUIRED TRUE +#define LOCK_NOT_ACQUIRED FALSE +#endif _PNP_POWER + +// +// Defined granularity of find name timeouts in milliseconds -- +// we make this the same as the spec'ed RIP gap to avoid +// flooding routers. +// + +#define FIND_NAME_GRANULARITY 55 + + +// +// Defines the number of milliseconds between expirations of the +// short and long timers. +// + +#define MILLISECONDS 10000 // number of NT time units in one + +#define SHORT_TIMER_DELTA 100 +#define LONG_TIMER_DELTA 2000 + + +// +// Convert a ushort netware order <-> machine order +// + +#define REORDER_USHORT(_Ushort) ((((_Ushort) & 0xff00) >> 8) | (((_Ushort) & 0x00ff) << 8)) + +// +// Convert a ulong netware order <-> machine order +// + +#define REORDER_ULONG(_Ulong) \ + ((((_Ulong) & 0xff000000) >> 24) | \ + (((_Ulong) & 0x00ff0000) >> 8) | \ + (((_Ulong) & 0x0000ff00) << 8) | \ + (((_Ulong) & 0x000000ff) << 24)) + + + +#include <packon.h> + +// +// Definition of the IPX header. +// + +typedef struct _IPX_HEADER { + USHORT CheckSum; + UCHAR PacketLength[2]; + UCHAR TransportControl; + UCHAR PacketType; + UCHAR DestinationNetwork[4]; + UCHAR DestinationNode[6]; + USHORT DestinationSocket; + UCHAR SourceNetwork[4]; + UCHAR SourceNode[6]; + USHORT SourceSocket; +} IPX_HEADER, *PIPX_HEADER; + + +// +// Definition of the Netbios header for name frames. +// + +typedef struct _NB_NAME_FRAME { + union { + struct { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + }; + UCHAR RoutingInfo[32]; + }; + UCHAR NameTypeFlag; + UCHAR DataStreamType2; + UCHAR Name[16]; +} NB_NAME_FRAME, *PNB_NAME_FRAME; + +// +// Definition of the Netbios header for directed datagrams. +// + +typedef struct _NB_DATAGRAM { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + UCHAR SourceName[16]; + UCHAR DestinationName[16]; +} NB_DATAGRAM, *PNB_DATAGRAM; + +// +// Definition of the Netbios header for a status query. +// + +typedef struct _NB_STATUS_QUERY { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + UCHAR Padding[14]; +} NB_STATUS_QUERY, *PNB_STATUS_QUERY; + +// +// Definition of the Netbios header for a status response +// (this does not include the status buffer itself). +// + +typedef struct _NB_STATUS_RESPONSE { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; +} NB_STATUS_RESPONSE, *PNB_STATUS_RESPONSE; + + +// +// Definition of the general Netbios connectionless header. +// + +typedef struct _NB_CONNECTIONLESS { + IPX_HEADER IpxHeader; + union { + NB_NAME_FRAME NameFrame; + NB_DATAGRAM Datagram; + NB_STATUS_QUERY StatusQuery; + NB_STATUS_RESPONSE StatusResponse; + }; +} NB_CONNECTIONLESS, *PNB_CONNECTIONLESS; + + +// +// Definition of the Netbios session frame. +// + +typedef struct _NB_SESSION { + UCHAR ConnectionControlFlag; + UCHAR DataStreamType; + USHORT SourceConnectionId; + USHORT DestConnectionId; + USHORT SendSequence; + USHORT TotalDataLength; + USHORT Offset; + USHORT DataLength; + USHORT ReceiveSequence; + union { + USHORT BytesReceived; + USHORT ReceiveSequenceMax; + }; +} NB_SESSION, *PNB_SESSION; + + +// +// Definition of the extra fields in a Netbios +// session frame for session init and session init +// ack. +// + +typedef struct _NB_SESSION_INIT { + UCHAR SourceName[16]; + UCHAR DestinationName[16]; + USHORT MaximumDataSize; + USHORT MaximumPacketTime; + USHORT StartTripTime; +} NB_SESSION_INIT, *PNB_SESSION_INIT; + + +// +// Definition of the general Netbios connection-oriented header. +// + +typedef struct _NB_CONNECTION { + IPX_HEADER IpxHeader; + NB_SESSION Session; +} NB_CONNECTION, *PNB_CONNECTION; + + +// +// Definition of a Netbios packet. +// + +typedef union _NB_FRAME { + NB_CONNECTIONLESS Connectionless; + NB_CONNECTION Connection; +} NB_FRAME, *PNB_FRAME; + +#include <packoff.h> + + +// +// Definitions for the DataStreamType field, with the +// format used shown in the comment afterward. +// + +#define NB_CMD_FIND_NAME 0x01 // NAME_FRAME +#define NB_CMD_NAME_RECOGNIZED 0x02 // NAME_FRAME +#define NB_CMD_ADD_NAME 0x03 // NAME_FRAME +#define NB_CMD_NAME_IN_USE 0x04 // NAME_FRAME +#define NB_CMD_DELETE_NAME 0x05 // NAME_FRAME +#define NB_CMD_SESSION_DATA 0x06 // SESSION +#define NB_CMD_SESSION_END 0x07 // SESSION +#define NB_CMD_SESSION_END_ACK 0x08 // SESSION +#define NB_CMD_STATUS_QUERY 0x09 // STATUS_QUERY +#define NB_CMD_STATUS_RESPONSE 0x0a // STATUS_RESPONSE +#define NB_CMD_DATAGRAM 0x0b // DATAGRAM +#define NB_CMD_BROADCAST_DATAGRAM 0x0c // BROADCAST_DATAGRAM + +#ifdef RSRC_TIMEOUT_DBG +#define NB_CMD_DEATH_PACKET 0x99 // +#endif // RSRC_TIMEOUT_DBG + +// +// Bit values in the NameTypeFlag of NB_NAME_FRAME frames. +// + +#define NB_NAME_UNIQUE 0x00 +#define NB_NAME_GROUP 0x80 +#define NB_NAME_USED 0x40 +#define NB_NAME_REGISTERED 0x04 +#define NB_NAME_DUPLICATED 0x02 +#define NB_NAME_DEREGISTERED 0x01 + +// +// Bit values in the ConnectionControlFlag. +// + +#define NB_CONTROL_SYSTEM 0x80 +#define NB_CONTROL_SEND_ACK 0x40 +#define NB_CONTROL_ATTENTION 0x20 +#define NB_CONTROL_EOM 0x10 +#define NB_CONTROL_RESEND 0x08 +#define NB_CONTROL_NEW_NB 0x01 + + + +#define NB_DEVICE_SIGNATURE 0x1401 +#if defined(_PNP_POWER) +#define NB_ADAPTER_ADDRESS_SIGNATURE 0x1403 +#endif _PNP_POWER +#define NB_ADDRESS_SIGNATURE 0x1404 +#define NB_ADDRESSFILE_SIGNATURE 0x1405 +#define NB_CONNECTION_SIGNATURE 0x1406 + + +// +// Useful in various places. +// +#if defined(_PNP_POWER) +extern IPX_LOCAL_TARGET BroadcastTarget; +#endif _PNP_POWER +extern UCHAR BroadcastAddress[6]; +extern UCHAR NetbiosBroadcastName[16]; + + +// +// Contains the default handler for each of the TDI event types +// that are supported. +// + +extern PVOID TdiDefaultHandlers[6]; + + +// +// Define a structure that can track lock acquire/release. +// + +typedef struct _NB_LOCK { + CTELock Lock; +#if DBG + ULONG LockAcquired; + UCHAR LastAcquireFile[8]; + ULONG LastAcquireLine; + UCHAR LastReleaseFile[8]; + ULONG LastReleaseLine; +#endif +} NB_LOCK, *PNB_LOCK; + + + +#if DBG + +extern ULONG NbiDebug; +extern ULONG NbiDebug2; +extern ULONG NbiMemoryDebug; + +#define NB_MEMORY_LOG_SIZE 128 +extern UCHAR NbiDebugMemory[NB_MEMORY_LOG_SIZE][64]; +extern PUCHAR NbiDebugMemoryLoc; +extern PUCHAR NbiDebugMemoryEnd; + +VOID +NbiDebugMemoryLog( + IN PUCHAR FormatString, + ... +); + +#define NB_DEBUG(_Flag, _Print) { \ + if (NbiDebug & (NB_DEBUG_ ## _Flag)) { \ + DbgPrint ("NBI: "); \ + DbgPrint _Print; \ + } \ + if (NbiMemoryDebug & (NB_DEBUG_ ## _Flag)) { \ + NbiDebugMemoryLog _Print; \ + } \ +} + +#define NB_DEBUG2(_Flag, _Print) { \ + if (NbiDebug2 & (NB_DEBUG_ ## _Flag)) { \ + DbgPrint ("NBI: "); \ + DbgPrint _Print; \ + } \ + if (NbiMemoryDebug & (NB_DEBUG_ ## _Flag)) { \ + NbiDebugMemoryLog _Print; \ + } \ +} + +#else + +#define NB_DEBUG(_Flag, _Print) +#define NB_DEBUG2(_Flag, _Print) + +#endif + + +// +// These definitions are for abstracting IRPs from the +// transport for portability. +// + +#if ISN_NT + +typedef IRP REQUEST, *PREQUEST; +typedef struct _REQUEST_LIST_HEAD { + PREQUEST Head; // list is empty if this is NULL + PREQUEST Tail; // undefined if the list is empty. +} REQUEST_LIST_HEAD, *PREQUEST_LIST_HEAD; + + +// +// PREQUEST +// NbiAllocateRequest( +// IN PDEVICE Device, +// IN PIRP Irp +// ); +// +// Allocates a request for the system-specific request structure. +// + +#define NbiAllocateRequest(_Device,_Irp) \ + (_Irp) + + +// +// BOOLEAN +// IF_NOT_ALLOCATED( +// IN PREQUEST Request +// ); +// +// Checks if a request was not successfully allocated. +// + +#define IF_NOT_ALLOCATED(_Request) \ + if (0) + + +// +// VOID +// NbiFreeRequest( +// IN PDEVICE Device, +// IN PREQUEST Request +// ); +// +// Frees a previously allocated request. +// + +#define NbiFreeRequest(_Device,_Request) \ + ; + + +// +// VOID +// MARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will pend. +// + +#define MARK_REQUEST_PENDING(_Request) \ + IoMarkIrpPending(_Request) + + +// +// VOID +// UNMARK_REQUEST_PENDING( +// IN PREQUEST Request +// ); +// +// Marks that a request will not pend. +// + +#define UNMARK_REQUEST_PENDING(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->Control) &= ~SL_PENDING_RETURNED) + + +// +// UCHAR +// REQUEST_MAJOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the major function code of a request. +// + +#define REQUEST_MAJOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MajorFunction) + + +// +// UCHAR +// REQUEST_MINOR_FUNCTION +// IN PREQUEST Request +// ); +// +// Returns the minor function code of a request. +// + +#define REQUEST_MINOR_FUNCTION(_Request) \ + ((IoGetCurrentIrpStackLocation(_Request))->MinorFunction) + + +// +// PNDIS_BUFFER +// REQUEST_NDIS_BUFFER +// IN PREQUEST Request +// ); +// +// Returns the NDIS buffer chain associated with a request. +// + +#define REQUEST_NDIS_BUFFER(_Request) \ + ((PNDIS_BUFFER)((_Request)->MdlAddress)) + + +// +// PVOID +// REQUEST_OPEN_CONTEXT( +// IN PREQUEST Request +// ); +// +// Gets the context associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_CONTEXT(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext) + + +// +// PVOID +// REQUEST_OPEN_TYPE( +// IN PREQUEST Request +// ); +// +// Gets the type associated with an opened address/connection/control channel. +// + +#define REQUEST_OPEN_TYPE(_Request) \ + (((IoGetCurrentIrpStackLocation(_Request))->FileObject)->FsContext2) + + +// +// PFILE_FULL_EA_INFORMATION +// OPEN_REQUEST_EA_INFORMATION( +// IN PREQUEST Request +// ); +// +// Returns the EA information associated with an open/close request. +// + +#define OPEN_REQUEST_EA_INFORMATION(_Request) \ + ((PFILE_FULL_EA_INFORMATION)((_Request)->AssociatedIrp.SystemBuffer)) + + +// +// PTDI_REQUEST_KERNEL +// REQUEST_PARAMETERS( +// IN PREQUEST Request +// ); +// +// Obtains a pointer to the parameters of a request. +// + +#define REQUEST_PARAMETERS(_Request) \ + (&((IoGetCurrentIrpStackLocation(_Request))->Parameters)) + + +// +// PLIST_ENTRY +// REQUEST_LINKAGE( +// IN PREQUEST Request +// ); +// +// Returns a pointer to a linkage field in the request. +// + +#define REQUEST_LINKAGE(_Request) \ + (&((_Request)->Tail.Overlay.ListEntry)) + + +// +// PREQUEST +// REQUEST_SINGLE_LINKAGE( +// IN PREQUEST Request +// ); +// +// Used to access a single list linkage field in the request. +// + +#define REQUEST_SINGLE_LINKAGE(_Request) \ + (*((PREQUEST *)&((_Request)->Tail.Overlay.ListEntry.Flink))) + + +// +// ULONG +// REQUEST_REFCOUNT( +// IN PREQUEST Request +// ); +// +// Used to access a field in the request which can be used for +// the reference count, as long as it is on a REQUEST_LIST. +// + +#define REQUEST_REFCOUNT(_Request) \ + (*((PULONG)&((_Request)->Tail.Overlay.ListEntry.Blink))) + + +// +// VOID +// REQUEST_LIST_INSERT_TAIL( +// IN PREQUEST_LIST_HEAD Head, +// IN PREQUEST Entry +// ); +// +// Inserts a request into a single list linkage queue. +// + +#define REQUEST_LIST_INSERT_TAIL(_Head,_Entry) { \ + if ((_Head)->Head == NULL) { \ + (_Head)->Head = (_Entry); \ + (_Head)->Tail = (_Entry); \ + } else { \ + REQUEST_SINGLE_LINKAGE((_Head)->Tail) = (_Entry); \ + (_Head)->Tail = (_Entry); \ + } \ +} + + +// +// PREQUEST +// LIST_ENTRY_TO_REQUEST( +// IN PLIST_ENTRY ListEntry +// ); +// +// Returns a request given a linkage field in it. +// + +#define LIST_ENTRY_TO_REQUEST(_ListEntry) \ + ((PREQUEST)(CONTAINING_RECORD(_ListEntry, REQUEST, Tail.Overlay.ListEntry))) + + +// +// NTSTATUS +// REQUEST_STATUS( +// IN PREQUEST Request +// ); +// +// Used to access the status field of a request. +// + +#define REQUEST_STATUS(_Request) \ + (_Request)->IoStatus.Status + + +// +// ULONG +// REQUEST_INFORMATION( +// IN PREQUEST Request) +// ); +// +// Used to access the information field of a request. +// + +#define REQUEST_INFORMATION(_Request) \ + (_Request)->IoStatus.Information + + +// +// VOID +// NbiCompleteRequest( +// IN PREQUEST Request +// ); +// +// Completes a request whose status and information fields have +// been filled in. +// + +#define NbiCompleteRequest(_Request) \ + IoCompleteRequest (_Request, IO_NETWORK_INCREMENT) + +#else + +// +// These routines must be defined for portability to a VxD. +// + +#endif + +// +// some utility macros. + +// Minimum of two +// +#define NB_MIN( _a , _b ) ( ( (_a) < (_b) ) ? (_a) : (_b) ) + +// +// Swap the _s1 and _s2 of Type _T +// + +#define NB_SWAP(_s1, _s2, _T) { \ + _T _temp; \ + _temp = (_s1); \ + (_s1) = (_s2); \ + (_s2) = _temp; \ +} + +#define NB_SWAP_IRQL( _s1, _s2 ) NB_SWAP( _s1, _s2, CTELockHandle ) + +// +// Define our own spinlock routines. +// + +#if DBG + +#define NB_GET_LOCK(_Lock, _LockHandle) { \ + CTEGetLock(&(_Lock)->Lock, _LockHandle); \ + (_Lock)->LockAcquired = TRUE; \ + strncpy((_Lock)->LastAcquireFile, strrchr(__FILE__,'\\')+1, 7); \ + (_Lock)->LastAcquireLine = __LINE__; \ +} + +#define NB_FREE_LOCK(_Lock, _LockHandle) { \ + (_Lock)->LockAcquired = FALSE; \ + strncpy((_Lock)->LastReleaseFile, strrchr(__FILE__,'\\')+1, 7); \ + (_Lock)->LastReleaseLine = __LINE__; \ + CTEFreeLock(&(_Lock)->Lock, _LockHandle); \ +} + +#define NB_GET_LOCK_DPC(_Lock) { \ + ExAcquireSpinLockAtDpcLevel(&(_Lock)->Lock); \ + (_Lock)->LockAcquired = TRUE; \ + strncpy((_Lock)->LastAcquireFile, strrchr(__FILE__,'\\')+1, 7); \ + (_Lock)->LastAcquireLine = __LINE__; \ +} + +#define NB_FREE_LOCK_DPC(_Lock) { \ + (_Lock)->LockAcquired = FALSE; \ + strncpy((_Lock)->LastReleaseFile, strrchr(__FILE__,'\\')+1, 7); \ + (_Lock)->LastReleaseLine = __LINE__; \ + ExReleaseSpinLockFromDpcLevel(&(_Lock)->Lock); \ +} + +#else + +#define NB_GET_LOCK(_Lock, _LockHandle) CTEGetLock(&(_Lock)->Lock, _LockHandle) +#define NB_FREE_LOCK(_Lock, _LockHandle) CTEFreeLock(&(_Lock)->Lock, _LockHandle) +#define NB_GET_LOCK_DPC(_Lock) ExAcquireSpinLockAtDpcLevel(&(_Lock)->Lock) +#define NB_FREE_LOCK_DPC(_Lock) ExReleaseSpinLockFromDpcLevel(&(_Lock)->Lock) + +#endif + + +#define NB_GET_CANCEL_LOCK( _LockHandle ) IoAcquireCancelSpinLock( _LockHandle ) + +#define NB_FREE_CANCEL_LOCK( _LockHandle ) IoReleaseCancelSpinLock( _LockHandle ) + + +// +// Routines to optimize for a uni-processor environment. +// + + +#define NB_INCREMENT(_Long, _Lock) InterlockedIncrement(_Long) +#define NB_DECREMENT(_Long, _Lock) InterlockedDecrement(_Long) + +#define NB_ADD_ULONG(_Pulong, _Ulong, _Lock) ExInterlockedAddUlong(_Pulong, _Ulong, &(_Lock)->Lock) + +#define NB_DEFINE_SYNC_CONTEXT(_SyncContext) +#define NB_BEGIN_SYNC(_SyncContext) +#define NB_END_SYNC(_SyncContext) + +#define NB_DEFINE_LOCK_HANDLE(_LockHandle) CTELockHandle _LockHandle; + +// +// BUGBUG: Make these be NB_XXX_LOCK_DPC calls -- then the definitions +// of the NB_SYNC_XXX_LOCK calls can be changed to not need _LockHandle +// and many of the functions won't need that as a parameter. +// + +#define NB_SYNC_GET_LOCK(_Lock, _LockHandle) NB_GET_LOCK(_Lock, _LockHandle) +#define NB_SYNC_FREE_LOCK(_Lock, _LockHandle) NB_FREE_LOCK(_Lock, _LockHandle) + +#define NB_REMOVE_HEAD_LIST(_Queue, _Lock) ExInterlockedRemoveHeadList(_Queue, &(_Lock)->Lock) +#define NB_LIST_WAS_EMPTY(_Queue, _OldHead) ((_OldHead) == NULL) +#define NB_INSERT_HEAD_LIST(_Queue, _Entry, _Lock) ExInterlockedInsertHeadList(_Queue, _Entry, &(_Lock)->Lock) +#define NB_INSERT_TAIL_LIST(_Queue, _Entry, _Lock) ExInterlockedInsertTailList(_Queue, _Entry, &(_Lock)->Lock) + +#define NB_POP_ENTRY_LIST(_Queue, _Lock) ExInterlockedPopEntryList(_Queue, &(_Lock)->Lock) +#define NB_PUSH_ENTRY_LIST(_Queue, _Entry, _Lock) ExInterlockedPushEntryList(_Queue, _Entry, &(_Lock)->Lock) + +#define NB_LOCK_HANDLE_PARAM(_LockHandle) , IN CTELockHandle _LockHandle +#define NB_LOCK_HANDLE_ARG(_LockHandle) , (_LockHandle) + +#define NB_SYNC_SWAP_IRQL( _s1, _s2 ) NB_SWAP( _s1, _s2, CTELockHandle ) + + +// +// This macro adds a ULONG to a LARGE_INTEGER (should be +// called with a spinlock held). +// + +#define ADD_TO_LARGE_INTEGER(_LargeInteger,_Ulong) \ + ExInterlockedAddLargeStatistic((_LargeInteger),(ULONG)(_Ulong)) + +#define NB_DEBUG_DEVICE 0x00000001 +#define NB_DEBUG_ADDRESS 0x00000004 +#define NB_DEBUG_SEND 0x00000008 +#define NB_DEBUG_RECEIVE 0x00000020 +#define NB_DEBUG_CONFIG 0x00000040 +#define NB_DEBUG_PACKET 0x00000080 +#define NB_DEBUG_BIND 0x00000200 +#define NB_DEBUG_ADDRESS_FRAME 0x00000400 +#define NB_DEBUG_CONNECTION 0x00000800 +#define NB_DEBUG_QUERY 0x00001000 +#define NB_DEBUG_DRIVER 0x00002000 +#define NB_DEBUG_CACHE 0x00004000 +#define NB_DEBUG_DATAGRAM 0x00008000 +#define NB_DEBUG_TIMER 0x00010000 +#define NB_DEBUG_SEND_WINDOW 0x00020000 + + + +// +// NB_GET_NBHDR_BUFF - gets the nb header in the packet. It is always the +// second buffer. +// +#define NB_GET_NBHDR_BUFF(Packet) (NDIS_BUFFER_LINKAGE((Packet)->Private.Head)) + + diff --git a/private/ntos/tdi/isnp/nb/mp/makefile b/private/ntos/tdi/isnp/nb/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/nb/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/nb/mp/sources b/private/ntos/tdi/isnp/nb/mp/sources new file mode 100644 index 000000000..dc48d81bb --- /dev/null +++ b/private/ntos/tdi/isnp/nb/mp/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=\nt\public\sdk\lib + +!include ..\sources.inc diff --git a/private/ntos/tdi/isnp/nb/nbcount/makefile b/private/ntos/tdi/isnp/nb/nbcount/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbcount/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/nb/nbcount/nbcount.c b/private/ntos/tdi/isnp/nb/nbcount/nbcount.c new file mode 100644 index 000000000..74a656f16 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbcount/nbcount.c @@ -0,0 +1,177 @@ +/**************************************************************************** +* (c) Copyright 1990, 1993 Micro Computer Systems, Inc. All rights reserved. +***************************************************************************** +* +* Title: IPX/SPX Compatible Source Routing Daemon for Windows NT +* +* Module: ipx/route/ipxroute.c +* +* Version: 1.00.00 +* +* Date: 04-08-93 +* +* Author: Brian Walker +* +***************************************************************************** +* +* Change Log: +* +* Date DevSFC Comment +* -------- ------ ------------------------------------------------------- +***************************************************************************** +* +* Functional Description: +* +* +****************************************************************************/ +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <process.h> +#include <ntstapi.h> +#include <sys/stropts.h> +#include <windows.h> +#include "errno.h" +#include "tdi.h" +#include "isnkrnl.h" + + +typedef struct _NB_ACTION_GET_COUNTS { + USHORT MaximumNicId; // returns maximum NIC ID + USHORT NicIdCounts[32]; // session counts for first 32 NIC IDs +} NB_ACTION_GET_COUNTS, *PNB_ACTION_GET_COUNTS; + +HANDLE isnnbfd; +wchar_t isnnbname[] = L"\\Device\\NwlnkNb"; +char pgmname[] = "NBCOUNT"; + +/** **/ + +#define INVALID_HANDLE (HANDLE)(-1) + +int do_isnnbioctl(HANDLE fd, int cmd, char *datap, int dlen); + +/*page************************************************************* + m a i n + + This is the main routine that gets executed when a NET START + happens. + + Arguments - None + + Returns - Nothing +********************************************************************/ +void _CRTAPI1 main(int argc, char **argv) +{ + UNICODE_STRING FileString; + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + NB_ACTION_GET_COUNTS GetCounts; + int rc; + int i; + + /** Open the nwlnknb driver **/ + + RtlInitUnicodeString (&FileString, isnnbname); + + InitializeObjectAttributes( + &ObjectAttributes, + &FileString, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtOpenFile( + &isnnbfd, + SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_ALERT); + + if (!NT_SUCCESS(Status)) { + isnnbfd = INVALID_HANDLE; + printf("Could not open transport\n"); + } + + if (isnnbfd == INVALID_HANDLE) { + exit(1); + } + + rc = do_isnnbioctl(isnnbfd, (I_MIPX | 351), (char *)&GetCounts, sizeof(NB_ACTION_GET_COUNTS)); + if (rc == 0) { + + printf("NB NIC count: %d\n", GetCounts.MaximumNicId); + for (i = 1; i <= GetCounts.MaximumNicId; i++) { + printf("NIC %d: %d sessions\n", i, GetCounts.NicIdCounts[i]); + } + } +} + + +/*page*************************************************************** + d o _ i s n i p x i o c t l + + Do the equivalent of a stream ioctl to isnnb + + Arguments - fd = Handle to put on + cmd = Command to send + datap = Ptr to ctrl buffer + dlen = Ptr to len of data buffer + + Returns - 0 = OK + else = Error +********************************************************************/ +int do_isnnbioctl(HANDLE fd, int cmd, char *datap, int dlen) +{ + NTSTATUS Status; + UCHAR buffer[300]; + PNWLINK_ACTION action; + IO_STATUS_BLOCK IoStatusBlock; + int rc; + + /** Fill out the structure **/ + + action = (PNWLINK_ACTION)buffer; + + action->Header.TransportId = ISN_ACTION_TRANSPORT_ID; + action->OptionType = NWLINK_OPTION_CONTROL; + action->BufferLength = sizeof(ULONG) + dlen; + action->Option = cmd; + RtlMoveMemory(action->Data, datap, dlen); + + /** Issue the ioctl **/ + + Status = NtDeviceIoControlFile( + fd, + NULL, + NULL, + NULL, + &IoStatusBlock, + IOCTL_TDI_ACTION, + NULL, + 0, + action, + FIELD_OFFSET(NWLINK_ACTION,Data) + dlen); + + if (Status != STATUS_SUCCESS) { + if (Status == STATUS_INVALID_PARAMETER) { + rc = ERANGE; + } else { + rc = EINVAL; + } + } else { + if (dlen > 0) { + RtlMoveMemory (datap, action->Data, dlen); + } + rc = 0; + } + + return rc; + +} + diff --git a/private/ntos/tdi/isnp/nb/nbcount/nbcount.rc b/private/ntos/tdi/isnp/nb/nbcount/nbcount.rc new file mode 100644 index 000000000..ada219b24 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbcount/nbcount.rc @@ -0,0 +1,11 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "NWLink Netbios Session Count Application" +#define VER_INTERNALNAME_STR "nbcount.exe" +#define VER_ORIGINALFILENAME_STR "nbcount.exe" + +#include "common.ver" diff --git a/private/ntos/tdi/isnp/nb/nbcount/sources b/private/ntos/tdi/isnp/nb/nbcount/sources new file mode 100644 index 000000000..f9dfe3561 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbcount/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1993 Micro Computer Systems, Inc. + +!ENDIF + +MAJORCOMP=nwlink +MINORCOMP=nbcount + +TARGETNAME=nbcount +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=UMAPPL_NOLIB + +USE_CRTDLL=1 + +C_DEFINES=$(C_DEFINES) + +!IF 0 +INCLUDES=..\h;..\..\..\..\..\inc;..\..\..\..\inc;..\..\..\inc +!ELSE +INCLUDES=..\h;$(BASEDIR)\private\inc;$(BASEDIR)\private\ntos\inc;$(BASEDIR)\private\ntos\streams\inc +!ENDIF + +SOURCES= nbcount.c nbcount.rc + +UMTYPE=console +UMAPPL=$(TARGETNAME) +UMLIBS=$(BASEDIR)\public\sdk\lib\*\ntdll.lib \ + diff --git a/private/ntos/tdi/isnp/nb/nbiprocs.h b/private/ntos/tdi/isnp/nb/nbiprocs.h new file mode 100644 index 000000000..dff399019 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbiprocs.h @@ -0,0 +1,1530 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + nbiprocs.h + +Abstract: + + This module contains definitions specific to the + Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +// +// MACROS. +// +// +// Debugging aids +// + +// +// VOID +// PANIC( +// IN PSZ Message +// ); +// + +#if DBG +#define PANIC(Msg) \ + CTEPrint ((Msg)) +#else +#define PANIC(Msg) +#endif + + +// +// These are define to allow CTEPrints that disappear when +// DBG is 0. +// + +#if DBG +#define NbiPrint0(fmt) DbgPrint(fmt) +#define NbiPrint1(fmt,v0) DbgPrint(fmt,v0) +#define NbiPrint2(fmt,v0,v1) DbgPrint(fmt,v0,v1) +#define NbiPrint3(fmt,v0,v1,v2) DbgPrint(fmt,v0,v1,v2) +#define NbiPrint4(fmt,v0,v1,v2,v3) DbgPrint(fmt,v0,v1,v2,v3) +#define NbiPrint5(fmt,v0,v1,v2,v3,v4) DbgPrint(fmt,v0,v1,v2,v3,v4) +#define NbiPrint6(fmt,v0,v1,v2,v3,v4,v5) DbgPrint(fmt,v0,v1,v2,v3,v4,v5) +#else +#define NbiPrint0(fmt) +#define NbiPrint1(fmt,v0) +#define NbiPrint2(fmt,v0,v1) +#define NbiPrint3(fmt,v0,v1,v2) +#define NbiPrint4(fmt,v0,v1,v2,v3) +#define NbiPrint5(fmt,v0,v1,v2,v3,v4) +#define NbiPrint6(fmt,v0,v1,v2,v3,v4,v5) +#endif + + +// +// Routines to log packets to a buffer. +// + +#if DBG +#define NB_PACKET_LOG 1 +#endif + +#ifdef NB_PACKET_LOG + +// +// The size of this is 64 bytes for easy display. +// + +typedef struct _NB_PACKET_LOG_ENTRY { + UCHAR SendReceive; + UCHAR TimeStamp[5]; // low 5 digits of tick count. + UCHAR DestMac[6]; + UCHAR SrcMac[6]; + UCHAR Length[2]; + IPX_HEADER NbiHeader; + UCHAR Data[14]; +} NB_PACKET_LOG_ENTRY, *PNB_PACKET_LOG_ENTRY; + +#define NB_PACKET_LOG_LENGTH 128 +extern ULONG NbiPacketLogDebug; +extern USHORT NbiPacketLogSocket; +EXTERNAL_LOCK(NbiPacketLogLock); +extern NB_PACKET_LOG_ENTRY NbiPacketLog[NB_PACKET_LOG_LENGTH]; +extern PNB_PACKET_LOG_ENTRY NbiPacketLogLoc; +extern PNB_PACKET_LOG_ENTRY NbiPacketLogEnd; + +// +// Bit fields in NbiPacketLogDebug +// + +#define NB_PACKET_LOG_RCV_RIP 0x0001 // All RIP packets +#define NB_PACKET_LOG_RCV_SPX 0x0002 // All SPX packets +#define NB_PACKET_LOG_RCV_NB 0x0004 // All Netbios packets +#define NB_PACKET_LOG_RCV_OTHER 0x0008 // All TDI client packets +#define NB_PACKET_LOG_RCV_SOCKET 0x0010 // All packets to NbiPacketLogSocket +#define NB_PACKET_LOG_RCV_ALL 0x0020 // All packets (even non-NB) + +#define NB_PACKET_LOG_SEND_RIP 0x0001 // All RIP packets +#define NB_PACKET_LOG_SEND_SPX 0x0002 // All SPX packets +#define NB_PACKET_LOG_SEND_NB 0x0004 // All Netbios packets +#define NB_PACKET_LOG_SEND_OTHER 0x0008 // All TDI client packets +#define NB_PACKET_LOG_SEND_SOCKET 0x0010 // All packets from NbiPacketLogSocket + +VOID +NbiLogPacket( + IN BOOLEAN Send, + IN PUCHAR DestMac, + IN PUCHAR SrcMac, + IN USHORT Length, + IN PVOID NbiHeader, + IN PVOID Data + ); + +#define PACKET_LOG(_Bit) (NbiPacketLogDebug & (_Bit)) + +#else // NB_PACKET_LOG + +#define NbiLogPacket(_MacHeader,_Length,_NbiHeader,_Data) +#define PACKET_LOG(_Bit) 0 + +#endif // NB_PACKET_LOG + + +#if DBG + +#define NbiReferenceDevice(_Device, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Device)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefDevice (_Device) + +#define NbiDereferenceDevice(_Device, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Device)->RefTypes[_Type], \ + (ULONG)-1, \ + &NbiGlobalInterlock); \ + NbiDerefDevice (_Device) + + +#define NbiReferenceAddress(_Address, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Address)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefAddress (_Address) + +#define NbiReferenceAddressLock(_Address, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Address)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefAddressLock (_Address) + +#define NbiDereferenceAddress(_Address, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Address)->RefTypes[_Type], \ + (ULONG)-1, \ + &NbiGlobalInterlock); \ + NbiDerefAddress (_Address) + + +#define NbiReferenceAddressFile(_AddressFile, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefAddressFile (_AddressFile) + +#define NbiReferenceAddressFileLock(_AddressFile, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefAddressFileLock (_AddressFile) + +#define NbiDereferenceAddressFile(_AddressFile, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_Type], \ + (ULONG)-1, \ + &NbiGlobalInterlock); \ + NbiDerefAddressFile (_AddressFile) + +#define NbiTransferReferenceAddressFile(_AddressFile, _OldType, _NewType) \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_NewType], \ + 1, \ + &NbiGlobalInterlock); \ + (VOID)ExInterlockedAddUlong ( \ + &(_AddressFile)->RefTypes[_OldType], \ + (ULONG)-1, \ + &NbiGlobalInterlock); + + +#define NbiReferenceConnection(_Connection, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefConnection (_Connection) + +#define NbiReferenceConnectionLock(_Connection, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefConnectionLock (_Connection) + +#define NbiReferenceConnectionSync(_Connection, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_Type], \ + 1, \ + &NbiGlobalInterlock); \ + NbiRefConnectionSync (_Connection) + +#define NbiDereferenceConnection(_Connection, _Type) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_Type], \ + (ULONG)-1, \ + &NbiGlobalInterlock); \ + NbiDerefConnection (_Connection) + +#define NbiTransferReferenceConnection(_Connection, _OldType, _NewType) \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_NewType], \ + 1, \ + &NbiGlobalInterlock); \ + (VOID)ExInterlockedAddUlong ( \ + &(_Connection)->RefTypes[_OldType], \ + (ULONG)-1, \ + &NbiGlobalInterlock); + +#else // DBG + +#define NbiReferenceDevice(_Device, _Type) \ + InterlockedIncrement(&(_Device)->ReferenceCount) + +#define NbiDereferenceDevice(_Device, _Type) \ + NbiDerefDevice (_Device) + + + +#define NbiReferenceAddress(_Address, _Type) \ + InterlockedIncrement( &(_Address)->ReferenceCount ) + +#define NbiReferenceAddressLock(_Address, _Type) \ + InterlockedIncrement( &(_Address)->ReferenceCount ) + +#define NbiDereferenceAddress(_Address, _Type) \ + NbiDerefAddress (_Address) + + +#define NbiReferenceAddressFile(_AddressFile, _Type) \ + InterlockedIncrement( &(_AddressFile)->ReferenceCount ) + +#define NbiReferenceAddressFileLock(_AddressFile, _Type) \ + InterlockedIncrement( &(_AddressFile)->ReferenceCount ) + +#define NbiDereferenceAddressFile(_AddressFile, _Type) \ + if ( !InterlockedDecrement(&(_AddressFile)->ReferenceCount )) { \ + NbiDestroyAddressFile (_AddressFile); \ + } + +#define NbiTransferReferenceAddressFile(_AddressFile, _OldType, _NewType) + + +#define NbiReferenceConnection(_Connection, _Type) { \ + (VOID)ExInterlockedAddUlong( \ + &(_Connection)->ReferenceCount, \ + 1, \ + &(_Connection)->DeviceLock->Lock); \ + (_Connection)->CanBeDestroyed = FALSE; \ +} + +#define NbiReferenceConnectionLock(_Connection, _Type) { \ + ++(_Connection)->ReferenceCount; \ + (_Connection)->CanBeDestroyed = FALSE; \ +} + +#define NbiReferenceConnectionSync(_Connection, _Type) { \ + (VOID)NB_ADD_ULONG( \ + &(_Connection)->ReferenceCount, \ + 1, \ + (_Connection)->DeviceLock); \ + (_Connection)->CanBeDestroyed = FALSE; \ +} + +#define NbiDereferenceConnection(_Connection, _Type) { \ + CTELockHandle _LockHandle; \ + NB_GET_LOCK( (_Connection)->DeviceLock, &_LockHandle ); \ + if ( !(--(_Connection)->ReferenceCount) ) { \ + (_Connection)->ThreadsInHandleConnectionZero++; \ + NB_FREE_LOCK( (_Connection)->DeviceLock, _LockHandle ); \ + NbiHandleConnectionZero (_Connection); \ + } else { \ + NB_FREE_LOCK( (_Connection)->DeviceLock, _LockHandle ); \ + } \ +} + + +#define NbiTransferReferenceConnection(_Connection, _OldType, _NewType) + +#endif // DBG + + + +#if DBG + +#define NbiAllocateMemory(_BytesNeeded,_Tag,_Description) \ + NbipAllocateTaggedMemory(_BytesNeeded,_Tag,_Description) + +#define NbiFreeMemory(_Memory,_BytesAllocated,_Tag,_Description) \ + NbipFreeTaggedMemory(_Memory,_BytesAllocated,_Tag,_Description) + +#else // DBG + +#define NbiAllocateMemory(_BytesNeeded,_Tag,_Description) \ + NbipAllocateMemory(_BytesNeeded,_Tag,(BOOLEAN)((_Tag) != MEMORY_CONFIG)) + +#define NbiFreeMemory(_Memory,_BytesAllocated,_Tag,_Description) \ + NbipFreeMemory(_Memory,_BytesAllocated,(BOOLEAN)((_Tag) != MEMORY_CONFIG)) + + +#endif // DBG + + +// +// Definition of the callback routine where an NdisTransferData +// call is not needed. +// + +typedef VOID +(*NB_CALLBACK_NO_TRANSFER) ( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + + + +// +// This routine compares two node addresses. +// + +#define NB_NODE_EQUAL(_A,_B) \ + ((*(UNALIGNED ULONG *)((PUCHAR)(_A)) == *(UNALIGNED ULONG *)((PUCHAR)(_B))) && \ + (*(UNALIGNED USHORT *)(((PUCHAR)(_A))+4) == *(UNALIGNED USHORT *)(((PUCHAR)(_B))+4))) + +// +// This routine checks if an address is the broadcast address. +// + +#define NB_NODE_BROADCAST(_A) \ + ((*(UNALIGNED ULONG *)((PUCHAR)(_A)) == 0xffffffff) && \ + (*(UNALIGNED USHORT *)(((PUCHAR)(_A))+4) == 0xffff)) + + +// +// Definition of the routine to handler a particular minor +// code for an IOCTL_MJ_INTERNAL_DEVICE_CONTROL IRP. +// + +typedef NTSTATUS +(*NB_TDI_DISPATCH_ROUTINE) ( + IN PDEVICE Device, + IN PREQUEST Request + ); + + + +// +// Routines in action.c +// + +NTSTATUS +NbiTdiAction( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +// +// Routines in address.c +// + +TDI_ADDRESS_NETBIOS UNALIGNED * +NbiParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN BOOLEAN BroadcastAddressOk + ); + +BOOLEAN +NbiValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength + ); + +NTSTATUS +NbiOpenAddress( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiStartRegistration( + IN PADDRESS Address + ); + +VOID +NbiRegistrationTimeout( + IN CTEEvent * Event, + IN PVOID Context + ); + +VOID +NbiProcessFindName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +NbiProcessAddName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +NTSTATUS +NbiOpenConnection( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +PADDRESS +NbiCreateAddress( + IN PDEVICE Device, + IN TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress + ); + +NTSTATUS +NbiVerifyAddressFile ( +#if defined(_PNP_POWER) + IN PADDRESS_FILE AddressFile, + IN BOOLEAN ConflictIsOk +#else + IN PADDRESS_FILE AddressFile +#endif _PNP_POWER + ); + +VOID +NbiDestroyAddress( + IN PVOID Parameter + ); + +#if DBG + +VOID +NbiRefAddress( + IN PADDRESS Address + ); + +VOID +NbiRefAddressLock( + IN PADDRESS Address + ); + +#endif + +VOID +NbiDerefAddress( + IN PADDRESS Address + ); + +PADDRESS_FILE +NbiCreateAddressFile( + IN PDEVICE Device + ); + +NTSTATUS +NbiDestroyAddressFile( + IN PADDRESS_FILE AddressFile + ); + +#if DBG + +VOID +NbiRefAddressFile( + IN PADDRESS_FILE AddressFile + ); + +VOID +NbiRefAddressFileLock( + IN PADDRESS_FILE AddressFile + ); + +#endif + +VOID +NbiDerefAddressFile( + IN PADDRESS_FILE AddressFile + ); + +#if !defined(_PNP_POWER) +PADDRESS +NbiLookupAddress( + IN PDEVICE Device, + IN TDI_ADDRESS_NETBIOS UNALIGNED * NetbiosAddress + ); +#endif !_PNP_POWER + +PADDRESS +NbiFindAddress( + IN PDEVICE Device, + IN PUCHAR NetbiosName + ); + +NTSTATUS +NbiStopAddressFile( + IN PADDRESS_FILE AddressFile, + IN PADDRESS Address + ); + +NTSTATUS +NbiCloseAddressFile( + IN PDEVICE Device, + IN PREQUEST Request + ); + +#if defined(_PNP_POWER) +PADAPTER_ADDRESS +NbiCreateAdapterAddress( + IN PCHAR AdapterMacAddress + ); + +NTSTATUS +NbiDestroyAdapterAddress( + IN PADAPTER_ADDRESS AdapterAddress OPTIONAL, + IN PCHAR AdapterMacAddress OPTIONAL + ); + +PADAPTER_ADDRESS +NbiFindAdapterAddress( + IN PCHAR NetbiosName, + IN BOOLEAN LockHeld + ); +#endif _PNP_POWER + + +// +// Routines in bind.c +// + +NTSTATUS +NbiBind( + IN PDEVICE Device, + IN PCONFIG Config + ); + +VOID +NbiUnbind( + IN PDEVICE Device + ); + +VOID +NbiStatus( + IN USHORT NicId, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength + ); + +VOID +NbiLineUp( + IN USHORT NicId, + IN PIPX_LINE_INFO LineInfo, + IN NDIS_MEDIUM DeviceType, + IN PVOID ConfigurationData + ); + +VOID +NbiLineDown( + IN USHORT NicId + ); + + +// +// Routines in cache.c +// + +NTSTATUS +CacheFindName( + IN PDEVICE Device, + IN FIND_NAME_TYPE Type, + IN PUCHAR RemoteName OPTIONAL, + OUT PNETBIOS_CACHE * CacheName +); + +VOID +FindNameTimeout( + CTEEvent * Event, + PVOID Context + ); + +VOID +CacheHandlePending( + IN PDEVICE Device, + IN PUCHAR RemoteName, + IN NETBIOS_NAME_RESULT Result, + IN PNETBIOS_CACHE CacheName OPTIONAL + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiProcessNameRecognized( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +PNETBIOS_CACHE +CacheUpdateNameCache( + IN PNETBIOS_CACHE NameCache, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress, + IN BOOLEAN ModifyQueue + ); + +VOID +CacheUpdateFromAddName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN NB_CONNECTIONLESS UNALIGNED * Connectionless, + IN BOOLEAN LocalFrame + ); + +VOID +NbiProcessDeleteName( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +InsertInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE CacheEntry + ); + +VOID +ReinsertInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE OldEntry, + IN PNETBIOS_CACHE NewEntry + ); + +VOID +RemoveFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PNETBIOS_CACHE CacheEntry + ); + +VOID +FlushOldFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN USHORT AgeLimit + ); + +VOID +FlushFailedNetbiosCacheEntries( + IN PNETBIOS_CACHE_TABLE CacheTable + ); + +VOID +RemoveInvalidRoutesFromNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN NIC_HANDLE UNALIGNED *InvalidNicHandle + ); + +NTSTATUS +FindInNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable, + IN PUCHAR NameToBeFound, + OUT PNETBIOS_CACHE *CacheEntry + ); + +NTSTATUS +CreateNetbiosCacheTable( + IN OUT PNETBIOS_CACHE_TABLE *NewTable, + IN USHORT MaxHashIndex + ); + +VOID +DestroyNetbiosCacheTable( + IN PNETBIOS_CACHE_TABLE CacheTable + ); + +// +// Routines in connect.c +// + +VOID +NbiFindRouteComplete( + IN PIPX_FIND_ROUTE_REQUEST FindRouteRequest, + IN BOOLEAN FoundRoute + ); + +NTSTATUS +NbiOpenConnection( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiStopConnection( + IN PCONNECTION Connection, + IN NTSTATUS DisconnectStatus + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +NTSTATUS +NbiCloseConnection( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiAssociateAddress( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiDisassociateAddress( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiListen( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiAccept( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiConnect( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiConnectFindName( + IN PDEVICE Device, + IN PREQUEST Request, + IN PCONNECTION Connection, + IN CTELockHandle CancelLH, + IN CTELockHandle ConnectionLH, + IN CTELockHandle DeviceLH, + IN PBOOLEAN pbLockFreed + ); + +NTSTATUS +NbiTdiDisconnect( + IN PDEVICE Device, + IN PREQUEST Request + ); + +BOOLEAN +NbiAssignConnectionId( + IN PDEVICE Device, + IN PCONNECTION Connection + ); + +VOID +NbiDeassignConnectionId( + IN PDEVICE Device, + IN PCONNECTION Connection + ); + +VOID +NbiConnectionTimeout( + IN CTEEvent * Event, + IN PVOID Context + ); + +VOID +NbiCancelListen( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbiCancelConnectFindName( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbiCancelConnectWaitResponse( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbiCancelDisconnectWait( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +PCONNECTION +NbiLookupConnectionByContext( + IN PADDRESS_FILE AddressFile, + IN CONNECTION_CONTEXT ConnectionContext + ); + +PCONNECTION +NbiCreateConnection( + IN PDEVICE Device + ); + +NTSTATUS +NbiVerifyConnection ( + IN PCONNECTION Connection + ); + +VOID +NbiDestroyConnection( + IN PCONNECTION Connection + ); + +#if DBG +VOID +NbiRefConnection( + IN PCONNECTION Connection + ); + +VOID +NbiRefConnectionLock( + IN PCONNECTION Connection + ); + +VOID +NbiRefConnectionSync( + IN PCONNECTION Connection + ); + +VOID +NbiDerefConnection( + IN PCONNECTION Connection + ); + +VOID +NbiDerefConnectionSync( + IN PCONNECTION Connection + ); +#endif + +VOID +NbiHandleConnectionZero( + IN PCONNECTION Connection + ); + + +// +// Routines in datagram.c +// + +VOID +NbiProcessDatagram( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize, + IN BOOLEAN Broadcast + ); + +VOID +NbiIndicateDatagram( + IN PADDRESS Address, + IN PUCHAR RemoteName, + IN PUCHAR Data, + IN ULONG DataLength + ); + +NTSTATUS +NbiTdiSendDatagram( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiTransmitDatagram( + IN PNB_SEND_RESERVED Reserved + ); + +NTSTATUS +NbiTdiReceiveDatagram( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + + +// +// Routines in device.c +// + +VOID +NbiRefDevice( + IN PDEVICE Device + ); + +VOID +NbiDerefDevice( + IN PDEVICE Device + ); + +NTSTATUS +NbiCreateDevice( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE *DevicePtr + ); + +VOID +NbiDestroyDevice( + IN PDEVICE Device + ); + + +// +// Routines in driver.c +// + +PVOID +NbipAllocateMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN BOOLEAN ChargeDevice + ); + +VOID +NbipFreeMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN BOOLEAN ChargeDevice + ); + +#if DBG + +PVOID +NbipAllocateTaggedMemory( + IN ULONG BytesNeeded, + IN ULONG Tag, + IN PUCHAR Description + ); + +VOID +NbipFreeTaggedMemory( + IN PVOID Memory, + IN ULONG BytesAllocated, + IN ULONG Tag, + IN PUCHAR Description + ); + +#endif + +VOID +NbiWriteResourceErrorLog( + IN PDEVICE Device, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ); + +VOID +NbiWriteGeneralErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ); + +VOID +NbiWriteOidErrorLog( + IN PDEVICE Device, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ); + + +// +// Routines in event.c +// + +NTSTATUS +NbiTdiSetEventHandler( + IN PDEVICE Device, + IN PREQUEST Request + ); + + +// +// Routines in frame.c +// + +VOID +NbiSendNameFrame( + IN PADDRESS Address, + IN UCHAR NameTypeFlag, + IN UCHAR DataStreamType, + IN PIPX_LOCAL_TARGET LocalTarget OPTIONAL, +#if defined(_PNP_POWER) + IN NB_CONNECTIONLESS UNALIGNED * ReqFrame OPTIONAL +#else + IN TDI_ADDRESS_IPX UNALIGNED * DestAddress OPTIONAL +#endif _PNP_POWER + ); + +VOID +NbiSendSessionInitialize( + IN PCONNECTION Connection + ); + +VOID +NbiSendSessionInitAck( + IN PCONNECTION Connection, + IN PUCHAR ExtraData, + IN ULONG ExtraDataLength, + IN CTELockHandle * LockHandle OPTIONAL + ); + +VOID +NbiSendDataAck( + IN PCONNECTION Connection, + IN NB_ACK_TYPE AckType + IN NB_LOCK_HANDLE_PARAM (LockHandle) + ); + +VOID +NbiSendSessionEnd( + IN PCONNECTION Connection + ); + +VOID +NbiSendSessionEndAck( + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PIPX_LOCAL_TARGET LocalTarget, + IN NB_SESSION UNALIGNED * SessionEnd + ); + + +// +// Routines in packet.c +// + +NTSTATUS +NbiInitializeSendPacket( + IN PDEVICE Device, + IN NDIS_HANDLE PoolHandle OPTIONAL, + IN PNB_SEND_PACKET Packet, + IN PUCHAR Header, + IN ULONG HeaderLength + ); + +NTSTATUS +NbiInitializeReceivePacket( + IN PDEVICE Device, + IN NDIS_HANDLE PoolHandle OPTIONAL, + IN PNB_RECEIVE_PACKET Packet + ); + +NTSTATUS +NbiInitializeReceiveBuffer( + IN PDEVICE Device, + IN PNB_RECEIVE_BUFFER ReceiveBuffer, + IN PUCHAR DataBuffer, + IN ULONG DataBufferLength + ); + +VOID +NbiDeinitializeSendPacket( + IN PDEVICE Device, + IN PNB_SEND_PACKET Packet, + IN ULONG HeaderLength + ); + +VOID +NbiDeinitializeReceivePacket( + IN PDEVICE Device, + IN PNB_RECEIVE_PACKET Packet + ); + +VOID +NbiDeinitializeReceiveBuffer( + IN PDEVICE Device, + IN PNB_RECEIVE_BUFFER ReceiveBuffer + ); + +VOID +NbiAllocateSendPool( + IN PDEVICE Device + ); + +VOID +NbiAllocateReceivePool( + IN PDEVICE Device + ); + +#if defined(_PNP_POWER) +VOID +NbiAllocateReceiveBufferPool( + IN PDEVICE Device, + IN UINT DataLength + ); + +VOID +NbiReAllocateReceiveBufferPool( + IN PWORK_QUEUE_ITEM WorkItem + ); + +VOID +NbiDestroyReceiveBufferPools( + IN PDEVICE Device + ); + +VOID +NbiPushReceiveBuffer ( + IN PNB_RECEIVE_BUFFER ReceiveBuffer + ); +#else +VOID +NbiAllocateReceiveBufferPool( + IN PDEVICE Device + ); +#endif _PNP_POWER + +PSINGLE_LIST_ENTRY +NbiPopSendPacket( + IN PDEVICE Device, + IN BOOLEAN LockAcquired + ); + +VOID +NbiPushSendPacket( + IN PNB_SEND_RESERVED Reserved + ); + +VOID +NbiCheckForWaitPacket( + IN PCONNECTION Connection + ); + +PSINGLE_LIST_ENTRY +NbiPopReceivePacket( + IN PDEVICE Device + ); + +PSINGLE_LIST_ENTRY +NbiPopReceiveBuffer( + IN PDEVICE Device + ); + + +// +// Routines in query.c +// + +NTSTATUS +NbiTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ); + +NTSTATUS +NbiStoreAdapterStatus( + IN ULONG MaximumLength, + IN USHORT NicId, + OUT PVOID * StatusBuffer, + OUT ULONG * StatusBufferLength, + OUT ULONG * ValidBufferLength + ); + +VOID +NbiUpdateNetbiosFindName( + IN PREQUEST Request, +#if defined(_PNP_POWER) + IN PNIC_HANDLE NicHandle, +#else + IN USHORT NicId, +#endif _PNP_POWER + IN TDI_ADDRESS_IPX UNALIGNED * RemoteIpxAddress, + IN BOOLEAN Unique + ); + +VOID +NbiSetNetbiosFindNameInformation( + IN PREQUEST Request + ); + +NTSTATUS +NbiTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiProcessStatusQuery( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +NbiSendStatusQuery( + IN PREQUEST Request + ); + +VOID +NbiProcessStatusResponse( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ); + + +// +// Routines in receive.c +// + + +VOID +NbiReceive( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ); + +VOID +NbiReceiveComplete( + IN USHORT NicId + ); + +VOID +NbiTransferDataComplete( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status, + IN UINT BytesTransferred + ); + +VOID +NbiAcknowledgeReceive( + IN PCONNECTION Connection + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiCompleteReceive( + IN PCONNECTION Connection, + IN BOOLEAN EndOfMessage, + IN CTELockHandle CancelLH + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +NTSTATUS +NbiTdiReceive( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiCancelReceive( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + + +// +// Routines in send.c +// + + +VOID +NbiSendComplete( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status + ); + +VOID +NbiAssignSequenceAndSend( + IN PCONNECTION Connection, + IN PNDIS_PACKET Packet + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +NTSTATUS +NbiTdiSend( + IN PDEVICE Device, + IN PREQUEST Request + ); + +VOID +NbiPacketizeSend( + IN PCONNECTION Connection + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiReframeConnection( + IN PCONNECTION Connection, + IN USHORT ReceiveSequence, + IN USHORT BytesReceived, + IN BOOLEAN Resend + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiRestartConnection( + IN PCONNECTION Connection + ); + +VOID +NbiAdvanceUnAckedByBytes( + IN PCONNECTION Connection, + IN ULONG BytesAcked + ); + +VOID +NbiAdvanceUnAckedBySequence( + IN PCONNECTION Connection, + IN USHORT ReceiveSequence + ); + +VOID +NbiCancelSend( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbiBuildBufferChainFromBufferChain ( + IN NDIS_HANDLE BufferPoolHandle, + IN PNDIS_BUFFER CurrentSourceBuffer, + IN ULONG CurrentByteOffset, + IN ULONG DesiredLength, + OUT PNDIS_BUFFER *DestinationBuffer, + OUT PNDIS_BUFFER *NewSourceBuffer, + OUT ULONG *NewByteOffset, + OUT ULONG *ActualLength + ); + + +// +// Routines in session.c +// + +VOID +NbiProcessSessionData( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ); + +VOID +NbiProcessDataAck( + IN PCONNECTION Connection, + IN NB_SESSION UNALIGNED * Sess, + IN PIPX_LOCAL_TARGET RemoteAddress + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiProcessSessionInitialize( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +NbiProcessSessionInitAck( + IN PCONNECTION Connection, + IN NB_SESSION UNALIGNED * Sess + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ); + +VOID +NbiProcessSessionEnd( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + +VOID +NbiProcessSessionEndAck( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ); + + +// +// Routines in timer.c +// + +VOID +NbiStartRetransmit( + IN PCONNECTION Connection + ); + +VOID +NbiStartWatchdog( + IN PCONNECTION Connection + ); + +#if DBG + +VOID +NbiStopRetransmit( + IN PCONNECTION Connection + ); + +VOID +NbiStopWatchdog( + IN PCONNECTION Connection + ); + +#else + +#define NbiStopRetransmit(_Connection) \ + (_Connection)->Retransmit = 0; + +#define NbiStopWatchdog(_Connection) \ + (_Connection)->Watchdog = 0; + +#endif + +VOID +NbiExpireRetransmit( + IN PCONNECTION Connection + ); + +VOID +NbiExpireWatchdog( + IN PCONNECTION Connection + ); + +VOID +NbiShortTimeout( + IN CTEEvent * Event, + IN PVOID Context + ); + +VOID +NbiLongTimeout( + IN CTEEvent * Event, + IN PVOID Context + ); + +VOID +NbiStartShortTimer( + IN PDEVICE Device + ); + +VOID +NbiInitializeTimers( + IN PDEVICE Device + ); + diff --git a/private/ntos/tdi/isnp/nb/nbitypes.h b/private/ntos/tdi/isnp/nb/nbitypes.h new file mode 100644 index 000000000..604df5fb5 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nbitypes.h @@ -0,0 +1,1511 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + nbitypes.h + +Abstract: + + This module contains definitions specific to the + Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 16-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +// +// For find name requests, defines the current status (SR_FN.Status). +// + +typedef enum { + FNStatusNoResponse, // no response has been received + FNStatusResponseUnique, // response received, is a unique name + FNStatusResponseGroup // response received, is a group name +}; + +// +// Defines the results we can get from sending a series of find +// names to locate a netbios name. +// + +typedef enum _NETBIOS_NAME_RESULT { + NetbiosNameFound, // name was located + NetbiosNameNotFoundNormal, // name not found, no response received + NetbiosNameNotFoundWanDown // name not found, all lines were down +} NETBIOS_NAME_RESULT, *PNETBIOS_NAME_RESULT; + + +// +// Definition of the protocol reserved field of a send packet. +// + +typedef struct _NB_SEND_RESERVED { + UCHAR Identifier; // 0 for NB packets + BOOLEAN SendInProgress; // used in an NdisSend + UCHAR Type; // what to do on completion + BOOLEAN OwnedByConnection; // if this is a connection's one packet +#if defined(_PNP_POWER) + PVOID Reserved[SEND_RESERVED_COMMON_SIZE]; // used by ipx for even-padding and local target etc. +#else + PVOID Reserved[2]; // used by ipx for even-padding +#endif _PNP_POWER + LIST_ENTRY GlobalLinkage; // all packets are on this + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue + LIST_ENTRY WaitLinkage; // when waiting on other queues +#ifdef NB_TRACK_POOL + PVOID Pool; // send pool it was allocated from +#endif + union { + struct { + UCHAR NetbiosName[16]; // name being searched for + UCHAR StatusAndSentOnUpLine; // low nibble: look at FNStatusXXX enum + // high nibble: TRUE if while sending, found lan or up wan line + UCHAR RetryCount; // number of times sent + USHORT SendTime; // based on Device->FindNameTime +#if !defined(_PNP_POWER) + USHORT CurrentNicId; // current nic id it is being sent on + USHORT MaximumNicId; // highest one it will be sent on +#endif !_PNP_POWER + struct _NETBIOS_CACHE * NewCache; // new cache entry for group names + } SR_FN; + struct { + struct _ADDRESS * Address; // that owns this packet, if one does + PREQUEST Request; // send datagram request + struct _ADDRESS_FILE * AddressFile; // that this send is on +#if !defined(_PNP_POWER) + USHORT CurrentNicId; // non-zero for frames that go to all +#endif !_PNP_POWER + UCHAR NameTypeFlag; // save these two values for frames + UCHAR DataStreamType; // that need to be sent to all nic id's + } SR_NF; + struct { + PREQUEST DatagramRequest; // holds the passed-in request + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteName; // will be -1 for broadcast + struct _ADDRESS_FILE * AddressFile; // that the datagram was sent on + struct _NETBIOS_CACHE * Cache; // how to route to the netbios address + ULONG CurrentNetwork; // within the cache entry + } SR_DG; + struct { + struct _CONNECTION * Connection; // that this frame was sent on. + PREQUEST Request; // that this frame was sent for. + ULONG PacketLength; // total packet length. + BOOLEAN NoNdisBuffer; // none allocate for send + } SR_CO; + struct { + ULONG ActualBufferLength; // real length of allocated buffer. + } SR_AS; + } u; + PUCHAR Header; // points to the MAC/IPX/NB header + PNDIS_BUFFER HeaderBuffer; // the NDIS_BUFFER describing Header +} NB_SEND_RESERVED, *PNB_SEND_RESERVED; + +// +// Values for Type. +// + +#define SEND_TYPE_NAME_FRAME 1 +#define SEND_TYPE_SESSION_INIT 2 +#define SEND_TYPE_FIND_NAME 3 +#define SEND_TYPE_DATAGRAM 4 +#define SEND_TYPE_SESSION_NO_DATA 5 +#define SEND_TYPE_SESSION_DATA 6 +#define SEND_TYPE_STATUS_QUERY 7 +#define SEND_TYPE_STATUS_RESPONSE 8 + +#ifdef RSRC_TIMEOUT_DBG +#define SEND_TYPE_DEATH_PACKET 9 +#endif //RSRC_TIMEOUT_DBG + +// +// Macros to access StatusAndSentOnUpLine. +// + +#define NB_GET_SR_FN_STATUS(_Reserved) \ + ((_Reserved)->u.SR_FN.StatusAndSentOnUpLine & 0x0f) + +#define NB_SET_SR_FN_STATUS(_Reserved,_Value) \ + (_Reserved)->u.SR_FN.StatusAndSentOnUpLine = \ + (((_Reserved)->u.SR_FN.StatusAndSentOnUpLine & 0xf0) | (_Value)); + +#define NB_GET_SR_FN_SENT_ON_UP_LINE(_Reserved) \ + (((_Reserved)->u.SR_FN.StatusAndSentOnUpLine & 0xf0) != 0) + +#define NB_SET_SR_FN_SENT_ON_UP_LINE(_Reserved,_Value) \ + (_Reserved)->u.SR_FN.StatusAndSentOnUpLine = \ + (((_Reserved)->u.SR_FN.StatusAndSentOnUpLine & 0x0f) | ((_Value) << 4)); + + +// +// Definition of the protocol reserved field of a receive packet. +// + +typedef struct _NB_RECEIVE_RESERVED { + UCHAR Identifier; // 0 for NB packets + BOOLEAN TransferInProgress; // used in an NdisTransferData + UCHAR Type; // what to do on completion +#if defined(_PNP_POWER) + PVOID Pool; // send pool it was allocated from +#else + +#ifdef IPX_TRACK_POOL + PVOID Pool; // send pool it was allocated from +#endif + +#endif _PNP_POWER + union { + struct { + struct _CONNECTION * Connection; // that the transfer is for + BOOLEAN EndOfMessage; // this was the last part of a message + BOOLEAN CompleteReceive; // receive should be completed + BOOLEAN NoNdisBuffer; // user's mdl chain was used + BOOLEAN PartialReceive; // (new nb) don't ack this packet + } RR_CO; + struct { + struct _NB_RECEIVE_BUFFER * ReceiveBuffer; // datagram receive buffer + } RR_DG; + struct { + PREQUEST Request; // for this request + } RR_AS; + } u; + LIST_ENTRY GlobalLinkage; // all packets are on this + SINGLE_LIST_ENTRY PoolLinkage; // when on free queue +} NB_RECEIVE_RESERVED, *PNB_RECEIVE_RESERVED; + +// +// Values for Type. +// + +#define RECEIVE_TYPE_DATAGRAM 1 +#define RECEIVE_TYPE_DATA 2 +#define RECEIVE_TYPE_ADAPTER_STATUS 3 + + + +typedef struct _NB_RECEIVE_BUFFER { + LIST_ENTRY GlobalLinkage; // all buffers are on this +#if defined(_PNP_POWER) + PVOID Pool; // receive buffer pool was allocated from +#else +#ifdef NB_TRACK_POOL + PVOID Pool; // receive buffer pool was allocated from +#endif +#endif _PNP_POWER + struct _ADDRESS * Address; // that the datagram is for + SINGLE_LIST_ENTRY PoolLinkage; // when in free pool + LIST_ENTRY WaitLinkage; // when in ReceiveDatagrams queue + PNDIS_BUFFER NdisBuffer; // describes the data + UCHAR RemoteName[16]; // datagram was received from + ULONG DataLength; // length for current one, not allocated + PUCHAR Data; // points to data to hold packet +} NB_RECEIVE_BUFFER, *PNB_RECEIVE_BUFFER; + + +// +// Types to abstract NDIS packets. This is to allow us to +// switch from using our own memory for packets to using +// authentically allocated NDIS packets. +// + +//#define NB_OWN_PACKETS 1 + +#ifdef NB_OWN_PACKETS + +#define NDIS_PACKET_SIZE 48 +// #define NDIS_PACKET_SIZE FIELD_OFFSET(NDIS_PACKET,ProtocolReserved[0]) + +typedef struct _NB_SEND_PACKET { + UCHAR Data[NDIS_PACKET_SIZE+sizeof(NB_SEND_RESERVED)]; +} NB_SEND_PACKET, *PNB_SEND_PACKET; + +typedef struct _NB_RECEIVE_PACKET { + UCHAR Data[NDIS_PACKET_SIZE+sizeof(NB_RECEIVE_RESERVED)]; +} NB_RECEIVE_PACKET, *PNB_RECEIVE_PACKET; + +typedef struct _NB_SEND_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + NB_SEND_PACKET Packets[1]; + // after the packets the header buffers are allocated also. +} NB_SEND_POOL, *PNB_SEND_POOL; + +typedef struct _NB_RECEIVE_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + NB_RECEIVE_PACKET Packets[1]; +} NB_RECEIVE_POOL, *PNB_RECEIVE_POOL; + +#define PACKET(_Packet) ((PNDIS_PACKET)((_Packet)->Data)) + +#define NbiAllocateSendPacket(_Device,_PoolHandle, _SendPacket,_Status) { \ + NdisReinitializePacket((PNDIS_PACKET)((_SendPacket)->Data)); \ + *(_Status) = STATUS_SUCCESS; \ +} + +#define NbiAllocateReceivePacket(_Device,_PoolHandle, _ReceivePacket,_Status) { \ + NdisReinitializePacket((PNDIS_PACKET)((_ReceivePacket)->Data)); \ + *(_Status) = STATUS_SUCCESS; \ +} + +#define NbiFreeSendPacket(_Device,_Packet) + +#define NbiFreeReceivePacket(_Device,_Packet) + + +#else // NB_OWN_PACKETS + +typedef struct _NB_SEND_PACKET { + PNDIS_PACKET Packet; +} NB_SEND_PACKET, *PNB_SEND_PACKET; + +typedef struct _NB_RECEIVE_PACKET { + PNDIS_PACKET Packet; +} NB_RECEIVE_PACKET, *PNB_RECEIVE_PACKET; + +typedef struct _NB_PACKET_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + NDIS_HANDLE PoolHandle; + NB_SEND_PACKET Packets[1]; + // after the packets the header buffers are allocated also. +} NB_SEND_POOL, *PNB_SEND_POOL; + +typedef struct _NB_RECEIVE_POOL { + LIST_ENTRY Linkage; + UINT PacketCount; + UINT PacketFree; + NDIS_HANDLE PoolHandle; + NB_RECEIVE_PACKET Packets[1]; +} NB_RECEIVE_POOL, *PNB_RECEIVE_POOL; + +#define PACKET(_Packet) ((_Packet)->Packet) + +#define NbiAllocateSendPacket(_Device,_PoolHandle, _SendPacket,_Status) { \ + NdisAllocatePacket(_Status, &(_SendPacket)->Packet, _PoolHandle); \ +} + +#define NbiAllocateReceivePacket(_Device, _PoolHandle, _ReceivePacket,_Status) { \ + NdisAllocatePacket(_Status, &(_ReceivePacket)->Packet, _PoolHandle); \ +} + +#define NbiFreeSendPacket(_Device,_Packet) { \ + NdisFreePacket(PACKET(_Packet)); \ +} + +#define NbiFreeReceivePacket(_Device,_Packet) { \ + NdisFreePacket(PACKET(_Packet)); \ +} + +#endif // NB_OWN_PACKETS + +#define SEND_RESERVED(_Packet) ((PNB_SEND_RESERVED)((PACKET(_Packet))->ProtocolReserved)) +#define RECEIVE_RESERVED(_Packet) ((PNB_RECEIVE_RESERVED)((PACKET(_Packet))->ProtocolReserved)) + + + +typedef struct _NB_RECEIVE_BUFFER_POOL { + LIST_ENTRY Linkage; + UINT BufferCount; + UINT BufferFree; +#if defined(_PNP_POWER) + UINT BufferDataSize; // allocation size of each buffer data +#endif _PNP_POWER + NB_RECEIVE_BUFFER Buffers[1]; + // after the packets the data buffers are allocated also. +} NB_RECEIVE_BUFFER_POOL, *PNB_RECEIVE_BUFFER_POOL; + + +// +// Tags for memory allocation. +// + +#define MEMORY_CONFIG 0 +#define MEMORY_ADAPTER 1 +#define MEMORY_ADDRESS 2 +#define MEMORY_PACKET 3 +#define MEMORY_CACHE 4 +#define MEMORY_CONNECTION 5 +#define MEMORY_STATUS 6 +#define MEMORY_QUERY 7 +#if defined(_PNP_POWER) +#define MEMORY_WORK_ITEM 8 +#define MEMORY_ADAPTER_ADDRESS 9 +#endif _PNP_POWER + +#if defined(_PNP_POWER) +#define MEMORY_MAX 10 +#else +#define MEMORY_MAX 8 +#endif _PNP_POWER + +#if DBG + +// +// Holds the allocations for a specific memory type. +// + +typedef struct _MEMORY_TAG { + ULONG Tag; + ULONG BytesAllocated; +} MEMORY_TAG, *PMEMORY_TAG; + +EXTERNAL_LOCK(NbiMemoryInterlock); +extern MEMORY_TAG NbiMemoryTag[MEMORY_MAX]; + +#endif + + + +// +// This structure holds a single remote network which a +// Netbios name exists on. +// + +typedef struct _NETBIOS_NETWORK { + ULONG Network; + IPX_LOCAL_TARGET LocalTarget; +} NETBIOS_NETWORK, *PNETBIOS_NETWORK; + +// +// This defines a netbios cache entry for a given name. +// + +typedef struct _NETBIOS_CACHE { + UCHAR NetbiosName[16]; + BOOLEAN Unique; + BOOLEAN FailedOnDownWan; // if NetworksUsed == 0, was it due to down wan lines? + USHORT TimeStamp; // in seconds - CacheTimeStamp when inserted + ULONG ReferenceCount; + LIST_ENTRY Linkage; + TDI_ADDRESS_IPX FirstResponse; + USHORT NetworksAllocated; + USHORT NetworksUsed; + NETBIOS_NETWORK Networks[1]; // may be more than one of these +} NETBIOS_CACHE, *PNETBIOS_CACHE; + +typedef struct _NETBIOS_CACHE_TABLE { + USHORT MaxHashIndex; + USHORT CurrentEntries; + LIST_ENTRY Bucket[1]; +} NETBIOS_CACHE_TABLE, *PNETBIOS_CACHE_TABLE; + +#define NB_NETBIOS_CACHE_TABLE_LARGE 26 // for server +#define NB_NETBIOS_CACHE_TABLE_SMALL 8 // for workstation +#define NB_MAX_AVG_CACHE_ENTRIES_PER_BUCKET 8 + +// +// This defines the different kind of requests that can be made +// to CacheFindName(). +// + +typedef enum _FIND_NAME_TYPE { + FindNameConnect, + FindNameNetbiosFindName, + FindNameOther +} FIND_NAME_TYPE, *PFIND_NAME_TYPE; + + +// +// The number of hash entries in the non-inactive connection +// database. +// + +#define CONNECTION_HASH_COUNT 8 + +// +// Mask and shift to retrieve the hash number from a connection +// ID. +// + +#define CONNECTION_HASH_MASK 0xe000 +#define CONNECTION_HASH_SHIFT 13 + +// +// The maximum connection ID we can assign, not counting the +// shifted-over hash id (which occupies the top 3 bits of the +// real id we use on the wire). We can use all the bits except +// the top one, to prevent an ID of 0xffff being used. +// + +#define CONNECTION_MAXIMUM_ID (USHORT)(~CONNECTION_HASH_MASK & ~1) + +// +// A single connection hash bucket. +// + +typedef struct _CONNECTION_HASH { + struct _CONNECTION * Connections; + USHORT ConnectionCount; + USHORT NextConnectionId; +} CONNECTION_HASH, *PCONNECTION_HASH; + + +// +// These are queued in the ConnectIndicationInProgress +// queue to track indications to TDI clients. +// + +typedef struct _CONNECT_INDICATION { + LIST_ENTRY Linkage; + UCHAR NetbiosName[16]; + TDI_ADDRESS_IPX RemoteAddress; + USHORT ConnectionId; +} CONNECT_INDICATION, *PCONNECT_INDICATION; + +// +// This structure defines the per-device structure for NB +// (one of these is allocated globally). +// + +#define DREF_CREATE 0 +#define DREF_LOADED 1 +#define DREF_ADAPTER 2 +#define DREF_ADDRESS 3 +#define DREF_CONNECTION 4 +#define DREF_FN_TIMER 5 +#define DREF_FIND_NAME 6 +#define DREF_SESSION_INIT 7 +#define DREF_NAME_FRAME 8 +#define DREF_FRAME 9 +#define DREF_SHORT_TIMER 10 +#define DREF_LONG_TIMER 11 +#define DREF_STATUS_QUERY 12 +#define DREF_STATUS_RESPONSE 13 +#define DREF_STATUS_FRAME 14 +#define DREF_NB_FIND_NAME 15 + +#define DREF_TOTAL 16 + +typedef struct _DEVICE { + + DEVICE_OBJECT DeviceObject; // the I/O system's device object. + +#if DBG + ULONG RefTypes[DREF_TOTAL]; +#endif + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + +#if DBG + UCHAR Signature1[4]; // contains "IDC1" +#endif + + NB_LOCK Interlock; // GLOBAL lock for reference count. + // (used in ExInterlockedXxx calls) + NB_LOCK Lock; + LONG ReferenceCount; // activity count/this provider. + + // + // These are kept around for error logging, and stored right + // after this structure. + // + + PWCHAR DeviceName; +#if defined(_PNP_POWER) + USHORT DeviceNameLength; +#else + ULONG DeviceNameLength; +#endif _PNP_POWER + + LIST_ENTRY GlobalSendPacketList; + LIST_ENTRY GlobalReceivePacketList; + LIST_ENTRY GlobalReceiveBufferList; + + // + // All send packet pools are chained on this list. + // + + LIST_ENTRY SendPoolList; + LIST_ENTRY ReceivePoolList; + LIST_ENTRY ReceiveBufferPoolList; + + SLIST_HEADER SendPacketList; + SLIST_HEADER ReceivePacketList; + SINGLE_LIST_ENTRY ReceiveBufferList; + + // + // Receive requests waiting to be completed. + // + + LIST_ENTRY ReceiveCompletionQueue; + + // + // Connections waiting for send packets. + // + + LIST_ENTRY WaitPacketConnections; + + // + // Connections waiting to packetize. + // + + LIST_ENTRY PacketizeConnections; + + // + // Connections waiting to send a data ack. + // + + LIST_ENTRY DataAckConnections; + + // + // The list changed while we were processing it. + // + + BOOLEAN DataAckQueueChanged; + + // + // Information to manage the Netbios name cache. + // + + LIST_ENTRY WaitingConnects; // connect requests waiting for a name + LIST_ENTRY WaitingDatagrams; // datagram requests waiting for a name + LIST_ENTRY WaitingAdapterStatus; // adapter status requests waiting for a name + LIST_ENTRY WaitingNetbiosFindName; // netbios find name requests waiting for a name + + // + // Holds adapter status request which have a name and + // are waiting for a response. The long timeout aborts + // these after a couple of expirations (BUGBUG: currently we + // do not do resends). + // + + LIST_ENTRY ActiveAdapterStatus; + + // + // Receive datagrams waiting to be indicated. + // + + LIST_ENTRY ReceiveDatagrams; + + // + // In-progress connect indications (used to make + // sure we don't indicate the same packet twice). + // + + LIST_ENTRY ConnectIndicationInProgress; + + // + // Listens that have been posted to connections. + // + + LIST_ENTRY ListenQueue; + + UCHAR State; + + // + // The following fields control the timer system. + // The short timer is used for retransmission and + // delayed acks, and the long timer is used for + // watchdog timeouts. + // + BOOLEAN ShortListActive; // ShortList is not empty. + BOOLEAN DataAckActive; // DataAckConnections is not empty. + BOOLEAN TimersInitialized; // has the timer system been initialized. + BOOLEAN ProcessingShortTimer; // TRUE if we are in ScanShortTimer. +#if defined(_PNP_POWER) + BOOLEAN LongTimerRunning; // True if the long timer is running. +#endif _PNP_POWER + LARGE_INTEGER ShortTimerStart; // tick count when the short timer was set. + CTETimer ShortTimer; // controls the short timer. + ULONG ShortAbsoluteTime; // up-count timer ticks, short timer. + CTETimer LongTimer; // kernel DPC object, long timer. + ULONG LongAbsoluteTime; // up-count timer ticks, long timer. + NB_LOCK TimerLock; // lock for following timer queues + LIST_ENTRY ShortList; // list of waiting connections + LIST_ENTRY LongList; // list of waiting connections + + + // + // Hash table of non-inactive connections. + // + + CONNECTION_HASH ConnectionHash[CONNECTION_HASH_COUNT]; + + // + // Control the queue of waiting find names. + // + + USHORT FindNameTime; // incremented each time the timer runs + BOOLEAN FindNameTimerActive; // TRUE if the timer is queued + CTETimer FindNameTimer; // runs every FIND_NAME_GRANULARITY + ULONG FindNameTimeout; // the retry count in timer ticks + + ULONG FindNamePacketCount; // Count of packets on the queue + LIST_ENTRY WaitingFindNames; // FIND_NAME frames waiting to go out + + // + // The cache of NETBIOS_CACHE entries. + // + + PNETBIOS_CACHE_TABLE NameCache; + + // + // The current time stamp, incremented every second. + // + + USHORT CacheTimeStamp; + + // + // Maximum valid NIC ID we can use. + // + + USHORT MaximumNicId; + + + // + // Handle for our binding to the IPX driver. + // + + HANDLE BindHandle; + + // + // Holds the output from binding to IPX. + // + union { + IPX_INTERNAL_BIND_OUTPUT Bind; + IPX_INTERNAL_BIND_INPUT BindInput; + }; + + // + // Holds our reserved netbios name, which is 10 bytes + // of zeros followed by our node address. + // +#if !defined(_PNP_POWER) + UCHAR ReservedNetbiosName[16]; +#endif !_PNP_POWER + + // + // This holds the total memory allocated for the above structures. + // + + LONG MemoryUsage; + LONG MemoryLimit; + + // + // How many packets have been allocated. + // + + ULONG AllocatedSendPackets; + ULONG AllocatedReceivePackets; + ULONG AllocatedReceiveBuffers; + +#if defined(_PNP_POWER) + // + // This is the size of each buffer in the receive buffer pool. + // We reallocate buffer pool when the LineInfo.MaxPacketSize changes(increases) + // from IPX because of a new adapter. The LineInfo.MaxPacketSize could + // also change(decrease) when a adapter disappears but our buffer pool size + // will stay at this value. + // + ULONG CurMaxReceiveBufferSize; +#endif _PNP_POWER + + // + // Other configuration parameters. + // + + ULONG AckDelayTime; // converted to short timeouts, rounded up + ULONG AckWindow; + ULONG AckWindowThreshold; + ULONG EnablePiggyBackAck; + ULONG Extensions; + ULONG RcvWindowMax; + ULONG BroadcastCount; + ULONG BroadcastTimeout; + ULONG ConnectionCount; + ULONG ConnectionTimeout; + ULONG InitPackets; + ULONG MaxPackets; + ULONG InitialRetransmissionTime; + ULONG Internet; + ULONG KeepAliveCount; + ULONG KeepAliveTimeout; + ULONG RetransmitMax; + ULONG RouterMtu; + + ULONG MaxReceiveBuffers; + + + // + // Where we tell upper drivers to put their headers. + // + + ULONG IncludedHeaderOffset; + + // + // The following field is a head of a list of ADDRESS objects that + // are defined for this transport provider. To edit the list, you must + // hold the spinlock of the device context object. + // + + LIST_ENTRY AddressDatabase; // list of defined transport addresses. +#if defined(_PNP_POWER) + LIST_ENTRY AdapterAddressDatabase; // list of netbios names made from adapter addresses. +#endif _PNP_POWER + + ULONG AddressCount; // number of addresses in the database. + + NDIS_HANDLE NdisBufferPoolHandle; + +#if DBG + UCHAR Signature2[4]; // contains "IDC2" +#endif + + // + // This structure holds a pre-built IPX header which is used + // to quickly fill in common fields of outgoing connectionless + // frames. + // + + IPX_HEADER ConnectionlessHeader; + + // + // This event is used when unloading to signal that + // the reference count is now 0. + // + + KEVENT UnloadEvent; + BOOLEAN UnloadWaiting; + +#if defined(_PNP_POWER) + HANDLE TdiRegistrationHandle; +#endif _PNP_POWER + + // + // Counters for most of the statistics that NB maintains; + // some of these are kept elsewhere. Including the structure + // itself wastes a little space but ensures that the alignment + // inside the structure is correct. + // + + TDI_PROVIDER_STATISTICS Statistics; + + // + // These are "temporary" versions of the other counters. + // During normal operations we update these, then during + // the short timer expiration we update the real ones. + // + + ULONG TempFrameBytesSent; + ULONG TempFramesSent; + ULONG TempFrameBytesReceived; + ULONG TempFramesReceived; + + + // + // This contains the next unique indentified to use as + // the FsContext in the file object associated with an + // open of the control channel. + // + + USHORT ControlChannelIdentifier; + + // + // Counters for "active" time. + // + + LARGE_INTEGER NbiStartTime; + + // + // This array is used to quickly dismiss connectionless frames + // that are not destined for us. The count is the number + // of addresses with that first letter that are registered + // on this device. + // + + UCHAR AddressCounts[256]; + + // + // This resource guards access to the ShareAccess + // and SecurityDescriptor fields in addresses. + // + + ERESOURCE AddressResource; + + // + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + // + + TDI_PROVIDER_INFO Information; // information about this provider. + +} DEVICE, * PDEVICE; + + +extern PDEVICE NbiDevice; +EXTERNAL_LOCK(NbiGlobalPoolInterlock); + +// +// This is used only for CHK build. For +// tracking the refcount problem on connection, this +// is moved here for now. +// + +EXTERNAL_LOCK(NbiGlobalInterlock); + + +// +// device state definitions +// +#if defined(_PNP_POWER) +#define DEVICE_STATE_CLOSED 0x00 // Initial state +#define DEVICE_STATE_LOADED 0x01 // Loaded and bound to IPX but no adapters +#define DEVICE_STATE_OPEN 0x02 // Fully operational +#define DEVICE_STATE_STOPPING 0x03 // Unload has been initiated, The I/O system + // will not call us until nobody above has Netbios open. +#else +#define DEVICE_STATE_CLOSED 0x00 +#define DEVICE_STATE_OPEN 0x01 +#define DEVICE_STATE_STOPPING 0x02 +#endif _PNP_POWER + + +#define NB_TDI_RESOURCES 9 + + +// +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Address. This structure is the base for all activities on +// the open file object within the transport provider. All active connections +// on the address point to this structure, although no queues exist here to do +// work from. This structure also maintains a reference to an ADDRESS +// structure, which describes the address that it is bound to. +// + +#define AFREF_CREATE 0 +#define AFREF_RCV_DGRAM 1 +#define AFREF_SEND_DGRAM 2 +#define AFREF_VERIFY 3 +#define AFREF_INDICATION 4 +#define AFREF_TIMEOUT 5 +#define AFREF_CONNECTION 6 + +#define AFREF_TOTAL 8 + +typedef struct _ADDRESS_FILE { + +#if DBG + ULONG RefTypes[AFREF_TOTAL]; +#endif + + CSHORT Type; + CSHORT Size; + + LIST_ENTRY Linkage; // next address file on this address. + // also used for linkage in the + // look-aside list + + ULONG ReferenceCount; // number of references to this object. + + // + // the current state of the address file structure; this is either open or + // closing + // + + UCHAR State; + + PNB_LOCK AddressLock; + + // + // The following fields are kept for housekeeping purposes. + // + + PREQUEST OpenRequest; // the request used for open + struct _ADDRESS *Address; // address to which we are bound. +#ifdef ISN_NT + PFILE_OBJECT FileObject; // easy backlink to file object. +#endif + struct _DEVICE *Device; // device to which we are attached. + + LIST_ENTRY ConnectionDatabase; // associated with this address. + + LIST_ENTRY ReceiveDatagramQueue; // posted by the client. + + // + // This holds the request used to close this address file, + // for pended completion. + // + + PREQUEST CloseRequest; + + // + // Handler for kernel event actions. First we have a set of booleans that + // indicate whether or not this address has an event handler of the given + // type registered. + // + + BOOLEAN RegisteredHandler[6]; + + // + // This is a list of handlers for a given event. They can be + // accessed using the explicit names for type-checking, or the + // array (indexed by the event type) for speed. + // + + union { + struct { + PTDI_IND_CONNECT ConnectionHandler; + PTDI_IND_DISCONNECT DisconnectHandler; + PTDI_IND_ERROR ErrorHandler; + PTDI_IND_RECEIVE ReceiveHandler; + PTDI_IND_RECEIVE_DATAGRAM ReceiveDatagramHandler; + PTDI_IND_RECEIVE_EXPEDITED ExpeditedDataHandler; + }; + PVOID Handlers[6]; + }; + + PVOID HandlerContexts[6]; + +} ADDRESS_FILE, *PADDRESS_FILE; + +#define ADDRESSFILE_STATE_OPENING 0x00 // not yet open for business +#define ADDRESSFILE_STATE_OPEN 0x01 // open for business +#define ADDRESSFILE_STATE_CLOSING 0x02 // closing + + +// +// This structure defines a NETBIOS name as a character array for use when +// passing preformatted NETBIOS names between internal routines. It is +// not a part of the external interface to the transport provider. +// + +typedef struct _NBI_NETBIOS_ADDRESS { + UCHAR NetbiosName[16]; + USHORT NetbiosNameType; + BOOLEAN Broadcast; +} NBI_NETBIOS_ADDRESS, *PNBI_NETBIOS_ADDRESS; + +// +// This structure defines an ADDRESS, or active transport address, +// maintained by the transport provider. It contains all the visible +// components of the address (such as the TSAP and network name components), +// and it also contains other maintenance parts, such as a reference count, +// ACL, and so on. All outstanding connection-oriented and connectionless +// data transfer requests are queued here. +// + +#define AREF_ADDRESS_FILE 0 +#define AREF_LOOKUP 1 +#define AREF_RECEIVE 2 +#define AREF_NAME_FRAME 3 +#define AREF_TIMER 4 +#define AREF_FIND 5 + +#define AREF_TOTAL 8 + + +typedef struct _ADDRESS { + +#if DBG + ULONG RefTypes[AREF_TOTAL]; +#endif + + USHORT Size; + CSHORT Type; + + LIST_ENTRY Linkage; // next address/this device object. + ULONG ReferenceCount; // number of references to this object. + + NB_LOCK Lock; + + // + // The following fields comprise the actual address itself. + // + + PREQUEST Request; // pointer to address creation request. + + UCHAR NameTypeFlag; // NB_NAME_UNIQUE or NB_NAME_GROUP + + NBI_NETBIOS_ADDRESS NetbiosAddress; // our netbios name. + + // + // The following fields are used to maintain state about this address. + // + + ULONG Flags; // attributes of the address. + ULONG State; // current state of the address. + struct _DEVICE *Device; // device context to which we are attached. + PNB_LOCK DeviceLock; + + // + // The following queues is used to hold send datagrams for this + // address. Receive datagrams are queued to the address file. Requests are + // processed in a first-in, first-out manner, so that the very next request + // to be serviced is always at the head of its respective queue. These + // queues are managed by the EXECUTIVE interlocked list management routines. + // The actual objects which get queued to this structure are request control + // blocks (RCBs). + // + + LIST_ENTRY AddressFileDatabase; // list of defined address file objects + + UCHAR SendPacketHeader[NB_MAXIMUM_MAC + sizeof(IPX_HEADER)]; + + // + // This timer is used for registering the name. + // + + CTETimer RegistrationTimer; + + // + // Number of times an add name frame has been sent. + // + + ULONG RegistrationCount; + +#ifdef ISN_NT + + // + // These two can be a union because they are not used + // concurrently. + // + + union { + + // + // This structure is used for checking share access. + // + + SHARE_ACCESS ShareAccess; + + // + // Used for delaying NbiDestroyAddress to a thread so + // we can access the security descriptor. + // + + WORK_QUEUE_ITEM DestroyAddressQueueItem; + + } u; + + // + // This structure is used to hold ACLs on the address. + + PSECURITY_DESCRIPTOR SecurityDescriptor; + +#endif + +} ADDRESS, *PADDRESS; + +// +// Values for Flags +// + +#define ADDRESS_FLAGS_DUPLICATE_NAME 0x00000002 +#if defined(_PNP_POWER) +#define ADDRESS_FLAGS_CONFLICT 0x00000010 +#endif _PNP_POWER + +#if defined(_PNP_POWER) +// +// this booleans are passed to nbiverifyaddressfile calls. +// +#define CONFLICT_IS_OK TRUE +#define CONFLICT_IS_NOT_OK FALSE +#endif _PNP_POWER + +// +// Values for State +// + +#define ADDRESS_STATE_REGISTERING 1 +#define ADDRESS_STATE_OPEN 2 +#define ADDRESS_STATE_STOPPING 3 + +#if defined(_PNP_POWER) +// +// This holds the adapters names i.e netbios names which are +// created from adater node address to support adapter status +// queries using adapter node addresses. +// +typedef struct _ADAPTER_ADDRESS { + + USHORT Size; + CSHORT Type; + + LIST_ENTRY Linkage; // next address/this device object. + NIC_HANDLE NicHandle; // NicHandle corresponding to this address. + UCHAR NetbiosName[16]; +} ADAPTER_ADDRESS, *PADAPTER_ADDRESS; +#endif _PNP_POWER + +// +// This defines the types of probe packets we can send. +// + +typedef enum _NB_ACK_TYPE { + NbiAckQuery, + NbiAckResponse, + NbiAckResend +} NB_ACK_TYPE, *PNB_ACK_TYPE; + + +// +// This defines the a packetizing location in a +// send. +// + +typedef struct _SEND_POINTER { + ULONG MessageOffset; // up count, bytes sent this message. + PREQUEST Request; // current send request in chain. + PNDIS_BUFFER Buffer; // current buffer in send chain. + ULONG BufferOffset; // current byte offset in current buffer. + USHORT SendSequence; +} SEND_POINTER, *PSEND_POINTER; + +// +// This defines the current location in a receive. +// + +typedef struct _RECEIVE_POINTER { + ULONG MessageOffset; // up count, bytes received this message. + ULONG Offset; // up count, bytes received this request. + PNDIS_BUFFER Buffer; // current buffer in receive request. + ULONG BufferOffset; // current byte offset in current buffer. +} RECEIVE_POINTER, *PRECEIVE_POINTER; + + +// +// This structure defines a connection, which controls a +// session with a remote. +// + +#define CREF_VERIFY 0 +#define CREF_LISTEN 1 +#define CREF_CONNECT 2 +#define CREF_WAIT_CACHE 3 +#define CREF_TIMER 4 +#define CREF_INDICATE 5 +#define CREF_ACTIVE 6 +#define CREF_FRAME 7 +#define CREF_BY_CONTEXT 8 +#define CREF_W_ACCEPT 9 +#define CREF_SEND 10 +#define CREF_RECEIVE 11 +#define CREF_PACKETIZE 12 +#define CREF_DISASSOC 13 +#define CREF_W_PACKET 14 +#define CREF_CANCEL 15 +#define CREF_NDIS_SEND 16 +#define CREF_SHORT_D_ACK 17 +#define CREF_LONG_D_ACK 18 +#define CREF_FIND_ROUTE 19 +#define CREF_ACCEPT 20 + +#define CREF_TOTAL 24 + +typedef struct _CONNECTION { + +#if DBG + ULONG RefTypes[CREF_TOTAL]; +#endif + + CSHORT Type; + USHORT Size; + + NB_LOCK Lock; + PNB_LOCK DeviceLock; + + ULONG ReferenceCount; // number of references to this object. + + CONNECTION_CONTEXT Context; // client-specified value. + + ULONG State; + ULONG SubState; + ULONG ReceiveState; // SubState tracks sends when active. + ULONG NewNetbios; // 1 if we negotiated this. + + REQUEST_LIST_HEAD SendQueue; + REQUEST_LIST_HEAD ReceiveQueue; + + USHORT ReceiveSequence; + + USHORT LocalRcvSequenceMax; // we advertise to him (will be near SendSequence) + USHORT RemoteRcvSequenceMax; // he advertises to us (will be near ReceiveSequence) + USHORT SendWindowSequenceLimit; // when this send window ends (may send past it however) + + // + // RemoteRcvSequenceMax is the largest frame number that he expects to + // receive, while SendWindowSequenceLimit is one more than the max + // we can send. I.e. if he is advertising a window of 4 and we think + // the window should be 2, and the current send sequence is 7, + // RemoteRcvSequenceMax is 10 and SendWindowSequenceLimit is 9. + // + + USHORT ReceiveWindowSize; // when it is open, how big to make it + USHORT SendWindowSize; // what we'll send, may be less than what he advertises + USHORT MaxSendWindowSize; // maximum we allow it to grow to + + USHORT IncreaseWindowFailures; // how many windows after increase have had retransmits + BOOLEAN RetransmitThisWindow; // we had to retransmit in this send window + BOOLEAN SendWindowIncrease; // send window was just increased. + BOOLEAN ResponseTimeout; // we hit timeout in SEND_W or REMOTE_W + + BOOLEAN SendBufferInUse; // current send's already queued on packet + + ULONG Retries; + + // + // Tracks the current send. + // + + SEND_POINTER CurrentSend; + + // + // Tracks the unacked point in the send. + // + + SEND_POINTER UnAckedSend; + + PREQUEST FirstMessageRequest; // first one in the message. + PREQUEST LastMessageRequest; // last one in the message. + + ULONG CurrentMessageLength; // total length of current message. + + // + // Tracks the current receive. + // + + RECEIVE_POINTER CurrentReceive; // where to receive next data + RECEIVE_POINTER PreviousReceive; // stores it while transfer in progress + + PREQUEST ReceiveRequest; // current one; not in ReceiveQueue + ULONG ReceiveLength; // length of ReceiveRequest + + ULONG ReceiveUnaccepted; // by client...only indicate when == 0 + + ULONG CurrentIndicateOffset; // if previous frame was partially accepted. + + IPX_LINE_INFO LineInfo; // for the adapter this connect is on. + ULONG MaximumPacketSize; // as negotiated during session init/ack + + // + // Links us in the non-inactive connection hash bucket. + // + + struct _CONNECTION * NextConnection; + + // + // These are used to determine when to piggyback and when not to. + // + + BOOLEAN NoPiggybackHeuristic; // we have reason to assume it would be bad. + BOOLEAN PiggybackAckTimeout; // we got a timeout last time we tried. + ULONG ReceivesWithoutAck; // used to do an auto ack. + + // + // The following field is used as linkage in the device's + // PacketizeConnections queue. + // + + LIST_ENTRY PacketizeLinkage; + + // + // The following field is used as linkage in the device's + // WaitPacketConnections queue. + // + + LIST_ENTRY WaitPacketLinkage; + + // + // The following field is used as linkage in the device's + // DataAckConnections queue. + // + + LIST_ENTRY DataAckLinkage; + + // + // TRUE if we are on these queues. + // + + BOOLEAN OnPacketizeQueue; + BOOLEAN OnWaitPacketQueue; + BOOLEAN OnDataAckQueue; + + // + // TRUE if we have a piggyback ack pending. + // + + BOOLEAN DataAckPending; + + // + // TRUE if the current receive does not allow piggyback acks. + // + + BOOLEAN CurrentReceiveNoPiggyback; + + // + // Number of short timer expirations with the data ack queued. + // + + ULONG DataAckTimeouts; + + // + // Used to queue sends so that no two are outstanding at once. + // + + ULONG NdisSendsInProgress; + LIST_ENTRY NdisSendQueue; + + // + // This pointer is valid when NdisSendsInProgress is non-zero; + // it holds a pointer to a location on the stack of the thread + // which is inside NbiAssignSequenceAndSend. If this location + // is set to TRUE, it means the connection was stopped by another + // thread and a reference was added to keep the connection around. + // + + PBOOLEAN NdisSendReference; + + // + // These are used for timeouts. + // + + ULONG BaseRetransmitTimeout; // config # of short timeouts we wait. + ULONG CurrentRetransmitTimeout; // possibly backed-off number + ULONG WatchdogTimeout; // how many long timeouts we wait. + ULONG Retransmit; // timer; based on Device->ShortAbsoluteTime + ULONG Watchdog; // timer; based on Device->LongAbsoluteTime + USHORT TickCount; // 18.21/second, # for 576-byte packet. + USHORT HopCount; // As returned by ipx on find route. + BOOLEAN OnShortList; // are we inserted in the list + BOOLEAN OnLongList; // are we inserted in the list + LIST_ENTRY ShortList; // queues us on Device->ShortList + LIST_ENTRY LongList; // queues us on Device->LongList + + // + // These are valid when we have a connection established; + // + + USHORT LocalConnectionId; + USHORT RemoteConnectionId; + + PREQUEST DisassociatePending; // guarded by device lock. + PREQUEST ClosePending; + + PREQUEST ConnectRequest; + PREQUEST ListenRequest; + PREQUEST AcceptRequest; + PREQUEST DisconnectRequest; + PREQUEST DisconnectWaitRequest; + + ULONG CanBeDestroyed; // FALSE if reference is non-zero + ULONG ThreadsInHandleConnectionZero; // # of threads in HandleConnectionZero + + // + // These are used to hold extra data that was sent on a session + // init, for use in sending the ack. Generally will be NULL and 0. + // + + PUCHAR SessionInitAckData; + ULONG SessionInitAckDataLength; + + IPX_LOCAL_TARGET LocalTarget; // for the remote when active. + IPX_HEADER RemoteHeader; + + CTETimer Timer; + + PADDRESS_FILE AddressFile; // guarded by device lock if associated. + LIST_ENTRY AddressFileLinkage; // guarded by device lock + ULONG AddressFileLinked; // TRUE if queued using AddressFileLinkage + + PDEVICE Device; +#ifdef ISN_NT + PFILE_OBJECT FileObject; // easy backlink to file object. +#endif + + CHAR RemoteName[16]; // for an active connection. + + IPX_FIND_ROUTE_REQUEST FindRouteRequest; // use this to verify route. + + TDI_CONNECTION_INFO ConnectionInfo; // can be queried from above. + + BOOLEAN FindRouteInProgress; // we have a request pending. + + BOOLEAN SendPacketInUse; // put this here to align packet/header. + BOOLEAN IgnoreNextDosProbe; + + NTSTATUS Status; // status code for connection rundown. + +#ifdef RSRC_TIMEOUT_DBG + LARGE_INTEGER FirstMessageRequestTime; +#endif //RSRC_TIMEOUT_DBG + + NDIS_HANDLE SendPacketPoolHandle; // poolhandle for sendpacket below when + // the packet is allocated from ndis pool. + + NB_SEND_PACKET SendPacket; // try to use this first for sends + + ULONG Flags; // miscellaneous connection flags + + UCHAR SendPacketHeader[1]; // connection is extended to include this + + // + // NOTE: This is variable length structure! + // Do not add fields below this comment. + // +} CONNECTION, *PCONNECTION; + + +#define CONNECTION_STATE_INACTIVE 1 +#define CONNECTION_STATE_CONNECTING 2 +#define CONNECTION_STATE_LISTENING 3 +#define CONNECTION_STATE_ACTIVE 4 +#define CONNECTION_STATE_DISCONNECT 5 +#define CONNECTION_STATE_CLOSING 6 + + +#define CONNECTION_SUBSTATE_L_WAITING 1 // queued by a listen +#define CONNECTION_SUBSTATE_L_W_ACCEPT 2 // waiting for user to accept +#define CONNECTION_SUBSTATE_L_W_ROUTE 3 // waiting for rip response + +#define CONNECTION_SUBSTATE_C_FIND_NAME 1 // waiting for cache response +#define CONNECTION_SUBSTATE_C_W_ACK 2 // waiting for session init ack +#define CONNECTION_SUBSTATE_C_W_ROUTE 3 // waiting for rip response +#define CONNECTION_SUBSTATE_C_DISCONN 4 // disconnect was issued + +#define CONNECTION_SUBSTATE_A_IDLE 1 // no sends in progress +#define CONNECTION_SUBSTATE_A_PACKETIZE 2 // packetizing a send +#define CONNECTION_SUBSTATE_A_W_ACK 3 // waiting for an ack +#define CONNECTION_SUBSTATE_A_W_PACKET 4 // waiting for a packet +#define CONNECTION_SUBSTATE_A_W_EOR 5 // waiting for eor to start packetizing +#define CONNECTION_SUBSTATE_A_W_PROBE 6 // waiting for a keep-alive response +#define CONNECTION_SUBSTATE_A_REMOTE_W 7 // remote shut down our window + +#define CONNECTION_RECEIVE_IDLE 1 // no receives queued +#define CONNECTION_RECEIVE_ACTIVE 2 // receive is queued +#define CONNECTION_RECEIVE_W_RCV 3 // waiting for receive to be posted +#define CONNECTION_RECEIVE_INDICATE 4 // indication in progress +#define CONNECTION_RECEIVE_TRANSFER 5 // transfer is in progress +#define CONNECTION_RECEIVE_PENDING 6 // last request is queued for completion + +#define CONNECTION_SUBSTATE_D_W_ACK 1 +#define CONNECTION_SUBSTATE_D_GOT_ACK 2 + +// +// Bit values for Flags field in +// the CONNECTION structure. +// +#define CONNECTION_FLAGS_AUTOCONNECTING 0x00000001 // RAS autodial in progress +#define CONNECTION_FLAGS_AUTOCONNECTED 0x00000002 // RAS autodial connected + +#ifdef RSRC_TIMEOUT_DBG +extern ULONG NbiGlobalDebugResTimeout; +extern LARGE_INTEGER NbiGlobalMaxResTimeout; +extern NB_SEND_PACKET NbiGlobalDeathPacket; // try to use this first for sends +#endif //RSRC_TIMEOUT_DBG diff --git a/private/ntos/tdi/isnp/nb/nwlnknb.ini b/private/ntos/tdi/isnp/nb/nwlnknb.ini new file mode 100644 index 000000000..e2df88f54 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nwlnknb.ini @@ -0,0 +1,43 @@ +\Registry\Machine\System\CurrentControlSet\Services\NwlnkNb + Type = REG_DWORD 0x00000001 + Start = REG_DWORD 0x00000003 + ErrorControl = REG_DWORD 0x00000001 + ImagePath = REG_EXPAND_SZ \SystemRoot\System32\drivers\nwlnknb.sys + DisplayName = NWLINK2 IPX Netbios Protocol + Group = TDI + DependOnService = REG_MULTI_SZ "NwlnkIpx" + DependOnGroup = REG_MULTI_SZ "NDIS" + Linkage + Bind = REG_MULTI_SZ "\Device\NwlnkIpx" + Export = REG_MULTI_SZ "\Device\NwlnkNb" + Route = REG_MULTI_SZ ""NwlnkIpx"" + Disabled + Bind = REG_MULTI_SZ + Export = REG_MULTI_SZ + Route = REG_MULTI_SZ + Parameters + AckDelayTime = REG_DWORD 0x000000fa + AckWindow = REG_DWORD 0x00000002 + AckWindowThreshold = REG_DWORD 0x000001f4 + EnablePiggyBackAck = REG_DWORD 0x00000001 + Extensions = REG_DWORD 0x00000001 + RcvWindowMax = REG_DWORD 0x00000004 + BroadcastCount = REG_DWORD 0x00000003 + BroadcastTimeout = REG_DWORD 0x00000001 + ConnectionCount = REG_DWORD 0x00000005 + ConnectionTimeout = REG_DWORD 0x00000002 + InitPackets= REG_DWORD 0x00000005 + MaxPackets = REG_DWORD 0x0000001e + InitialRetransmissionTime = REG_DWORD 0x000001f4 + Internet = REG_DWORD 0x00000001 + KeepAliveCount = REG_DWORD 0x00000008 + KeepAliveTimeout = REG_DWORD 0x0000003c + RetransmitMax = REG_DWORD 0x00000008 + Performance + Library = Perfctrs.dll + Open = OpenNbfPerformanceData + Collect = CollectNbfPerformanceData + Close = CloseNbfPerformanceData +\Registry\Machine\System\CurrentControlSet\Services\EventLog\System\NwlnkNb + EventMessageFile = REG_EXPAND_SZ %SystemRoot%\System32\netevent.dll + TypesSupported = REG_DWORD 0x00000007 diff --git a/private/ntos/tdi/isnp/nb/nwlnknb.rc b/private/ntos/tdi/isnp/nb/nwlnknb.rc new file mode 100644 index 000000000..1b0163cf3 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/nwlnknb.rc @@ -0,0 +1,12 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NWLINK2 IPX Netbios Protocol Driver" +#define VER_INTERNALNAME_STR "nwlnknb.sys" +#define VER_ORIGINALFILENAME_STR "nwlnknb.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/isnp/nb/packet.c b/private/ntos/tdi/isnp/nb/packet.c new file mode 100644 index 000000000..7e22534a8 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/packet.c @@ -0,0 +1,1482 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + packet.c + +Abstract: + + This module contains code that implements the SEND_PACKET and + RECEIVE_PACKET objects, which describe NDIS packets used + by the transport. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// Local Function Protos +// +#if defined(_PNP_POWER) +#if !defined(DBG) +__inline +#endif +VOID +NbiFreeReceiveBufferPool ( + IN PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool + ); +#endif _PNP_POWER + + +NTSTATUS +NbiInitializeSendPacket( + IN PDEVICE Device, + IN NDIS_HANDLE PoolHandle OPTIONAL, + IN PNB_SEND_PACKET Packet, + IN PUCHAR Header, + IN ULONG HeaderLength + ) + +/*++ + +Routine Description: + + This routine initializes a send packet by chaining the + buffer for the header on it. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD, + AND RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + + PoolHandle - Ndis packet pool handle if !NB_OWN_PACKETS + + Packet - The packet to initialize. + + Header - Points to storage for the header. + + HeaderLength - The length of the header. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + NTSTATUS Status; + PNDIS_BUFFER NdisBuffer; + PNDIS_BUFFER NdisNbBuffer; + PNB_SEND_RESERVED Reserved; + ULONG MacHeaderNeeded = NbiDevice->Bind.MacHeaderNeeded; + + NbiAllocateSendPacket (Device, PoolHandle, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } +// DbgPrint("NbiInitializeSendPacket: PACKET is (%x)\n", PACKET(Packet)); + + // + // allocate the mac header. + // + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + Device->NdisBufferPoolHandle, + Header, + MacHeaderNeeded); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbiFreeSendPacket (Device, Packet); + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + NdisChainBufferAtFront (PACKET(Packet), NdisBuffer); + +// DbgPrint("NbiInitializeSendPacket: MAC header address is (%x)\n", NdisBuffer); + // + // Allocate the nb header + // + NdisAllocateBuffer( + &NdisStatus, + &NdisNbBuffer, + Device->NdisBufferPoolHandle, + Header + MacHeaderNeeded, + HeaderLength - MacHeaderNeeded); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NdisUnchainBufferAtFront (PACKET(Packet), &NdisBuffer); + CTEAssert (NdisBuffer); + + NdisAdjustBufferLength (NdisBuffer, MacHeaderNeeded); + NdisFreeBuffer (NdisBuffer); + NbiFreeSendPacket (Device, Packet); + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + // DbgPrint("NbiInitializeSendPacket: IPX header address is (%x)\n", NdisNbBuffer); + NdisChainBufferAtBack (PACKET(Packet), NdisNbBuffer); + + Reserved = SEND_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_NB; + Reserved->SendInProgress = FALSE; + Reserved->OwnedByConnection = FALSE; + Reserved->Header = Header; + Reserved->HeaderBuffer = NdisBuffer; + + Reserved->Reserved[0] = NULL; + Reserved->Reserved[1] = NULL; + + InsertHeadList( + &Device->GlobalSendPacketList, + &Reserved->GlobalLinkage); + + return STATUS_SUCCESS; + +} /* NbiInitializeSendPacket */ + + +NTSTATUS +NbiInitializeReceivePacket( + IN PDEVICE Device, + IN NDIS_HANDLE PoolHandle OPTIONAL, + IN PNB_RECEIVE_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine initializes a receive packet. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD, + AND RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + + PoolHandle - Ndis packet pool handle if !NB_OWN_PACKETS + + Packet - The packet to initialize. + +Return Value: + + None. + +--*/ + +{ + + NTSTATUS Status; + PNB_RECEIVE_RESERVED Reserved; + + NbiAllocateReceivePacket (Device, PoolHandle, Packet, &Status); + + if (Status != STATUS_SUCCESS) { + // ERROR LOG + return Status; + } + + Reserved = RECEIVE_RESERVED(Packet); + Reserved->Identifier = IDENTIFIER_NB; + Reserved->TransferInProgress = FALSE; + + InsertHeadList( + &Device->GlobalReceivePacketList, + &Reserved->GlobalLinkage); + + return STATUS_SUCCESS; + +} /* NbiInitializeReceivePacket */ + + +NTSTATUS +NbiInitializeReceiveBuffer( + IN PDEVICE Device, + IN PNB_RECEIVE_BUFFER ReceiveBuffer, + IN PUCHAR DataBuffer, + IN ULONG DataBufferLength + ) + +/*++ + +Routine Description: + + This routine initializes a receive buffer by allocating + an NDIS_BUFFER to describe the data buffer. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD, + AND RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + + ReceiveBuffer - The receive buffer to initialize. + + DataBuffer - The data buffer. + + DataBufferLength - The length of the data buffer. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + PNDIS_BUFFER NdisBuffer; + + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + Device->NdisBufferPoolHandle, + DataBuffer, + DataBufferLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + // ERROR LOG + return STATUS_INSUFFICIENT_RESOURCES; + } + + ReceiveBuffer->NdisBuffer = NdisBuffer; + ReceiveBuffer->Data = DataBuffer; + ReceiveBuffer->DataLength = 0; + + InsertHeadList( + &Device->GlobalReceiveBufferList, + &ReceiveBuffer->GlobalLinkage); + + return STATUS_SUCCESS; + +} /* NbiInitializeReceiveBuffer */ + + + +VOID +NbiDeinitializeSendPacket( + IN PDEVICE Device, + IN PNB_SEND_PACKET Packet, + IN ULONG HeaderLength + ) + +/*++ + +Routine Description: + + This routine deinitializes a send packet. + +Arguments: + + Device - The device. + + Packet - The packet to deinitialize. + + HeaderLength - The length of the first buffer on the packet. + +Return Value: + + None. + +--*/ + +{ + + PNDIS_BUFFER NdisBuffer; + PNB_SEND_RESERVED Reserved; + CTELockHandle LockHandle; + ULONG MacHeaderNeeded = NbiDevice->Bind.MacHeaderNeeded; + + CTEAssert(HeaderLength > MacHeaderNeeded); + Reserved = SEND_RESERVED(Packet); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // Free the mac header + // + // DbgPrint("NbiDeinitializeSendPacket: PACKET is (%x)\n", PACKET(Packet)); + NdisUnchainBufferAtFront (PACKET(Packet), &NdisBuffer); + CTEAssert (NdisBuffer); + // DbgPrint("NbiDeinitializeSendPacket: MAC header address is (%x)\n", NdisBuffer); + + NdisAdjustBufferLength (NdisBuffer, MacHeaderNeeded); + NdisFreeBuffer (NdisBuffer); + + // + // Free the nb header + // + NdisUnchainBufferAtFront (PACKET(Packet), &NdisBuffer); + // DbgPrint("NbiDeinitializeSendPacket: IPX header address is (%x)\n", NdisBuffer); + CTEAssert (NdisBuffer); + + NdisAdjustBufferLength (NdisBuffer, HeaderLength - MacHeaderNeeded); + NdisFreeBuffer (NdisBuffer); + + // + // free the packet + // + NbiFreeSendPacket (Device, Packet); + +} /* NbiDeinitializeSendPacket */ + + +VOID +NbiDeinitializeReceivePacket( + IN PDEVICE Device, + IN PNB_RECEIVE_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine initializes a receive packet. + +Arguments: + + Device - The device. + + Packet - The packet to initialize. + +Return Value: + + None. + +--*/ + +{ + + PNB_RECEIVE_RESERVED Reserved; + CTELockHandle LockHandle; + + Reserved = RECEIVE_RESERVED(Packet); + + NB_GET_LOCK (&Device->Lock, &LockHandle); + RemoveEntryList (&Reserved->GlobalLinkage); + NB_FREE_LOCK (&Device->Lock, LockHandle); + + NbiFreeReceivePacket (Device, Packet); + +} /* NbiDeinitializeReceivePacket */ + + + +VOID +NbiDeinitializeReceiveBuffer( + IN PDEVICE Device, + IN PNB_RECEIVE_BUFFER ReceiveBuffer + ) + +/*++ + +Routine Description: + + This routine deinitializes a receive buffer. + +Arguments: + + Device - The device. + + ReceiveBuffer - The receive buffer. + +Return Value: + + None. + + THIS ROUTINE SHOULD BE CALLED WITH THE DEVICE LOCK HELD. If this + routine also called from the DestroyDevice routine, it is not + necessary to call this with the lock. + +--*/ + +{ +#if defined(_PNP_POWER) + RemoveEntryList (&ReceiveBuffer->GlobalLinkage); +#else + CTELockHandle LockHandle; + + NB_GET_LOCK (&Device->Lock, &LockHandle); + RemoveEntryList (&ReceiveBuffer->GlobalLinkage); + NB_FREE_LOCK (&Device->Lock, LockHandle); +#endif _PNP_POWER + + NdisFreeBuffer (ReceiveBuffer->NdisBuffer); + +} /* NbiDeinitializeReceiveBuffer */ + + + +VOID +NbiAllocateSendPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 10 packets to the pool for this device. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PNB_SEND_POOL SendPool; + UINT SendPoolSize; + UINT PacketNum; + PNB_SEND_PACKET Packet; + PNB_SEND_RESERVED Reserved; + PUCHAR Header; + ULONG HeaderLength; + NTSTATUS Status; + + HeaderLength = Device->Bind.MacHeaderNeeded + sizeof(NB_CONNECTIONLESS); + SendPoolSize = FIELD_OFFSET (NB_SEND_POOL, Packets[0]) + + (sizeof(NB_SEND_PACKET) * Device->InitPackets) + + (HeaderLength * Device->InitPackets); + + SendPool = (PNB_SEND_POOL)NbiAllocateMemory (SendPoolSize, MEMORY_PACKET, "SendPool"); + if (SendPool == NULL) { + NB_DEBUG (PACKET, ("Could not allocate send pool memory\n")); + return; + } + + RtlZeroMemory (SendPool, SendPoolSize); + + +#if !defined(NB_OWN_PACKETS) + // + // Now allocate the ndis packet pool + // + NdisAllocatePacketPool( &Status, &SendPool->PoolHandle, Device->InitPackets, sizeof(NB_SEND_RESERVED)); + if (!NT_SUCCESS(Status)){ + NB_DEBUG (PACKET, ("Could not allocate Ndis Packet Pool memory\n")); + NbiFreeMemory( SendPool, SendPoolSize, MEMORY_PACKET, "Send Pool Freed"); + return; + } +#endif + + NB_DEBUG2 (PACKET, ("Initializing send pool %lx, %d packets, header %d\n", + SendPool, Device->InitPackets, HeaderLength)); + + Header = (PUCHAR)(&SendPool->Packets[Device->InitPackets]); + + for (PacketNum = 0; PacketNum < Device->InitPackets; PacketNum++) { + + Packet = &SendPool->Packets[PacketNum]; + + if (NbiInitializeSendPacket ( + Device, +#ifdef NB_OWN_PACKETS + NULL, +#else + SendPool->PoolHandle, +#endif + Packet, + Header, + HeaderLength) != STATUS_SUCCESS) { + NB_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = SEND_RESERVED(Packet); + Reserved->u.SR_NF.Address = NULL; +#ifdef NB_TRACK_POOL + Reserved->Pool = SendPool; +#endif + + Header += HeaderLength; + + } + + SendPool->PacketCount = PacketNum; + SendPool->PacketFree = PacketNum; + + for (PacketNum = 0; PacketNum < SendPool->PacketCount; PacketNum++) { + + Packet = &SendPool->Packets[PacketNum]; + Reserved = SEND_RESERVED(Packet); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + } + + InsertTailList (&Device->SendPoolList, &SendPool->Linkage); + + Device->AllocatedSendPackets += SendPool->PacketCount; + +} /* NbiAllocateSendPool */ + + +VOID +NbiAllocateReceivePool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds 5 receive packets to the pool for this device. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PNB_RECEIVE_POOL ReceivePool; + UINT ReceivePoolSize; + UINT PacketNum; + PNB_RECEIVE_PACKET Packet; + PNB_RECEIVE_RESERVED Reserved; + NTSTATUS Status; + + ReceivePoolSize = FIELD_OFFSET (NB_RECEIVE_POOL, Packets[0]) + + (sizeof(NB_RECEIVE_PACKET) * Device->InitPackets); + + ReceivePool = (PNB_RECEIVE_POOL)NbiAllocateMemory (ReceivePoolSize, MEMORY_PACKET, "ReceivePool"); + if (ReceivePool == NULL) { + NB_DEBUG (PACKET, ("Could not allocate receive pool memory\n")); + return; + } + + RtlZeroMemory (ReceivePool, ReceivePoolSize); + +#if !defined(NB_OWN_PACKETS) + // + // Now allocate the ndis packet pool + // + NdisAllocatePacketPool( &Status, &ReceivePool->PoolHandle, Device->InitPackets, sizeof(NB_RECEIVE_RESERVED)); + if (!NT_SUCCESS(Status)){ + NB_DEBUG (PACKET, ("Could not allocate Ndis Packet Pool memory\n")); + NbiFreeMemory( ReceivePool, ReceivePoolSize, MEMORY_PACKET, "Receive Pool Freed"); + return; + } +#endif NB_OWN_PACKETS + + NB_DEBUG2 (PACKET, ("Initializing receive pool %lx, %d packets\n", + ReceivePool, Device->InitPackets)); + + for (PacketNum = 0; PacketNum < Device->InitPackets; PacketNum++) { + + Packet = &ReceivePool->Packets[PacketNum]; + + if (NbiInitializeReceivePacket ( + Device, +#ifdef NB_OWN_PACKETS + NULL, +#else + ReceivePool->PoolHandle, +#endif + Packet) != STATUS_SUCCESS) { + NB_DEBUG (PACKET, ("Could not initialize packet %lx\n", Packet)); + break; + } + + Reserved = RECEIVE_RESERVED(Packet); +#ifdef NB_TRACK_POOL + Reserved->Pool = ReceivePool; +#endif + + } + + ReceivePool->PacketCount = PacketNum; + ReceivePool->PacketFree = PacketNum; + + for (PacketNum = 0; PacketNum < ReceivePool->PacketCount; PacketNum++) { + + Packet = &ReceivePool->Packets[PacketNum]; + Reserved = RECEIVE_RESERVED(Packet); + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); +// PushEntryList (&Device->ReceivePacketList, &Reserved->PoolLinkage); + + } + + InsertTailList (&Device->ReceivePoolList, &ReceivePool->Linkage); + + Device->AllocatedReceivePackets += ReceivePool->PacketCount; + +} /* NbiAllocateReceivePool */ + + +#if defined(_PNP_POWER) + +VOID +NbiAllocateReceiveBufferPool( + IN PDEVICE Device, + IN UINT DataLength + ) + +/*++ + +Routine Description: + + This routine adds receive buffers to the pool for this device. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + + DataLength - Max length of the data in each buffer. + +Return Value: + + None. + +--*/ + +{ + PNB_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize; + UINT BufferNum; + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PUCHAR Data; + + + ReceiveBufferPoolSize = FIELD_OFFSET (NB_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(NB_RECEIVE_BUFFER) * Device->InitPackets) + + (DataLength * Device->InitPackets); + + ReceiveBufferPool = (PNB_RECEIVE_BUFFER_POOL)NbiAllocateMemory (ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + if (ReceiveBufferPool == NULL) { + NB_DEBUG (PACKET, ("Could not allocate receive buffer pool memory\n")); + return; + } + + RtlZeroMemory (ReceiveBufferPool, ReceiveBufferPoolSize); + + NB_DEBUG2 (PACKET, ("Initializing receive buffer pool %lx, %d buffers, data %d\n", + ReceiveBufferPool, Device->InitPackets, DataLength)); + + Data = (PUCHAR)(&ReceiveBufferPool->Buffers[Device->InitPackets]); + + for (BufferNum = 0; BufferNum < Device->InitPackets; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + + if (NbiInitializeReceiveBuffer (Device, ReceiveBuffer, Data, DataLength) != STATUS_SUCCESS) { + NB_DEBUG (PACKET, ("Could not initialize buffer %lx\n", ReceiveBuffer)); + break; + } + + ReceiveBuffer->Pool = ReceiveBufferPool; + + Data += DataLength; + + } + + ReceiveBufferPool->BufferCount = BufferNum; + ReceiveBufferPool->BufferFree = BufferNum; + ReceiveBufferPool->BufferDataSize = DataLength; + + for (BufferNum = 0; BufferNum < ReceiveBufferPool->BufferCount; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + PushEntryList (&Device->ReceiveBufferList, &ReceiveBuffer->PoolLinkage); + + } + + InsertTailList (&Device->ReceiveBufferPoolList, &ReceiveBufferPool->Linkage); + + Device->AllocatedReceiveBuffers += ReceiveBufferPool->BufferCount; + Device->CurMaxReceiveBufferSize = DataLength; + +} /* NbiAllocateReceiveBufferPool */ +#else + +VOID +NbiAllocateReceiveBufferPool( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine adds receive buffers to the pool for this device. + + NOTE: THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND + RETURNS WITH IT HELD. + +Arguments: + + Device - The device. + +Return Value: + + None. + +--*/ + +{ + PNB_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize; + UINT BufferNum; + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + UINT DataLength; + PUCHAR Data; + + DataLength = Device->Bind.LineInfo.MaximumPacketSize; + + ReceiveBufferPoolSize = FIELD_OFFSET (NB_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(NB_RECEIVE_BUFFER) * Device->InitPackets) + + (DataLength * Device->InitPackets); + + ReceiveBufferPool = (PNB_RECEIVE_BUFFER_POOL)NbiAllocateMemory (ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + if (ReceiveBufferPool == NULL) { + NB_DEBUG (PACKET, ("Could not allocate receive buffer pool memory\n")); + return; + } + + RtlZeroMemory (ReceiveBufferPool, ReceiveBufferPoolSize); + + NB_DEBUG2 (PACKET, ("Initializing receive buffer pool %lx, %d buffers, data %d\n", + ReceiveBufferPool, Device->InitPackets, DataLength)); + + Data = (PUCHAR)(&ReceiveBufferPool->Buffers[Device->InitPackets]); + + for (BufferNum = 0; BufferNum < Device->InitPackets; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + + if (NbiInitializeReceiveBuffer (Device, ReceiveBuffer, Data, DataLength) != STATUS_SUCCESS) { + NB_DEBUG (PACKET, ("Could not initialize buffer %lx\n", ReceiveBuffer)); + break; + } + +#ifdef NB_TRACK_POOL + ReceiveBuffer->Pool = ReceiveBufferPool; +#endif + + Data += DataLength; + + } + + ReceiveBufferPool->BufferCount = BufferNum; + ReceiveBufferPool->BufferFree = BufferNum; + + for (BufferNum = 0; BufferNum < ReceiveBufferPool->BufferCount; BufferNum++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[BufferNum]; + PushEntryList (&Device->ReceiveBufferList, &ReceiveBuffer->PoolLinkage); + + } + + InsertTailList (&Device->ReceiveBufferPoolList, &ReceiveBufferPool->Linkage); + + Device->AllocatedReceiveBuffers += ReceiveBufferPool->BufferCount; + +} /* NbiAllocateReceiveBufferPool */ +#endif _PNP_POWER + +#if defined(_PNP_POWER) + +VOID +NbiReAllocateReceiveBufferPool( + IN PWORK_QUEUE_ITEM WorkItem + ) + +/*++ + +Routine Description: + + This routines destroys all the existing Buffer Pools and creates + new one using the larger packet size given to us by IPX because + a new card was inserted with a larger packet size. + +Arguments: + + WorkItem - The work item that was allocated for this. + +Return Value: + + None. + +--*/ +{ + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; + + NB_GET_LOCK ( &Device->Lock, &LockHandle ); + + if ( Device->Bind.LineInfo.MaximumPacketSize > Device->CurMaxReceiveBufferSize ) { + +#if DBG + DbgPrint("Reallocating new pools due to new maxpacketsize\n"); +#endif + NbiDestroyReceiveBufferPools( Device ); + NbiAllocateReceiveBufferPool( Device, Device->Bind.LineInfo.MaximumPacketSize ); + + } + + NB_FREE_LOCK( &Device->Lock, LockHandle ); + + NbiFreeMemory( WorkItem, sizeof(WORK_QUEUE_ITEM), MEMORY_WORK_ITEM, "Alloc Rcv Buff Work Item freed"); +} + +#if !defined(DBG) +__inline +#endif +VOID +NbiFreeReceiveBufferPool ( + IN PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool + ) + +/*++ + +Routine Description: + + This routine frees the +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ +{ + PDEVICE Device = NbiDevice; + PNB_RECEIVE_BUFFER ReceiveBuffer; + UINT ReceiveBufferPoolSize,i; + + CTEAssert( ReceiveBufferPool->BufferDataSize ); + + ReceiveBufferPoolSize = FIELD_OFFSET (NB_RECEIVE_BUFFER_POOL, Buffers[0]) + + (sizeof(NB_RECEIVE_BUFFER) * Device->InitPackets) + + (ReceiveBufferPool->BufferDataSize * Device->InitPackets); + + // + // Check if we can free this pool + // + CTEAssert(ReceiveBufferPool->BufferCount == ReceiveBufferPool->BufferFree ); + + for (i = 0; i < ReceiveBufferPool->BufferCount; i++) { + + ReceiveBuffer = &ReceiveBufferPool->Buffers[i]; + NbiDeinitializeReceiveBuffer (Device, ReceiveBuffer); + + } + + RemoveEntryList( &ReceiveBufferPool->Linkage ); + + NB_DEBUG2 (PACKET, ("Free buffer pool %lx\n", ReceiveBufferPool)); + + NbiFreeMemory (ReceiveBufferPool, ReceiveBufferPoolSize, MEMORY_PACKET, "ReceiveBufferPool"); + +} + + +VOID +NbiDestroyReceiveBufferPools( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routines walks the ReceiveBufferPoolList and destroys the + pool which does not have any buffer in use. + +Arguments: + +Return Value: + + None. + + THIS ROUTINE COULD BE CALLED WITH THE DEVICE LOCK HELD. If this + routine is also called from the DestroyDevice routine, it is not + necessary to call this with the lock. + +--*/ +{ + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY Unused; + + + // + // Clean up this list before we call NbiFreeReceiveBufferPool bcoz that will + // simply destroy all the buffer which might be queue here on this list. + // At the end of this routine we must start with a fresh ReceiveBufferList. + // + do { + Unused = PopEntryList( &Device->ReceiveBufferList ); + } while( Unused ); + + // + // Now destroy each individual ReceiveBufferPool. + // + for ( p = Device->ReceiveBufferPoolList.Flink; + p != &Device->ReceiveBufferPoolList; + ) { + + + ReceiveBufferPool = CONTAINING_RECORD (p, NB_RECEIVE_BUFFER_POOL, Linkage); + p = p->Flink; + + // + // This will destroy and unlink this Pool if none of its buffer is + // in use currently. + // + + if ( ReceiveBufferPool->BufferCount == ReceiveBufferPool->BufferFree ) { + NbiFreeReceiveBufferPool( ReceiveBufferPool ); + } else { + // + // When the device is stopping we must succeed in freeing the pool. + CTEAssert( Device->State != DEVICE_STATE_STOPPING ); + } + + } + +} + + +VOID +NbiPushReceiveBuffer ( + IN PNB_RECEIVE_BUFFER ReceiveBuffer + ) + +/*++ + +Routine Description: + + This routine returns the receive buffer back to the free list. + It checks the size of this buffer. If it is smaller than the + the CurMaxReceiveBufferSize, then it does not return this back + to the free list, instead it destroys it and possibly also + destroys the pool associated with it. O/w it simply returns this + to the free list. + +Arguments: + + ReceiveBuffer - Pointer to the buffer to be returned to the free list. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool = (PNB_RECEIVE_BUFFER_POOL)ReceiveBuffer->Pool; + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; +#if defined(DBG) + ULONG BufLen = 0; +#endif + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + +#if defined(DBG) + NdisQueryBuffer( ReceiveBuffer->NdisBuffer, NULL, &BufLen ); + CTEAssert( BufLen == ReceiveBufferPool->BufferDataSize ); +#endif + + // + // This is an old buffer which was in use when we changed + // the CurMaxReceiveBufferSize due to new adapter. We must not + // return this buffer back to free list. Infact, if the pool + // associated with this buffer does not have any other buffers + // in use, we should free the pool also. + CTEAssert( ReceiveBufferPool->BufferFree < ReceiveBufferPool->BufferCount ); + ReceiveBufferPool->BufferFree++; + + if ( ReceiveBufferPool->BufferDataSize < Device->CurMaxReceiveBufferSize ) { + +#if DBG + DbgPrint("ReceiveBuffer %lx, not returned to pool %lx( Free %d)\n", ReceiveBuffer, ReceiveBufferPool, ReceiveBufferPool->BufferFree); +#endif + + + if ( ReceiveBufferPool->BufferFree == ReceiveBufferPool->BufferCount ) { + NbiFreeReceiveBufferPool( ReceiveBufferPool ); + } + } else { + + PushEntryList( &Device->ReceiveBufferList, &ReceiveBuffer->PoolLinkage ); + + + } + + NB_FREE_LOCK( &Device->Lock, LockHandle ); +} +#endif _PNP_POWER + + +PSINGLE_LIST_ENTRY +NbiPopSendPacket( + IN PDEVICE Device, + IN BOOLEAN LockAcquired + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + + LockAcquired - TRUE if Device->Lock is acquired. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + CTELockHandle LockHandle; + + s = ExInterlockedPopEntrySList( + &Device->SendPacketList, + &NbiGlobalPoolInterlock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (!LockAcquired) { + NB_GET_LOCK (&Device->Lock, &LockHandle); + } + + if (Device->AllocatedSendPackets < Device->MaxPackets) { + + // + // Allocate a pool and try again. + // + + + NbiAllocateSendPool (Device); + + + if (!LockAcquired) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + } + + s = ExInterlockedPopEntrySList( + &Device->SendPacketList, + &NbiGlobalPoolInterlock); + + return s; + } else { + + if (!LockAcquired) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + } + return NULL; + } + +} /* NbiPopSendPacket */ + + +VOID +NbiPushSendPacket( + IN PNB_SEND_RESERVED Reserved + ) + +/*++ + +Routine Description: + + This routine frees a packet back to the device context's pool. + If there are connections waiting for packets, it removes + one from the list and inserts it on the packetize queue. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PDEVICE Device = NbiDevice; + PLIST_ENTRY p; + PCONNECTION Connection; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + + ExInterlockedPushEntrySList( + &Device->SendPacketList, + &Reserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + // + // BUGBUG: Make this a function. Optimize for + // UP by not doing two checks? + // + + if (!IsListEmpty (&Device->WaitPacketConnections)) { + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + p = RemoveHeadList (&Device->WaitPacketConnections); + + // + // Take a connection off the WaitPacketQueue and put it + // on the PacketizeQueue. We don't worry about if the + // connection has stopped, that will get checked when + // the PacketizeQueue is run down. + // + // Since this is in send completion, we may not get + // a receive complete. We guard against this by calling + // NbiReceiveComplete from the long timer timeout. + // + + if (p != &Device->WaitPacketConnections) { + + Connection = CONTAINING_RECORD (p, CONNECTION, WaitPacketLinkage); + + CTEAssert (Connection->OnWaitPacketQueue); + Connection->OnWaitPacketQueue = FALSE; + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + + if (Connection->SubState == CONNECTION_SUBSTATE_A_W_PACKET) { + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NbiTransferReferenceConnection (Connection, CREF_W_PACKET, CREF_PACKETIZE); + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + } else { + + NbiDereferenceConnection (Connection, CREF_W_PACKET); + + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } + + } + +} /* NbiPushSendPacket */ + + +VOID +NbiCheckForWaitPacket( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine checks if a connection is on the wait packet + queue and if so takes it off and queues it to be packetized. + It is meant to be called when the connection's packet has + been freed. + +Arguments: + + Connection - The connection to check. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle1); + + if (Connection->OnWaitPacketQueue) { + + Connection->OnWaitPacketQueue = FALSE; + RemoveEntryList (&Connection->WaitPacketLinkage); + + if (Connection->SubState == CONNECTION_SUBSTATE_A_W_PACKET) { + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NbiTransferReferenceConnection (Connection, CREF_W_PACKET, CREF_PACKETIZE); + + InsertTailList( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage); + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle1); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NbiDereferenceConnection (Connection, CREF_W_PACKET); + + return; + } + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle1); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + +} /* NbiCheckForWaitPacket */ + + +PSINGLE_LIST_ENTRY +NbiPopReceivePacket( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool. + If there are no packets in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the packet to. + +Return Value: + + The pointer to the Linkage field in the allocated packet. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + CTELockHandle LockHandle; + + s = ExInterlockedPopEntrySList( + &Device->ReceivePacketList, + &NbiGlobalPoolInterlock); + + if (s != NULL) { + return s; + } + + // + // No packets in the pool, see if we can allocate more. + // + + if (Device->AllocatedReceivePackets < Device->MaxPackets) { + + // + // Allocate a pool and try again. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + NbiAllocateReceivePool (Device); + NB_FREE_LOCK (&Device->Lock, LockHandle); + s = ExInterlockedPopEntrySList( + &Device->ReceivePacketList, + &NbiGlobalPoolInterlock); + + + return s; + + } else { + + return NULL; + + } + +} /* NbiPopReceivePacket */ + + +PSINGLE_LIST_ENTRY +NbiPopReceiveBuffer( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine allocates a receive buffer from the device context's pool. + If there are no buffers in the pool, it allocates one up to + the configured limit. + +Arguments: + + Device - Pointer to our device to charge the buffer to. + +Return Value: + + The pointer to the Linkage field in the allocated receive buffer. + +--*/ + +{ +#if defined(_PNP_POWER) + PSINGLE_LIST_ENTRY s; + PNB_RECEIVE_BUFFER ReceiveBuffer; + PNB_RECEIVE_BUFFER_POOL ReceiveBufferPool; + CTELockHandle LockHandle; + + NB_GET_LOCK( &Device->Lock, &LockHandle ); + + s = PopEntryList( &Device->ReceiveBufferList ); + + + if ( !s ) { + + // + // No buffer in the pool, see if we can allocate more. + // + if (Device->AllocatedReceiveBuffers < Device->MaxReceiveBuffers) { + + // + // Allocate a pool and try again. + // + + + NbiAllocateReceiveBufferPool (Device, Device->CurMaxReceiveBufferSize ); + s = PopEntryList(&Device->ReceiveBufferList); + } + } + + if ( s ) { + + + // + // Decrement the BufferFree count on the corresponding ReceiveBufferPool. + // so that we know that + ReceiveBuffer = CONTAINING_RECORD( s, NB_RECEIVE_BUFFER, PoolLinkage ); + + + ReceiveBufferPool = (PNB_RECEIVE_BUFFER_POOL)ReceiveBuffer->Pool; + + CTEAssert( ReceiveBufferPool->BufferFree && ( ReceiveBufferPool->BufferFree <= ReceiveBufferPool->BufferCount ) ); + CTEAssert( ReceiveBufferPool->BufferDataSize == Device->CurMaxReceiveBufferSize ); + + ReceiveBufferPool->BufferFree--; + + } + NB_FREE_LOCK (&Device->Lock, LockHandle); + + return s; +#else + PSINGLE_LIST_ENTRY s; + CTELockHandle LockHandle; + + s = ExInterlockedPopEntryList( + &Device->ReceiveBufferList, + &Device->Lock.Lock); + + if (s != NULL) { + return s; + } + + // + // No buffer in the pool, see if we can allocate more. + // + + if (Device->AllocatedReceiveBuffers < Device->MaxReceiveBuffers) { + + // + // Allocate a pool and try again. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + NbiAllocateReceiveBufferPool (Device); + s = PopEntryList(&Device->ReceiveBufferList); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + return s; + + } else { + + return NULL; + + } +#endif _PNP_POWER +} /* NbiPopReceiveBuffer */ + diff --git a/private/ntos/tdi/isnp/nb/precomp.h b/private/ntos/tdi/isnp/nb/precomp.h new file mode 100644 index 000000000..a024f2d3d --- /dev/null +++ b/private/ntos/tdi/isnp/nb/precomp.h @@ -0,0 +1,42 @@ +/*++ + +Copyright (c) 1993-1995 Microsoft Corporation + +Module Name: + + precomp.h + +Abstract: + + Precompilation header file. + +Author: + + Adam Barr (adamba) 08-Sep-1993 + +Revision History: + +--*/ + +#define ISN_NT 1 + +// +// These are needed for CTE +// + +#if DBG +#define DEBUG 1 +#endif + +#define NT 1 + +#include <ntos.h> +#include <tdikrnl.h> +#include <ndis.h> +#include <cxport.h> +#include <bind.h> +#include "isnnb.h" +#include "config.h" +#include "nbitypes.h" +#include "nbiprocs.h" +#include "zwapi.h" diff --git a/private/ntos/tdi/isnp/nb/query.c b/private/ntos/tdi/isnp/nb/query.c new file mode 100644 index 000000000..6ee33adf3 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/query.c @@ -0,0 +1,1817 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + query.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiQueryInformation + o TdiSetInformation + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// Remove the warning -- this is defined in windef also. +// + +#ifdef FAR +#undef FAR +#endif + +#include <windef.h> +#include <nb30.h> + + +// +// Useful macro to obtain the total length of a buffer chain. +// BUGBUG: Make this use NDIS macros. +// + +#define NbiGetBufferChainLength(Buffer, Length) { \ + PNDIS_BUFFER _Buffer = (Buffer); \ + *(Length) = 0; \ + while (_Buffer) { \ + *(Length) += MmGetMdlByteCount(_Buffer); \ + _Buffer = _Buffer->Next; \ + } \ +} + + +NTSTATUS +NbiTdiQueryInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiQueryInformation request for the transport + provider. + +Arguments: + + Request - the request for the operation. + +Return Value: + + The status of operation. + +--*/ + +{ + NTSTATUS Status; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION Query; + PADDRESS_FILE AddressFile; + PADDRESS Address; + PCONNECTION Connection; + union { + struct { + ULONG ActivityCount; + TA_NETBIOS_ADDRESS NbiAddress; + } AddressInfo; + TA_NETBIOS_ADDRESS BroadcastAddress; + TDI_ADDRESS_IPX IpxAddress; + TDI_DATAGRAM_INFO DatagramInfo; + struct { + FIND_NAME_HEADER Header; + FIND_NAME_BUFFER Buffer; + } FindNameInfo; + } TempBuffer; + IPX_SOURCE_ROUTING_INFO SourceRoutingInfo; + PADAPTER_STATUS AdapterStatus; + BOOLEAN RemoteAdapterStatus; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteAddress; + ULONG TargetBufferLength; + ULONG AdapterStatusLength; + ULONG ValidStatusLength; + ULONG ElementSize, TransportAddressSize; + PTRANSPORT_ADDRESS TransportAddress; + TA_ADDRESS UNALIGNED * CurAddress; + PNETBIOS_CACHE CacheName; + FIND_NAME_HEADER UNALIGNED * FindNameHeader; + UINT FindNameBufferLength; + NTSTATUS QueryStatus; + CTELockHandle LockHandle; + PLIST_ENTRY p; + BOOLEAN UsedConnection; + UINT i; + + + // + // what type of status do we want? + // + + Query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)REQUEST_PARAMETERS(Request); + + switch (Query->QueryType) { + + case TDI_QUERY_ADDRESS_INFO: + + // + // The caller wants the exact address value. + // + + if (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + + AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request); + +#if defined(_PNP_POWER) + Status = NbiVerifyAddressFile (AddressFile, CONFLICT_IS_NOT_OK); +#else + Status = NbiVerifyAddressFile (AddressFile); +#endif _PNP_POWER + + if (!NT_SUCCESS(Status)) { + break; + } + + UsedConnection = FALSE; + + } else if (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE) { + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + + if (!NT_SUCCESS(Status)) { + break; + } + + UsedConnection = TRUE; + + AddressFile = Connection->AddressFile; + + } else { + + Status = STATUS_INVALID_ADDRESS; + break; + + } + + Address = AddressFile->Address; + + NB_DEBUG2 (QUERY, ("Query address info on %lx\n", AddressFile)); + + TempBuffer.AddressInfo.ActivityCount = 0; + + NB_GET_LOCK (&Address->Lock, &LockHandle); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + if (CONTAINING_RECORD (p, ADDRESS_FILE, Linkage)->State == ADDRESSFILE_STATE_OPEN) { + ++TempBuffer.AddressInfo.ActivityCount; + } + } + + NB_FREE_LOCK (&Address->Lock, LockHandle); + + TdiBuildNetbiosAddress( + AddressFile->Address->NetbiosAddress.NetbiosName, + (BOOLEAN)(AddressFile->Address->NetbiosAddress.NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_GROUP), + &TempBuffer.AddressInfo.NbiAddress); + + Status = TdiCopyBufferToMdl( + &TempBuffer.AddressInfo, + 0, + sizeof(ULONG) + sizeof(TA_NETBIOS_ADDRESS), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + if (UsedConnection) { + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + } else { + + NbiDereferenceAddressFile (AddressFile, AFREF_VERIFY); + + } + + break; + + case TDI_QUERY_CONNECTION_INFO: + + // + // Connection info is queried on a connection, + // verify this. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + Status = NbiVerifyConnection (Connection); + + if (!NT_SUCCESS (Status)) { + return Status; + } + + if (Connection->State != CONNECTION_STATE_ACTIVE) { + + Status = STATUS_INVALID_CONNECTION; + + } else { + + // + // Assume 50 ms of delay for every hop after the + // first. The delay is returned as a negative number. + // + + if (Connection->HopCount > 1) { + Connection->ConnectionInfo.Delay.HighPart = (ULONG)-1; + Connection->ConnectionInfo.Delay.LowPart = + -((Connection->HopCount-1) * 50 * MILLISECONDS); + } else { + Connection->ConnectionInfo.Delay.HighPart = 0; + Connection->ConnectionInfo.Delay.LowPart = 0; + } + + // + // We have tick count; to convert to bytes/second we do: + // + // packet 576 bytes 18.21 ticks + // ---------------- * --------- * ----------- + // tick_count ticks packet seconds + // + // to get 10489/tick_count = bytes/second. We + // double this because routers tend to + // overestimate it. + // + // Since tick_count has such a low granularity, + // a tick count of 1 gives us a throughput of + // only 84 kbps, which is much too low. In + // that case we return twice the link speed + // which is in 100 bps units; that corresponds + // to about 1/6 of our bandwidth in bytes/sec. + // + + if (Connection->TickCount <= Connection->HopCount) { + + Connection->ConnectionInfo.Throughput.QuadPart = + UInt32x32To64 (Connection->LineInfo.LinkSpeed, 2); + + } else { + + Connection->ConnectionInfo.Throughput.HighPart = 0; + Connection->ConnectionInfo.Throughput.LowPart = + 20978 / (Connection->TickCount - Connection->HopCount); + + } + + Connection->ConnectionInfo.Unreliable = FALSE; + + Status = TdiCopyBufferToMdl ( + &Connection->ConnectionInfo, + 0, + sizeof(TDI_CONNECTION_INFO), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + } + + NbiDereferenceConnection (Connection, CREF_VERIFY); + + break; + + case TDI_QUERY_PROVIDER_INFO: + + NB_DEBUG2 (QUERY, ("Query provider info\n")); + + Status = TdiCopyBufferToMdl ( + &Device->Information, + 0, + sizeof (TDI_PROVIDER_INFO), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_BROADCAST_ADDRESS: + + // + // for this provider, the broadcast address is a zero byte name, + // contained in a Transport address structure. + // + + NB_DEBUG2 (QUERY, ("Query broadcast address\n")); + + TempBuffer.BroadcastAddress.TAAddressCount = 1; + TempBuffer.BroadcastAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + TempBuffer.BroadcastAddress.Address[0].AddressLength = 0; + + Status = TdiCopyBufferToMdl ( + (PVOID)&TempBuffer.BroadcastAddress, + 0L, + sizeof (TempBuffer.BroadcastAddress.TAAddressCount) + + sizeof (TempBuffer.BroadcastAddress.Address[0].AddressType) + + sizeof (TempBuffer.BroadcastAddress.Address[0].AddressLength), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + break; + + case TDI_QUERY_ADAPTER_STATUS: + + // + // Determine if this is a local or remote query. + // + + RemoteAdapterStatus = FALSE; + + if (Query->RequestConnectionInformation != NULL) { + + RemoteAddress = NbiParseTdiAddress(Query->RequestConnectionInformation->RemoteAddress, FALSE); + + if (RemoteAddress == NULL) { + return STATUS_BAD_NETWORK_PATH; + } + +#if defined(_PNP_POWER) + if ( !NbiFindAdapterAddress( + RemoteAddress->NetbiosName, + LOCK_NOT_ACQUIRED ) ) { + + RemoteAdapterStatus = TRUE; + } +#else + if (!RtlEqualMemory( + RemoteAddress->NetbiosName, + Device->ReservedNetbiosName, + 16)) { + + RemoteAdapterStatus = TRUE; + + } +#endif _PNP_POWER + + } + + if (RemoteAdapterStatus) { + + // + // See if we have this name cached. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Status = CacheFindName( + Device, + FindNameOther, + RemoteAddress->NetbiosName, + &CacheName); + + if (Status == STATUS_PENDING) { + + // + // A request for routes to this name has been + // sent out on the net, we queue up this status + // request and processing will be resumed when + // we get a response. + // + // The status field in the request will hold + // the cache entry for the remote. The information + // field will hold the remote netbios name while + // it is in the WaitingAdapterStatus queue, and + // will hold a timeout value while we it is in + // the ActiveAdapterStatus queue. + // + + NB_DEBUG2 (QUERY, ("Queueing up adapter status %lx\n", Request)); + + NbiReferenceDevice (Device, DREF_STATUS_QUERY); + + REQUEST_INFORMATION (Request) = (ULONG)RemoteAddress; + + InsertTailList( + &Device->WaitingAdapterStatus, + REQUEST_LINKAGE (Request)); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } else if (Status == STATUS_SUCCESS) { + + NB_DEBUG2 (QUERY, ("Found adapter status cached %lx\n", Request)); + + // + // We reference the cache name entry so it won't + // go away while we are using it. + // + + REQUEST_STATUS(Request) = (NTSTATUS)CacheName; + ++CacheName->ReferenceCount; + + NbiReferenceDevice (Device, DREF_STATUS_QUERY); + + REQUEST_INFORMATION (Request) = 0; + + InsertTailList( + &Device->ActiveAdapterStatus, + REQUEST_LINKAGE (Request)); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + NbiSendStatusQuery (Request); + + Status = STATUS_PENDING; + + } else { + + if (Status != STATUS_INSUFFICIENT_RESOURCES) { + Status = STATUS_IO_TIMEOUT; + } + + REQUEST_INFORMATION (Request) = 0; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + } else { + + // + // Local adapter status. + // + + NbiGetBufferChainLength (REQUEST_NDIS_BUFFER(Request), &TargetBufferLength); + + Status = NbiStoreAdapterStatus( + TargetBufferLength, + 1, // NIC ID, was 0, changed to 1 for Bug #18026 + // because for NicId = 0, Ipx returns virtual + // address. Netbios uses that to register the + // name (00...01) and fails. + &AdapterStatus, + &AdapterStatusLength, + &ValidStatusLength); + + if (Status != STATUS_INSUFFICIENT_RESOURCES) { + + // + // This should succeed since we know the length + // will fit. + // + + (VOID)TdiCopyBufferToMdl( + AdapterStatus, + 0, + ValidStatusLength, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + NbiFreeMemory (AdapterStatus, AdapterStatusLength, MEMORY_STATUS, "Adapter Status"); + + } + + } + + break; + + case TDI_QUERY_FIND_NAME: + + // + // Check that there is a valid Netbios remote address. + // + + if ((Query->RequestConnectionInformation == NULL) || + ((RemoteAddress = NbiParseTdiAddress(Query->RequestConnectionInformation->RemoteAddress, FALSE)) == NULL)) { + + return STATUS_BAD_NETWORK_PATH; + } + + // + // We assume the entire request buffer is in the first + // piece of the MDL chain (BUGBUG: Can we do this?). + // Make sure there is room for at least the header. + // + + NdisQueryBuffer(REQUEST_NDIS_BUFFER(Request), (PVOID *)&FindNameHeader, &FindNameBufferLength); + if (FindNameBufferLength < sizeof(FIND_NAME_HEADER)) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // See if we have this name cached. We specify that this is + // a netbios name query, so this will only succeed if this is a + // unique name -- for a group name it will queue up a find + // name query and when we get the response we will fill in + // the request's buffer based on it. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Status = CacheFindName( + Device, + FindNameNetbiosFindName, + RemoteAddress->NetbiosName, + &CacheName); + + if (Status == STATUS_PENDING) { + + // + // A request for routes to this name has been + // sent out on the net, we queue up this find + // name request and processing will be resumed when + // we get a response. + // + // The information field will hold the remote + // netbios name while it is in the WaitingNetbiosFindName + // queue. The status will hold the current status -- + // initially failure, then success, then overflow + // if the buffer is too small. + // + + NB_DEBUG2 (QUERY, ("Queueing up find name %lx\n", Request)); + + NbiReferenceDevice (Device, DREF_NB_FIND_NAME); + + FindNameHeader->node_count = 0; + FindNameHeader->reserved = 0; + FindNameHeader->unique_group = 0; + + REQUEST_INFORMATION (Request) = (ULONG)RemoteAddress; + + // + // Assume it fails, we update the status to + // SUCCESS or BUFFER_OVERFLOW if needed. + // + + REQUEST_STATUS (Request) = STATUS_IO_TIMEOUT; + + InsertTailList( + &Device->WaitingNetbiosFindName, + REQUEST_LINKAGE (Request)); + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } else if (Status == STATUS_SUCCESS) { + + NB_DEBUG2 (QUERY, ("Found find name cached %lx\n", Request)); + + // + // We don't need to reference the cache entry since + // we only use it here with the lock still held. + // + + // + // Query the local address, which we will return as + // the destination address of this query. Since we + // use TempBuffer.IpxAddress for this query, we have + // to immediately copy it to its correct place in + // TempBuffer.FindNameInfo.Buffer. + // +#if defined(_PNP_POWER) + if( (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + &CacheName->Networks[0].LocalTarget.NicHandle, + &TempBuffer.IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL) != STATUS_SUCCESS ) { + NB_DEBUG( QUERY, ("Ipx Query %d failed for Nic %x\n",IPX_QUERY_IPX_ADDRESS, + CacheName->Networks[0].LocalTarget.NicHandle.NicId )); + + goto QueryFindNameFailed; + } +#else + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + CacheName->Networks[0].LocalTarget.NicId, + &TempBuffer.IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); +#endif _PNP_POWER + + RtlMoveMemory (TempBuffer.FindNameInfo.Buffer.destination_addr, TempBuffer.IpxAddress.NodeAddress, 6); + TempBuffer.FindNameInfo.Buffer.access_control = 0x10; // standard token-ring values + TempBuffer.FindNameInfo.Buffer.frame_control = 0x40; + RtlCopyMemory (TempBuffer.FindNameInfo.Buffer.source_addr, CacheName->FirstResponse.NodeAddress, 6); + + // + // Query source routing information about this remote, if any. + // + + SourceRoutingInfo.Identifier = IDENTIFIER_NB; + RtlCopyMemory (SourceRoutingInfo.RemoteAddress, CacheName->FirstResponse.NodeAddress, 6); + + QueryStatus = (*Device->Bind.QueryHandler)( + IPX_QUERY_SOURCE_ROUTING, +#if defined(_PNP_POWER) + &CacheName->Networks[0].LocalTarget.NicHandle, +#else + CacheName->Networks[0].LocalTarget.NicId, +#endif _PNP_POWER + &SourceRoutingInfo, + sizeof(IPX_SOURCE_ROUTING_INFO), + NULL); + + RtlZeroMemory(TempBuffer.FindNameInfo.Buffer.routing_info, 18); + if (QueryStatus != STATUS_SUCCESS) { + SourceRoutingInfo.SourceRoutingLength = 0; + } else if (SourceRoutingInfo.SourceRoutingLength > 0) { + RtlMoveMemory( + TempBuffer.FindNameInfo.Buffer.routing_info, + SourceRoutingInfo.SourceRouting, + SourceRoutingInfo.SourceRoutingLength); + } + + TempBuffer.FindNameInfo.Buffer.length = (UCHAR)(14 + SourceRoutingInfo.SourceRoutingLength); + + TempBuffer.FindNameInfo.Header.node_count = 1; + TempBuffer.FindNameInfo.Header.reserved = 0; + TempBuffer.FindNameInfo.Header.unique_group = 0; // unique + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + // + // 33 is sizeof(FIND_NAME_BUFFER) without the padding. + // + + Status = TdiCopyBufferToMdl ( + (PVOID)&TempBuffer.FindNameInfo, + 0, + sizeof(FIND_NAME_HEADER) + 33, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + + } else { + +#if defined(_PNP_POWER) +QueryFindNameFailed: +#endif _PNP_POWER + + if (Status != STATUS_INSUFFICIENT_RESOURCES) { + Status = STATUS_IO_TIMEOUT; + } + + REQUEST_INFORMATION (Request) = 0; + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + } + + break; + + case TDI_QUERY_PROVIDER_STATISTICS: + + // + // BETABUGBUG: Keep track of more of these. + // + + NB_DEBUG2 (QUERY, ("Query provider statistics\n")); + + Status = TdiCopyBufferToMdl ( + &Device->Statistics, + 0, + FIELD_OFFSET (TDI_PROVIDER_STATISTICS, ResourceStats[0]), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_DATAGRAM_INFO: + + NB_DEBUG2 (QUERY, ("Query datagram info\n")); + + TempBuffer.DatagramInfo.MaximumDatagramBytes = 0; + TempBuffer.DatagramInfo.MaximumDatagramCount = 0; + + Status = TdiCopyBufferToMdl ( + &TempBuffer.DatagramInfo, + 0, + sizeof(TempBuffer.DatagramInfo), + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + break; + + case TDI_QUERY_DATA_LINK_ADDRESS: + case TDI_QUERY_NETWORK_ADDRESS:{ +#if defined(_PNP_POWER) + Status = (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + (Query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS + ? IPX_QUERY_DATA_LINK_ADDRESS + : IPX_QUERY_NETWORK_ADDRESS ), + NULL, + Request, + 0, + NULL); +#else + ULONG TransportAddressAllocSize; + + if (Query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + ElementSize = (2 * sizeof(USHORT)) + 6; + } else { + ElementSize = (2 * sizeof(USHORT)) + sizeof(TDI_ADDRESS_IPX); + } + +// TransportAddress = CTEAllocMem(sizeof(int) + (ElementSize * Device->MaximumNicId)); + TransportAddressAllocSize = sizeof(int) + ( ElementSize * Device->MaximumNicId); + TransportAddress = NbiAllocateMemory( TransportAddressAllocSize, MEMORY_QUERY, "Temp Query Allocation"); + + if (TransportAddress == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + TransportAddress->TAAddressCount = 0; + TransportAddressSize = sizeof(int); + CurAddress = (TA_ADDRESS UNALIGNED *)TransportAddress->Address; + + for (i = 1; i <= Device->MaximumNicId; i++) { + + Status = (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + (USHORT)i, + &TempBuffer.IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); + + if (Status != STATUS_SUCCESS) { + continue; + } + + if (Query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + CurAddress->AddressLength = 6; + CurAddress->AddressType = TDI_ADDRESS_TYPE_UNSPEC; + RtlCopyMemory (CurAddress->Address, TempBuffer.IpxAddress.NodeAddress, 6); + } else { + CurAddress->AddressLength = sizeof(TDI_ADDRESS_IPX); + CurAddress->AddressType = TDI_ADDRESS_TYPE_IPX; + RtlCopyMemory (CurAddress->Address, &TempBuffer.IpxAddress, sizeof(TDI_ADDRESS_IPX)); + } + ++TransportAddress->TAAddressCount; + TransportAddressSize += ElementSize; + CurAddress = (TA_ADDRESS UNALIGNED *)(((PUCHAR)CurAddress) + ElementSize); + + } + + Status = TdiCopyBufferToMdl ( + TransportAddress, + 0, + TransportAddressSize, + REQUEST_NDIS_BUFFER(Request), + 0, + &REQUEST_INFORMATION(Request)); + +// CTEFreeMem (TransportAddress); + NbiFreeMemory( TransportAddress, TransportAddressAllocSize, MEMORY_QUERY, "Temp Query Allocation"); + + } +#endif _PNP_POWER + break; + } + default: + + NB_DEBUG (QUERY, ("Invalid query type %d\n", Query->QueryType)); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return Status; + +} /* NbiTdiQueryInformation */ + + +NTSTATUS +NbiStoreAdapterStatus( + IN ULONG MaximumLength, + IN USHORT NicId, + OUT PVOID * StatusBuffer, + OUT ULONG * StatusBufferLength, + OUT ULONG * ValidBufferLength + ) + +/*++ + +Routine Description: + + This routine allocates an ADAPTER_STATUS buffer and + fills it in. The buffer will be allocated at most + MaximumLength size. The caller is responsible for + freeing the buffer. + +Arguments: + + MaximumLength - The maximum length to allocate. + + NicId - The NIC ID the query was received on, or 0 for + a local query. + + StatusBuffer - Returns the allocated buffer. + + StatusBufferLength - Returns the length of the buffer. + + ValidBufferLength - Returns the length of the buffer which + contains valid adapter status data. + +Return Value: + + STATUS_SUCCESS - The buffer was written successfully. + STATUS_BUFFER_OVERFLOW - The buffer was written but not all + data could fit in MaximumLength bytes. + STATUS_INSUFFICIENT_RESOURCES - The buffer could not be allocated. + +--*/ + +{ + + PADAPTER_STATUS AdapterStatus; + PNAME_BUFFER NameBuffer; + ADAPTER_STATUS TempAdapterStatus; +#if !defined(_PNP_POWER) + TDI_ADDRESS_IPX IpxAddress; +#endif !_PNP_POWER + PDEVICE Device = NbiDevice; + PADDRESS Address; + UCHAR NameCount; + ULONG LengthNeeded; + ULONG BytesWritten; + NTSTATUS Status; + PLIST_ENTRY p; + CTELockHandle LockHandle; + + + // + // First fill in the basic adapter status structure, to make + // it easier to copy over if the target buffer is really short. + // + + RtlZeroMemory ((PVOID)&TempAdapterStatus, sizeof(ADAPTER_STATUS)); + +#if defined(_PNP_POWER) + RtlCopyMemory (TempAdapterStatus.adapter_address, Device->Bind.Node, 6); +#else + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + NicId, + &IpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); + + RtlCopyMemory (TempAdapterStatus.adapter_address, IpxAddress.NodeAddress, 6); +#endif _PNP_POWER + + + // + // Some of the fields mean different things for Novell Netbios, + // as described in the comments. + // + + TempAdapterStatus.rev_major = 0; // Jumpers + TempAdapterStatus.reserved0 = 0; // SelfTest + TempAdapterStatus.adapter_type = 0; // MajorVersion + TempAdapterStatus.rev_minor = 0; // MinorVersion + + TempAdapterStatus.duration = 0; // ReportingPeriod + TempAdapterStatus.frmr_recv = 0; // ReceiveCRCErrors + TempAdapterStatus.frmr_xmit = 0; // ReceiveAlignErrors + + TempAdapterStatus.iframe_recv_err = 0; // XmitCollisions + TempAdapterStatus.xmit_aborts = 0; // XmitAbort + + TempAdapterStatus.xmit_success = Device->Statistics.DataFramesSent; // SuccessfulXmits + TempAdapterStatus.recv_success = Device->Statistics.DataFramesReceived; // SuccessfulReceive + + TempAdapterStatus.iframe_xmit_err = (WORD)Device->Statistics.DataFramesResent; // XmitRetries + TempAdapterStatus.recv_buff_unavail = (WORD)Device->Statistics.DataFramesRejected; // ExhaustedResource + + // t1_timeouts, ti_timeouts, and reserved1 are unused. + + TempAdapterStatus.free_ncbs = 0xffff; // FreeBlocks + TempAdapterStatus.max_cfg_ncbs = 0xffff; // ConfiguredNCB + TempAdapterStatus.max_ncbs = 0xffff; // MaxNCB + + // xmit_bug_unavail and max_dgram_size are unused. + + TempAdapterStatus.pending_sess = (WORD)Device->Statistics.OpenConnections; // CurrentSessions + TempAdapterStatus.max_cfg_sess = 0xffff; // MaxSessionConfigured + TempAdapterStatus.max_sess = 0xffff; // MaxSessionPossible + TempAdapterStatus.max_sess_pkt_size = + Device->Bind.LineInfo.MaximumSendSize - sizeof(NB_CONNECTION); // MaxSessionPacketSize + + TempAdapterStatus.name_count = 0; + + + // + // Do a quick estimate of how many names we need room for. + // This includes stopping addresses and the broadcast + // address, for the moment. BUGBUG: Fix this? + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + LengthNeeded = sizeof(ADAPTER_STATUS) + (Device->AddressCount * sizeof(NAME_BUFFER)); + + if (LengthNeeded > MaximumLength) { + LengthNeeded = MaximumLength; + } + + AdapterStatus = NbiAllocateMemory(LengthNeeded, MEMORY_STATUS, "Adapter Status"); + if (AdapterStatus == NULL) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + + *StatusBuffer = AdapterStatus; + *StatusBufferLength = LengthNeeded; + + if (LengthNeeded < sizeof(ADAPTER_STATUS)) { + RtlCopyMemory (AdapterStatus, &TempAdapterStatus, LengthNeeded); + *ValidBufferLength = LengthNeeded; + NB_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_BUFFER_OVERFLOW; + } + + RtlCopyMemory (AdapterStatus, &TempAdapterStatus, sizeof(ADAPTER_STATUS)); + + BytesWritten = sizeof(ADAPTER_STATUS); + NameBuffer = (PNAME_BUFFER)(AdapterStatus+1); + NameCount = 0; + + // + // Scan through the device's address database, filling in + // the NAME_BUFFERs. + // + + Status = STATUS_SUCCESS; + + for (p = Device->AddressDatabase.Flink; + p != &Device->AddressDatabase; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, ADDRESS, Linkage); + + // + // Ignore addresses that are shutting down. + // + +#if defined(_PNP_POWER) + if ((Address->State != ADDRESS_STATE_OPEN) || + (Address->Flags & ADDRESS_FLAGS_CONFLICT)) { + continue; + } +#else + if ((Address->State != ADDRESS_STATE_OPEN) != 0) { + continue; + } +#endif _PNP_POWER + + // + // Ignore the broadcast address. + // + + if (Address->NetbiosAddress.Broadcast) { + continue; + } + + // + // Ignore our reserved address. + // +#if defined(_PNP_POWER) + if ( NbiFindAdapterAddress( + Address->NetbiosAddress.NetbiosName, + LOCK_ACQUIRED + )) { + continue; + } +#else + if (RtlEqualMemory( + Address->NetbiosAddress.NetbiosName, + Device->ReservedNetbiosName, + 16)) { + continue; + } + +#endif _PNP_POWER + // + // Make sure we still have room. + // + + if (BytesWritten + sizeof(NAME_BUFFER) > LengthNeeded) { + Status = STATUS_BUFFER_OVERFLOW; + break; + } + + RtlCopyMemory( + NameBuffer->name, + Address->NetbiosAddress.NetbiosName, + 16); + + ++NameCount; + NameBuffer->name_num = NameCount; + + NameBuffer->name_flags = REGISTERED; + if (Address->NameTypeFlag == NB_NAME_GROUP) { + NameBuffer->name_flags |= GROUP_NAME; + } + + // + // BUGBUG: name_flags should be done more accurately. + // + + BytesWritten += sizeof(NAME_BUFFER); + ++NameBuffer; + + } + + AdapterStatus->name_count = (WORD)NameCount; + *ValidBufferLength = BytesWritten; + NB_FREE_LOCK (&Device->Lock, LockHandle); + return Status; + +} /* NbiStoreAdapterStatus */ + + +VOID +NbiUpdateNetbiosFindName( + IN PREQUEST Request, +#if defined(_PNP_POWER) + IN PNIC_HANDLE NicHandle, +#else + IN USHORT NicId, +#endif _PNP_POWER + IN TDI_ADDRESS_IPX UNALIGNED * RemoteIpxAddress, + IN BOOLEAN Unique + ) + +/*++ + +Routine Description: + + This routine updates the find name request with the + new information received. It updates the status in + the request if needed. + +Arguments: + + Request - The netbios find name request. + + NicId - The NIC ID the response was received on. + + RemoteIpxAddress - The IPX address of the remote. + + Unique - TRUE if the name is unique. + +Return Value: + + None. + +--*/ + +{ + FIND_NAME_HEADER UNALIGNED * FindNameHeader; + FIND_NAME_BUFFER UNALIGNED * FindNameBuffer; + UINT FindNameBufferLength; + TDI_ADDRESS_IPX LocalIpxAddress; + IPX_SOURCE_ROUTING_INFO SourceRoutingInfo; + NTSTATUS QueryStatus; + UINT i; + + + NdisQueryBuffer(REQUEST_NDIS_BUFFER(Request), (PVOID *)&FindNameHeader, &FindNameBufferLength); + + // + // Scan through the names saved so far and see if this one + // is there. + // + + FindNameBuffer = (FIND_NAME_BUFFER UNALIGNED *)(FindNameHeader+1); + + for (i = 0; i < FindNameHeader->node_count; i++) { + + if (RtlEqualMemory( + FindNameBuffer->source_addr, + RemoteIpxAddress->NodeAddress, + 6)) { + + // + // This remote already responded, ignore it. + // + + return; + + } + + FindNameBuffer = (FIND_NAME_BUFFER UNALIGNED *) + (((PUCHAR)FindNameBuffer) + 33); + + } + + // + // Make sure there is room for this new node. 33 is + // sizeof(FIND_NAME_BUFFER) without padding. + // + + if (FindNameBufferLength < sizeof(FIND_NAME_HEADER) + ((FindNameHeader->node_count+1) * 33)) { + REQUEST_STATUS(Request) = STATUS_BUFFER_OVERFLOW; + return; + } + + // + // Query the local address, which we will return as + // the destination address of this query. + // + +#if defined(_PNP_POWER) + if( (*NbiDevice->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + NicHandle, + &LocalIpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL) != STATUS_SUCCESS ) { + // + // Ignore this response if the query fails. maybe the NicHandle + // is bad or it just got removed. + // + NB_DEBUG( QUERY, ("Ipx Query %d failed for Nic %x\n",IPX_QUERY_IPX_ADDRESS, + NicHandle->NicId )); + return; + } +#else + (VOID)(*NbiDevice->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_IPX_ADDRESS, + NicId, + &LocalIpxAddress, + sizeof(TDI_ADDRESS_IPX), + NULL); +#endif _PNP_POWER + + FindNameBuffer->access_control = 0x10; // standard token-ring values + FindNameBuffer->frame_control = 0x40; + RtlMoveMemory (FindNameBuffer->destination_addr, LocalIpxAddress.NodeAddress, 6); + RtlCopyMemory (FindNameBuffer->source_addr, RemoteIpxAddress->NodeAddress, 6); + + // + // Query source routing information about this remote, if any. + // + + SourceRoutingInfo.Identifier = IDENTIFIER_NB; + RtlCopyMemory (SourceRoutingInfo.RemoteAddress, RemoteIpxAddress->NodeAddress, 6); + + QueryStatus = (*NbiDevice->Bind.QueryHandler)( + IPX_QUERY_SOURCE_ROUTING, +#if defined(_PNP_POWER) + NicHandle, +#else + NicId, +#endif _PNP_POWER + &SourceRoutingInfo, + sizeof(IPX_SOURCE_ROUTING_INFO), + NULL); + + RtlZeroMemory(FindNameBuffer->routing_info, 18); + if (QueryStatus != STATUS_SUCCESS) { + SourceRoutingInfo.SourceRoutingLength = 0; + } else if (SourceRoutingInfo.SourceRoutingLength > 0) { + RtlMoveMemory( + FindNameBuffer->routing_info, + SourceRoutingInfo.SourceRouting, + SourceRoutingInfo.SourceRoutingLength); + } + + FindNameBuffer->length = (UCHAR)(14 + SourceRoutingInfo.SourceRoutingLength); + + ++FindNameHeader->node_count; + if (!Unique) { + FindNameHeader->unique_group = 1; // group + } + + REQUEST_STATUS(Request) = STATUS_SUCCESS; + +} /* NbiUpdateNetbiosFindName */ + + +VOID +NbiSetNetbiosFindNameInformation( + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine sets the REQUEST_INFORMATION field to the right + value based on the number of responses recorded in the netbios + find name request's buffer. + +Arguments: + + Request - The netbios find name request. + +Return Value: + + None. + +--*/ + +{ + FIND_NAME_HEADER UNALIGNED * FindNameHeader; + UINT FindNameBufferLength; + + + NdisQueryBuffer(REQUEST_NDIS_BUFFER(Request), (PVOID *)&FindNameHeader, &FindNameBufferLength); + + // + // 33 is sizeof(FIND_NAME_BUFFER) without the padding. + // + + REQUEST_INFORMATION(Request) = sizeof(FIND_NAME_HEADER) + (FindNameHeader->node_count * 33); + +} /* NbiSetNetbiosFindNameInformation */ + + +NTSTATUS +NbiTdiSetInformation( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetInformation request for the transport + provider. + +Arguments: + + Device - the device. + + Request - the request for the operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (Device); + UNREFERENCED_PARAMETER (Request); + + return STATUS_NOT_IMPLEMENTED; + +} /* NbiTdiSetInformation */ + + +VOID +NbiProcessStatusQuery( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_STATUS_QUERY frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + NDIS_STATUS NdisStatus; + IPX_LINE_INFO LineInfo; + ULONG ResponseSize; + NTSTATUS Status; + PNDIS_BUFFER AdapterStatusBuffer; + PADAPTER_STATUS AdapterStatus; + ULONG AdapterStatusLength; + ULONG ValidStatusLength; + PDEVICE Device = NbiDevice; + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; + + + // + // The old stack does not include the 14 bytes of padding in + // the 802.3 or IPX length of the packet. + // + + if (PacketSize < (sizeof(IPX_HEADER) + 2)) { + return; + } + + // + // Get the maximum size we can send. + // +#if defined(_PNP_POWER) + if( (*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_LINE_INFO, + &RemoteAddress->NicHandle, + &LineInfo, + sizeof(IPX_LINE_INFO), + NULL) != STATUS_SUCCESS ) { + // + // Bad NicHandle or it just got removed. + // + NB_DEBUG( QUERY, ("Ipx Query %d failed for Nic %x\n",IPX_QUERY_LINE_INFO, + RemoteAddress->NicHandle.NicId )); + + return; + } + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + if (s == NULL) { + return; + } +#else + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + if (s == NULL) { + return; + } + + // + // Get the maximum size we can send. + // + + (VOID)(*Device->Bind.QueryHandler)( // BUGBUG: Check return code + IPX_QUERY_LINE_INFO, + RemoteAddress->NicId, + &LineInfo, + sizeof(IPX_LINE_INFO), + NULL); +#endif _PNP_POWER + + ResponseSize = LineInfo.MaximumSendSize - sizeof(IPX_HEADER) - sizeof(NB_STATUS_RESPONSE); + + // + // Get the local adapter status (this allocates a buffer). + // + + Status = NbiStoreAdapterStatus( + ResponseSize, +#if defined(_PNP_POWER) + RemoteAddress->NicHandle.NicId, +#else + RemoteAddress->NicId, +#endif _PNP_POWER + &AdapterStatus, + &AdapterStatusLength, + &ValidStatusLength); + + if (Status == STATUS_INSUFFICIENT_RESOURCES) { + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + // + // Allocate an NDIS buffer to map the extra buffer. + // + + NdisAllocateBuffer( + &NdisStatus, + &AdapterStatusBuffer, + Device->NdisBufferPoolHandle, + AdapterStatus, + ValidStatusLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbiFreeMemory (AdapterStatus, AdapterStatusLength, MEMORY_STATUS, "Adapter Status"); + ExInterlockedPushEntrySList( + &Device->SendPacketList, + s, + &NbiGlobalPoolInterlock); + return; + } + + NB_DEBUG2 (QUERY, ("Reply to AdapterStatus from %lx %2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + *(UNALIGNED ULONG *)Connectionless->IpxHeader.SourceNetwork, + Connectionless->IpxHeader.SourceNode[0], + Connectionless->IpxHeader.SourceNode[1], + Connectionless->IpxHeader.SourceNode[2], + Connectionless->IpxHeader.SourceNode[3], + Connectionless->IpxHeader.SourceNode[4], + Connectionless->IpxHeader.SourceNode[5])); + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_STATUS_RESPONSE; + Reserved->u.SR_AS.ActualBufferLength = AdapterStatusLength; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + RtlCopyMemory(&Header->IpxHeader.DestinationNetwork, Connectionless->IpxHeader.SourceNetwork, 12); + + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_RESPONSE)+ValidStatusLength) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_RESPONSE)+ValidStatusLength) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + Header->StatusResponse.ConnectionControlFlag = 0x00; + Header->StatusResponse.DataStreamType = NB_CMD_STATUS_RESPONSE; + + NbiReferenceDevice (Device, DREF_STATUS_RESPONSE); + + NdisChainBufferAtBack (Packet, AdapterStatusBuffer); + + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + RemoteAddress, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + ValidStatusLength, + sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiProcessStatusQuery */ + + +VOID +NbiSendStatusQuery( + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine sends NB_CMD_STATUS_QUERY frames. + +Arguments: + + Request - Holds the request describing the remote adapter + status query. REQUEST_STATUS(Request) points + to the netbios cache entry for the remote name. + +Return Value: + + None. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PNB_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + NB_CONNECTIONLESS UNALIGNED * Header; + NDIS_STATUS NdisStatus; + PNETBIOS_CACHE CacheName; + PIPX_LOCAL_TARGET LocalTarget; + PDEVICE Device = NbiDevice; + + // + // Allocate a packet from the pool. + // + + s = NbiPopSendPacket(Device, FALSE); + if (s == NULL) { + return; + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_STATUS_QUERY; + + CacheName = (PNETBIOS_CACHE)REQUEST_STATUS(Request); + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + RtlCopyMemory (Header->IpxHeader.DestinationNetwork, &CacheName->FirstResponse, 12); + + LocalTarget = &CacheName->Networks[0].LocalTarget; + + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_QUERY)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_STATUS_QUERY)) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + + Header->StatusResponse.ConnectionControlFlag = 0x00; + Header->StatusResponse.DataStreamType = NB_CMD_STATUS_QUERY; + + NbiReferenceDevice (Device, DREF_STATUS_FRAME); + + + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + sizeof(NB_STATUS_QUERY)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_STATUS_QUERY), + sizeof(IPX_HEADER) + sizeof(NB_STATUS_QUERY))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} /* NbiProcessStatusQuery */ + + +VOID +NbiProcessStatusResponse( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_STATUS_RESPONSE frames. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + CTELockHandle LockHandle; + PREQUEST AdapterStatusRequest; + PNETBIOS_CACHE CacheName; + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PNDIS_BUFFER TargetBuffer; + ULONG TargetBufferLength, BytesToTransfer; + ULONG BytesTransferred; + NDIS_STATUS NdisStatus; + PNB_RECEIVE_RESERVED ReceiveReserved; + PNDIS_PACKET Packet; + BOOLEAN Found; + PNAME_BUFFER NameBuffer; + UINT i,NameCount = 0; + NB_CONNECTIONLESS UNALIGNED * Connectionless = + (NB_CONNECTIONLESS UNALIGNED *)LookaheadBuffer; + + + if (PacketSize < (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE))) { + return; + } + + // + // Find out how many names are there. + // + NameBuffer = (PNAME_BUFFER)(LookaheadBuffer + sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + sizeof(ADAPTER_STATUS)); + if ( LookaheadBufferSize > sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + sizeof(ADAPTER_STATUS) ) { + NameCount = (LookaheadBufferSize - (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE) + sizeof(ADAPTER_STATUS)) ) / + sizeof(NAME_BUFFER); + } + // + // Find a request queued to this remote. If there are + // multiple requests outstanding for the same name we + // should get multiple responses, so we only need to + // find one. + // + + NB_GET_LOCK (&Device->Lock, &LockHandle); + + Found = FALSE; + p = Device->ActiveAdapterStatus.Flink; + + while (p != &Device->ActiveAdapterStatus) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + CacheName = (PNETBIOS_CACHE)REQUEST_STATUS(AdapterStatusRequest); + if ( CacheName->Unique ) { + if (RtlEqualMemory( + &CacheName->FirstResponse, + Connectionless->IpxHeader.SourceNetwork, + 12)) { + Found = TRUE; + break; + } + } else if ( RtlEqualMemory( CacheName->NetbiosName,NetbiosBroadcastName,16)){ + // + // It's a broadcast name. Any response is fine. + // + Found = TRUE; + break; + } else { + // + // It's group name. Make sure that this remote + // has this group name registered with him. + // + for (i =0;i<NameCount;i++) { + if ( (RtlEqualMemory( + CacheName->NetbiosName, + NameBuffer[i].name, + 16)) && + + (NameBuffer[i].name_flags & GROUP_NAME) ) { + + Found = TRUE; + break; + } + } + } + + } + + if (!Found) { + NB_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NB_DEBUG2 (QUERY, ("Got response to AdapterStatus %lx\n", AdapterStatusRequest)); + + RemoveEntryList (REQUEST_LINKAGE(AdapterStatusRequest)); + + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free delete name cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Name deleted"); + + } + + NB_FREE_LOCK (&Device->Lock, LockHandle); + + s = NbiPopReceivePacket (Device); + if (s == NULL) { + + REQUEST_INFORMATION (AdapterStatusRequest) = 0; + REQUEST_STATUS (AdapterStatusRequest) = STATUS_INSUFFICIENT_RESOURCES; + + NbiCompleteRequest (AdapterStatusRequest); + NbiFreeRequest (Device, AdapterStatusRequest); + + NbiDereferenceDevice (Device, DREF_STATUS_QUERY); + + return; + } + + ReceiveReserved = CONTAINING_RECORD (s, NB_RECEIVE_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (ReceiveReserved, NDIS_PACKET, ProtocolReserved[0]); + + // + // Initialize the receive packet. + // + + ReceiveReserved->Type = RECEIVE_TYPE_ADAPTER_STATUS; + ReceiveReserved->u.RR_AS.Request = AdapterStatusRequest; + REQUEST_STATUS(AdapterStatusRequest) = STATUS_SUCCESS; + CTEAssert (!ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = TRUE; + + // + // Now that we have a packet and a buffer, set up the transfer. + // We will complete the request when the transfer completes. + // + + TargetBuffer = REQUEST_NDIS_BUFFER (AdapterStatusRequest); + + NdisChainBufferAtFront (Packet, TargetBuffer); + + NbiGetBufferChainLength (TargetBuffer, &TargetBufferLength); + BytesToTransfer = PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE)); + if (TargetBufferLength < BytesToTransfer) { + BytesToTransfer = TargetBufferLength; + REQUEST_STATUS(AdapterStatusRequest) = STATUS_BUFFER_OVERFLOW; + } + + (*Device->Bind.TransferDataHandler) ( + &NdisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadBufferOffset + (sizeof(IPX_HEADER) + sizeof(NB_STATUS_RESPONSE)), + BytesToTransfer, + Packet, + &BytesTransferred); + + if (NdisStatus != NDIS_STATUS_PENDING) { +#if DBG + if (NdisStatus == STATUS_SUCCESS) { + CTEAssert (BytesTransferred == BytesToTransfer); + } +#endif + + NbiTransferDataComplete( + Packet, + NdisStatus, + BytesTransferred); + + } + +} /* NbiProcessStatusResponse */ + diff --git a/private/ntos/tdi/isnp/nb/receive.c b/private/ntos/tdi/isnp/nb/receive.c new file mode 100644 index 000000000..54ce78944 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/receive.c @@ -0,0 +1,1303 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + receive.c + +Abstract: + + This module contains the code to handle receive indication + and posted receives for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 22-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// This routine is a no-op to put in the NbiCallbacks table so +// we can avoid checking for runt session frames (this is because +// of how the if is structure below). +// + +VOID +NbiProcessSessionRunt( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) +{ + return; +} + +NB_CALLBACK_NO_TRANSFER NbiCallbacksNoTransfer[] = { + NbiProcessFindName, + NbiProcessNameRecognized, + NbiProcessAddName, + NbiProcessAddName, // processes name in use frames also + NbiProcessDeleteName, + NbiProcessSessionRunt, // in case get a short session packet + NbiProcessSessionEnd, + NbiProcessSessionEndAck, + NbiProcessStatusQuery + }; + +#ifdef RSRC_TIMEOUT_DBG +VOID +NbiProcessDeathPacket( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION_DATA frames. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)LookaheadBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + PCONNECTION Connection; + PDEVICE Device = NbiDevice; + ULONG Hash; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + + DbgPrint("******Received death packet - connid %x\n",Sess->DestConnectionId); + + if ( !NbiGlobalDebugResTimeout ) { + return; + } + + if (Sess->DestConnectionId != 0xffff) { + + // + // This is an active connection, find it using + // our session id. + // + + Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if (Connection->LocalConnectionId == Sess->DestConnectionId) { + break; + } + Connection = Connection->NextConnection; + } + + if (Connection == NULL) { + DbgPrint("********No Connection found with %x id\n",Sess->DestConnectionId); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + DbgPrint("******Received death packet on conn %lx from <%.16s>\n",Connection,Connection->RemoteName); + DbgBreakPoint(); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + } +} +#endif //RSRC_TIMEOUT_DBG + + +VOID +NbiReceive( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles receive indications from IPX. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + PNB_FRAME NbFrame = (PNB_FRAME)LookaheadBuffer; + UCHAR DataStreamType; + + // + // We know that this is a frame with a valid IPX header + // because IPX would not give it to use otherwise. However, + // it does not check the source socket. + // + + if (NbFrame->Connectionless.IpxHeader.SourceSocket != NB_SOCKET) { + return; + } + + ++NbiDevice->Statistics.PacketsReceived; + + // First assume that the DataStreamType is at the normal place i.e 2nd byte + // + + // Now see if this is a name frame. + // + if ( PacketSize == sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME) ) { + // In the internet mode, the DataStreamType2 becomes DataStreamType + if (NbFrame->Connectionless.IpxHeader.PacketType == 0x14 ) { + DataStreamType = NbFrame->Connectionless.NameFrame.DataStreamType2; + } else { + DataStreamType = NbFrame->Connectionless.NameFrame.DataStreamType; + } + + // Is this a name frame? + // NB_CMD_FIND_NAME = 1 .... NB_CMD_DELETE_NAME = 5 + // + if ((DataStreamType >= NB_CMD_FIND_NAME) && (DataStreamType <= NB_CMD_DELETE_NAME)) { + if (LookaheadBufferSize == PacketSize) { + (*NbiCallbacksNoTransfer[DataStreamType-1])( + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize); + } + return; + } + + } + +#ifdef RSRC_TIMEOUT_DBG + if ((PacketSize >= sizeof(NB_CONNECTION)) && + (NbFrame->Connection.Session.DataStreamType == NB_CMD_DEATH_PACKET)) { + + NbiProcessDeathPacket( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + } +#endif //RSRC_TIMEOUT_DBG + + if ((PacketSize >= sizeof(NB_CONNECTION)) && + (NbFrame->Connection.Session.DataStreamType == NB_CMD_SESSION_DATA)) { + + NbiProcessSessionData( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + + } else { + + DataStreamType = NbFrame->Connectionless.NameFrame.DataStreamType; + // Handle NB_CMD_SESSION_END = 7 ... NB_CMD_STATUS_QUERY = 9 + // + if ((DataStreamType >= NB_CMD_SESSION_END ) && (DataStreamType <= NB_CMD_STATUS_QUERY)) { + if (LookaheadBufferSize == PacketSize) { + (*NbiCallbacksNoTransfer[DataStreamType-1])( + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize); + } + + } else if (DataStreamType == NB_CMD_STATUS_RESPONSE) { + + NbiProcessStatusResponse( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize); + + } else if ((DataStreamType == NB_CMD_DATAGRAM) || + (DataStreamType == NB_CMD_BROADCAST_DATAGRAM)) { + + NbiProcessDatagram( + MacBindingHandle, + MacReceiveContext, + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize, + LookaheadBufferOffset, + PacketSize, + (BOOLEAN)(DataStreamType == NB_CMD_BROADCAST_DATAGRAM)); + + } + + } + +} /* NbiReceive */ + + +VOID +NbiReceiveComplete( + IN USHORT NicId + ) + +/*++ + +Routine Description: + + This routine handles receive complete indications from IPX. + +Arguments: + + NicId - The NIC ID on which a receive was previously indicated. + +Return Value: + + None. + +--*/ + +{ + + PLIST_ENTRY p; + PADDRESS Address; + PREQUEST Request; + PNB_RECEIVE_BUFFER ReceiveBuffer; + PDEVICE Device = NbiDevice; + LIST_ENTRY LocalList; + PCONNECTION Connection; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + // + // Complete any pending receive requests. + // + + + if (!IsListEmpty (&Device->ReceiveCompletionQueue)) { + + p = NB_REMOVE_HEAD_LIST( + &Device->ReceiveCompletionQueue, + &Device->Lock); + + while (!NB_LIST_WAS_EMPTY(&Device->ReceiveCompletionQueue, p)) { + + Request = LIST_ENTRY_TO_REQUEST (p); + + // + // BUGBUG: Cache the connection somewhere easier + // to retrieve? + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + NB_DEBUG2 (RECEIVE, ("Completing receive %lx (%d), status %lx\n", + Request, REQUEST_INFORMATION(Request), REQUEST_STATUS(Request))); + + NbiCompleteRequest (Request); + NbiFreeRequest (NbiDevice, Request); + + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + + NbiDereferenceConnection (Connection, CREF_RECEIVE); + + p = NB_REMOVE_HEAD_LIST( + &Device->ReceiveCompletionQueue, + &Device->Lock); + + } + + } + + + // + // Indicate any datagrams to clients. + // + + if (!IsListEmpty (&Device->ReceiveDatagrams)) { + + p = NB_REMOVE_HEAD_LIST( + &Device->ReceiveDatagrams, + &Device->Lock); + + while (!NB_LIST_WAS_EMPTY(&Device->ReceiveDatagrams, p)) { + + ReceiveBuffer = CONTAINING_RECORD (p, NB_RECEIVE_BUFFER, WaitLinkage); + Address = ReceiveBuffer->Address; + + NbiIndicateDatagram( + Address, + ReceiveBuffer->RemoteName, + ReceiveBuffer->Data, + ReceiveBuffer->DataLength); + +#if defined(_PNP_POWER) + NbiPushReceiveBuffer ( ReceiveBuffer ); +#else + NB_PUSH_ENTRY_LIST( + &Device->ReceiveBufferList, + &ReceiveBuffer->PoolLinkage, + &Device->Lock); +#endif _PNP_POWER + + NbiDereferenceAddress (Address, AREF_FIND); + + p = NB_REMOVE_HEAD_LIST( + &Device->ReceiveDatagrams, + &Device->Lock); + + } + } + + + // + // Start packetizing connections. + // + + if (!IsListEmpty (&Device->PacketizeConnections)) { + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + // + // Check again because it may just have become + // empty, and the code below depends on it being + // non-empty. + // + + if (!IsListEmpty (&Device->PacketizeConnections)) { + + // + // We copy the list locally, in case someone gets + // put back on it. We have to hack the end so + // it points to LocalList instead of PacketizeConnections. + // + + LocalList = Device->PacketizeConnections; + LocalList.Flink->Blink = &LocalList; + LocalList.Blink->Flink = &LocalList; + + InitializeListHead (&Device->PacketizeConnections); + + // + // Set all these connections to not be on the list, so + // NbiStopConnection won't try to take them off. + // + + for (p = LocalList.Flink; p != &LocalList; p = p->Flink) { + Connection = CONTAINING_RECORD (p, CONNECTION, PacketizeLinkage); + CTEAssert (Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = FALSE; + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + while (TRUE) { + + p = RemoveHeadList (&LocalList); + if (p == &LocalList) { + break; + } + + Connection = CONTAINING_RECORD (p, CONNECTION, PacketizeLinkage); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->SubState == CONNECTION_SUBSTATE_A_PACKETIZE)) { + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG (LockHandle) + ); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NbiDereferenceConnection (Connection, CREF_PACKETIZE); + + } + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + } + } + +} /* NbiReceiveComplete */ + + +VOID +NbiTransferDataComplete( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status, + IN UINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine handles a transfer data complete indication from + IPX, indicating that a previously issued NdisTransferData + call has completed. + +Arguments: + + Packet - The packet associated with the transfer. + + Status - The status of the transfer. + + BytesTransferred - The number of bytes transferred. + +Return Value: + + None. + +--*/ + +{ + PNB_RECEIVE_RESERVED ReceiveReserved; + PNB_RECEIVE_BUFFER ReceiveBuffer; + PADDRESS Address; + PCONNECTION Connection; + PNDIS_BUFFER CurBuffer, TmpBuffer; + PREQUEST AdapterStatusRequest; + PDEVICE Device = NbiDevice; + CTELockHandle CancelLH; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + ReceiveReserved = (PNB_RECEIVE_RESERVED)(Packet->ProtocolReserved); + + switch (ReceiveReserved->Type) { + + case RECEIVE_TYPE_DATA: + + CTEAssert (ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = FALSE; + + Connection = ReceiveReserved->u.RR_CO.Connection; + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Status != NDIS_STATUS_SUCCESS) { + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + Connection->CurrentReceive = Connection->PreviousReceive; + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + + // + // BUGBUG: Send a resend ack? + // + + } else { + + // + // This aborts the current receive and + // releases the connection lock. + // + + NbiCompleteReceive( + Connection, + ReceiveReserved->u.RR_CO.EndOfMessage, + CancelLH + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + } else { + + + Connection->CurrentReceive.Offset += BytesTransferred; + Connection->CurrentReceive.MessageOffset += BytesTransferred; + + if (ReceiveReserved->u.RR_CO.CompleteReceive || + (Connection->State != CONNECTION_STATE_ACTIVE)) { + + if (ReceiveReserved->u.RR_CO.EndOfMessage) { + + CTEAssert (!ReceiveReserved->u.RR_CO.PartialReceive); + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; // harmless if NewNetbios is FALSE + Connection->CurrentReceive.MessageOffset = 0; + Connection->CurrentIndicateOffset = 0; + + } else if (Connection->NewNetbios) { + + if (ReceiveReserved->u.RR_CO.PartialReceive) { + Connection->CurrentIndicateOffset += BytesTransferred; + } else { + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; + Connection->CurrentIndicateOffset = 0; + } + } + + // + // This sends an ack and releases the connection lock. + // + + NbiCompleteReceive( + Connection, + ReceiveReserved->u.RR_CO.EndOfMessage, + CancelLH + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + + if (Connection->NewNetbios) { + + // + // A partial receive should only happen if we are + // completing the receive. + // + + CTEAssert (!ReceiveReserved->u.RR_CO.PartialReceive); + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; + Connection->CurrentIndicateOffset = 0; + + if ((Connection->CurrentReceiveNoPiggyback) || + ((Device->AckWindow != 0) && + (++Connection->ReceivesWithoutAck >= Device->AckWindow))) { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + } + + } + + // + // Free the NDIS buffer chain if we allocated one. + // + + if (!ReceiveReserved->u.RR_CO.NoNdisBuffer) { + + NdisQueryPacket (Packet, NULL, NULL, &CurBuffer, NULL); + + while (CurBuffer) { + TmpBuffer = NDIS_BUFFER_LINKAGE (CurBuffer); + NdisFreeBuffer (CurBuffer); + CurBuffer = TmpBuffer; + } + + } + + NdisReinitializePacket (Packet); + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &ReceiveReserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + NbiDereferenceConnection (Connection, CREF_INDICATE); + + break; + + case RECEIVE_TYPE_DATAGRAM: + + CTEAssert (ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = FALSE; + + ReceiveBuffer = ReceiveReserved->u.RR_DG.ReceiveBuffer; + + // + // Free the packet used for the transfer. + // + + ReceiveReserved->u.RR_DG.ReceiveBuffer = NULL; + NdisReinitializePacket (Packet); + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &ReceiveReserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + // + // If it succeeded then queue it for indication, + // otherwise free the receive buffer also. + // + + if (Status == STATUS_SUCCESS) { + + ReceiveBuffer->DataLength = BytesTransferred; + NB_INSERT_HEAD_LIST( + &Device->ReceiveDatagrams, + &ReceiveBuffer->WaitLinkage, + &Device->Lock); + + } else { + + Address = ReceiveBuffer->Address; + +#if defined(_PNP_POWER) + NbiPushReceiveBuffer ( ReceiveBuffer ); +#else + NB_PUSH_ENTRY_LIST( + &Device->ReceiveBufferList, + &ReceiveBuffer->PoolLinkage, + &Device->Lock); +#endif _PNP_POWER + + NbiDereferenceAddress (Address, AREF_FIND); + + } + + break; + + case RECEIVE_TYPE_ADAPTER_STATUS: + + CTEAssert (ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = FALSE; + + AdapterStatusRequest = ReceiveReserved->u.RR_AS.Request; + + // + // Free the packet used for the transfer. + // + + NdisReinitializePacket (Packet); + ExInterlockedPushEntrySList( + &Device->ReceivePacketList, + &ReceiveReserved->PoolLinkage, + &NbiGlobalPoolInterlock); + + // + // Complete the request. + // + + if (Status == STATUS_SUCCESS) { + + // + // REQUEST_STATUS() is already to set to SUCCESS or + // BUFFER_OVERFLOW based on whether the buffer was + // big enough. + // + + REQUEST_INFORMATION(AdapterStatusRequest) = BytesTransferred; + + } else { + + REQUEST_INFORMATION(AdapterStatusRequest) = 0; + REQUEST_STATUS(AdapterStatusRequest) = STATUS_UNEXPECTED_NETWORK_ERROR; + + } + + NbiCompleteRequest (AdapterStatusRequest); + NbiFreeRequest (Device, AdapterStatusRequest); + + NbiDereferenceDevice (Device, DREF_STATUS_QUERY); + + break; + + } + +} /* NbiTransferDataComplete */ + + +VOID +NbiAcknowledgeReceive( + IN PCONNECTION Connection + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is called when a receive needs to be acked to + the remote. It either sends a data ack or queues up a piggyback + ack request. + + NOTE: THIS FUNCTION IS CALLED WITH THE CONNECTION LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - Pointer to the connection. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + + if (Connection->NewNetbios) { + + // + // CurrentReceiveNoPiggyback is based on the bits he + // set in his frame, NoPiggybackHeuristic is based on + // guesses about the traffic pattern, it is set to + // TRUE if we think we should not piggyback. + // + + if ((!Device->EnablePiggyBackAck) || + (Connection->CurrentReceiveNoPiggyback) || + (Connection->PiggybackAckTimeout) || + (Connection->NoPiggybackHeuristic)) { + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + if (!Connection->DataAckPending) { + + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + // + // Some stacks can have multiple messages + // outstanding, so we may already have an + // ack queued. + // + + Connection->DataAckTimeouts = 0; + Connection->DataAckPending = TRUE; + + ++Device->Statistics.PiggybackAckQueued; + + if (!Connection->OnDataAckQueue) { + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle1); + + if (!Connection->OnDataAckQueue) { + Connection->OnDataAckQueue = TRUE; + InsertTailList (&Device->DataAckConnections, &Connection->DataAckLinkage); + } + + if (!Device->DataAckActive) { + NbiStartShortTimer (Device); + Device->DataAckActive = TRUE; + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle1); + } + + // + // Clear this, since a message ack resets the count. + // + + Connection->ReceivesWithoutAck = 0; + + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + } else { + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + +} + + +VOID +NbiCompleteReceive( + IN PCONNECTION Connection, + IN BOOLEAN EndOfMessage, + IN CTELockHandle CancelLH + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is called when we have filled up a receive request + and need to complete it. + + NOTE: THIS FUNCTION IS CALLED WITH THE CONNECTION LOCK HELD + AND RETURNS WITH IT RELEASED. + + THIS ROUTINE ALSO HOLDS CANCEL SPIN LOCK WHEN IT IS CALLED + AND RELEASES IT WHEN IT RETURNS. +Arguments: + + Connection - Pointer to the connection. + + EndOfMessage - BOOLEAN set to true if the message end was received. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request; + PDEVICE Device = NbiDevice; + + // + // Complete the current receive request. If the connection + // has shut down then we complete it right here, otherwise + // we queue it for completion in the receive complete + // handler. + // + + Request = Connection->ReceiveRequest; + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + if (Connection->State != CONNECTION_STATE_ACTIVE) { + + Connection->ReceiveRequest = NULL; // StopConnection won't do this + + REQUEST_STATUS(Request) = Connection->Status; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NB_DEBUG2 (RECEIVE, ("Completing receive %lx (%d), status %lx\n", + Request, REQUEST_INFORMATION(Request), REQUEST_STATUS(Request))); + + NbiCompleteRequest (Request); + NbiFreeRequest (NbiDevice, Request); + + ++Connection->ConnectionInfo.ReceiveErrors; + + NbiDereferenceConnection (Connection, CREF_RECEIVE); + + } else { + + REQUEST_INFORMATION (Request) = Connection->CurrentReceive.Offset; + + if (EndOfMessage) { + + REQUEST_STATUS(Request) = STATUS_SUCCESS; + + } else { + + REQUEST_STATUS(Request) = STATUS_BUFFER_OVERFLOW; + + } + + // + // If we indicated to the client, adjust this down by the + // amount of data taken, when it hits zero we can reindicate. + // + + if (Connection->ReceiveUnaccepted) { + NB_DEBUG2 (RECEIVE, ("Moving Unaccepted %d down by %d\n", + Connection->ReceiveUnaccepted, Connection->CurrentReceive.Offset)); + if (Connection->CurrentReceive.Offset >= Connection->ReceiveUnaccepted) { + Connection->ReceiveUnaccepted = 0; + } else { + Connection->ReceiveUnaccepted -= Connection->CurrentReceive.Offset; + } + } + + // + // BUGBUG: Check whether to activate another receive? + // + + Connection->ReceiveState = CONNECTION_RECEIVE_PENDING; + Connection->ReceiveRequest = NULL; + + // + // This releases the lock. + // + + if (Connection->NewNetbios) { + + if (EndOfMessage) { + + NbiAcknowledgeReceive( + Connection + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + if (Connection->CurrentIndicateOffset != 0) { + + NbiSendDataAck( + Connection, + NbiAckResend + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else if ((Connection->CurrentReceiveNoPiggyback) || + ((Device->AckWindow != 0) && + (++Connection->ReceivesWithoutAck >= Device->AckWindow))) { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + } + + } else { + + NbiSendDataAck( + Connection, + EndOfMessage ? NbiAckResponse : NbiAckResend + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + ++Connection->ConnectionInfo.ReceivedTsdus; + + // + // This will complete the request inside ReceiveComplete, + // dereference the connection, and set the state to IDLE. + // + + NB_INSERT_TAIL_LIST( + &Device->ReceiveCompletionQueue, + REQUEST_LINKAGE (Request), + &Device->Lock); + + } + +} /* NbiCompleteReceive */ + + +NTSTATUS +NbiTdiReceive( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine does a receive on an active connection. + +Arguments: + + Device - The netbios device. + + Request - The request describing the receive. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + PCONNECTION Connection; + NB_DEFINE_SYNC_CONTEXT (SyncContext) + NB_DEFINE_LOCK_HANDLE (LockHandle) + CTELockHandle CancelLH; + + // + // First make sure the connection is valid. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + if (Connection->Type == NB_CONNECTION_SIGNATURE) { + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_BEGIN_SYNC (&SyncContext); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // Make sure the connection is in a good state. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + // + // If the connection is idle then send it now, otherwise + // queue it. + // + + + if (!Request->Cancel) { + + IoSetCancelRoutine (Request, NbiCancelReceive); + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiReferenceConnectionSync (Connection, CREF_RECEIVE); + + // + // Insert this in our queue, then see if we need + // to wake up the remote. + // + + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->ReceiveQueue, Request); + + if (Connection->ReceiveState != CONNECTION_RECEIVE_W_RCV) { + + NB_DEBUG2 (RECEIVE, ("Receive %lx, connection %lx idle\n", Request, Connection)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } else { + + NB_DEBUG2 (RECEIVE, ("Receive %lx, connection %lx awakened\n", Request, Connection)); + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + + // + // This releases the lock. + // + + if (Connection->NewNetbios) { + + Connection->LocalRcvSequenceMax = (USHORT) + (Connection->ReceiveSequence + Connection->ReceiveWindowSize - 1); + + } + + NbiSendDataAck( + Connection, + NbiAckResend + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + NB_END_SYNC (&SyncContext); + return STATUS_PENDING; + + } else { + + NB_DEBUG2 (RECEIVE, ("Receive %lx, connection %lx cancelled\n", Request, Connection)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_CANCELLED; + + } + + } else { + + NB_DEBUG2 (RECEIVE, ("Receive connection %lx state is %d\n", Connection, Connection->State)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_INVALID_CONNECTION; + + } + + } else { + + NB_DEBUG (RECEIVE, ("Receive connection %lx has bad signature\n", Connection)); + return STATUS_INVALID_CONNECTION; + + } + +} /* NbiTdiReceive */ + + +VOID +NbiCancelReceive( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a receive. + The request is found on the connection's receive queue. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + PCONNECTION Connection; + PREQUEST Request = (PREQUEST)Irp; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_RECEIVE)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + + // + // Just stop the connection, that will tear down any + // receives. + // + // BUGBUG: Do we care about cancelling non-active + // receives without stopping the connection?? + // + // BUGBUG: This routine is the same as NbiCancelSend, + // so if we don't make it more specific, merge the two. + // + + NbiReferenceConnectionSync (Connection, CREF_CANCEL); + + IoReleaseCancelSpinLock (Irp->CancelIrql); + + + NB_BEGIN_SYNC (&SyncContext); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // This frees the lock, cancels any sends, etc. + // + + NbiStopConnection( + Connection, + STATUS_CANCELLED + NB_LOCK_HANDLE_ARG (LockHandle)); + + NbiDereferenceConnection (Connection, CREF_CANCEL); + + NB_END_SYNC (&SyncContext); + +} /* NbiCancelReceive */ + diff --git a/private/ntos/tdi/isnp/nb/send.c b/private/ntos/tdi/isnp/nb/send.c new file mode 100644 index 000000000..a4443c73c --- /dev/null +++ b/private/ntos/tdi/isnp/nb/send.c @@ -0,0 +1,2886 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + send.c + +Abstract: + + This module contains the send routines for the Netbios + module of the ISN transport. + +Author: + + Adam Barr (adamba) 22-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + + +VOID +NbiSendComplete( + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status +) + +/*++ + +Routine Description: + + This routine handles a send completion call from IPX. + +Arguments: + + Packet - The packet which has been completed. + + Status - The status of the send. + +Return Value: + + None. + +--*/ + + + +{ + PDEVICE Device = NbiDevice; + PADDRESS Address; + PADDRESS_FILE AddressFile; + PCONNECTION Connection; + PREQUEST DatagramRequest; + PREQUEST SendRequest, TmpRequest; + PNDIS_BUFFER CurBuffer, TmpBuffer; + PNETBIOS_CACHE CacheName; + PNDIS_BUFFER SecondBuffer; + PVOID SecondBufferMemory; + UINT SecondBufferLength; + ULONG oldvalue; + PNB_SEND_RESERVED Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + CTELockHandle CancelLH; +#if defined(_PNP_POWER) + CTELockHandle LockHandle; +#endif _PNP_POWER + + // + // We jump back here if we re-call send from inside this + // function and it doesn't pend (to avoid stack overflow). + // + +FunctionStart:; + + ++Device->Statistics.PacketsSent; + + switch (Reserved->Type) { + + case SEND_TYPE_SESSION_DATA: + + // + // This was a send on a session. This references the + // IRP. + // + + NB_DEBUG2 (SEND, ("Complete NDIS packet %lx\n", Reserved)); + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + Connection = Reserved->u.SR_CO.Connection; + SendRequest = Reserved->u.SR_CO.Request; + + if (!Reserved->u.SR_CO.NoNdisBuffer) { + + CurBuffer = NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)); + while (CurBuffer) { + TmpBuffer = NDIS_BUFFER_LINKAGE (CurBuffer); + NdisFreeBuffer (CurBuffer); + CurBuffer = TmpBuffer; + } + + } + + // + // If NoNdisBuffer is TRUE, then we could set + // Connection->SendBufferInUse to FALSE here. The + // problem is that a new send might be in progress + // by the time this completes and it may have + // used the user buffer, then if we need to + // retransmit that packet we would use the buffer + // twice. We instead rely on the fact that whenever + // we make a new send active we set SendBufferInUse + // to FALSE. The net effect is that the user's buffer + // can be used the first time a send is packetize + // but not on resends. + // + + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = NULL; + NdisRecalculatePacketCounts (Packet); + +#if DBG + if (REQUEST_REFCOUNT(SendRequest) > 100) { + DbgPrint ("Request %lx (%lx) has high refcount\n", + Connection, SendRequest); + DbgBreakPoint(); + } +#endif + +#if defined(__PNP) + NB_GET_LOCK( &Connection->Lock, &LockHandle ); + oldvalue = REQUEST_REFCOUNT(SendRequest)--; + if ( DEVICE_NETWORK_PATH_NOT_FOUND == Status ) { + Connection->LocalTarget = Reserved->LocalTarget; + } + NB_FREE_LOCK( &Connection->Lock, LockHandle ); +#else + oldvalue = NB_ADD_ULONG( + &REQUEST_REFCOUNT (SendRequest), + (ULONG)-1, + &Connection->Lock); +#endif __PNP + + if (oldvalue == 1) { + + // + // If the refcount on this request is now zero then + // we already got the ack for it, which means + // that the ack-processing code has unlinked the + // request from Connection->SendQueue. So we + // can just run the queue of connections here + // and complete them. + // + // We dereference the connection for all but one + // of the requests, we hang on to that until a bit + // later so everything stays around. + // + + while (TRUE) { + + TmpRequest = REQUEST_SINGLE_LINKAGE (SendRequest); + NB_DEBUG2 (SEND, ("Completing request %lx from send complete\n", SendRequest)); + REQUEST_STATUS (SendRequest) = STATUS_SUCCESS; + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (SendRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiCompleteRequest (SendRequest); + NbiFreeRequest (Device, SendRequest); + ++Connection->ConnectionInfo.TransmittedTsdus; + SendRequest = TmpRequest; + + if (SendRequest == NULL) { + break; + } + NbiDereferenceConnection (Connection, CREF_SEND); + + } + + } + + if (Reserved->OwnedByConnection) { + + Connection->SendPacketInUse = FALSE; + + if (Connection->OnWaitPacketQueue) { + + // + // This will put the connection on the packetize + // queue if appropriate. + // + + NbiCheckForWaitPacket (Connection); + + } + + } else { + + NbiPushSendPacket(Reserved); + + } + + if (oldvalue == 1) { + NbiDereferenceConnection (Connection, CREF_SEND); + } + + break; + + case SEND_TYPE_NAME_FRAME: + + // + // The frame is an add name/delete name; put it back in + // the pool and deref the address. + // + + CTEAssert (Reserved->SendInProgress); + + Address = Reserved->u.SR_NF.Address; + +#if !defined(_PNP_POWER) + if ((Reserved->u.SR_NF.CurrentNicId) && + (Reserved->u.SR_NF.CurrentNicId < Device->MaximumNicId)) { + + NB_CONNECTIONLESS UNALIGNED * Header; + IPX_LOCAL_TARGET TempLocalTarget; + + // + // This is a name frame being sent to every address, so + // resent it to the next NIC ID. We hold the address + // reference through this send. + // + + CTEAssert (Address != NULL); + + ++Reserved->u.SR_NF.CurrentNicId; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256; + + Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04); + + // + // Now fill in the Netbios header. + // + + RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); + Header->NameFrame.ConnectionControlFlag = 0x00; + Header->NameFrame.DataStreamType = Reserved->u.SR_NF.DataStreamType; + Header->NameFrame.NameTypeFlag = Reserved->u.SR_NF.NameTypeFlag; + + // + // This is not a name in use frame so DataStreamType2 + // is the same as DataStreamType. + // + + Header->NameFrame.DataStreamType2 = Reserved->u.SR_NF.DataStreamType; + + RtlCopyMemory( + Header->NameFrame.Name, + Address->NetbiosAddress.NetbiosName, + 16); + + // + // Now send the frame (because it is all in the first segment, + // IPX will adjust the length of the buffer correctly). + // + + TempLocalTarget.NicId = Reserved->u.SR_NF.CurrentNicId; + RtlCopyMemory (TempLocalTarget.MacAddress, BroadcastAddress, 6); + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)); + if ((Status = + (*Device->Bind.SendHandler)( + &TempLocalTarget, + Packet, + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), + sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) { + + goto FunctionStart; + + } + + return; + + } +#endif !_PNP_POWER + + Reserved->SendInProgress = FALSE; + + NbiPushSendPacket (Reserved); + + if (Address) { + NbiDereferenceAddress (Address, AREF_NAME_FRAME); + } else { + NbiDereferenceDevice (Device, DREF_NAME_FRAME); + } + + break; + + case SEND_TYPE_SESSION_INIT: + + // + // This is a session initialize or session init ack; free + // the second buffer, put the packet back in the pool and + // deref the device. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + NdisUnchainBufferAtBack (Packet, &SecondBuffer); + NdisQueryBuffer (SecondBuffer, &SecondBufferMemory, &SecondBufferLength); + CTEAssert (SecondBufferLength == sizeof(NB_SESSION_INIT)); + + NdisFreeBuffer(SecondBuffer); + NbiFreeMemory (SecondBufferMemory, sizeof(NB_SESSION_INIT), MEMORY_CONNECTION, "Session Initialize"); + + NbiPushSendPacket (Reserved); + + NbiDereferenceDevice (Device, DREF_SESSION_INIT); + + break; + + case SEND_TYPE_SESSION_NO_DATA: + + // + // This is a frame which was sent on a connection but + // has no data (ack, session end, session end ack). + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + Connection = Reserved->u.SR_CO.Connection; + + if (Reserved->OwnedByConnection) { + + CTEAssert (Connection != NULL); + Connection->SendPacketInUse = FALSE; + + if (Connection->OnWaitPacketQueue) { + + // + // This will put the connection on the packetize + // queue if appropriate. + // + + NbiCheckForWaitPacket (Connection); + + } + + } else { + + NbiPushSendPacket(Reserved); + + } + + if (Connection != NULL) { + NbiDereferenceConnection (Connection, CREF_FRAME); + } else { + NbiDereferenceDevice (Device, DREF_FRAME); + } + + break; + + case SEND_TYPE_FIND_NAME: + + // + // The frame is a find name; just set SendInProgress to + // FALSE and FindNameTimeout will clean it up. + // +#if defined(_PNP_POWER) + NB_GET_LOCK( &Device->Lock, &LockHandle); + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + // + // We keep track of when it finds a net that isn't + // a down wan line so that we can tell when datagram + // sends should fail (otherwise we succeed them, so + // the browser won't think this is a down wan line). + // + if ( STATUS_SUCCESS == Status ) { + NB_SET_SR_FN_SENT_ON_UP_LINE (Reserved, TRUE); + } else { + NB_DEBUG( CACHE, ("Send complete of find name with failure %lx\n",Status )); + } + NB_FREE_LOCK(&Device->Lock, LockHandle); +#else + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; +#endif _PNP_POWER + break; + + case SEND_TYPE_DATAGRAM: + + // + // If there are any more networks to send this on then + // do so, otherwise put it back in the pool and complete + // the request. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + if ((Reserved->u.SR_DG.Cache == NULL) || + (++Reserved->u.SR_DG.CurrentNetwork >= + Reserved->u.SR_DG.Cache->NetworksUsed)) { + + AddressFile = Reserved->u.SR_DG.AddressFile; + DatagramRequest = Reserved->u.SR_DG.DatagramRequest; + + NB_DEBUG2 (DATAGRAM, ("Completing datagram %lx on %lx\n", DatagramRequest, AddressFile)); + + // + // Remove any user buffers chained on this packet. + // + + NdisReinitializePacket (Packet); + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = NULL; + NdisChainBufferAtFront (Packet, Reserved->HeaderBuffer); + + // + // Complete the request. + // + + REQUEST_STATUS(DatagramRequest) = Status; + + NbiCompleteRequest(DatagramRequest); + NbiFreeRequest (Device, DatagramRequest); + + CacheName = Reserved->u.SR_DG.Cache; + + NbiPushSendPacket (Reserved); + + // + // Since we are no longer referencing the cache + // name, see if we should delete it (this will + // happen if the cache entry was aged out while + // the datagram was being processed). + // + + if (CacheName != NULL) { + + oldvalue = NB_ADD_ULONG( + &CacheName->ReferenceCount, + (ULONG)-1, + &Device->Lock); + + if (oldvalue == 1) { + + NB_DEBUG2 (CACHE, ("Free aged cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Free old cache"); + + } + } + + NbiDereferenceAddressFile (AddressFile, AFREF_SEND_DGRAM); + + } else { + + NB_CONNECTIONLESS UNALIGNED * Header; + PIPX_LOCAL_TARGET LocalTarget; + ULONG HeaderLength; + ULONG PacketLength; + + // send the datagram on the next net. + CTEAssert (!Reserved->u.SR_DG.Cache->Unique); + Reserved->SendInProgress = TRUE; + + CacheName = Reserved->u.SR_DG.Cache; + + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address, so we modify + // that for the current netbios cache entry if needed. + // + + Header = (NB_CONNECTIONLESS UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); + + + *(UNALIGNED ULONG *)Header->IpxHeader.DestinationNetwork = CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].Network; + RtlCopyMemory (&Header->IpxHeader.DestinationNode, BroadcastAddress, 6); + + LocalTarget = &CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].LocalTarget; + + + HeaderLength = sizeof(IPX_HEADER) + sizeof(NB_DATAGRAM); + + PacketLength = HeaderLength + REQUEST_INFORMATION(Reserved->u.SR_DG.DatagramRequest); + + Header->IpxHeader.PacketLength[0] = (UCHAR)(PacketLength / 256); + Header->IpxHeader.PacketLength[1] = (UCHAR)(PacketLength % 256); + Header->IpxHeader.PacketType = 0x04; + + + // + // Now fill in the Netbios header. + // + + Header->Datagram.ConnectionControlFlag = 0x00; + RtlCopyMemory( + Header->Datagram.SourceName, + Reserved->u.SR_DG.AddressFile->Address->NetbiosAddress.NetbiosName, + 16); + + if (Reserved->u.SR_DG.RemoteName != (PVOID)-1) { + + // + // This is a directed, as opposed to broadcast, datagram. + // + + Header->Datagram.DataStreamType = NB_CMD_DATAGRAM; + RtlCopyMemory( + Header->Datagram.DestinationName, + Reserved->u.SR_DG.RemoteName->NetbiosName, + 16); + + } else { + + Header->Datagram.DataStreamType = NB_CMD_BROADCAST_DATAGRAM; + RtlZeroMemory( + Header->Datagram.DestinationName, + 16); + + } + + + // + // Now send the frame (IPX will adjust the length of the + // first buffer and the whole frame correctly). + // + + if ((Status = + (*Device->Bind.SendHandler)( + LocalTarget, + Packet, + PacketLength, + HeaderLength)) != STATUS_PENDING) { + + goto FunctionStart; + } + + } + + break; + + case SEND_TYPE_STATUS_QUERY: + + // + // This is an adapter status query, which is a simple + // packet. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + NbiPushSendPacket (Reserved); + + NbiDereferenceDevice (Device, DREF_STATUS_FRAME); + + break; + + case SEND_TYPE_STATUS_RESPONSE: + + // + // This is an adapter status response, we have to free the + // second buffer. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + + NdisUnchainBufferAtBack (Packet, &SecondBuffer); + NdisQueryBuffer (SecondBuffer, &SecondBufferMemory, &SecondBufferLength); + + NdisFreeBuffer(SecondBuffer); + NbiFreeMemory (SecondBufferMemory, Reserved->u.SR_AS.ActualBufferLength, MEMORY_STATUS, "Adapter Status"); + + NbiPushSendPacket (Reserved); + + NbiDereferenceDevice (Device, DREF_STATUS_RESPONSE); + + break; + +#ifdef RSRC_TIMEOUT_DBG + case SEND_TYPE_DEATH_PACKET: + + // + // This is a session initialize or session init ack; free + // the second buffer, put the packet back in the pool and + // deref the device. + // + + CTEAssert (Reserved->SendInProgress); + Reserved->SendInProgress = FALSE; + DbgPrint("********Death packet send completed status %lx\n",Status); + DbgBreakPoint(); + break; +#endif //RSRC_TIMEOUT_DBG + + default: + + CTEAssert (FALSE); + break; + + } + +} /* NbiSendComplete */ + +#if 0 +ULONG NbiLoudSendQueue = 1; +#endif + +VOID +NbiAssignSequenceAndSend( + IN PCONNECTION Connection, + IN PNDIS_PACKET Packet + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is used to ensure that receive sequence numbers on + packets are numbered correctly. It is called in place of the lower-level + send handler; after assigning the receive sequence number it locks out + other sends until the NdisSend call has returned (not necessarily completed), + insuring that the packets with increasing receive sequence numbers + are queue in the right order by the MAC. + + NOTE: THIS ROUTINE IS CALLED WITH THE CONNECTION LOCK HELD, AND + RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection the send is on. + + Packet - The packet to send. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS NdisStatus; + PNB_SEND_RESERVED Reserved; + PLIST_ENTRY p; + NB_CONNECTION UNALIGNED * Header; + PDEVICE Device = NbiDevice; + BOOLEAN NdisSendReference; + ULONG result; + + + Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + + CTEAssert (Connection->State == CONNECTION_STATE_ACTIVE); + + // + // If there is a send in progress, then queue this packet + // and return. + // + + if (Connection->NdisSendsInProgress > 0) { + + NB_DEBUG2 (SEND, ("Queueing send packet %lx on %lx\n", Reserved, Connection)); + InsertTailList (&Connection->NdisSendQueue, &Reserved->WaitLinkage); + ++Connection->NdisSendsInProgress; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + } + + // + // No send in progress. Set the flag to true, and fill in the + // receive sequence fields in the packet. + // + + Connection->NdisSendsInProgress = 1; + NdisSendReference = FALSE; + Connection->NdisSendReference = &NdisSendReference; + + while (TRUE) { + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + Header->Session.ReceiveSequence = Connection->ReceiveSequence; + if (Connection->NewNetbios) { + Header->Session.ReceiveSequenceMax = Connection->LocalRcvSequenceMax; + } else { + Header->Session.BytesReceived = (USHORT)Connection->CurrentReceive.MessageOffset; + } + + // + // Since we are acking as much as we know, we can clear + // this flag. The connection will eventually get removed + // from the queue by the long timeout. + // + + Connection->DataAckPending = FALSE; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + NdisStatus = (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + Reserved->u.SR_CO.PacketLength, + sizeof(NB_CONNECTION)); + + if (NdisStatus != NDIS_STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + + // + // Take the ref count down, which may allow others + // to come through. + // + + result = NB_ADD_ULONG( + &Connection->NdisSendsInProgress, + (ULONG)-1, + &Connection->Lock); + + // + // We have now sent a packet, see if any queued up while we + // were doing it. If the count was zero after removing ours, + // then anything else queued is being processed, so we can + // exit. If the connection was stopped while we were sending, + // a special reference was added which we remove (NbiStopConnection + // sets NdisSendReference to TRUE, using the pointer saved + // in Connection->NdisSendReference). + // + + if (result == 1) { + if (NdisSendReference) { + NB_DEBUG2 (SEND, ("Remove CREF_NDIS_SEND from %lx\n", Connection)); + NbiDereferenceConnection (Connection, CREF_NDIS_SEND); + } + return; + } + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + p = RemoveHeadList(&Connection->NdisSendQueue); + + // + // If the refcount was not zero, then nobody else should + // have taken packets off since they would have been + // blocked by us. So, the queue should not be empty. + // + + ASSERT (p != &Connection->NdisSendQueue); + + Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } // while loop + + // + // We should never reach here. + // + + CTEAssert (FALSE); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + +} /* NbiAssignSequenceAndSend */ + + +NTSTATUS +NbiTdiSend( + IN PDEVICE Device, + IN PREQUEST Request + ) + +/*++ + +Routine Description: + + This routine does a send on an active connection. + +Arguments: + + Device - The netbios device. + + Request - The request describing the send. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PCONNECTION Connection; + PTDI_REQUEST_KERNEL_SEND Parameters; + NB_DEFINE_SYNC_CONTEXT (SyncContext) + NB_DEFINE_LOCK_HANDLE (LockHandle) + CTELockHandle CancelLH; + + // + // First make sure the connection is valid. + // + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + if (Connection->Type == NB_CONNECTION_SIGNATURE) { + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_BEGIN_SYNC (&SyncContext); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // Make sure the connection is in a good state. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + // + // If the connection is idle then send it now, otherwise + // queue it. + // + + + if (!Request->Cancel) { + + + Parameters = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request); + + // + // For old netbios, don't allow sends greater than 64K-1. + // + + if ((Connection->NewNetbios) || + (Parameters->SendLength <= 0xffff)) { + + IoSetCancelRoutine (Request, NbiCancelSend); + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); + NB_FREE_CANCEL_LOCK( CancelLH ); + + REQUEST_INFORMATION (Request) = Parameters->SendLength; // assume it succeeds. + + REQUEST_REFCOUNT (Request) = 1; // refcount starts at 1. + NbiReferenceConnectionSync (Connection, CREF_SEND); + + // + // NOTE: The connection send queue is managed such + // that the current send being packetized is not on + // the queue. For multiple-request messages, the + // first one is not on the queue, but its linkage + // field points to the next request in the message + // (which will be on the head of the queue). + // + + if ((Parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { + + // + // This is a final send. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_A_IDLE) { + + NB_DEBUG2 (SEND, ("Send %lx, connection %lx idle\n", Request, Connection)); + + Connection->CurrentSend.Request = Request; + Connection->CurrentSend.MessageOffset = 0; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentSend.BufferOffset = 0; + Connection->SendBufferInUse = FALSE; + + Connection->UnAckedSend = Connection->CurrentSend; + + Connection->FirstMessageRequest = Request; +#ifdef RSRC_TIMEOUT_DBG + KeQuerySystemTime(&Connection->FirstMessageRequestTime); + + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Connection->FirstMessageRequestTime.QuadPart; +#endif //RSRC_TIMEOUT_DBG + + Connection->LastMessageRequest = Request; + Connection->CurrentMessageLength = Parameters->SendLength; + + // + // This frees the connection lock. + // + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } else if (Connection->SubState == CONNECTION_SUBSTATE_A_W_EOR) { + + // + // We have been collecting partial sends waiting + // for a final one, which we have now received, + // so start packetizing. + // + // We chain it on the back of the send queue, + // in addition if this is the second request in the + // message, we have to link the first request (which + // is not on the queue) to this one. + // + // + + NB_DEBUG2 (SEND, ("Send %lx, connection %lx got eor\n", Request, Connection)); + + Connection->LastMessageRequest = Request; + Connection->CurrentMessageLength += Parameters->SendLength; + + if (Connection->SendQueue.Head == NULL) { + REQUEST_SINGLE_LINKAGE(Connection->FirstMessageRequest) = Request; + } + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request); + + Connection->UnAckedSend = Connection->CurrentSend; +#ifdef RSRC_TIMEOUT_DBG + { + LARGE_INTEGER Time; + + KeQuerySystemTime(&Time); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Time.QuadPart; + } +#endif //RSRC_TIMEOUT_DBG + // + // This frees the connection lock. + // + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } else { + + // + // The state is PACKETIZE, W_ACK, or W_PACKET. + // + + NB_DEBUG2 (SEND, ("Send %lx, connection %lx busy\n", Request, Connection)); + + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request); + +#ifdef RSRC_TIMEOUT_DBG + { + LARGE_INTEGER Time; + KeQuerySystemTime(&Time); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Time.QuadPart; + } +#endif //RSRC_TIMEOUT_DBG + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + // + // This is a partial send. We queue them up without + // packetizing until we get a final (this is because + // we have to put a correct Connection->CurrentMessageLength + // in the frames. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_A_IDLE) { + + // + // Start collecting partial sends. NOTE: Partial sends + // are always inserted in the send queue + // + + Connection->CurrentSend.Request = Request; + Connection->CurrentSend.MessageOffset = 0; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentSend.BufferOffset = 0; + Connection->SendBufferInUse = FALSE; + + Connection->FirstMessageRequest = Request; +#ifdef RSRC_TIMEOUT_DBG + KeQuerySystemTime(&Connection->FirstMessageRequestTime); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Connection->FirstMessageRequestTime.QuadPart; +#endif //RSRC_TIMEOUT_DBG + + Connection->CurrentMessageLength = Parameters->SendLength; + + Connection->SubState = CONNECTION_SUBSTATE_A_W_EOR; + + } else if (Connection->SubState == CONNECTION_SUBSTATE_A_W_EOR) { + + // + // We have got another partial send to add to our + // list. We chain it on the back of the send queue, + // in addition if this is the second request in the + // message, we have to link the first request (which + // is not on the queue) to this one. + // + + Connection->LastMessageRequest = Request; + Connection->CurrentMessageLength += Parameters->SendLength; + + if (Connection->SendQueue.Head == NULL) { + REQUEST_SINGLE_LINKAGE(Connection->FirstMessageRequest) = Request; + } + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request); +#ifdef RSRC_TIMEOUT_DBG + { + LARGE_INTEGER Time; + KeQuerySystemTime(&Time); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Time.QuadPart; + } +#endif //RSRC_TIMEOUT_DBG + } else { + + REQUEST_SINGLE_LINKAGE(Request) = NULL; + REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request); + +#ifdef RSRC_TIMEOUT_DBG + { + LARGE_INTEGER Time; + KeQuerySystemTime(&Time); + (((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart = + Time.QuadPart; + } +#endif //RSRC_TIMEOUT_DBG + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NB_END_SYNC (&SyncContext); + return STATUS_PENDING; + + } else { + + NB_DEBUG2 (SEND, ("Send %lx, too long for connection %lx (%d)\n", Request, Connection, Parameters->SendLength)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_INVALID_PARAMETER; + + } + + } else { + + NB_DEBUG2 (SEND, ("Send %lx, connection %lx cancelled\n", Request, Connection)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_CANCELLED; + + } + + } else { + + NB_DEBUG (SEND, ("Send connection %lx state is %d\n", Connection, Connection->State)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_END_SYNC (&SyncContext); + NB_FREE_CANCEL_LOCK( CancelLH ); + return STATUS_INVALID_CONNECTION; + + } + + } else { + + NB_DEBUG (SEND, ("Send connection %lx has bad signature\n", Connection)); + return STATUS_INVALID_CONNECTION; + + } + +} /* NbiTdiSend */ + + +VOID +NbiPacketizeSend( + IN PCONNECTION Connection + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine does a send on an active connection. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection. + + LockHandle - The handle used to acquire the lock. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request; + PNDIS_PACKET Packet; + PNDIS_BUFFER BufferChain; + PNB_SEND_RESERVED Reserved; + PDEVICE Device = NbiDevice; + NB_CONNECTION UNALIGNED * Header; + ULONG PacketLength; + ULONG PacketSize; + ULONG DesiredLength; + ULONG ActualLength; + NTSTATUS Status; + PSINGLE_LIST_ENTRY s; + USHORT ThisSendSequence; + USHORT ThisOffset; + BOOLEAN ExitAfterSend; + UCHAR ConnectionControlFlag; + CTELockHandle DeviceLockHandle; + + // + // We jump back here if we are talking new Netbios and it + // is OK to packetize another send. + // + +SendAnotherPacket: + + // + // If we decide to packetize another send after this, we + // change ExitAfterSend to FALSE and SubState to PACKETIZE. + // Right now we don't change SubState in case it is W_PACKET. + // + + ExitAfterSend = TRUE; + + CTEAssert (Connection->CurrentSend.Request != NULL); + + if (Connection->NewNetbios) { + + // + // Check that we have send window, both that advertised + // by the remote and our own locally-decided window which + // may be smaller. + // + + if (((USHORT)(Connection->CurrentSend.SendSequence-1) == Connection->RemoteRcvSequenceMax) || + (((USHORT)(Connection->CurrentSend.SendSequence - Connection->UnAckedSend.SendSequence)) >= Connection->SendWindowSize)) { + + // + // Keep track of whether we are waiting because of his window + // or because of our local window. If it is because of our local + // window then we may want to adjust it after this window + // is acked. + // + + if ((USHORT)(Connection->CurrentSend.SendSequence-1) != Connection->RemoteRcvSequenceMax) { + Connection->SubState = CONNECTION_SUBSTATE_A_W_ACK; + NB_DEBUG2 (SEND, ("Connection %lx local shut down at %lx, %lx\n", Connection, Connection->CurrentSend.SendSequence, Connection->UnAckedSend.SendSequence)); + } else { + Connection->SubState = CONNECTION_SUBSTATE_A_REMOTE_W; + NB_DEBUG2 (SEND, ("Connection %lx remote shut down at %lx\n", Connection, Connection->CurrentSend.SendSequence)); + } + + // + // Start the timer so we will keep bugging him about + // this. BUGBUG: What if he doesn't get a receive down + // quickly -- but this is better than losing his ack + // and then dying. We won't really back off our timer + // because we will keep getting acks, and resetting it. + // + + NbiStartRetransmit (Connection); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + + } + + } + + Request = Connection->CurrentSend.Request; + + // + // If we are in this routine then we know that + // we are coming out of IDLE, W_ACK, or W_PACKET + // and we still have the lock held. We also know + // that there is a send request in progress. If + // an ack for none or part of the last packet was + // received, then our send pointers have been + // adjusted to reflect that. + // + + // + // First get a packet for the current send. + // + + if (!Connection->SendPacketInUse) { + + Connection->SendPacketInUse = TRUE; + Packet = PACKET(&Connection->SendPacket); + Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + + } else { + + s = ExInterlockedPopEntrySList( + &Device->SendPacketList, + &NbiGlobalPoolInterlock); + + if (s == NULL) { + + // + // This function tries to allocate another packet pool. + // + + s = NbiPopSendPacket(Device, FALSE); + + if (s == NULL) { + + // + // It is possible to come in here and already be in + // W_PACKET state -- this is because we may packetize + // when in that state, and rather than always be + // checking that we weren't in W_PACKET, we go + // ahead and check again here. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_W_PACKET) { + + Connection->SubState = CONNECTION_SUBSTATE_A_W_PACKET; + + NB_GET_LOCK (&Device->Lock, &DeviceLockHandle); + if (!Connection->OnWaitPacketQueue) { + + NbiReferenceConnectionLock (Connection, CREF_W_PACKET); + + Connection->OnWaitPacketQueue = TRUE; + + InsertTailList( + &Device->WaitPacketConnections, + &Connection->WaitPacketLinkage + ); + +// NB_INSERT_TAIL_LIST( +// &Device->WaitPacketConnections, +// &Connection->WaitPacketLinkage, +// &Device->Lock); + + } + NB_FREE_LOCK (&Device->Lock, DeviceLockHandle); + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + } + } + + Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + } + + // + // Set this now, we will change it later if needed. + // + + Connection->SubState = CONNECTION_SUBSTATE_A_W_ACK; + + + // + // Save these since they go in this next packet. + // + + ThisSendSequence = Connection->CurrentSend.SendSequence; + ThisOffset = (USHORT)Connection->CurrentSend.MessageOffset; + + + // + // Now see if we need to copy the buffer chain. + // + + PacketSize = Connection->MaximumPacketSize; + + if (Connection->CurrentSend.MessageOffset + PacketSize >= Connection->CurrentMessageLength) { + + PacketSize = Connection->CurrentMessageLength - Connection->CurrentSend.MessageOffset; + + if ((Connection->CurrentSend.MessageOffset == 0) && + (!Connection->SendBufferInUse)) { + + // + // If the entire send remaining fits in one packet, + // and this is also the first packet in the send, + // then the entire send fits in one packet and + // we don't need to build a duplicate buffer chain. + // + + BufferChain = Connection->CurrentSend.Buffer; + Reserved->u.SR_CO.NoNdisBuffer = TRUE; + Connection->CurrentSend.Buffer = NULL; + Connection->CurrentSend.BufferOffset = 0; + Connection->CurrentSend.MessageOffset = Connection->CurrentMessageLength; + Connection->CurrentSend.Request = NULL; + ++Connection->CurrentSend.SendSequence; + Connection->SendBufferInUse = TRUE; + if (Connection->NewNetbios) { + if ((ThisSendSequence == Connection->RemoteRcvSequenceMax) || + ((((PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request))->SendFlags) & + TDI_SEND_NO_RESPONSE_EXPECTED)) { // BUGBUG: optimize this check + ConnectionControlFlag = NB_CONTROL_EOM | NB_CONTROL_SEND_ACK; + } else { + ConnectionControlFlag = NB_CONTROL_EOM; + } + Connection->PiggybackAckTimeout = FALSE; + } else { + ConnectionControlFlag = NB_CONTROL_SEND_ACK; + } + + if (BufferChain != NULL) { + NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), user buffer\n", + Reserved, Connection, + Connection->CurrentSend.SendSequence, + Connection->CurrentSend.MessageOffset)); + NdisChainBufferAtBack (Packet, BufferChain); + } else { + NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), no buffer\n", + Reserved, Connection, + Connection->CurrentSend.SendSequence, + Connection->CurrentSend.MessageOffset)); + } + + goto GotBufferChain; + + } + + } + + // + // We need to build a partial buffer chain. In the case + // where the current request is a partial one, we may + // build this from the ndis buffer chains of several + // requests. + // + + if (PacketSize > 0) { + + DesiredLength = PacketSize; + + NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), allocate buffer\n", + Reserved, Connection, + Connection->CurrentSend.SendSequence, + Connection->CurrentSend.MessageOffset)); + + while (TRUE) { + + Status = NbiBuildBufferChainFromBufferChain ( + Device->NdisBufferPoolHandle, + Connection->CurrentSend.Buffer, + Connection->CurrentSend.BufferOffset, + DesiredLength, + &BufferChain, + &Connection->CurrentSend.Buffer, + &Connection->CurrentSend.BufferOffset, + &ActualLength); + + if (Status != STATUS_SUCCESS) { + + PNDIS_BUFFER CurBuffer, TmpBuffer; + + NB_DEBUG2 (SEND, ("Allocate buffer chain failed for packet %lx\n", Reserved)); + + // + // We could not allocate resources for this send. + // We'll put the connection on the packetize + // queue and hope we get more resources later. + // + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + // + // Connection->CurrentSend can stay where it is. + // + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + // + // Free any buffers we have allocated on previous calls + // to BuildBufferChain inside this same while(TRUE) loop, + // then free the packet. + // + + CurBuffer = NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)); + while (CurBuffer) { + TmpBuffer = NDIS_BUFFER_LINKAGE (CurBuffer); + NdisFreeBuffer (CurBuffer); + CurBuffer = TmpBuffer; + } + + NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = NULL; + NdisRecalculatePacketCounts (Packet); + + if (Reserved->OwnedByConnection) { + Connection->SendPacketInUse = FALSE; + } else { + NbiPushSendPacket(Reserved); + } + + return; + + } + + NdisChainBufferAtBack (Packet, BufferChain); + Connection->CurrentSend.MessageOffset += ActualLength; + + DesiredLength -= ActualLength; + + if (DesiredLength == 0) { + + // + // We have gotten enough data for our packet. + // + + if (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) { + Connection->CurrentSend.Request = NULL; + } + break; + } + + // + // We ran out of buffer chain on this send, which means + // that we must have another one behind it (since we + // don't start packetizing partial sends until all of + // them are queued). + // + + Request = REQUEST_SINGLE_LINKAGE(Request); + if (Request == NULL) { + KeBugCheck (NDIS_INTERNAL_ERROR); + } + + Connection->CurrentSend.Request = Request; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentSend.BufferOffset = 0; + + } + + } else { + + // + // This is a zero-length send (in general we will go + // through the code before the if that uses the user's + // buffer, but not on a resend). + // + + Connection->CurrentSend.Buffer = NULL; + Connection->CurrentSend.BufferOffset = 0; + CTEAssert (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength); + Connection->CurrentSend.Request = NULL; + + NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), no alloc buf\n", + Reserved, Connection, + Connection->CurrentSend.SendSequence, + Connection->CurrentSend.MessageOffset)); + + } + + Reserved->u.SR_CO.NoNdisBuffer = FALSE; + + if (Connection->NewNetbios) { + + ++Connection->CurrentSend.SendSequence; + if (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) { + + if (((USHORT)(Connection->CurrentSend.SendSequence - Connection->UnAckedSend.SendSequence)) >= Connection->SendWindowSize) { + + ConnectionControlFlag = NB_CONTROL_EOM | NB_CONTROL_SEND_ACK; + + } else if ((ThisSendSequence == Connection->RemoteRcvSequenceMax) || + ((((PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request))->SendFlags) & + TDI_SEND_NO_RESPONSE_EXPECTED)) { // BUGBUG: optimize this check + + ConnectionControlFlag = NB_CONTROL_EOM | NB_CONTROL_SEND_ACK; + + } else { + + ConnectionControlFlag = NB_CONTROL_EOM; + } + Connection->PiggybackAckTimeout = FALSE; + + } else if (((USHORT)(Connection->CurrentSend.SendSequence - Connection->UnAckedSend.SendSequence)) >= Connection->SendWindowSize) { + + ConnectionControlFlag = NB_CONTROL_SEND_ACK; + + } else if (ThisSendSequence == Connection->RemoteRcvSequenceMax) { + + ConnectionControlFlag = NB_CONTROL_SEND_ACK; + + } else { + + ConnectionControlFlag = 0; + ExitAfterSend = FALSE; + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + } + + } else { + + ConnectionControlFlag = NB_CONTROL_SEND_ACK; + if (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) { + ++Connection->CurrentSend.SendSequence; + } + } + +GotBufferChain: + + // + // We have a packet and a buffer chain, there are + // no other resources required for a send so we can + // fill in the header and go. + // + + CTEAssert (Reserved->SendInProgress == FALSE); + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_SESSION_DATA; + Reserved->u.SR_CO.Connection = Connection; + Reserved->u.SR_CO.Request = Connection->FirstMessageRequest; + + PacketLength = PacketSize + sizeof(NB_CONNECTION); + Reserved->u.SR_CO.PacketLength = PacketLength; + + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = (UCHAR)(PacketLength / 256); + Header->IpxHeader.PacketLength[1] = (UCHAR)(PacketLength % 256); + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. BUGBUG: Put this in + // a contiguous buffer in the connection. + // + + Header->Session.ConnectionControlFlag = ConnectionControlFlag; + Header->Session.DataStreamType = NB_CMD_SESSION_DATA; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = ThisSendSequence; + Header->Session.TotalDataLength = (USHORT)Connection->CurrentMessageLength; + Header->Session.Offset = ThisOffset; + Header->Session.DataLength = (USHORT)PacketSize; + +#if 0 + // + // These are set by NbiAssignSequenceAndSend. + // + + Header->Session.ReceiveSequence = Connection->ReceiveSequence; + Header->Session.BytesReceived = (USHORT)Connection->CurrentReceive.MessageOffset; +#endif + + // + // Reference the request to account for this send. + // + +#if DBG + if (REQUEST_REFCOUNT(Request) > 100) { + DbgPrint ("Request %lx (%lx) has high refcount\n", + Connection, Request); + DbgBreakPoint(); + } +#endif + ++REQUEST_REFCOUNT (Request); + + ++Device->TempFramesSent; + Device->TempFrameBytesSent += PacketSize; + + // + // Start the timer. + // + + NbiStartRetransmit (Connection); + + // + // This frees the lock. IPX will adjust the length of + // the first buffer correctly. + // + + NbiAssignSequenceAndSend( + Connection, + Packet + NB_LOCK_HANDLE_ARG(LockHandle)); + + if (!ExitAfterSend) { + + // + // BUGBUG: Did we need to reference the connection until we + // get the lock back?? + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->SubState == CONNECTION_SUBSTATE_A_PACKETIZE)) { + + // + // Jump back to the beginning of the function to + // repacketize. + + goto SendAnotherPacket; + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } + +} /* NbiPacketizeSend */ + + +VOID +NbiAdjustSendWindow( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine adjusts a connection's send window if needed. It is + assumed that we just got an ack for a full send window. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT HELD. + +Arguments: + + Connection - The connection. + +Return Value: + + None. + +--*/ + +{ + + if (Connection->RetransmitThisWindow) { + + // + // Move it down. Check if this keeps happening. + // + + if (Connection->SendWindowSize > 2) { + --Connection->SendWindowSize; + NB_DEBUG2 (SEND_WINDOW, ("Lower window to %d on %lx (%lx)\n", Connection->SendWindowSize, Connection, Connection->CurrentSend.SendSequence)); + } + + if (Connection->SendWindowIncrease) { + + // + // We just increased the window. + // + + ++Connection->IncreaseWindowFailures; + NB_DEBUG2 (SEND_WINDOW, ("%d consecutive increase failues on %lx (%lx)\n", Connection->IncreaseWindowFailures, Connection, Connection->CurrentSend.SendSequence)); + + if (Connection->IncreaseWindowFailures >= 2) { + + if (Connection->MaxSendWindowSize > 2) { + + // + // Lock ourselves at a smaller window. + // + + Connection->MaxSendWindowSize = Connection->SendWindowSize; + NB_DEBUG2 (SEND_WINDOW, ("Lock send window at %d on %lx (%lx)\n", Connection->MaxSendWindowSize, Connection, Connection->CurrentSend.SendSequence)); + } + + Connection->IncreaseWindowFailures = 0; + } + + Connection->SendWindowIncrease = FALSE; + } + + } else { + + // + // Increase it if allowed, and make a note + // in case this increase causes problems in + // the next window. + // + + if (Connection->SendWindowSize < Connection->MaxSendWindowSize) { + + ++Connection->SendWindowSize; + NB_DEBUG2 (SEND_WINDOW, ("Raise window to %d on %lx (%lx)\n", Connection->SendWindowSize, Connection, Connection->CurrentSend.SendSequence)); + Connection->SendWindowIncrease = TRUE; + + } else { + + if (Connection->SendWindowIncrease) { + + // + // We just increased it and nothing failed, + // which is good. + // + + Connection->SendWindowIncrease = FALSE; + Connection->IncreaseWindowFailures = 0; + NB_DEBUG2 (SEND_WINDOW, ("Raised window OK on %lx (%lx)\n", Connection, Connection->CurrentSend.SendSequence)); + } + } + } + + + // + // This controls when we'll check this again. + // + + Connection->SendWindowSequenceLimit += Connection->SendWindowSize; + +} /* NbiAdjustSendWindow */ + + +VOID +NbiReframeConnection( + IN PCONNECTION Connection, + IN USHORT ReceiveSequence, + IN USHORT BytesReceived, + IN BOOLEAN Resend + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine is called when we have gotten an ack + for some data. It completes any sends that have + been acked, and if needed modifies the current send + pointer and queues the connection for repacketizing. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection. + + ReceiveSequence - The receive sequence from the remote. + + BytesReceived - The number of bytes received in this message. + + Resend - If it is OK to resend based on this packet. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request, TmpRequest; + PREQUEST RequestToComplete; + PDEVICE Device = NbiDevice; + CTELockHandle CancelLH; + + + // + // BUGBUG: We should change to stop the timer + // only if we go idle, since otherwise we still + // want it running, or will restart it when we + // packetize. + // + + // + // See how much is acked here. + // + + if ((Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) && + (ReceiveSequence == (USHORT)(Connection->CurrentSend.SendSequence)) && + (Connection->FirstMessageRequest != NULL)) { + + // Special check for 0 length send which was not accepted by the remote. + // In this case it will pass the above 3 conditions yet, nothing + // is acked. BUG#10395 + if (!Connection->CurrentSend.MessageOffset && Connection->CurrentSend.SendSequence == Connection->UnAckedSend.SendSequence ) { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + } + + // + // This acks the entire message. + // + + NB_DEBUG2 (SEND, ("Got ack for entire message on %lx (%d)\n", Connection, Connection->CurrentSend.SendSequence)); + + NbiStopRetransmit (Connection); + + Connection->CurrentSend.MessageOffset = 0; // BUGBUG: Needed? + Connection->UnAckedSend.MessageOffset = 0; + + // + // We don't adjust the send window since we likely stopped + // packetizing before we hit it. + // + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + + if (Connection->NewNetbios) { + + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + + // + // See if we need to adjust our send window. + // + + if (((SHORT)(Connection->CurrentSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) { + + NbiAdjustSendWindow (Connection); + + } else { + + // + // Advance this, we won't get meaningful results until we + // send a full window in one message. + // + + Connection->SendWindowSequenceLimit = Connection->CurrentSend.SendSequence + Connection->SendWindowSize; + } + + + } + + Connection->RetransmitThisWindow = FALSE; + + Request = Connection->FirstMessageRequest; + + // + // We dequeue these requests from the connection's + // send queue. + // + + if (Connection->FirstMessageRequest == Connection->LastMessageRequest) { + + REQUEST_SINGLE_LINKAGE (Request) = NULL; + + } else { + + Connection->SendQueue.Head = REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest); + REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest) = NULL; + + } + +#if DBG + if (REQUEST_REFCOUNT(Request) > 100) { + DbgPrint ("Request %lx (%lx) has high refcount\n", + Connection, Request); + DbgBreakPoint(); + } +#endif + if (--REQUEST_REFCOUNT(Request) == 0) { + + RequestToComplete = Request; + + } else { + + // + // There are still sends pending, this will get + // completed when the last send completes. Since + // we have already unlinked the request from the + // connection's send queue we can do this without + // any locks. + // + + RequestToComplete = NULL; + + } + + // + // Now see if there is a send to activate. + // + + NbiRestartConnection (Connection); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + // + // Now complete any requests we need to. + // + + while (RequestToComplete != NULL) { + + TmpRequest = REQUEST_SINGLE_LINKAGE (RequestToComplete); + REQUEST_STATUS (RequestToComplete) = STATUS_SUCCESS; + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (RequestToComplete, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + NbiCompleteRequest (RequestToComplete); + NbiFreeRequest (Device, RequestToComplete); + ++Connection->ConnectionInfo.TransmittedTsdus; + RequestToComplete = TmpRequest; + + NbiDereferenceConnection (Connection, CREF_SEND); + + } + + } else if ((ReceiveSequence == Connection->CurrentSend.SendSequence) && + (Connection->NewNetbios || (BytesReceived == Connection->CurrentSend.MessageOffset)) && + (Connection->CurrentSend.Request != NULL)) { + + // + // This acks whatever we sent last time, and we are + // not done packetizing this send, so we can repacketize. + // BUGBUG: With SendSequence changing as it does now, + // don't need the CurrentSend.Request check??? + // + + NB_DEBUG2 (SEND, ("Got full ack on %lx (%d)\n", Connection, Connection->CurrentSend.SendSequence)); + + NbiStopRetransmit (Connection); + + if (Connection->NewNetbios) { + + // + // If we are waiting for a window, and this does not open it + // anymore, then we don't reset our timers/retries. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_A_REMOTE_W) { + + if (Connection->RemoteRcvSequenceMax != BytesReceived) { + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + } + + // + // Advance this, we won't get meaningful results until we + // send a full window in one message. + // + + Connection->SendWindowSequenceLimit = Connection->CurrentSend.SendSequence + Connection->SendWindowSize; + + } else { + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + + if (((SHORT)(Connection->CurrentSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) { + + NbiAdjustSendWindow (Connection); + + } else { + + // + // Advance this, we won't get meaningful results until we + // send a full window in one message. + // + + Connection->SendWindowSequenceLimit = Connection->CurrentSend.SendSequence + Connection->SendWindowSize; + } + + } + + } else { + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + } + + Connection->RetransmitThisWindow = FALSE; + + Connection->UnAckedSend = Connection->CurrentSend; + + if (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE) { + + // + // We may be on if this ack is duplicated. + // + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert(!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } else if( Connection->FirstMessageRequest ) { + + // + // This acked part of the current send. If the + // remote is requesting a resend then we advance + // the current send location by the amount + // acked and resend from there. If he does + // not want a resend, just ignore this. + // + // We repacketize immediately because we have + // backed up the pointer, and this would + // cause us to ignore an ack for the amount + // sent. Since we don't release the lock + // until we have packetized, the current + // pointer will be advanced past there. + // + // BUGBUG: If he is acking more than we sent, we + // ignore this -- the remote is confused and there + // is nothing much we can do. + // + + if (Resend) { + + if (Connection->NewNetbios && + (((Connection->UnAckedSend.SendSequence < Connection->CurrentSend.SendSequence) && + (ReceiveSequence >= Connection->UnAckedSend.SendSequence) && + (ReceiveSequence < Connection->CurrentSend.SendSequence)) || + ((Connection->UnAckedSend.SendSequence > Connection->CurrentSend.SendSequence) && + ((ReceiveSequence >= Connection->UnAckedSend.SendSequence) || + (ReceiveSequence < Connection->CurrentSend.SendSequence))))) { + + BOOLEAN SomethingAcked = (BOOLEAN) + (ReceiveSequence != Connection->UnAckedSend.SendSequence); + + // + // New netbios and the receive sequence is valid. + // + + NbiStopRetransmit (Connection); + + // + // Advance our unacked pointer by the amount + // acked in this response. + // + + NbiAdvanceUnAckedBySequence( + Connection, + ReceiveSequence); + + Connection->RetransmitThisWindow = TRUE; + + ++Connection->ConnectionInfo.TransmissionErrors; + ++Device->Statistics.DataFramesResent; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesResent, + Connection->CurrentSend.MessageOffset - Connection->UnAckedSend.MessageOffset); + + // + // Packetize from that point on. + // + + Connection->CurrentSend = Connection->UnAckedSend; + + // + // If anything was acked, then reset the retry count. + // + + if (SomethingAcked) { + + // + // See if we need to adjust our send window. + // + + if (((SHORT)(Connection->UnAckedSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) { + + NbiAdjustSendWindow (Connection); + + } + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + } + + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + + // + // Now packetize. This will set the state to + // something meaningful and release the lock. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE) { + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else if (!Connection->NewNetbios && + ((ReceiveSequence == Connection->UnAckedSend.SendSequence) && + (BytesReceived <= Connection->CurrentSend.MessageOffset))) { + + ULONG BytesAcked = + BytesReceived - Connection->UnAckedSend.MessageOffset; + + // + // Old netbios. + // + + NbiStopRetransmit (Connection); + + // + // Advance our unacked pointer by the amount + // acked in this response. + // + + NbiAdvanceUnAckedByBytes( + Connection, + BytesAcked); + + ++Connection->ConnectionInfo.TransmissionErrors; + ++Device->Statistics.DataFramesResent; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesResent, + Connection->CurrentSend.MessageOffset - Connection->UnAckedSend.MessageOffset); + + // + // Packetize from that point on. + // + + Connection->CurrentSend = Connection->UnAckedSend; + + // + // If anything was acked, reset the retry count + // + if ( BytesAcked ) { + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + } + + // + // Now packetize. This will set the state to + // something meaningful and release the lock. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE) { + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + if (Connection->NewNetbios && + (((Connection->UnAckedSend.SendSequence < Connection->CurrentSend.SendSequence) && + (ReceiveSequence >= Connection->UnAckedSend.SendSequence) && + (ReceiveSequence < Connection->CurrentSend.SendSequence)) || + ((Connection->UnAckedSend.SendSequence > Connection->CurrentSend.SendSequence) && + ((ReceiveSequence >= Connection->UnAckedSend.SendSequence) || + (ReceiveSequence < Connection->CurrentSend.SendSequence))))) { + + BOOLEAN SomethingAcked = (BOOLEAN) + (ReceiveSequence != Connection->UnAckedSend.SendSequence); + + // + // New netbios and the receive sequence is valid. We advance + // the back of our send window, but we don't repacketize. + // + + // + // Advance our unacked pointer by the amount + // acked in this response. + // + + NbiAdvanceUnAckedBySequence( + Connection, + ReceiveSequence); + + Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac + + // + // If anything was acked, then reset the retry count. + // + + if (SomethingAcked) { + + // + // See if we need to adjust our send window. + // + + if (((SHORT)(Connection->UnAckedSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) { + + NbiAdjustSendWindow (Connection); + + } + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout; + + + // + // Now packetize. This will set the state to + // something meaningful and release the lock. + // + + if ((Connection->CurrentSend.Request != NULL) && + (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE)) { + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert(!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + } + } + } + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + +} /* NbiReframeConnection */ + + +VOID +NbiRestartConnection( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called when have gotten an ack for + a full message, or received a response to a watchdog + probe, and need to check if the connection should + start packetizing. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT HELD. + +Arguments: + + Connection - The connection. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request, TmpRequest; + ULONG TempCount; + PTDI_REQUEST_KERNEL_SEND Parameters; + PDEVICE Device = NbiDevice; + + // + // See if there is a send to activate. + // + + if (Connection->SendQueue.Head != NULL) { + + // + // Take the first send off the queue and make + // it current. + // + + Request = Connection->SendQueue.Head; + Connection->SendQueue.Head = REQUEST_SINGLE_LINKAGE (Request); + + // + // BUGBUG: Cache the information about being EOM + // in a more easily accessible location? + // + + Parameters = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request); + if ((Parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { + + // + // This is a one-request message. + // + + Connection->CurrentSend.Request = Request; + Connection->CurrentSend.MessageOffset = 0; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentSend.BufferOffset = 0; + Connection->SendBufferInUse = FALSE; + + Connection->UnAckedSend = Connection->CurrentSend; + + Connection->FirstMessageRequest = Request; +#ifdef RSRC_TIMEOUT_DBG + KeQuerySystemTime(&Connection->FirstMessageRequestTime); +#endif //RSRC_TIMEOUT_DBG + + Connection->LastMessageRequest = Request; + Connection->CurrentMessageLength = Parameters->SendLength; + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + } else { + + // + // This is a multiple-request message. We scan + // to see if we have the end of message received + // yet. + // + + TempCount = Parameters->SendLength; + TmpRequest = Request; + Request = REQUEST_SINGLE_LINKAGE(Request); + + while (Request != NULL) { + + TempCount += Parameters->SendLength; + + Parameters = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request); + if ((Parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { + + Connection->CurrentSend.Request = TmpRequest; + Connection->CurrentSend.MessageOffset = 0; + Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (TmpRequest); + Connection->CurrentSend.BufferOffset = 0; + Connection->SendBufferInUse = FALSE; + + Connection->UnAckedSend = Connection->CurrentSend; + + Connection->FirstMessageRequest = TmpRequest; + Connection->LastMessageRequest = Request; +#ifdef RSRC_TIMEOUT_DBG + KeQuerySystemTime(&Connection->FirstMessageRequestTime); +#endif //RSRC_TIMEOUT_DBG + + Connection->CurrentMessageLength = TempCount; + + Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE; + + NbiReferenceConnectionSync (Connection, CREF_PACKETIZE); + + CTEAssert (!Connection->OnPacketizeQueue); + Connection->OnPacketizeQueue = TRUE; + + NB_INSERT_TAIL_LIST( + &Device->PacketizeConnections, + &Connection->PacketizeLinkage, + &Device->Lock); + + break; + + } + + Request = REQUEST_SINGLE_LINKAGE(Request); + + } + + if (Request == NULL) { + + Connection->SubState = CONNECTION_SUBSTATE_A_W_EOR; + + } + + } + + } else { + + Connection->FirstMessageRequest = NULL; + Connection->SubState = CONNECTION_SUBSTATE_A_IDLE; + + NbiStartWatchdog (Connection); + + } + +} /* NbiRestartConnection */ + + +VOID +NbiAdvanceUnAckedByBytes( + IN PCONNECTION Connection, + IN ULONG BytesAcked + ) + +/*++ + +Routine Description: + + This routine advances the Connection->UnAckedSend + send pointer by the specified number of bytes. It + assumes that there are enough send requests to + handle the number specified. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT HELD. + +Arguments: + + Connection - The connection. + + BytesAcked - The number of bytes acked. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + ULONG CurSendBufferLength; + ULONG BytesLeft = BytesAcked; + ULONG TempBytes; + + while (BytesLeft > 0) { + + NdisQueryBuffer (Connection->UnAckedSend.Buffer, NULL, &CurSendBufferLength); + + // + // See if bytes acked ends within the current buffer. + // + + if (Connection->UnAckedSend.BufferOffset + BytesLeft < + CurSendBufferLength) { + + Connection->UnAckedSend.BufferOffset += BytesLeft; + Connection->UnAckedSend.MessageOffset += BytesLeft; + break; + + } else { + + TempBytes = CurSendBufferLength - Connection->UnAckedSend.BufferOffset; + BytesLeft -= TempBytes; + Connection->UnAckedSend.MessageOffset += TempBytes; + + // + // No, so advance the buffer. + // + + Connection->UnAckedSend.BufferOffset = 0; + Connection->UnAckedSend.Buffer = + NDIS_BUFFER_LINKAGE (Connection->UnAckedSend.Buffer); + + // + // Is there a next buffer in this request? + // + + if (Connection->UnAckedSend.Buffer == NULL) { + + // + // No, so advance the request unless we are done. + // + + if (BytesLeft == 0) { + return; + } + + Connection->UnAckedSend.Request = + REQUEST_SINGLE_LINKAGE(Connection->UnAckedSend.Request); + + if (Connection->UnAckedSend.Request == NULL) { + KeBugCheck (NDIS_INTERNAL_ERROR); + } + + Connection->UnAckedSend.Buffer = + REQUEST_NDIS_BUFFER (Connection->UnAckedSend.Request); + + } + } + } + +} /* NbiAdvanceUnAckedByBytes */ + + +VOID +NbiAdvanceUnAckedBySequence( + IN PCONNECTION Connection, + IN USHORT ReceiveSequence + ) + +/*++ + +Routine Description: + + This routine advances the Connection->UnAckedSend + send pointer so that the next packet to send will be + the correct one for ReceiveSequence. UnAckedSend + must point to a known valid combination. It + assumes that there are enough send requests to + handle the sequence specified. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT HELD. + +Arguments: + + Connection - The connection. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + USHORT PacketsAcked; + + // + // BUGBUG: Fix this to account for partial sends, where + // we might not have used the max. for all packets. + // + + PacketsAcked = ReceiveSequence - Connection->UnAckedSend.SendSequence; + + NbiAdvanceUnAckedByBytes( + Connection, + PacketsAcked * Connection->MaximumPacketSize); + + Connection->UnAckedSend.SendSequence += PacketsAcked; + +} /* NbiAdvanceUnAckedBySequence */ + + +VOID +NbiCancelSend( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a send + The request is found on the connection's send queue. + + NOTE: This routine is called with the CancelSpinLock held and + is responsible for releasing it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + none. + +--*/ + +{ + PCONNECTION Connection; + PREQUEST Request = (PREQUEST)Irp; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_SYNC_CONTEXT (SyncContext) + + + CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) && + (REQUEST_MINOR_FUNCTION(Request) == TDI_SEND)); + + CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); + + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + // + // Just stop the connection, that will tear down any + // sends. + // + // BUGBUG: Do we care about cancelling non-active + // sends without stopping the connection?? + // + + NbiReferenceConnectionSync (Connection, CREF_CANCEL); + + IoReleaseCancelSpinLock (Irp->CancelIrql); + + + NB_BEGIN_SYNC (&SyncContext); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // This frees the lock, cancels any sends, etc. + // + + NbiStopConnection( + Connection, + STATUS_CANCELLED + NB_LOCK_HANDLE_ARG (LockHandle)); + + NbiDereferenceConnection (Connection, CREF_CANCEL); + + NB_END_SYNC (&SyncContext); + +} /* NbiCancelSend */ + + +NTSTATUS +NbiBuildBufferChainFromBufferChain ( + IN NDIS_HANDLE BufferPoolHandle, + IN PNDIS_BUFFER CurrentSourceBuffer, + IN ULONG CurrentByteOffset, + IN ULONG DesiredLength, + OUT PNDIS_BUFFER *DestinationBuffer, + OUT PNDIS_BUFFER *NewSourceBuffer, + OUT ULONG *NewByteOffset, + OUT ULONG *ActualLength + ) + +/*++ + +Routine Description: + + This routine is called to build an NDIS_BUFFER chain from a source + NDIS_BUFFER chain and offset into it. We assume we don't know the + length of the source Mdl chain, and we must allocate the NDIS_BUFFERs + for the destination chain, which we do from the NDIS buffer pool. + + If the system runs out of memory while we are building the destination + NDIS_BUFFER chain, we completely clean up the built chain and return with + NewCurrentMdl and NewByteOffset set to the current values of CurrentMdl + and ByteOffset. + +Environment: + +Arguments: + + BufferPoolHandle - The buffer pool to allocate buffers from. + + CurrentSourceBuffer - Points to the start of the NDIS_BUFFER chain + from which to draw the packet. + + CurrentByteOffset - Offset within this NDIS_BUFFER to start the packet at. + + DesiredLength - The number of bytes to insert into the packet. + + DestinationBuffer - returned pointer to the NDIS_BUFFER chain describing + the packet. + + NewSourceBuffer - returned pointer to the NDIS_BUFFER that would + be used for the next byte of packet. NULL if the source NDIS_BUFFER + chain was exhausted. + + NewByteOffset - returned offset into the NewSourceBuffer for the next byte + of packet. NULL if the source NDIS_BUFFER chain was exhausted. + + ActualLength - The actual length of the data copied. + +Return Value: + + STATUS_SUCCESS if the build of the returned NDIS_BUFFER chain succeeded + and was the correct length. + + STATUS_INSUFFICIENT_RESOURCES if we ran out of NDIS_BUFFERs while + building the destination chain. + +--*/ +{ + ULONG AvailableBytes; + ULONG CurrentByteCount; + ULONG BytesCopied; + PNDIS_BUFFER OldNdisBuffer; + PNDIS_BUFFER NewNdisBuffer; + NDIS_STATUS NdisStatus; + + + OldNdisBuffer = CurrentSourceBuffer; + NdisQueryBuffer (OldNdisBuffer, NULL, &CurrentByteCount); + + AvailableBytes = CurrentByteCount - CurrentByteOffset; + if (AvailableBytes > DesiredLength) { + AvailableBytes = DesiredLength; + } + + // + // Build the first NDIS_BUFFER, which could conceivably be the only one... + // + + NdisCopyBuffer( + &NdisStatus, + &NewNdisBuffer, + BufferPoolHandle, + OldNdisBuffer, + CurrentByteOffset, + AvailableBytes); + + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + *NewSourceBuffer = CurrentSourceBuffer; + *NewByteOffset = CurrentByteOffset; + return STATUS_INSUFFICIENT_RESOURCES; + } + + *DestinationBuffer = NewNdisBuffer; + BytesCopied = AvailableBytes; + + // + // Was the first NDIS_BUFFER enough data. + // + + if (BytesCopied == DesiredLength) { + if (CurrentByteOffset + AvailableBytes == CurrentByteCount) { + *NewSourceBuffer = CurrentSourceBuffer->Next; + *NewByteOffset = 0; + } else { + *NewSourceBuffer = CurrentSourceBuffer; + *NewByteOffset = CurrentByteOffset + AvailableBytes; + } + *ActualLength = BytesCopied; + return STATUS_SUCCESS; + } + + if (CurrentSourceBuffer->Next == NULL) { + + *NewSourceBuffer = NULL; + *NewByteOffset = 0; + *ActualLength = BytesCopied; + return STATUS_SUCCESS; + + } + + // + // Need more data, so follow the in Mdl chain to create a packet. + // + + OldNdisBuffer = OldNdisBuffer->Next; + NdisQueryBuffer (OldNdisBuffer, NULL, &CurrentByteCount); + + while (OldNdisBuffer != NULL) { + + AvailableBytes = DesiredLength - BytesCopied; + if (AvailableBytes > CurrentByteCount) { + AvailableBytes = CurrentByteCount; + } + + NdisCopyBuffer( + &NdisStatus, + &(NDIS_BUFFER_LINKAGE(NewNdisBuffer)), + BufferPoolHandle, + OldNdisBuffer, + 0, + AvailableBytes); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + // + // ran out of resources. put back what we've used in this call and + // return the error. + // + + while (*DestinationBuffer != NULL) { + NewNdisBuffer = NDIS_BUFFER_LINKAGE(*DestinationBuffer); + NdisFreeBuffer (*DestinationBuffer); + *DestinationBuffer = NewNdisBuffer; + } + + *NewByteOffset = CurrentByteOffset; + *NewSourceBuffer = CurrentSourceBuffer; + + return STATUS_INSUFFICIENT_RESOURCES; + } + + NewNdisBuffer = NDIS_BUFFER_LINKAGE(NewNdisBuffer); + + BytesCopied += AvailableBytes; + + if (BytesCopied == DesiredLength) { + if (AvailableBytes == CurrentByteCount) { + *NewSourceBuffer = OldNdisBuffer->Next; + *NewByteOffset = 0; + } else { + *NewSourceBuffer = OldNdisBuffer; + *NewByteOffset = AvailableBytes; + } + *ActualLength = BytesCopied; + return STATUS_SUCCESS; + } + + OldNdisBuffer = OldNdisBuffer->Next; + NdisQueryBuffer (OldNdisBuffer, NULL, &CurrentByteCount); + + } + + // + // We ran out of source buffer chain. + // + + *NewSourceBuffer = NULL; + *NewByteOffset = 0; + *ActualLength = BytesCopied; + return STATUS_SUCCESS; + +} /* NbiBuildBufferChainFromBufferChain */ + diff --git a/private/ntos/tdi/isnp/nb/session.c b/private/ntos/tdi/isnp/nb/session.c new file mode 100644 index 000000000..fe998feb2 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/session.c @@ -0,0 +1,2450 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + session.c + +Abstract: + + This module contains the code to handle session frames + for the Netbios module of the ISN transport. + +Author: + + Adam Barr (adamba) 28-November-1993 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#ifdef RASAUTODIAL +#include <acd.h> +#include <acdapi.h> +#endif // RASAUTODIAL +#pragma hdrstop + +#ifdef RASAUTODIAL +extern BOOLEAN fAcdLoadedG; +extern ACD_DRIVER AcdDriverG; + +VOID +NbiNoteNewConnection( + PCONNECTION pConnection + ); +#endif + +#ifdef RSRC_TIMEOUT_DBG +VOID +NbiSendDeathPacket( + IN PCONNECTION Connection, + IN CTELockHandle LockHandle + ) +{ + PNDIS_PACKET Packet = PACKET(&NbiGlobalDeathPacket); + PNB_SEND_RESERVED Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved); + NB_CONNECTION UNALIGNED * Header; + PDEVICE Device = NbiDevice; + NDIS_STATUS NdisStatus; + + if ( Reserved->SendInProgress ) { + DbgPrint("***Could not send death packet - in use\n"); + NB_FREE_LOCK(&Connection->Lock, LockHandle); + return; + } + + Reserved->SendInProgress = TRUE; + Reserved->Type = SEND_TYPE_DEATH_PACKET; + + // + // Fill in the IPX header -- the default header has the broadcast + // address on net 0 as the destination IPX address. + // + + Header = (NB_CONNECTION UNALIGNED *) + (&Reserved->Header[Device->Bind.IncludedHeaderOffset]); + RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER)); + + Header->IpxHeader.PacketLength[0] = (sizeof(NB_CONNECTION)) / 256; + Header->IpxHeader.PacketLength[1] = (sizeof(NB_CONNECTION)) % 256; + + Header->IpxHeader.PacketType = 0x04; + + // + // Now fill in the Netbios header. + // + Header->Session.ConnectionControlFlag = 0; + Header->Session.DataStreamType = NB_CMD_DEATH_PACKET; + Header->Session.SourceConnectionId = Connection->LocalConnectionId; + Header->Session.DestConnectionId = Connection->RemoteConnectionId; + Header->Session.SendSequence = 0; + Header->Session.TotalDataLength = 0; + Header->Session.Offset = 0; + Header->Session.DataLength = 0; + + + NB_FREE_LOCK(&Connection->Lock, LockHandle); + + DbgPrint("*****Death packet is being sent for connection %lx, to <%.16s>\n",Connection, Connection->RemoteName); + // + // Now send the frame, IPX will adjust the length of the + // first buffer correctly. + // + + NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION)); + if ((NdisStatus = + (*Device->Bind.SendHandler)( + &Connection->LocalTarget, + Packet, + sizeof(NB_CONNECTION), + sizeof(NB_CONNECTION))) != STATUS_PENDING) { + + NbiSendComplete( + Packet, + NdisStatus); + + } + +} +#endif //RSRC_TIMEOUT_DBG + + +VOID +NbiProcessSessionData( + IN NDIS_HANDLE MacBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT LookaheadBufferOffset, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION_DATA frames. + +Arguments: + + MacBindingHandle - A handle to use when calling NdisTransferData. + + MacReceiveContext - A context to use when calling NdisTransferData. + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The lookahead buffer, starting at the IPX + header. + + LookaheadBufferSize - The length of the lookahead data. + + LookaheadBufferOffset - The offset to add when calling + NdisTransferData. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)LookaheadBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + PCONNECTION Connection; + PREQUEST Request; + PDEVICE Device = NbiDevice; + ULONG Hash; + ULONG ReceiveFlags; + ULONG IndicateBytesTransferred; + ULONG DataAvailable, DataIndicated; + ULONG DestBytes, BytesToTransfer; + PUCHAR DataHeader; + BOOLEAN Last, CompleteReceive, EndOfMessage, PartialReceive, CopyLookahead; + NTSTATUS Status; + NDIS_STATUS NdisStatus; + ULONG NdisBytesTransferred; + PIRP ReceiveIrp; + PSINGLE_LIST_ENTRY s; + PNB_RECEIVE_RESERVED ReceiveReserved; + PNDIS_PACKET Packet; + PNDIS_BUFFER BufferChain; + ULONG BufferChainLength; + NB_DEFINE_LOCK_HANDLE (LockHandle) + CTELockHandle CancelLH; + + if (Sess->DestConnectionId != 0xffff) { + + // + // This is an active connection, find it using + // our session id. + // + + Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if (Connection->LocalConnectionId == Sess->DestConnectionId) { + break; + } + Connection = Connection->NextConnection; + } + + if (Connection == NULL) { + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NbiReferenceConnectionLock (Connection, CREF_INDICATE); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + // + // See what is happening with this connection. + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + +#ifdef RSRC_TIMEOUT_DBG + if ( Connection->FirstMessageRequest && NbiGlobalDebugResTimeout ) { + LARGE_INTEGER CurrentTime, ElapsedTime; + KeQuerySystemTime(&CurrentTime); + ElapsedTime.QuadPart = CurrentTime.QuadPart - Connection->FirstMessageRequestTime.QuadPart; +// DbgPrint("*****Elapsed %lx.%lx time\n",ElapsedTime.HighPart,ElapsedTime.LowPart); + if ( ElapsedTime.QuadPart > NbiGlobalMaxResTimeout.QuadPart ) { + + DbgPrint("*****Connection %lx is not copleting irp %lx for %lx.%lx time\n",Connection, Connection->FirstMessageRequest, + ElapsedTime.HighPart,ElapsedTime.LowPart); + DbgPrint("************irp arrived at %lx.%lx current time %lx.%lx\n", + Connection->FirstMessageRequestTime.HighPart,Connection->FirstMessageRequestTime.LowPart, + CurrentTime.HighPart, CurrentTime.LowPart); + + NbiSendDeathPacket( Connection, LockHandle ); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + } + } +#endif //RSRC_TIMEOUT_DBG + + // + // The connection is up, see if this is data should + // be received. + // + + if (Sess->ConnectionControlFlag & NB_CONTROL_SYSTEM) { + + // + // This is an ack. This call releases + // the lock. BUGBUG: Does this need to + // be a function? + // + + NbiProcessDataAck( + Connection, + Sess, + RemoteAddress + NB_LOCK_HANDLE_ARG (LockHandle) + ); + + } else { + + // + // See if there is any piggyback ack here. + // + + if (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK) { + + // + // We are waiting for an ack, so see if this acks + // anything. Even the old netbios sometimes piggyback + // acks (and doesn't send the explicit ack). + // + // This releases the lock. BUGBUG: Fix this. + // + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + FALSE + NB_LOCK_HANDLE_ARG(LockHandle)); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State != CONNECTION_STATE_ACTIVE) { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + } + + } else if ((Connection->NewNetbios) && + (Connection->CurrentSend.SendSequence != Connection->UnAckedSend.SendSequence)) { + + // + // For the new netbios, even if we are not waiting + // for an ack he may have acked something with this + // send and we should check, since it may allow + // us to open our send window. + // + // This releases the lock. BUGBUG: Fix this. + // + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + FALSE + NB_LOCK_HANDLE_ARG(LockHandle)); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State != CONNECTION_STATE_ACTIVE) { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + } + + } + + // + // This is data on the connection. First make sure + // it is the data we expect next. + // + + if (Connection->NewNetbios) { + + if (Sess->SendSequence != Connection->ReceiveSequence) { + + ++Connection->ConnectionInfo.ReceiveErrors; + ++Device->Statistics.DataFramesRejected; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesRejected, + PacketSize - sizeof(NB_CONNECTION)); + + if ((Connection->ReceiveState == CONNECTION_RECEIVE_IDLE) || + (Connection->ReceiveState == CONNECTION_RECEIVE_ACTIVE)) { + + NB_ACK_TYPE AckType; + + NB_DEBUG2 (RECEIVE, ("Got unexp data on %lx, %x(%d) expect %x(%d)\n", + Connection, Sess->SendSequence, Sess->Offset, + Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset)); + + // + // If we are receiving a packet we have already seen, just + // send a normal ack, otherwise force a resend. This test + // we do is equivalent to + // Sess->SendSequence < Connection->ReceiveSequence + // but rearranged so it works when the numbers wrap. + // + + if ((SHORT)(Sess->SendSequence - Connection->ReceiveSequence) < 0) { + + // + // Since this is a resend, check if the local + // target has changed. + // +#if defined(_PNP_POWER) + + if (!RtlEqualMemory (&Connection->LocalTarget, RemoteAddress, sizeof(IPX_LOCAL_TARGET))) { +#if DBG + DbgPrint ("NBI: Switch local target for %lx, (%d,%d)\n", Connection, + Connection->LocalTarget.NicHandle.NicId, RemoteAddress->NicHandle.NicId); +#endif + Connection->LocalTarget = *RemoteAddress; + } + +#else + + if (!RtlEqualMemory (&Connection->LocalTarget, RemoteAddress, 8)) { +#if DBG + DbgPrint ("NBI: Switch local target for %lx\n", Connection); +#endif + Connection->LocalTarget = *RemoteAddress; + } + +#endif _PNP_POWER + AckType = NbiAckResponse; + + } else { + + AckType = NbiAckResend; + } + + // + // This frees the lock. + // + + NbiSendDataAck( + Connection, + AckType + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_DEBUG (RECEIVE, ("Got unexp on %lx RcvState %d, %x(%d) exp %x(%d)\n", + Connection, Connection->ReceiveState, + Sess->SendSequence, Sess->Offset, + Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + } else { + + // + // Old netbios. + // + + if ((Sess->SendSequence != Connection->ReceiveSequence) || + (Sess->Offset != Connection->CurrentReceive.MessageOffset)) { + + ++Connection->ConnectionInfo.ReceiveErrors; + ++Device->Statistics.DataFramesRejected; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesRejected, + PacketSize - sizeof(NB_CONNECTION)); + + if ((Connection->ReceiveState == CONNECTION_RECEIVE_IDLE) || + (Connection->ReceiveState == CONNECTION_RECEIVE_ACTIVE)) { + + NB_ACK_TYPE AckType; + + NB_DEBUG2 (RECEIVE, ("Got unexp on %lx, %x(%d) expect %x(%d)\n", + Connection, Sess->SendSequence, Sess->Offset, + Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset)); + + // + // If we are receiving the last packet again, just + // send a normal ack, otherwise force a resend. + // + + if (((Sess->SendSequence == Connection->ReceiveSequence) && + ((ULONG)(Sess->Offset + Sess->DataLength) == Connection->CurrentReceive.MessageOffset)) || + (Sess->SendSequence == (USHORT)(Connection->ReceiveSequence-1))) { + AckType = NbiAckResponse; + } else { + AckType = NbiAckResend; + } + + // + // This frees the lock. + // + + NbiSendDataAck( + Connection, + AckType + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_DEBUG (RECEIVE, ("Got unexp on %lx RcvState %d, %x(%d) exp %x(%d)\n", + Connection, Connection->ReceiveState, + Sess->SendSequence, Sess->Offset, + Connection->ReceiveSequence, Connection->CurrentReceive.MessageOffset)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + } + + + IndicateBytesTransferred = 0; + DataAvailable = PacketSize - sizeof(NB_CONNECTION); + DataIndicated = LookaheadBufferSize - sizeof(NB_CONNECTION); + DataHeader = LookaheadBuffer + sizeof(NB_CONNECTION); + + ++Device->TempFramesReceived; + Device->TempFrameBytesReceived += DataAvailable; + + if (Connection->CurrentIndicateOffset) { + CTEAssert (DataAvailable >= Connection->CurrentIndicateOffset); + DataAvailable -= Connection->CurrentIndicateOffset; + if (DataIndicated >= Connection->CurrentIndicateOffset) { + DataIndicated -= Connection->CurrentIndicateOffset; + } else { + DataIndicated = 0; + } + DataHeader += Connection->CurrentIndicateOffset; + } + + CopyLookahead = (BOOLEAN)(MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA); + + if (Connection->NewNetbios) { + Last = (BOOLEAN)((Sess->ConnectionControlFlag & NB_CONTROL_EOM) != 0); + } else { + Last = (BOOLEAN)(Sess->Offset + Sess->DataLength == Sess->TotalDataLength); + } + + Connection->CurrentReceiveNoPiggyback = + (BOOLEAN)((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) != 0); + + if (Connection->ReceiveState == CONNECTION_RECEIVE_IDLE) { + + // + // We don't have a receive posted, so see if we can + // get one from the queue or our client. + // + + if (Connection->ReceiveQueue.Head != NULL) { + + PTDI_REQUEST_KERNEL_RECEIVE ReceiveParameters; + + Request = Connection->ReceiveQueue.Head; + Connection->ReceiveQueue.Head = REQUEST_SINGLE_LINKAGE(Request); + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + + Connection->ReceiveRequest = Request; + ReceiveParameters = (PTDI_REQUEST_KERNEL_RECEIVE) + (REQUEST_PARAMETERS(Request)); + Connection->ReceiveLength = ReceiveParameters->ReceiveLength; + + // + // If there is a send in progress, then we assume + // we are not in straight request-response mode + // and disable piggybacking of this ack. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_IDLE) { + Connection->NoPiggybackHeuristic = TRUE; + } else { + Connection->NoPiggybackHeuristic = (BOOLEAN) + ((ReceiveParameters->ReceiveFlags & TDI_RECEIVE_NO_RESPONSE_EXP) != 0); + } + + Connection->CurrentReceive.Offset = 0; + Connection->CurrentReceive.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentReceive.BufferOffset = 0; + + NB_DEBUG2 (RECEIVE, ("Activated receive %lx on %lx (%d)\n", Request, Connection, Connection->ReceiveSequence)); + + // + // Fall through the if and process the data. + // + + } else { + + if ((Connection->ReceiveUnaccepted == 0) && + (Connection->AddressFile->RegisteredHandler[TDI_EVENT_RECEIVE])) { + + Connection->ReceiveState = CONNECTION_RECEIVE_INDICATE; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + ReceiveFlags = TDI_RECEIVE_AT_DISPATCH_LEVEL; + if (Last) { + ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE; + } + if (CopyLookahead) { + ReceiveFlags |= TDI_RECEIVE_COPY_LOOKAHEAD; + } + + Status = (*Connection->AddressFile->ReceiveHandler)( + Connection->AddressFile->HandlerContexts[TDI_EVENT_RECEIVE], + Connection->Context, + ReceiveFlags, + DataIndicated, + DataAvailable, + &IndicateBytesTransferred, + DataHeader, + &ReceiveIrp); + + if (Status == STATUS_MORE_PROCESSING_REQUIRED) { + + // + // We got an IRP, activate it. + // + + Request = NbiAllocateRequest (Device, ReceiveIrp); + + IF_NOT_ALLOCATED(Request) { + + ReceiveIrp->IoStatus.Information = 0; + ReceiveIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest (ReceiveIrp, IO_NETWORK_INCREMENT); + + Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV; + + if (Connection->NewNetbios) { + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + Connection->LocalRcvSequenceMax = + (USHORT)(Connection->ReceiveSequence - 1); + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + CTEAssert (REQUEST_OPEN_CONTEXT(Request) == Connection); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + PTDI_REQUEST_KERNEL_RECEIVE ReceiveParameters; + + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + Connection->ReceiveUnaccepted = DataAvailable - IndicateBytesTransferred; + + Connection->ReceiveRequest = Request; + ReceiveParameters = (PTDI_REQUEST_KERNEL_RECEIVE) + (REQUEST_PARAMETERS(Request)); + Connection->ReceiveLength = ReceiveParameters->ReceiveLength; + + // + // If there is a send in progress, then we assume + // we are not in straight request-response mode + // and disable piggybacking of this ack. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_IDLE) { + Connection->NoPiggybackHeuristic = TRUE; + } else { + Connection->NoPiggybackHeuristic = (BOOLEAN) + ((ReceiveParameters->ReceiveFlags & TDI_RECEIVE_NO_RESPONSE_EXP) != 0); + } + + Connection->CurrentReceive.Offset = 0; + Connection->CurrentReceive.Buffer = REQUEST_NDIS_BUFFER (Request); + Connection->CurrentReceive.BufferOffset = 0; + + NbiReferenceConnectionSync (Connection, CREF_RECEIVE); + + NB_DEBUG2 (RECEIVE, ("Indicate got receive %lx on %lx (%d)\n", Request, Connection, Connection->ReceiveSequence)); + + // + // Fall through the if and process the data. + // + + } else { + + // + // The connection has been stopped. + // + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + } + + } else if (Status == STATUS_SUCCESS) { + + // + // He accepted some or all of the data. + // + + NB_DEBUG2 (RECEIVE, ("Indicate took receive data %lx (%d)\n", Connection, Connection->ReceiveSequence)); + + if ( (IndicateBytesTransferred >= DataAvailable)) { + + CTEAssert (IndicateBytesTransferred == DataAvailable); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; // harmless if NewNetbios is FALSE + Connection->CurrentIndicateOffset = 0; + if ( Last ) { + Connection->CurrentReceive.MessageOffset = 0; + } else { + Connection->CurrentReceive.MessageOffset+= IndicateBytesTransferred; + } + + + ++Connection->ConnectionInfo.ReceivedTsdus; + + // + // If there is a send in progress, then we assume + // we are not in straight request-response mode + // and disable piggybacking of this ack. + // + + Connection->NoPiggybackHeuristic = (BOOLEAN) + (Connection->SubState != CONNECTION_SUBSTATE_A_IDLE); + + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + Connection->ReceiveRequest = NULL; + + // + // This releases the lock. + // + + NbiAcknowledgeReceive( + Connection + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + // + // We will do the easiest thing here, which + // is to send an ack for the amount he + // took, and force a retransmit on the + // remote. For net netbios we make a note + // of how many bytes were taken and ask + // for a resend. + // + // BUGBUG: Handle this better?? + // + +#if DBG + DbgPrint ("NBI: Client took partial indicate data\n"); +#endif + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + Connection->CurrentReceive.MessageOffset += + IndicateBytesTransferred; + Connection->ReceiveUnaccepted = + DataAvailable - IndicateBytesTransferred; + Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV; + + if (Connection->NewNetbios) { + Connection->CurrentIndicateOffset = IndicateBytesTransferred; + // + // NOTE: We don't advance ReceiveSequence + // + } + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + Connection->NewNetbios ? + NbiAckResend : NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } else { + + // + // No IRP returned. + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + Connection->ReceiveUnaccepted = DataAvailable; + Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV; + NB_DEBUG (RECEIVE, ("Indicate got no receive on %lx (%lx)\n", Connection, Status)); + + if (Connection->NewNetbios) { + + Connection->LocalRcvSequenceMax = + (USHORT)(Connection->ReceiveSequence - 1); + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + } else { + + // + // No receive handler. + // + + Connection->ReceiveState = CONNECTION_RECEIVE_W_RCV; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + if (Connection->ReceiveUnaccepted == 0) { + NB_DEBUG (RECEIVE, ("No receive, no handler on %lx\n", Connection)); + } else { + NB_DEBUG (RECEIVE, ("No receive, ReceiveUnaccepted %d on %lx\n", + Connection->ReceiveUnaccepted, Connection)); + } + + if (Connection->NewNetbios) { + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + Connection->LocalRcvSequenceMax = + (USHORT)(Connection->ReceiveSequence - 1); + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + } + + } else if (Connection->ReceiveState != CONNECTION_RECEIVE_ACTIVE) { + + // + // If we have a transfer in progress, or are waiting for + // a receive to be posted, then ignore this frame. + // + + NB_DEBUG2 (RECEIVE, ("Got data on %lx, state %d (%d)\n", Connection, Connection->ReceiveState, Connection->ReceiveSequence)); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + // + // At this point we have a receive and it is set to + // the correct current location. + // + + DestBytes = Connection->ReceiveLength - Connection->CurrentReceive.Offset; + BytesToTransfer = DataAvailable - IndicateBytesTransferred; + + if (DestBytes < BytesToTransfer) { + + // + // If the data overflows the current receive, then make a + // note that we should complete the receive at the end of + // transfer data, but with EOR false. + // + + EndOfMessage = FALSE; + CompleteReceive = TRUE; + PartialReceive = TRUE; + BytesToTransfer = DestBytes; + + } else if (DestBytes == BytesToTransfer) { + + // + // If the data just fills the current receive, then complete + // the receive; EOR depends on whether this is a DOL or not. + // + + EndOfMessage = Last; + CompleteReceive = TRUE; + PartialReceive = FALSE; + + } else { + + // + // Complete the receive if this is a DOL. + // + + EndOfMessage = Last; + CompleteReceive = Last; + PartialReceive = FALSE; + + } + + // + // If we can copy the data directly, then update our + // pointers, send an ack, and do the copy. + // + + if ((BytesToTransfer > 0) && + (IndicateBytesTransferred + BytesToTransfer <= DataIndicated)) { + + ULONG BytesNow, BytesLeft; + PUCHAR CurTarget, CurSource; + ULONG CurTargetLen; + PNDIS_BUFFER CurBuffer; + ULONG CurByteOffset; + + NB_DEBUG2 (RECEIVE, ("Direct copy of %d bytes %lx (%d)\n", BytesToTransfer, Connection, Connection->ReceiveSequence)); + + Connection->ReceiveState = CONNECTION_RECEIVE_TRANSFER; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + CurBuffer = Connection->CurrentReceive.Buffer; + CurByteOffset = Connection->CurrentReceive.BufferOffset; + + NdisQueryBuffer (CurBuffer, &CurTarget, &CurTargetLen); + CurTarget += CurByteOffset; + CurTargetLen -= CurByteOffset; + + CurSource = DataHeader + IndicateBytesTransferred; + BytesLeft = BytesToTransfer; + + while (TRUE) { + + if (CurTargetLen < BytesLeft) { + BytesNow = CurTargetLen; + } else { + BytesNow = BytesLeft; + } + TdiCopyLookaheadData( + CurTarget, + CurSource, + BytesNow, + CopyLookahead ? TDI_RECEIVE_COPY_LOOKAHEAD : 0); + + if (BytesNow == CurTargetLen) { + BytesLeft -= BytesNow; + CurBuffer = CurBuffer->Next; + CurByteOffset = 0; + if (BytesLeft > 0) { + NdisQueryBuffer (CurBuffer, &CurTarget, &CurTargetLen); + CurSource += BytesNow; + } else { + break; + } + } else { + CurByteOffset += BytesNow; + CTEAssert (BytesLeft == BytesNow); + break; + } + + } + + NB_GET_CANCEL_LOCK( &CancelLH ); + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + Connection->CurrentReceive.Buffer = CurBuffer; + Connection->CurrentReceive.BufferOffset = CurByteOffset; + + Connection->CurrentReceive.Offset += BytesToTransfer; + Connection->CurrentReceive.MessageOffset += BytesToTransfer; + + if (CompleteReceive || + (Connection->State != CONNECTION_STATE_ACTIVE)) { + + if (EndOfMessage) { + + CTEAssert (!PartialReceive); + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; // harmless if NewNetbios is FALSE + Connection->CurrentReceive.MessageOffset = 0; + Connection->CurrentIndicateOffset = 0; + + } else if (Connection->NewNetbios) { + + if (PartialReceive) { + Connection->CurrentIndicateOffset += BytesToTransfer; + } else { + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; + Connection->CurrentIndicateOffset = 0; + } + } + + // + // This sends an ack and releases the connection lock. + // and CANCEL Lock. + // + + NbiCompleteReceive( + Connection, + EndOfMessage, + CancelLH + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_SWAP_IRQL( CancelLH, LockHandle); + NB_FREE_CANCEL_LOCK( CancelLH ); + // + // CompleteReceive is FALSE, so EndOfMessage is FALSE. + // + + Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; + + // + // This releases the lock. + // + + if (Connection->NewNetbios) { + + // + // A partial receive should only happen if we are + // completing the receive. + // + + CTEAssert (!PartialReceive); + + ++Connection->ReceiveSequence; + ++Connection->LocalRcvSequenceMax; + Connection->CurrentIndicateOffset = 0; + + if ((Connection->CurrentReceiveNoPiggyback) || + ((Device->AckWindow != 0) && + (++Connection->ReceivesWithoutAck >= Device->AckWindow))) { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + } else { + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + + // + // We have to set up a call to transfer data and send + // the ack after it completes (if it succeeds). + // + + s = NbiPopReceivePacket (Device); + if (s == NULL) { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + ++Connection->ConnectionInfo.ReceiveErrors; + ++Device->Statistics.DataFramesRejected; + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesRejected, + DataAvailable); + + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + } + + ReceiveReserved = CONTAINING_RECORD (s, NB_RECEIVE_RESERVED, PoolLinkage); + Packet = CONTAINING_RECORD (ReceiveReserved, NDIS_PACKET, ProtocolReserved[0]); + + // + // Initialize the receive packet. + // + + ReceiveReserved->u.RR_CO.Connection = Connection; + ReceiveReserved->u.RR_CO.EndOfMessage = EndOfMessage; + ReceiveReserved->u.RR_CO.CompleteReceive = CompleteReceive; + ReceiveReserved->u.RR_CO.PartialReceive = PartialReceive; + + ReceiveReserved->Type = RECEIVE_TYPE_DATA; + CTEAssert (!ReceiveReserved->TransferInProgress); + ReceiveReserved->TransferInProgress = TRUE; + + // + // if we've got zero bytes left, avoid the TransferData below and + // just deliver. + // + + if (BytesToTransfer <= 0) { + + ReceiveReserved->u.RR_CO.NoNdisBuffer = TRUE; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_DEBUG2 (RECEIVE, ("TransferData of 0 bytes %lx (%d)\n", Connection, Connection->ReceiveSequence)); + NbiTransferDataComplete( + Packet, + NDIS_STATUS_SUCCESS, + 0); + + return; + } + + // + // If needed, build a buffer chain to describe this + // to NDIS. + // + + Connection->PreviousReceive = Connection->CurrentReceive; + + if ((Connection->CurrentReceive.Offset == 0) && + CompleteReceive) { + + BufferChain = Connection->CurrentReceive.Buffer; + BufferChainLength = BytesToTransfer; + Connection->CurrentReceive.Buffer = NULL; + ReceiveReserved->u.RR_CO.NoNdisBuffer = TRUE; + + } else { + + if (NbiBuildBufferChainFromBufferChain ( + Device->NdisBufferPoolHandle, + Connection->CurrentReceive.Buffer, + Connection->CurrentReceive.BufferOffset, + BytesToTransfer, + &BufferChain, + &Connection->CurrentReceive.Buffer, + &Connection->CurrentReceive.BufferOffset, + &BufferChainLength) != NDIS_STATUS_SUCCESS) { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NB_DEBUG2 (RECEIVE, ("Could not build receive buffer chain %lx (%d)\n", Connection, Connection->ReceiveSequence)); + NbiDereferenceConnection (Connection, CREF_INDICATE); + return; + + } + + ReceiveReserved->u.RR_CO.NoNdisBuffer = FALSE; + + } + + + NdisChainBufferAtFront (Packet, BufferChain); + + Connection->ReceiveState = CONNECTION_RECEIVE_TRANSFER; + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NB_DEBUG2 (RECEIVE, ("TransferData of %d bytes %lx (%d)\n", BytesToTransfer, Connection, Connection->ReceiveSequence)); + + (*Device->Bind.TransferDataHandler) ( + &NdisStatus, + MacBindingHandle, + MacReceiveContext, + LookaheadBufferOffset + sizeof(NB_CONNECTION) + + Connection->CurrentIndicateOffset + IndicateBytesTransferred, + BytesToTransfer, + Packet, + (PUINT)&NdisBytesTransferred); + + if (NdisStatus != NDIS_STATUS_PENDING) { +#if DBG + if (NdisStatus == STATUS_SUCCESS) { + CTEAssert (NdisBytesTransferred == BytesToTransfer); + } +#endif + + NbiTransferDataComplete ( + Packet, + NdisStatus, + NdisBytesTransferred); + + } + + return; + + } + + } else if ((Connection->State == CONNECTION_STATE_CONNECTING) && + (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN)) { + + // + // If this is the ack for the session initialize, then + // complete the pending connects. This routine releases + // the connection lock. + // + + NbiProcessSessionInitAck( + Connection, + Sess + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + + } else { + + // + // This is a session initialize frame. + // + // BUGBUG: If there is more data than in the lookahead + // buffer, we won't be able to echo it back in the + // response. + // + + NbiProcessSessionInitialize( + RemoteAddress, + MacOptions, + LookaheadBuffer, + LookaheadBufferSize); + + } + +} /* NbiProcessSessionData */ + + +VOID +NbiProcessDataAck( + IN PCONNECTION Connection, + IN NB_SESSION UNALIGNED * Sess, + IN PIPX_LOCAL_TARGET RemoteAddress + NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine processes an ack on an active connection. + + NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection. + + Sess - The session frame. + + RemoteAddress - The local target this packet was received from. + + LockHandle - The handle used to acquire the lock. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + BOOLEAN Resend; + + // + // Make sure we expect an ack right now. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + if (((Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK) || + (Connection->SubState == CONNECTION_SUBSTATE_A_REMOTE_W)) && + ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0)) { + + // + // We are waiting for an ack (because we completed + // packetizing a send, or ran out of receive window). + // + // This will complete any sends that are acked by + // this receive, and if necessary readjust the + // send pointer and requeue the connection for + // packetizing. It release the connection lock. + // + + if (Connection->ResponseTimeout) { + Resend = TRUE; + Connection->ResponseTimeout = FALSE; + } else { + Resend = (BOOLEAN) + ((Sess->ConnectionControlFlag & NB_CONTROL_RESEND) != 0); + } + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + Resend + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else if ((Connection->SubState == CONNECTION_SUBSTATE_A_W_PROBE) && + ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0)) { + + // + // We had a probe outstanding and got a response. Restart + // the connection if needed (a send may have just been + // posted while the probe was outstanding). + // + // BUGBUG: We should check that the response is really + // correct. + // + + if (Connection->NewNetbios) { + Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax; + } + + NbiRestartConnection (Connection); + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } else if ((Connection->SubState == CONNECTION_SUBSTATE_A_PACKETIZE) && + ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0)) { + + if (Connection->NewNetbios) { + + // + // We are packetizing, reframe. In the unlikely + // event that this acks everything we may packetize + // in this call, but that is OK (the other thread + // will exit if we finish up). More normally we + // will just advance UnAcked send a bit. + // + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + (BOOLEAN)((Sess->ConnectionControlFlag & NB_CONTROL_RESEND) != 0) + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + +#if 0 + + // + // BUGBUG: Should handle this case (i.e. may be in W_PACKET). + // + + } else if ((Sess->ConnectionControlFlag & NB_CONTROL_SEND_ACK) == 0) { + + DbgPrint ("NWLNKNB: Ignoring ack, state is %d\n", Connection->SubState); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); +#endif + + } else { + + // + // We got a probe from the remote. Some old DOS clients + // send probes that do not have the send ack bit on, + // so we respond to any probe if none of the conditions + // above are true. This call releases the lock. + // + // We use the IgnoreNextDosProbe flag to ignore every + // second probe of this nature, to avoid a data ack + // war between two machines who each think they are + // responding to the other. This flag is set to FALSE + // whenever we send an ack or a probe. + // + + if (!Connection->IgnoreNextDosProbe) { + + // + // Since this is a probe, check if the local + // target has changed. + // + + if (!RtlEqualMemory (&Connection->LocalTarget, RemoteAddress, 8)) { +#if DBG + DbgPrint ("NBI: Switch local target for %lx\n", Connection); +#endif + Connection->LocalTarget = *RemoteAddress; + } + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + Connection->IgnoreNextDosProbe = TRUE; + + } else { + + Connection->IgnoreNextDosProbe = FALSE; + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + return; + + } + +} /* NbiProcessDataAck */ + + +VOID +NbiProcessSessionInitialize( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION frames which have + a remote connection ID of 0xffff -- these are session + initialize frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + PacketBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)PacketBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + NB_SESSION_INIT UNALIGNED * SessInit = (NB_SESSION_INIT UNALIGNED *)(Sess+1); + CONNECT_INDICATION TempConnInd; + PCONNECT_INDICATION ConnInd; + PCONNECTION Connection; + PADDRESS Address; + PREQUEST Request, ListenRequest, AcceptRequest; + PDEVICE Device = NbiDevice; + PLIST_ENTRY p; + ULONG Hash; + TA_NETBIOS_ADDRESS SourceName; + PIRP AcceptIrp; + CONNECTION_CONTEXT ConnectionContext; + NTSTATUS AcceptStatus; + PADDRESS_FILE AddressFile, ReferencedAddressFile; + PTDI_REQUEST_KERNEL_LISTEN ListenParameters; + PTDI_CONNECTION_INFORMATION ListenInformation; + PTDI_CONNECTION_INFORMATION RemoteInformation; + TDI_ADDRESS_NETBIOS UNALIGNED * ListenAddress; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_LOCK_HANDLE (LockHandle2) + NB_DEFINE_LOCK_HANDLE (LockHandle3) + CTELockHandle CancelLH; + + // + // Verify that the whole packet is there. + // + + if (PacketSize < (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT))) { +#if DBG + DbgPrint ("NBI: Got short session initialize, %d/%d\n", PacketSize, + sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)); +#endif + return; + } + + // + // Verify that MaximumDataSize that remote can support is > 0 + // Bug # 19405 + // + if ( SessInit->MaximumDataSize == 0 ) { + NB_DEBUG(CONNECTION, ("Connect request with MaximumDataSize == 0\n" +)); + return; + } + + // + // Make sure this is for an address we care about. + // + + if (Device->AddressCounts[SessInit->DestinationName[0]] == 0) { + return; + } + + Address = NbiFindAddress (Device, (PUCHAR)SessInit->DestinationName); + + if (Address == NULL) { + return; + } + + // + // First see if we have a session to this remote. We check + // this in case our ack of the session initialize was dropped, + // we don't want to reindicate our client. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle3); + + for (Hash = 0; Hash < CONNECTION_HASH_COUNT; Hash++) { + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if ((RtlEqualMemory (&Connection->RemoteHeader.DestinationNetwork, Conn->IpxHeader.SourceNetwork, 12)) && + (Connection->RemoteConnectionId == Sess->SourceConnectionId) && + (Connection->State != CONNECTION_STATE_DISCONNECT)) { + + // + // Yes, we are talking to this remote, if it is active then + // respond, otherwise we are in the process of connecting + // and we will respond eventually. + // + +#if DBG + DbgPrint ("NBI: Got connect request on active connection %lx\n", Connection); +#endif + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + NbiReferenceConnectionLock (Connection, CREF_INDICATE); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + NbiSendSessionInitAck( + Connection, + (PUCHAR)(SessInit+1), + PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)), + NULL); // lock is not held + NbiDereferenceConnection (Connection, CREF_INDICATE); + + } else { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + } + + NbiDereferenceAddress (Address, AREF_FIND); + return; + } + + Connection = Connection->NextConnection; + } + } + + + TdiBuildNetbiosAddress ((PUCHAR)SessInit->SourceName, FALSE, &SourceName); + + // + // Scan the queue of listens to see if there is one that + // satisfies this request. + // + // NOTE: The device lock is held here. + // + + for (p = Device->ListenQueue.Flink; + p != &Device->ListenQueue; + p = p->Flink) { + + Request = LIST_ENTRY_TO_REQUEST (p); + Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); + + if (Connection->AddressFile->Address != Address) { + continue; + } + + // + // Check that this listen is not specific to a different + // netbios name. + // + + ListenParameters = (PTDI_REQUEST_KERNEL_LISTEN)REQUEST_PARAMETERS(Request); + ListenInformation = ListenParameters->RequestConnectionInformation; + + if (ListenInformation && + (ListenInformation->RemoteAddress) && + (ListenAddress = NbiParseTdiAddress(ListenInformation->RemoteAddress, FALSE)) && + (!RtlEqualMemory( + SessInit->SourceName, + ListenAddress->NetbiosName, + 16))) { + continue; + } + + // + // This connection is valid, so we use it. + // + + NB_DEBUG2 (CONNECTION, ("Activating queued listen %lx\n", Connection)); + + RemoveEntryList (REQUEST_LINKAGE(Request)); + + RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, Conn->IpxHeader.SourceNetwork, 12); + RtlCopyMemory (Connection->RemoteName, SessInit->SourceName, 16); + Connection->LocalTarget = *RemoteAddress; + Connection->RemoteConnectionId = Sess->SourceConnectionId; + + Connection->SessionInitAckDataLength = + PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)); + if (Connection->SessionInitAckDataLength > 0) { + Connection->SessionInitAckData = NbiAllocateMemory( + Connection->SessionInitAckDataLength, MEMORY_CONNECTION, "SessionInitAckData"); + RtlCopyMemory( + Connection->SessionInitAckData, + (PUCHAR)(SessInit+1), + Connection->SessionInitAckDataLength); + } + + + Connection->MaximumPacketSize = SessInit->MaximumDataSize; + + Connection->CurrentSend.SendSequence = 0; + Connection->UnAckedSend.SendSequence = 0; + Connection->RetransmitThisWindow = FALSE; + Connection->ReceiveSequence = 1; + Connection->CurrentReceive.MessageOffset = 0; + Connection->Retries = Device->KeepAliveCount; + if (Device->Extensions && ((Sess->ConnectionControlFlag & NB_CONTROL_NEW_NB) != 0)) { + Connection->NewNetbios = TRUE; + Connection->LocalRcvSequenceMax = 4; // may get modified after ripping based on card + Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax; + Connection->SendWindowSequenceLimit = 2; + if (Connection->RemoteRcvSequenceMax == 0) { + Connection->RemoteRcvSequenceMax = 1; + } + } else { + Connection->NewNetbios = FALSE; + } + + // + // Save this information now for whenever we complete the listen. + // + + RemoteInformation = ListenParameters->ReturnConnectionInformation; + + if (RemoteInformation != NULL) { + + RtlCopyMemory( + (PTA_NETBIOS_ADDRESS)RemoteInformation->RemoteAddress, + &SourceName, + (RemoteInformation->RemoteAddressLength < sizeof(TA_NETBIOS_ADDRESS)) ? + RemoteInformation->RemoteAddressLength : sizeof(TA_NETBIOS_ADDRESS)); + } + + + if (ListenParameters->RequestFlags & TDI_QUERY_ACCEPT) { + + // + // We have to wait for an accept before sending the + // session init ack, so we complete the listen and wait. + // + + ListenRequest = Request; + Connection->ListenRequest = NULL; + + NB_DEBUG2 (CONNECTION, ("Queued listen on %lx awaiting accept\n", Connection)); + + Connection->SubState = CONNECTION_SUBSTATE_L_W_ACCEPT; + + NbiTransferReferenceConnection (Connection, CREF_LISTEN, CREF_W_ACCEPT); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + } else { + + // + // We are ready to go, so we send out the find route request + // for the remote. We keep the listen alive and the CREF_LISTEN + // reference on until this completes. + // + + NB_DEBUG2 (CONNECTION, ("Activating queued listen on %lx\n", Connection)); + + ListenRequest = NULL; + + Connection->SubState = CONNECTION_SUBSTATE_L_W_ROUTE; + + NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = + *(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork; + RtlCopyMemory(Connection->FindRouteRequest.Node,Conn->IpxHeader.SourceNode,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_NO_RIP; + + // + // When this completes, we will send the session init + // ack. We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // + + if (*(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + } + + // + // Complete the listen if needed. + // + + if (ListenRequest != NULL) { + + REQUEST_INFORMATION (ListenRequest) = 0; + REQUEST_STATUS (ListenRequest) = STATUS_SUCCESS; + + NB_GET_CANCEL_LOCK ( &CancelLH ); + IoSetCancelRoutine (ListenRequest, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + NbiCompleteRequest (ListenRequest); + NbiFreeRequest (Device, ListenRequest); + + } + + NbiDereferenceAddress (Address, AREF_FIND); + + return; + + } + + // + // We could not find a listen, so we indicate to every + // client. Make sure there is no session initialize for this + // remote being indicated. If there is not, we insert + // ourselves in the queue to block others. + // + // NOTE: The device lock is held here. + // + + for (p = Device->ConnectIndicationInProgress.Flink; + p != &Device->ConnectIndicationInProgress; + p = p->Flink) { + + ConnInd = CONTAINING_RECORD (p, CONNECT_INDICATION, Linkage); + + if ((RtlEqualMemory(ConnInd->NetbiosName, SessInit->DestinationName, 16)) && + (RtlEqualMemory(&ConnInd->RemoteAddress, Conn->IpxHeader.SourceNetwork, 12)) && + (ConnInd->ConnectionId == Sess->SourceConnectionId)) { + + // + // We are processing a request from this remote for + // the same ID, to avoid confusion we just exit. + // + +#if DBG + DbgPrint ("NBI: Already processing connect to <%.16s>\n", SessInit->DestinationName); +#endif + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + NbiDereferenceAddress (Address, AREF_FIND); + return; + } + + } + + RtlCopyMemory (&TempConnInd.RemoteAddress, SessInit->DestinationName, 16); + RtlCopyMemory (&TempConnInd.RemoteAddress, Conn->IpxHeader.SourceNetwork, 12); + TempConnInd.ConnectionId = Sess->SourceConnectionId; + + InsertTailList (&Device->ConnectIndicationInProgress, &TempConnInd.Linkage); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + + + // + // Now scan through the address to find someone who has + // an indication routine registed and wants this connection. + // + + + ReferencedAddressFile = NULL; + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle1); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + + // + // Find the next open address file in the list. + // + + AddressFile = CONTAINING_RECORD (p, ADDRESS_FILE, Linkage); + if (AddressFile->State != ADDRESSFILE_STATE_OPEN) { + continue; + } + + NbiReferenceAddressFileLock (AddressFile, AFREF_INDICATION); + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle1); + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + ReferencedAddressFile = AddressFile; + + // + // No posted listen requests; is there a kernel client? + // + + if (AddressFile->RegisteredHandler[TDI_EVENT_CONNECT]) { + + if ((*AddressFile->ConnectionHandler)( + AddressFile->HandlerContexts[TDI_EVENT_CONNECT], + sizeof (TA_NETBIOS_ADDRESS), + &SourceName, + 0, // user data + NULL, + 0, // options + NULL, + &ConnectionContext, + &AcceptIrp) != STATUS_MORE_PROCESSING_REQUIRED) { + + // + // The client did not return a request, go to the + // next address file. + // + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle1); + continue; + + } + + AcceptRequest = NbiAllocateRequest (Device, AcceptIrp); + + IF_NOT_ALLOCATED(AcceptRequest) { + + AcceptStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + // + // The client accepted the connect, so activate + // the connection and complete the accept. + // listen. This lookup references the connection + // so we know it will remain valid. + // + + Connection = NbiLookupConnectionByContext ( + AddressFile, + ConnectionContext); + + if (Connection != NULL) { + + ASSERT (Connection->AddressFile == AddressFile); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle2); + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle3); + + if ((Connection->State == CONNECTION_STATE_INACTIVE) && + (Connection->DisassociatePending == NULL) && + (Connection->ClosePending == NULL)) { + + NB_DEBUG2 (CONNECTION, ("Indication on %lx returned connection %lx\n", AddressFile, Connection)); + + Connection->State = CONNECTION_STATE_LISTENING; + Connection->SubState = CONNECTION_SUBSTATE_L_W_ROUTE; + + Connection->Retries = Device->KeepAliveCount; + + RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, Conn->IpxHeader.SourceNetwork, 12); + RtlCopyMemory (Connection->RemoteName, SessInit->SourceName, 16); + Connection->LocalTarget = *RemoteAddress; + + Connection->SessionInitAckDataLength = + PacketSize - (sizeof(IPX_HEADER) + sizeof(NB_SESSION) + sizeof(NB_SESSION_INIT)); + if (Connection->SessionInitAckDataLength > 0) { + Connection->SessionInitAckData = NbiAllocateMemory( + Connection->SessionInitAckDataLength, MEMORY_CONNECTION, "SessionInitAckData"); + RtlCopyMemory( + Connection->SessionInitAckData, + (PUCHAR)(SessInit+1), + Connection->SessionInitAckDataLength); + } + + Connection->MaximumPacketSize = SessInit->MaximumDataSize; + + (VOID)NbiAssignConnectionId (Device, Connection); // BUGBUG: Check return code. + Connection->RemoteConnectionId = Sess->SourceConnectionId; + + Connection->CurrentSend.SendSequence = 0; + Connection->UnAckedSend.SendSequence = 0; + Connection->RetransmitThisWindow = FALSE; + Connection->ReceiveSequence = 1; + Connection->CurrentReceive.MessageOffset = 0; + Connection->Retries = Device->KeepAliveCount; + if (Device->Extensions && ((Sess->ConnectionControlFlag & NB_CONTROL_NEW_NB) != 0)) { + Connection->NewNetbios = TRUE; + Connection->LocalRcvSequenceMax = 4; // may get modified after ripping based on card + Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax; + Connection->SendWindowSequenceLimit = 2; + if (Connection->RemoteRcvSequenceMax == 0) { + Connection->RemoteRcvSequenceMax = 1; + } + } else { + Connection->NewNetbios = FALSE; + } + + NbiReferenceConnectionLock (Connection, CREF_ACCEPT); + NbiReferenceConnectionLock (Connection, CREF_FIND_ROUTE); + + Connection->AcceptRequest = AcceptRequest; + AcceptStatus = STATUS_PENDING; + + // + // Take us out of this list now, we will jump to + // FoundConnection which is past the removal below. + // + + RemoveEntryList (&TempConnInd.Linkage); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle2); + + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = + *(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork; + RtlCopyMemory(Connection->FindRouteRequest.Node,Conn->IpxHeader.SourceNode,6); + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_NO_RIP; + + // + // When this completes, we will send the session init + // ack. We don't call it if the client is for network 0, + // instead just fake as if no route could be found + // and we will use the local target we got here. + // The accept is completed when this completes. + // + + if (*(UNALIGNED ULONG *)Conn->IpxHeader.SourceNetwork != 0) { + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } else { + + NbiFindRouteComplete( + &Connection->FindRouteRequest, + FALSE); + + } + + } else { + + NB_DEBUG (CONNECTION, ("Indication on %lx returned invalid connection %lx\n", AddressFile, Connection)); + AcceptStatus = STATUS_INVALID_CONNECTION; + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle2); + + + } + + NbiDereferenceConnection (Connection, CREF_BY_CONTEXT); + + } else { + + NB_DEBUG (CONNECTION, ("Indication on %lx returned unknown connection %lx\n", AddressFile, Connection)); + AcceptStatus = STATUS_INVALID_CONNECTION; + + } + } + + // + // Complete the accept request in the failure case. + // + + if (AcceptStatus != STATUS_PENDING) { + + REQUEST_STATUS (AcceptRequest) = AcceptStatus; + + NbiCompleteRequest (AcceptRequest); + NbiFreeRequest (Device, AcceptRequest); + + } else { + + // + // We found a connection, so we break; this is + // a jump since the while exit assumes the + // address lock is held. + // + + goto FoundConnection; + + } + + } + + NB_SYNC_GET_LOCK (&Address->Lock, &LockHandle1); + + } // end of for loop through the address files + + NB_SYNC_FREE_LOCK (&Address->Lock, LockHandle1); + + + // + // Take us out of the list that blocks other indications + // from this remote to this address. + // + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle3); + RemoveEntryList (&TempConnInd.Linkage); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle3); + +FoundConnection: + + if (ReferencedAddressFile != NULL) { + NbiDereferenceAddressFile (ReferencedAddressFile, AFREF_INDICATION); + } + + NbiDereferenceAddress (Address, AREF_FIND); + +} /* NbiProcessSessionInitialize */ + + +VOID +NbiProcessSessionInitAck( + IN PCONNECTION Connection, + IN NB_SESSION UNALIGNED * Sess + IN NB_LOCK_HANDLE_PARAM(LockHandle) + ) + +/*++ + +Routine Description: + + This routine handles session init ack frames. + + THIS ROUTINE IS CALLED WITH THE CONNECTION LOCK HELD + AND RETURNS WITH IT RELEASED. + +Arguments: + + Connection - The connection. + + Sess - The netbios header for the received frame. + + LockHandle - The handle with which Connection->Lock was acquired. + +Return Value: + + None. + +--*/ + +{ + PREQUEST Request; + NB_SESSION_INIT UNALIGNED * SessInit = (NB_SESSION_INIT UNALIGNED *)(Sess+1); + BOOLEAN TimerWasStopped = FALSE; + CTELockHandle CancelLH; + + if ((Sess->ConnectionControlFlag & NB_CONTROL_SYSTEM) && + (Sess->SendSequence == 0x0000) && + (Sess->ReceiveSequence == 0x0001)) { + + NB_DEBUG2 (CONNECTION, ("Completing connect on %lx\n", Connection)); + + if (CTEStopTimer (&Connection->Timer)) { + TimerWasStopped = TRUE; + } + + Connection->State = CONNECTION_STATE_ACTIVE; + Connection->SubState = CONNECTION_SUBSTATE_A_IDLE; + Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; + + if (Connection->Retries == NbiDevice->ConnectionCount) { + ++NbiDevice->Statistics.ConnectionsAfterNoRetry; + } else { + ++NbiDevice->Statistics.ConnectionsAfterRetry; + } + ++NbiDevice->Statistics.OpenConnections; + + Connection->Retries = NbiDevice->KeepAliveCount; + NbiStartWatchdog (Connection); + + Connection->RemoteConnectionId = Sess->SourceConnectionId; + + Connection->CurrentSend.SendSequence = 1; + Connection->UnAckedSend.SendSequence = 1; + Connection->RetransmitThisWindow = FALSE; + Connection->ReceiveSequence = 0; + Connection->CurrentReceive.MessageOffset = 0; + Connection->Retries = NbiDevice->KeepAliveCount; + if (NbiDevice->Extensions && ((Sess->ConnectionControlFlag & NB_CONTROL_NEW_NB) != 0)) { + Connection->NewNetbios = TRUE; + Connection->LocalRcvSequenceMax = + (USHORT)(Connection->ReceiveWindowSize - 1); + Connection->RemoteRcvSequenceMax = Sess->ReceiveSequenceMax; + Connection->SendWindowSequenceLimit = 3; + } else { + Connection->NewNetbios = FALSE; + } + + if (Connection->MaximumPacketSize > SessInit->MaximumDataSize) { + Connection->MaximumPacketSize = SessInit->MaximumDataSize; + } + + Request = Connection->ConnectRequest; + +#ifdef RASAUTODIAL + // + // Check to see if we have to notify + // the automatic connection driver about + // this connection. + // + if (fAcdLoadedG) { + BOOLEAN fEnabled; + CTELockHandle AcdHandle; + + CTEGetLock(&AcdDriverG.SpinLock, &AcdHandle); + fEnabled = AcdDriverG.fEnabled; + CTEFreeLock(&AcdDriverG.SpinLock, AcdHandle); + if (fEnabled) + NbiNoteNewConnection(Connection); + } +#endif // RASAUTODIAL + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + NB_GET_CANCEL_LOCK( &CancelLH ); + IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); + NB_FREE_CANCEL_LOCK( CancelLH ); + + REQUEST_STATUS (Request) = STATUS_SUCCESS; + NbiCompleteRequest (Request); + NbiFreeRequest (Device, Request); + + NbiTransferReferenceConnection (Connection, CREF_CONNECT, CREF_ACTIVE); + + if (TimerWasStopped) { + NbiDereferenceConnection (Connection, CREF_TIMER); + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + +} /* NbiProcessSessionInitAck */ + + +VOID +NbiProcessSessionEnd( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION_END frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)PacketBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + PCONNECTION Connection; + PDEVICE Device = NbiDevice; + ULONG Hash; + NB_DEFINE_LOCK_HANDLE (LockHandle1) + NB_DEFINE_LOCK_HANDLE (LockHandle2) + + // + // This is an active connection, find it using + // our session id (BUGBUG: Make this a function). + // + + Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2); + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if (Connection->LocalConnectionId == Sess->DestConnectionId) { + break; + } + Connection = Connection->NextConnection; + } + + + // + // We reply to any session end, even if we don't know the + // connection, to speed up the disconnect on the remote. + // + + if (Connection == NULL) { + + NB_DEBUG (CONNECTION, ("Session end received on unknown id %lx\n", Sess->DestConnectionId)); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + NbiSendSessionEndAck( + (TDI_ADDRESS_IPX UNALIGNED *)(Conn->IpxHeader.SourceNetwork), + RemoteAddress, + Sess); + return; + } + + NbiReferenceConnectionLock (Connection, CREF_INDICATE); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle2); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + + if (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK) { + + // + // We are waiting for an ack, so see if this acks + // anything. We do this in case a full send has been + // received by the remote but he did not send an + // ack before the session went down -- this will + // prevent us from failing a send which actually + // succeeded. If we are not in W_ACK this may ack + // part of a send, but in that case we don't care + // since StopConnection will abort it anyway and + // the amount successfully received by the remote + // doesn't matter. + // + // This releases the lock. BUGBUG: Fix this. + // + + NB_DEBUG2 (CONNECTION, ("Session end at W_ACK, reframing %lx (%d)\n", Connection, Sess->ReceiveSequence)); + + NbiReframeConnection( + Connection, + Sess->ReceiveSequence, + Sess->BytesReceived, + FALSE + NB_LOCK_HANDLE_ARG(LockHandle1)); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + + } else { + + NB_DEBUG2 (CONNECTION, ("Session end received on connection %lx\n", Connection)); + + } + + // + // This call sets the state to DISCONNECT and + // releases the connection lock. It will also + // complete a disconnect wait request if one + // is pending, and indicate to our client + // if needed. + // + + NbiStopConnection( + Connection, + STATUS_REMOTE_DISCONNECT + NB_LOCK_HANDLE_ARG (LockHandle1)); + + } else { + + NB_DEBUG2 (CONNECTION, ("Session end received on inactive connection %lx\n", Connection)); + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle2); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + } + + NbiSendSessionEndAck( + (TDI_ADDRESS_IPX UNALIGNED *)(Conn->IpxHeader.SourceNetwork), + RemoteAddress, + Sess); + + NbiDereferenceConnection (Connection, CREF_INDICATE); + +} /* NbiProcessSessionEnd */ + + +VOID +NbiProcessSessionEndAck( + IN PIPX_LOCAL_TARGET RemoteAddress, + IN ULONG MacOptions, + IN PUCHAR PacketBuffer, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine handles NB_CMD_SESSION_END_ACK frames. + +Arguments: + + RemoteAddress - The local target this packet was received from. + + MacOptions - The MAC options for the underlying NDIS binding. + + LookaheadBuffer - The packet data, starting at the IPX + header. + + PacketSize - The total length of the packet, starting at the + IPX header. + +Return Value: + + None. + +--*/ + +{ + NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)PacketBuffer; + NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); + PCONNECTION Connection; + PDEVICE Device = NbiDevice; + ULONG Hash; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + // + // This is an active connection, find it using + // our session id (BUGBUG: Make this a function). + // + + Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + Connection = Device->ConnectionHash[Hash].Connections; + + while (Connection != NULL) { + + if (Connection->LocalConnectionId == Sess->DestConnectionId) { + break; + } + Connection = Connection->NextConnection; + } + + if (Connection == NULL) { + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + NbiReferenceConnectionLock (Connection, CREF_INDICATE); + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + // + // See what is happening with this connection. + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_DISCONNECT) { + + // + // Stop the timer, when the reference goes away it + // will shut down. We set the substate so if the + // timer is running it will not restart (BUGBUG: + // there is a small window here, but it is not + // harmful, we will just have to timeout one + // more time). + // + + NB_DEBUG2 (CONNECTION, ("Got session end ack on %lx\n", Connection)); + + Connection->SubState = CONNECTION_SUBSTATE_D_GOT_ACK; + if (CTEStopTimer (&Connection->Timer)) { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + NbiDereferenceConnection (Connection, CREF_TIMER); + } else { + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NbiDereferenceConnection (Connection, CREF_INDICATE); + +} /* NbiProcessSessionEndAck */ + diff --git a/private/ntos/tdi/isnp/nb/sources.inc b/private/ntos/tdi/isnp/nb/sources.inc new file mode 100644 index 000000000..f54b4918b --- /dev/null +++ b/private/ntos/tdi/isnp/nb/sources.inc @@ -0,0 +1,69 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=nwlnknb + +TARGETNAME=nwlnknb +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..;..\inc;..\..\inc;..\..\..\..\inc;..\..\..\..\..\inc + +MSC_WARNING_LEVEL=/W3 /WX + +C_DEFINES=$(C_DEFINES) -D_NTDRIVER_ -D_PNP_POWER=1 -DRASAUTODIAL +#-DRSRC_TIMEOUT_DBG + +!IFDEF BUILD_FOR_3_51 +C_DEFINES= $(C_DEFINES) -D_NTIFS_ +!ENDIF + +SOURCES= \ + ..\action.c \ + ..\address.c \ + ..\autodial.c \ + ..\bind.c \ + ..\cache.c \ + ..\config.c \ + ..\connect.c \ + ..\datagram.c \ + ..\device.c \ + ..\driver.c \ + ..\event.c \ + ..\frame.c \ + ..\nwlnknb.rc \ + ..\packet.c \ + ..\query.c \ + ..\receive.c \ + ..\send.c \ + ..\session.c \ + ..\timer.c + +PRECOMPILED_INCLUDE=..\precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj +
\ No newline at end of file diff --git a/private/ntos/tdi/isnp/nb/timer.c b/private/ntos/tdi/isnp/nb/timer.c new file mode 100644 index 000000000..381b120e5 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/timer.c @@ -0,0 +1,1233 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + timer.c + +Abstract: + + This module contains code which implements the timers for + netbios. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +ULONG NbiTickIncrement = 0; +ULONG NbiShortTimerDeltaTicks = 0; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NbiInitializeTimers) +#endif + + +VOID +NbiStartRetransmit( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine starts the retransmit timer for the given connection. + The connection is inserted on the short list if it isn't on already. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - pointer to the connection. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle) + + // + // Insert us in the queue if we aren't in it. + // + + Connection->Retransmit = + Device->ShortAbsoluteTime + Connection->CurrentRetransmitTimeout; + + if (!Connection->OnShortList) { + + CTEAssert (KeGetCurrentIrql() == DISPATCH_LEVEL); + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + if (!Connection->OnShortList) { + Connection->OnShortList = TRUE; + InsertTailList (&Device->ShortList, &Connection->ShortList); + } + + if (!Device->ShortListActive) { + NbiStartShortTimer (Device); + Device->ShortListActive = TRUE; + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + } + +} /* NbiStartRetransmit */ + + +VOID +NbiStartWatchdog( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine starts the watchdog timer for a connection. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - pointer to the connection. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = NbiDevice; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + Connection->Watchdog = Device->LongAbsoluteTime + Connection->WatchdogTimeout; + + if (!Connection->OnLongList) { + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + if (!Connection->OnLongList) { + Connection->OnLongList = TRUE; + InsertTailList (&Device->LongList, &Connection->LongList); + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + } + +} /* NbiStartWatchdog */ + +#if DBG + +VOID +NbiStopRetransmit( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine stops the retransmit timer for a connection. + +Arguments: + + Connection - pointer to the connection. + +Return Value: + + None. + +--*/ + +{ + Connection->Retransmit = 0; + +} /* NbiStopRetransmit */ + + +VOID +NbiStopWatchdog( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine stops the watchdog timer for a connection. + +Arguments: + + Connection - pointer to the connection. + +Return Value: + + None. + +--*/ + +{ + Connection->Watchdog = 0; + +} /* NbiStopWatchdog */ +#endif + + +VOID +NbiExpireRetransmit( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called when the connection's retransmit timer + expires. It is called from NbiShortTimeout. + +Arguments: + + Connection - Pointer to the connection whose timer has expired. + +Return Value: + + none. + +--*/ + +{ + PDEVICE Device = NbiDevice; + BOOLEAN SendFindRoute; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + SendFindRoute = FALSE; + + ++Device->Statistics.ResponseTimerExpirations; + + if (!(Connection->NewNetbios) && + (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK)) { + + if (--Connection->Retries == 0) { + + // + // Shut down the connection. This will send + // out half the usual number of session end + // frames. + // + + NB_DEBUG2 (CONNECTION, ("Wait for ack timeout of active connection %lx\n", Connection)); + + // + // This free the connection lock. + // + + NbiStopConnection( + Connection, + STATUS_LINK_FAILED + NB_LOCK_HANDLE_ARG (LockHandle) + ); + + } else { + + // + // Set our current packetize location back to the + // spot of the last ack, and start up again. + // + // BUGBUG: Should we send a probe here? + // + + Connection->CurrentSend = Connection->UnAckedSend; + Connection->RetransmitThisWindow = TRUE; + if (Connection->CurrentRetransmitTimeout < (Connection->BaseRetransmitTimeout*8)) { + Connection->CurrentRetransmitTimeout = + (Connection->CurrentRetransmitTimeout * 3) / 2; + } + + NB_DEBUG2 (SEND, ("Connection %lx retransmit timeout\n", Connection)); + + // + // After half the retries, send a find route unless we + // are already doing one, or the connection is to network + // 0. When this completes we update the local target, + // for whatever good that does. + // + + if ((!Connection->FindRouteInProgress) && + (Connection->Retries == (Device->KeepAliveCount/2)) && + (*(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork != 0)) { + + SendFindRoute = TRUE; + Connection->FindRouteInProgress = TRUE; + NbiReferenceConnectionSync (Connection, CREF_FIND_ROUTE); + + } + + // + // This releases the lock. + // + + NbiPacketizeSend( + Connection + NB_LOCK_HANDLE_ARG(LockHandle) + ); + + } + + } else if ((Connection->SubState == CONNECTION_SUBSTATE_A_W_PROBE) || + (Connection->SubState == CONNECTION_SUBSTATE_A_REMOTE_W) || + (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK)) { + + if (--Connection->Retries == 0) { + + // + // Shut down the connection. This will send + // out half the usual number of session end + // frames. + // + + NB_DEBUG2 (CONNECTION, ("Probe timeout of active connection %lx\n", Connection)); + + // + // This free the connection lock. + // + + NbiStopConnection( + Connection, + STATUS_LINK_FAILED + NB_LOCK_HANDLE_ARG (LockHandle) + ); + + } else { + + Connection->RetransmitThisWindow = TRUE; + if (Connection->CurrentRetransmitTimeout < (Connection->BaseRetransmitTimeout*8)) { + Connection->CurrentRetransmitTimeout = + (Connection->CurrentRetransmitTimeout * 3) / 2; + } + + NbiStartRetransmit (Connection); + + // + // After half the retries, send a find route unless we + // are already doing one, or the connection is to network + // 0. When this completes we update the local target, + // for whatever good that does. + // + + if ((!Connection->FindRouteInProgress) && + (Connection->Retries == (Device->KeepAliveCount/2)) && + (*(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork != 0)) { + + SendFindRoute = TRUE; + Connection->FindRouteInProgress = TRUE; + NbiReferenceConnectionSync (Connection, CREF_FIND_ROUTE); + + } + + // + // Set this so we know to retransmit when the ack + // is received. + // + + if (Connection->SubState != CONNECTION_SUBSTATE_A_W_PROBE) { + Connection->ResponseTimeout = TRUE; + } + + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckQuery + NB_LOCK_HANDLE_ARG(LockHandle)); + + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + if (SendFindRoute) { + + Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; + *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = + *(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork; + RtlCopyMemory(Connection->FindRouteRequest.Node,Connection->RemoteHeader.DestinationNode,6); + Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_FORCE_RIP; + + (*Device->Bind.FindRouteHandler)( + &Connection->FindRouteRequest); + + } + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + +} /* NbiExpireRetansmit */ + + +VOID +NbiExpireWatchdog( + IN PCONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called when the connection's watchdog timer + expires. It is called from NbiLongTimeout. + +Arguments: + + Connection - Pointer to the connection whose timer has expired. + +Return Value: + + none. + +--*/ + +{ + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + // + // If we are not idle, then something else is happening + // so the watchdog is unnecessary. + // + + if ((Connection->State == CONNECTION_STATE_ACTIVE) && + (Connection->SubState == CONNECTION_SUBSTATE_A_IDLE)) { + + Connection->Retries = NbiDevice->KeepAliveCount; + Connection->SubState = CONNECTION_SUBSTATE_A_W_PROBE; + NbiStartRetransmit (Connection); + + // + // This releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckQuery + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + +} /* NbiExpireWatchdog */ + + +VOID +NbiShortTimeout( + IN CTEEvent * Event, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called at regular intervals to see if any of + the short connection timers have expired, and if so to execute their + expiration routines. + +Arguments: + + Event - The event controlling the timer. + + Context - Points to our device. + +Return Value: + + none. + +--*/ + +{ + PLIST_ENTRY p, nextp; + PDEVICE Device = (PDEVICE)Context; + PCONNECTION Connection; + BOOLEAN RestartTimer = FALSE; + LARGE_INTEGER CurrentTick; + LARGE_INTEGER TickDifference; + ULONG TickDelta; + NB_DEFINE_LOCK_HANDLE (LockHandle); + + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + // + // This prevents anybody from starting the timer while we + // are in this routine (the main reason for this is that it + // makes it easier to determine whether we should restart + // it at the end of this routine). + // + + Device->ProcessingShortTimer = TRUE; + + // + // Advance the up-counter used to mark time in SHORT_TIMER_DELTA units. If we + // advance it all the way to 0xf0000000, then reset it to 0x10000000. + // We also run all the lists, decreasing all counters by 0xe0000000. + // + + + KeQueryTickCount (&CurrentTick); + + TickDifference.QuadPart = CurrentTick.QuadPart - + Device->ShortTimerStart.QuadPart; + + TickDelta = TickDifference.LowPart / NbiShortTimerDeltaTicks; + if (TickDelta == 0) { + TickDelta = 1; + } + + Device->ShortAbsoluteTime += TickDelta; + + if (Device->ShortAbsoluteTime >= 0xf0000000) { + + ULONG Timeout; + + Device->ShortAbsoluteTime -= 0xe0000000; + + p = Device->ShortList.Flink; + while (p != &Device->ShortList) { + + Connection = CONTAINING_RECORD (p, CONNECTION, ShortList); + + Timeout = Connection->Retransmit; + if (Timeout) { + Connection->Retransmit = Timeout - 0xe0000000; + } + + p = p->Flink; + } + + } + + p = Device->ShortList.Flink; + while (p != &Device->ShortList) { + + Connection = CONTAINING_RECORD (p, CONNECTION, ShortList); + + ASSERT (Connection->OnShortList); + + // + // To avoid problems with the refcount being 0, don't + // do this if we are in ADM. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + if (Connection->Retransmit && + (Device->ShortAbsoluteTime > Connection->Retransmit)) { + + Connection->Retransmit = 0; + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + NbiExpireRetransmit (Connection); // no locks held + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + } + + } + + if (!Connection->OnShortList) { + + // + // The link has been taken out of the list while + // we were processing it. In this (rare) case we + // stop processing the whole list, we'll get it + // next time. + // + + break; + + } + + nextp = p->Flink; + + if (Connection->Retransmit == 0) { + + Connection->OnShortList = FALSE; + RemoveEntryList(p); + + // + // Do another check; that way if someone slipped in between + // the check of Connection->Tx and the OnShortList = FALSE and + // therefore exited without inserting, we'll catch that here. + // + + if (Connection->Retransmit != 0) { + InsertTailList(&Device->ShortList, &Connection->ShortList); + Connection->OnShortList = TRUE; + } + + } + + p = nextp; + + } + + // + // If the list is empty note that, otherwise ShortListActive + // remains TRUE. + // + + if (IsListEmpty (&Device->ShortList)) { + Device->ShortListActive = FALSE; + } + + + // + // Connection Data Ack timers. This queue is used to indicate + // that a piggyback ack is pending for this connection. We walk + // the queue, for each element we check if the connection has + // been on the queue for enough times through here, + // If so, we take it off and send an ack. Note that + // we have to be very careful how we walk the queue, since + // it may be changing while this is running. + // + + for (p = Device->DataAckConnections.Flink; + p != &Device->DataAckConnections; + p = p->Flink) { + + Connection = CONTAINING_RECORD (p, CONNECTION, DataAckLinkage); + + // + // Skip this connection if it is not queued or it is + // too recent to matter. We may skip incorrectly if + // the connection is just being queued, but that is + // OK, we will get it next time. + // + + if (!Connection->DataAckPending) { + continue; + } + + ++Connection->DataAckTimeouts; + + if (Connection->DataAckTimeouts < Device->AckDelayTime) { + continue; + } + + NbiReferenceConnectionSync (Connection, CREF_SHORT_D_ACK); + + Device->DataAckQueueChanged = FALSE; + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + // + // Check the correct connection flag, to ensure that a + // send has not just taken him off the queue. + // + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); + + if (Connection->DataAckPending) { + + // + // Yes, we were waiting to piggyback an ack, but no send + // has come along. Turn off the flags and send an ack. + // We set PiggybackAckTimeout to TRUE so that we won't try + // to piggyback a response until we get back traffic. + // + + Connection->DataAckPending = FALSE; + Connection->PiggybackAckTimeout = TRUE; + ++Device->Statistics.AckTimerExpirations; + ++Device->Statistics.PiggybackAckTimeouts; + + // + // This call releases the lock. + // + + NbiSendDataAck( + Connection, + NbiAckResponse + NB_LOCK_HANDLE_ARG(LockHandle)); + + } else { + + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); + + } + + NbiDereferenceConnection (Connection, CREF_SHORT_D_ACK); + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + // + // If the list has changed, then we need to stop processing + // since p->Flink is not valid. + // + + if (Device->DataAckQueueChanged) { + break; + } + + } + + if (IsListEmpty (&Device->DataAckConnections)) { + Device->DataAckActive = FALSE; + } + + + // + // Update the real counters from the temp ones. We have + // TimerLock here, which is good enough. + // + + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesSent, + Device->TempFrameBytesSent); + Device->Statistics.DataFramesSent += Device->TempFramesSent; + + Device->TempFrameBytesSent = 0; + Device->TempFramesSent = 0; + + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DataFrameBytesReceived, + Device->TempFrameBytesReceived); + Device->Statistics.DataFramesReceived += Device->TempFramesReceived; + + Device->TempFrameBytesReceived = 0; + Device->TempFramesReceived = 0; + + + // + // Determine if we have to restart the timer. + // + + Device->ProcessingShortTimer = FALSE; + + if ((Device->ShortListActive || Device->DataAckActive) && + (Device->State != DEVICE_STATE_STOPPING)) { + + RestartTimer = TRUE; + + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + if (RestartTimer) { + + // + // Start up the timer again. Note that because we start the timer + // after doing work (above), the timer values will slip somewhat, + // depending on the load on the protocol. This is entirely acceptable + // and will prevent us from using the timer DPC in two different + // threads of execution. + // + + KeQueryTickCount(&Device->ShortTimerStart); + + CTEStartTimer( + &Device->ShortTimer, + SHORT_TIMER_DELTA, + NbiShortTimeout, + (PVOID)Device); + + } else { + + NbiDereferenceDevice (Device, DREF_SHORT_TIMER); + + } + +} /* NbiShortTimeout */ + + +VOID +NbiLongTimeout( + IN CTEEvent * Event, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called at regular intervals to see if any of + the long connection timers have expired, and if so to execute their + expiration routines. + +Arguments: + + Event - The event controlling the timer. + + Context - Points to our device. + +Return Value: + + none. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PLIST_ENTRY p, nextp; + LIST_ENTRY AdapterStatusList; + PREQUEST AdapterStatusRequest; + PCONNECTION Connection; + PNETBIOS_CACHE CacheName; + NB_DEFINE_LOCK_HANDLE (LockHandle) + NB_DEFINE_LOCK_HANDLE (LockHandle1) + + + // + // Advance the up-counter used to mark time in LONG_TIMER_DELTA units. If we + // advance it all the way to 0xf0000000, then reset it to 0x10000000. + // We also run all the lists, decreasing all counters by 0xe0000000. + // + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + if (++Device->LongAbsoluteTime == 0xf0000000) { + + ULONG Timeout; + + Device->LongAbsoluteTime = 0x10000000; + + p = Device->LongList.Flink; + while (p != &Device->LongList) { + + Connection = CONTAINING_RECORD (p, CONNECTION, LongList); + + Timeout = Connection->Watchdog; + if (Timeout) { + Connection->Watchdog = Timeout - 0xe0000000; + } + + p = p->Flink; + } + + } + + + if ((Device->LongAbsoluteTime % 4) == 0) { + + p = Device->LongList.Flink; + while (p != &Device->LongList) { + + Connection = CONTAINING_RECORD (p, CONNECTION, LongList); + + ASSERT (Connection->OnLongList); + + // + // To avoid problems with the refcount being 0, don't + // do this if we are in ADM. + // + + if (Connection->State == CONNECTION_STATE_ACTIVE) { + + if (Connection->Watchdog && (Device->LongAbsoluteTime > Connection->Watchdog)) { + + Connection->Watchdog = 0; + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + NbiExpireWatchdog (Connection); // no spinlocks held + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + } + + } + + if (!Connection->OnLongList) { + + // + // The link has been taken out of the list while + // we were processing it. In this (rare) case we + // stop processing the whole list, we'll get it + // next time. + // + +#if DBG + DbgPrint ("NBI: Stop processing LongList, %lx removed\n", Connection); +#endif + break; + + } + + nextp = p->Flink; + + if (Connection->Watchdog == 0) { + + Connection->OnLongList = FALSE; + RemoveEntryList(p); + + if (Connection->Watchdog != 0) { + InsertTailList(&Device->LongList, &Connection->LongList); + Connection->OnLongList = TRUE; + } + + } + + p = nextp; + + } + + } + + + // + // Now scan the data ack queue, looking for connections with + // no acks queued that we can get rid of. + // + // Note: The timer spinlock is held here. + // + + for (p = Device->DataAckConnections.Flink; + p != &Device->DataAckConnections; + p = p->Flink) { + + Connection = CONTAINING_RECORD (p, CONNECTION, DataAckLinkage); + + if (Connection->DataAckPending) { + continue; + } + + NbiReferenceConnectionSync (Connection, CREF_LONG_D_ACK); + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1); + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + // + // Have to check again, because the connection might + // just have been stopped, and it also might just have + // had a data ack queued. + // + + if (Connection->OnDataAckQueue) { + + Connection->OnDataAckQueue = FALSE; + + RemoveEntryList (&Connection->DataAckLinkage); + + if (Connection->DataAckPending) { + InsertTailList (&Device->DataAckConnections, &Connection->DataAckLinkage); + Connection->OnDataAckQueue = TRUE; + } + + Device->DataAckQueueChanged = TRUE; + + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); + + NbiDereferenceConnection (Connection, CREF_LONG_D_ACK); + + NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle); + + // + // Since we have changed the list, we can't tell if p->Flink + // is valid, so break. The effect is that we gradually peel + // connections off the queue. + // + + break; + + } + + NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle); + + + // + // Scan for any uncompleted receive IRPs, this may happen if + // the cable is pulled and we don't get any more ReceiveComplete + // indications. + + NbiReceiveComplete((USHORT)0); + + + // + // Check if any adapter status queries are getting old. + // + + InitializeListHead (&AdapterStatusList); + + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); + + p = Device->ActiveAdapterStatus.Flink; + + while (p != &Device->ActiveAdapterStatus) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + + p = p->Flink; + + if (REQUEST_INFORMATION(AdapterStatusRequest) == 1) { + + // + // BUGBUG: We should resend a certain number of times. + // + + RemoveEntryList (REQUEST_LINKAGE(AdapterStatusRequest)); + InsertTailList (&AdapterStatusList, REQUEST_LINKAGE(AdapterStatusRequest)); + + // + // We are going to abort this request, so dereference + // the cache entry it used. + // + + CacheName = (PNETBIOS_CACHE)REQUEST_STATUS(AdapterStatusRequest); + if (--CacheName->ReferenceCount == 0) { + + NB_DEBUG2 (CACHE, ("Free delete name cache entry %lx\n", CacheName)); + NbiFreeMemory( + CacheName, + sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), + MEMORY_CACHE, + "Name deleted"); + + } + + } else { + + ++REQUEST_INFORMATION(AdapterStatusRequest); + + } + + } + + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); + + + for (p = AdapterStatusList.Flink; p != &AdapterStatusList; ) { + + AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + + NB_DEBUG2 (QUERY, ("AdapterStatus %lx got name but no response\n", AdapterStatusRequest)); + + REQUEST_INFORMATION(AdapterStatusRequest) = 0; + REQUEST_STATUS(AdapterStatusRequest) = STATUS_IO_TIMEOUT; + + NbiCompleteRequest(AdapterStatusRequest); + NbiFreeRequest (Device, AdapterStatusRequest); + + NbiDereferenceDevice (Device, DREF_STATUS_QUERY); + + } + + // + // See if a minute has passed and we need to check for empty + // cache entries to age out. We check for 64 seconds to make + // the mod operation faster. + // + +#if defined(_PNP_POWER) + NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); +#endif _PNP_POWER + + ++Device->CacheTimeStamp; + + if ((Device->CacheTimeStamp % 64) == 0) { + + + // + // flush all the entries which have been around for ten minutes + // (LONG_TIMER_DELTA is in milliseconds). + // + + FlushOldFromNetbiosCacheTable( Device->NameCache, (600000 / LONG_TIMER_DELTA) ); + + } + + + // + // Start up the timer again. Note that because we start the timer + // after doing work (above), the timer values will slip somewhat, + // depending on the load on the protocol. This is entirely acceptable + // and will prevent us from using the timer DPC in two different + // threads of execution. + // + + if (Device->State != DEVICE_STATE_STOPPING) { + + CTEStartTimer( + &Device->LongTimer, + LONG_TIMER_DELTA, + NbiLongTimeout, + (PVOID)Device); + + } else { +#if defined(_PNP_POWER) + Device->LongTimerRunning = FALSE; +#endif _PNP_POWER + NbiDereferenceDevice (Device, DREF_LONG_TIMER); + } + +#if defined(_PNP_POWER) + NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); +#endif _PNP_POWER +} /* NbiLongTimeout */ + + +VOID +NbiStartShortTimer( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine starts the short timer, if it is not already running. + +Arguments: + + Device - Pointer to our device context. + +Return Value: + + none. + +--*/ + +{ + + // + // Start the timer unless it the DPC is already running (in + // which case it will restart the timer itself if needed), + // or some list is active (meaning the timer is already + // queued up). + // + + if ((!Device->ProcessingShortTimer) && + (!(Device->ShortListActive)) && + (!(Device->DataAckActive))) { + + NbiReferenceDevice (Device, DREF_SHORT_TIMER); + + KeQueryTickCount(&Device->ShortTimerStart); + + CTEStartTimer( + &Device->ShortTimer, + SHORT_TIMER_DELTA, + NbiShortTimeout, + (PVOID)Device); + + } + +} /* NbiStartShortTimer */ + + +VOID +NbiInitializeTimers( + IN PDEVICE Device + ) + +/*++ + +Routine Description: + + This routine initializes the lightweight timer system for the transport + provider. + +Arguments: + + Device - Pointer to our device. + +Return Value: + + none. + +--*/ + +{ + + // + // NbiTickIncrement is the number of NT time increments + // which pass between each tick. NbiShortTimerDeltaTicks + // is the number of ticks which should happen in + // SHORT_TIMER_DELTA milliseconds (i.e. between each + // expiration of the short timer). + // + + NbiTickIncrement = KeQueryTimeIncrement(); + + if (NbiTickIncrement > (SHORT_TIMER_DELTA * MILLISECONDS)) { + NbiShortTimerDeltaTicks = 1; + } else { + NbiShortTimerDeltaTicks = (SHORT_TIMER_DELTA * MILLISECONDS) / NbiTickIncrement; + } + + // + // The AbsoluteTime cycles between 0x10000000 and 0xf0000000. + // + + Device->ShortAbsoluteTime = 0x10000000; + Device->LongAbsoluteTime = 0x10000000; + + CTEInitTimer (&Device->ShortTimer); + CTEInitTimer (&Device->LongTimer); + +#if !defined(_PNP_POWER) + // + // One reference for the long timer. + // + + NbiReferenceDevice (Device, DREF_LONG_TIMER); + + CTEStartTimer( + &Device->LongTimer, + LONG_TIMER_DELTA, + NbiLongTimeout, + (PVOID)Device); + +#endif !_PNP_POWER + + Device->TimersInitialized = TRUE; + Device->ShortListActive = FALSE; + Device->ProcessingShortTimer = FALSE; + + InitializeListHead (&Device->ShortList); + InitializeListHead (&Device->LongList); + + CTEInitLock (&Device->TimerLock.Lock); + +} /* NbiInitializeTimers */ + diff --git a/private/ntos/tdi/isnp/nb/up/makefile b/private/ntos/tdi/isnp/nb/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/isnp/nb/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/isnp/nb/up/sources b/private/ntos/tdi/isnp/nb/up/sources new file mode 100644 index 000000000..85cdb3764 --- /dev/null +++ b/private/ntos/tdi/isnp/nb/up/sources @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +TARGETPATH=obj + +!include ..\sources.inc |