/*++
Copyright (c) 1989-1993 Microsoft Corporation
Module Name:
uframes.c
Abstract:
This module contains a routine called StProcessConnectionless,
that gets control from routines in IND.C when a connectionless
frame is received.
Environment:
Kernel mode, DISPATCH_LEVEL.
Revision History:
--*/
#include "st.h"
NTSTATUS
StIndicateDatagram(
IN PDEVICE_CONTEXT DeviceContext,
IN PTP_ADDRESS Address,
IN PUCHAR Header,
IN ULONG Length
)
/*++
Routine Description:
This routine processes an incoming DATAGRAM or DATAGRAM_BROADCAST frame.
BROADCAST and normal datagrams have the same receive logic, except
for broadcast datagrams Address will be the broadcast address.
When we return STATUS_MORE_PROCESSING_REQUIRED, the caller of
this routine will continue to call us for each address for the device
context. When we return STATUS_SUCCESS, the caller will switch to the
next address. When we return any other status code, including
STATUS_ABANDONED, the caller will stop distributing the frame.
Arguments:
DeviceContext - Pointer to our device context.
Address - Pointer to the transport address object.
StHeader - Pointer to a buffer that contains the receive datagram.
The first byte of information is the ST header.
Length - The length of the MDL pointed to by StHeader.
Return Value:
NTSTATUS - status of operation.
--*/
{
NTSTATUS status;
PLIST_ENTRY p, q;
PIRP irp;
PIO_STACK_LOCATION irpSp;
PTP_REQUEST Request;
ULONG IndicateBytesCopied, MdlBytesCopied;
KIRQL oldirql;
TA_NETBIOS_ADDRESS SourceName;
TA_NETBIOS_ADDRESS DestinationName;
PTDI_CONNECTION_INFORMATION remoteInformation;
ULONG returnLength;
PTP_ADDRESS_FILE addressFile, prevaddressFile;
PST_HEADER StHeader;
//
// If this datagram wasn't big enough for a transport header, then don't
// let the caller look at any data.
//
if (Length < sizeof(ST_HEADER)) {
return STATUS_ABANDONED;
}
//
// Update our statistics.
//
++DeviceContext->DatagramsReceived;
ADD_TO_LARGE_INTEGER(
&DeviceContext->DatagramBytesReceived,
Length - sizeof(ST_HEADER));
//
// Call the client's ReceiveDatagram indication handler. He may
// want to accept the datagram that way.
//
StHeader = (PST_HEADER)Header;
TdiBuildNetbiosAddress (StHeader->Source, FALSE, &SourceName);
TdiBuildNetbiosAddress (StHeader->Destination, FALSE, &DestinationName);
ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql);
//
// Find the first open address file in the list.
//
p = Address->AddressFileDatabase.Flink;
while (p != &Address->AddressFileDatabase) {
addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage);
if (addressFile->State != ADDRESSFILE_STATE_OPEN) {
p = p->Flink;
continue;
}
StReferenceAddressFile(addressFile);
break;
}
while (p != &Address->AddressFileDatabase) {
//
// do we have a datagram receive request outstanding? If so, we will
// satisfy it first.
//
// NOTE: We should check if this receive dataframs is for
// a specific address.
//
q = RemoveHeadList (&addressFile->ReceiveDatagramQueue);
RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql);
if (q != &addressFile->ReceiveDatagramQueue) {
Request = CONTAINING_RECORD (q, TP_REQUEST, Linkage);
//
// Copy the actual user data.
//
MdlBytesCopied = 0;
status = TdiCopyBufferToMdl (
StHeader,
sizeof(ST_HEADER), // offset
Length - sizeof(ST_HEADER), // length
Request->IoRequestPacket->MdlAddress,
0,
&MdlBytesCopied);
irpSp = IoGetCurrentIrpStackLocation (Request->IoRequestPacket);
remoteInformation =
((PTDI_REQUEST_KERNEL_RECEIVEDG)(&irpSp->Parameters))->
ReturnDatagramInformation;
if (remoteInformation != NULL) {
try {
if (remoteInformation->RemoteAddressLength != 0) {
if (remoteInformation->RemoteAddressLength >=
sizeof (TA_NETBIOS_ADDRESS)) {
RtlCopyMemory (
(PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress,
&SourceName,
sizeof (TA_NETBIOS_ADDRESS));
returnLength = sizeof(TA_NETBIOS_ADDRESS);
remoteInformation->RemoteAddressLength = returnLength;
} else {
RtlCopyMemory (
(PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress,
&SourceName,
remoteInformation->RemoteAddressLength);
returnLength = remoteInformation->RemoteAddressLength;
remoteInformation->RemoteAddressLength = returnLength;
}
} else {
returnLength = 0;
}
status = STATUS_SUCCESS;
} except (EXCEPTION_EXECUTE_HANDLER) {
returnLength = 0;
status = GetExceptionCode ();
}
}
StCompleteRequest (Request, STATUS_SUCCESS, MdlBytesCopied);
} else {
//
// no receive datagram requests; is there a kernel client?
//
if (addressFile->RegisteredReceiveDatagramHandler) {
IndicateBytesCopied = 0;
//
// Note that we can always set the COPY_LOOKAHEAD
// flag because we are indicating from our own
// buffer, not directly from a lookahead indication.
//
status = (*addressFile->ReceiveDatagramHandler)(
addressFile->ReceiveDatagramHandlerContext,
sizeof (TA_NETBIOS_ADDRESS),
&SourceName,
0,
NULL,
TDI_RECEIVE_COPY_LOOKAHEAD,
Length - sizeof(ST_HEADER), // indicated
Length - sizeof(ST_HEADER), // available
&IndicateBytesCopied,
Header + sizeof(ST_HEADER),
&irp);
if (status == STATUS_SUCCESS) {
//
// The client accepted the datagram and so we're done.
//
} else if (status == STATUS_DATA_NOT_ACCEPTED) {
//
// The client did not accept the datagram and we need to satisfy
// a TdiReceiveDatagram, if possible.
//
status = STATUS_MORE_PROCESSING_REQUIRED;
} else if (status == STATUS_MORE_PROCESSING_REQUIRED) {
//
// The client returned an IRP that we should queue up to the
// address to satisfy the request.
//
irp->IoStatus.Status = STATUS_PENDING; // init status information.
irp->IoStatus.Information = 0;
irpSp = IoGetCurrentIrpStackLocation (irp); // get current stack loctn.
if ((irpSp->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL) ||
(irpSp->MinorFunction != TDI_RECEIVE_DATAGRAM)) {
irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
return status;
}
//
// Now copy the actual user data.
//
MdlBytesCopied = 0;
status = TdiCopyBufferToMdl (
StHeader,
sizeof(ST_HEADER) + IndicateBytesCopied,
Length - sizeof(ST_HEADER) - IndicateBytesCopied,
irp->MdlAddress,
0,
&MdlBytesCopied);
irp->IoStatus.Information = MdlBytesCopied;
irp->IoStatus.Status = status;
IoCompleteRequest (irp, IO_NETWORK_INCREMENT);
}
}
}
//
// Save this to dereference it later.
//
prevaddressFile = addressFile;
//
// Reference the next address file on the list, so it
// stays around.
//
ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql);
p = p->Flink;
while (p != &Address->AddressFileDatabase) {
addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage);
if (addressFile->State != ADDRESSFILE_STATE_OPEN) {
p = p->Flink;
continue;
}
StReferenceAddressFile(addressFile);
break;
}
RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql);
//
// Now dereference the previous address file with
// the lock released.
//
StDereferenceAddressFile (prevaddressFile);
ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql);
} // end of while loop
RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql);
return status; // to dispatcher.
} /* StIndicateDatagram */
NTSTATUS
StProcessConnect(
IN PDEVICE_CONTEXT DeviceContext,
IN PTP_ADDRESS Address,
IN PST_HEADER Header,
IN PHARDWARE_ADDRESS SourceAddress,
IN PUCHAR SourceRouting,
IN UINT SourceRoutingLength
)
/*++
Routine Description:
This routine processes an incoming connect frame. It scans for
posted listens, otherwise it indicates to connect handlers
on this address if they are registered.
Arguments:
DeviceContext - Pointer to our device context.
Address - Pointer to the transport address object.
Header - Pointer to the ST header of the frame.
SourceAddress - Pointer to the source hardware address in the received
frame.
SourceRouting - Pointer to the source routing information in
the frame.
SourceRoutingLength - Length of the source routing information.
Return Value:
NTSTATUS - status of operation.
--*/
{
KIRQL oldirql, oldirql1, cancelirql;
NTSTATUS status;
PTP_CONNECTION Connection;
BOOLEAN ConnectIndicationBlocked = FALSE;
PLIST_ENTRY p;
BOOLEAN UsedListeningConnection = FALSE;
PTP_ADDRESS_FILE addressFile, prevaddressFile;
PTP_REQUEST request;
PIO_STACK_LOCATION irpSp;
ULONG returnLength;
PTDI_CONNECTION_INFORMATION remoteInformation;
TA_NETBIOS_ADDRESS TempAddress;
PIRP acceptIrp;
CONNECTION_CONTEXT connectionContext;
//
// If we are just registering or deregistering this address, then don't
// allow state changes. Just throw the packet away, and let the frame
// distributor try the next address.
//
if (Address->Flags & (ADDRESS_FLAGS_REGISTERING | ADDRESS_FLAGS_DEREGISTERING)) {
return STATUS_SUCCESS;
}
//
// This is an incoming connection request. If we have a listening
// connection on this address, then continue with the connection setup.
// If there is no outstanding listen, then indicate any kernel mode
// clients that want to know about this frame. If a listen was posted,
// then a connection has already been set up for it.
//
//
// First, check if we already have an active connection with
// this remote on this address. If so, we ignore this
// (NOTE: This is not the correct behaviour for a real
// transport).
//
//
// If successful this adds a reference.
//
if (Connection = StLookupRemoteName(Address, Header->Source)) {
StDereferenceConnection ("Lookup done", Connection);
return STATUS_ABANDONED;
}
// If successful, this adds a reference which is removed before
// this function returns.
Connection = StLookupListeningConnection (Address);
if (Connection == NULL) {
//
// not having a listening connection is not reason to bail out here.
// we need to indicate to the user that a connect attempt occurred,
// and see if there is a desire to use this connection. We
// indicate in order to all address files that are
// using this address.
//
// If we already have an indication pending on this address,
// we ignore this frame (the NAME_QUERY may have come from
// a different address, but we can't know that). Also, if
// there is already an active connection on this remote
// name, then we ignore the frame.
//
ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql);
p = Address->AddressFileDatabase.Flink;
while (p != &Address->AddressFileDatabase) {
addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage);
if (addressFile->State != ADDRESSFILE_STATE_OPEN) {
p = p->Flink;
continue;
}
StReferenceAddressFile(addressFile);
break;
}
while (p != &Address->AddressFileDatabase) {
RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql);
if ((addressFile->RegisteredConnectionHandler == TRUE) &&
(!addressFile->ConnectIndicationInProgress)) {
TdiBuildNetbiosAddress (
Header->Source,
FALSE,
&TempAddress);
addressFile->ConnectIndicationInProgress = TRUE;
//
// we have a connection handler, now indicate that a connection
// attempt occurred.
//
status = (addressFile->ConnectionHandler)(
addressFile->ConnectionHandlerContext,
sizeof (TDI_ADDRESS_NETBIOS),
&TempAddress,
0,
NULL,
0,
NULL,
&connectionContext,
&acceptIrp);
if (status == STATUS_MORE_PROCESSING_REQUIRED) {
// the user has connected a currently open connection, but
// we have to figure out which one it is.
//
//
// If successful this adds a reference of type LISTENING
// (the same what StLookupListeningConnection adds).
//
Connection = StLookupConnectionByContext (
Address,
connectionContext);
if (Connection == NULL) {
//
// BUGBUG: We have to tell the client that
// his connection is bogus (or has this
// already happened??).
//
StPrint0("MORE_PROCESSING_REQUIRED, connection not found\n");
addressFile->ConnectIndicationInProgress = FALSE;
acceptIrp->IoStatus.Status = STATUS_INVALID_CONNECTION;
IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT);
goto whileend; // try next address file
} else {
if (Connection->AddressFile->Address != Address) {
addressFile->ConnectIndicationInProgress = FALSE;
StPrint0("MORE_PROCESSING_REQUIRED, address wrong\n");
StStopConnection (Connection, STATUS_INVALID_ADDRESS);
StDereferenceConnection("Bad Address", Connection);
Connection = NULL;
acceptIrp->IoStatus.Status = STATUS_INVALID_CONNECTION;
IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT);
goto whileend; // try next address file
}
//
// OK, we have a valid connection. If the response to
// this connection was disconnect, we need to reject
// the connection request and return. If it was accept
// or not specified (to be done later), we simply
// fall through and continue processing on the U Frame.
//
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql1);
if ((Connection->Flags2 & CONNECTION_FLAGS2_DISCONNECT) != 0) {
Connection->Flags2 &= ~CONNECTION_FLAGS2_DISCONNECT;
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1);
StPrint0("MORE_PROCESSING_REQUIRED, disconnect\n");
addressFile->ConnectIndicationInProgress = FALSE;
StDereferenceConnection("Disconnecting", Connection);
Connection = NULL;
acceptIrp->IoStatus.Status = STATUS_INVALID_CONNECTION;
IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT);
goto whileend; // try next address file
}
}
//
// This connection is ready.
//
Connection->Flags &= ~CONNECTION_FLAGS_STOPPING;
Connection->Status = STATUS_PENDING;
Connection->Flags2 |= CONNECTION_FLAGS2_ACCEPTED;
Connection->Flags |= CONNECTION_FLAGS_READY;
INCREMENT_COUNTER (Connection->Provider, OpenConnections);
Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED;
StReferenceConnection("Indication completed", Connection);
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1);
//
// Make a note that we have to set
// addressFile->ConnectIndicationInProgress to
// FALSE once the address is safely stored
// in the connection.
//
ConnectIndicationBlocked = TRUE;
StDereferenceAddressFile (addressFile);
ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql);
break; // exit the while
} else if (status == STATUS_INSUFFICIENT_RESOURCES) {
//
// we know the address, but can't create a connection to
// use on it. This gets passed to the network as a response
// saying I'm here, but can't help.
//
addressFile->ConnectIndicationInProgress = FALSE;
StDereferenceAddressFile (addressFile);
return STATUS_ABANDONED;
} else {
addressFile->ConnectIndicationInProgress = FALSE;
goto whileend; // try next address file
} // end status ifs
} else {
goto whileend; // try next address file
} // end no indication handler
whileend:
//
// Jumping here is like a continue, except that the
// addressFile pointer is advanced correctly.
//
//
// Save this to dereference it later.
//
prevaddressFile = addressFile;
//
// Reference the next address file on the list, so it
// stays around.
//
ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql);
p = p->Flink;
while (p != &Address->AddressFileDatabase) {
addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage);
if (addressFile->State != ADDRESSFILE_STATE_OPEN) {
p = p->Flink;
continue;
}
StReferenceAddressFile(addressFile);
break;
}
RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql);
//
// Now dereference the previous address file with
// the lock released.
//
StDereferenceAddressFile (prevaddressFile);
ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql);
} // end of loop through the address files.
RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql);
if (Connection == NULL) {
//
// We used to return MORE_PROCESSING_REQUIRED, but
// since we matched with this address, no other
// address is going to match, so abandon it.
//
return STATUS_ABANDONED;
}
} else { // end connection == null
UsedListeningConnection = TRUE;
IoAcquireCancelSpinLock (&cancelirql);
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
p = RemoveHeadList (&Connection->InProgressRequest);
if (p == &Connection->InProgressRequest) {
Connection->IndicationInProgress = FALSE;
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
IoReleaseCancelSpinLock (cancelirql);
return STATUS_SUCCESS;
}
//
// If this listen indicated that we should wait for a
// TdiAccept, then do that, otherwise the connection is
// ready.
//
if ((Connection->Flags2 & CONNECTION_FLAGS2_PRE_ACCEPT) == 0) {
Connection->Flags2 |= CONNECTION_FLAGS2_WAIT_ACCEPT;
} else {
Connection->Flags |= CONNECTION_FLAGS_READY;
INCREMENT_COUNTER (Connection->Provider, OpenConnections);
Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED;
StReferenceConnection("Listen completed", Connection);
}
//
// We have a completed connection with a queued listen. Complete
// the listen and let the user do an accept at some time down the
// road.
//
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)NULL;
IoReleaseCancelSpinLock (cancelirql);
irpSp = IoGetCurrentIrpStackLocation (request->IoRequestPacket);
remoteInformation =
((PTDI_REQUEST_KERNEL)(&irpSp->Parameters))->ReturnConnectionInformation;
if (remoteInformation != NULL) {
try {
if (remoteInformation->RemoteAddressLength != 0) {
//
// Build a temporary TA_NETBIOS_ADDRESS, then
// copy over as many bytes as fit.
//
TdiBuildNetbiosAddress(
Connection->CalledAddress.NetbiosName,
(BOOLEAN)(Connection->CalledAddress.NetbiosNameType ==
TDI_ADDRESS_NETBIOS_TYPE_GROUP),
&TempAddress);
if (remoteInformation->RemoteAddressLength >=
sizeof (TA_NETBIOS_ADDRESS)) {
returnLength = sizeof(TA_NETBIOS_ADDRESS);
remoteInformation->RemoteAddressLength = returnLength;
} else {
returnLength = remoteInformation->RemoteAddressLength;
}
RtlCopyMemory(
(PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress,
&TempAddress,
returnLength);
} else {
returnLength = 0;
}
status = STATUS_SUCCESS;
} except (EXCEPTION_EXECUTE_HANDLER) {
returnLength = 0;
status = GetExceptionCode ();
}
} else {
status = STATUS_SUCCESS;
returnLength = 0;
}
//
// Don't clear this until now, so that the connection is all
// set up before we allow more indications.
//
Connection->IndicationInProgress = FALSE;
StCompleteRequest (request, status, 0);
}
//
// Before we continue, store the remote guy's transport address
// into the TdiListen's TRANSPORT_CONNECTION buffer. This allows
// the client to determine who called him.
//
Connection->CalledAddress.NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
NdisMoveFromMappedMemory(
Connection->CalledAddress.NetbiosName,
Header->Source,
16);
NdisMoveFromMappedMemory(
Connection->RemoteName,
Header->Source,
16);
Connection->Flags2 |= CONNECTION_FLAGS2_REMOTE_VALID;
if (ConnectIndicationBlocked) {
addressFile->ConnectIndicationInProgress = FALSE;
}
StDereferenceConnection("ProcessNameQuery done", Connection);
return STATUS_ABANDONED;
} /* StProcessConnect */
NTSTATUS
StProcessConnectionless(
IN PDEVICE_CONTEXT DeviceContext,
IN PHARDWARE_ADDRESS SourceAddress,
IN PST_HEADER StHeader,
IN ULONG StLength,
IN PUCHAR SourceRouting,
IN UINT SourceRoutingLength,
OUT PTP_ADDRESS * DatagramAddress
)
/*++
Routine Description:
This routine receives control from the data link provider as an
indication that a connectionless frame has been received on the data link.
Here we dispatch to the correct handler.
Arguments:
DeviceContext - Pointer to our device context.
SourceAddress - Pointer to the source hardware address in the received
frame.
StHeader - Points to the ST header of the incoming packet.
StLength - Actual length in bytes of the packet, starting at the
StHeader.
SourceRouting - Source routing information in the MAC header.
SourceRoutingLength - The length of SourceRouting.
DatagramAddress - If this function returns STATUS_MORE_PROCESSING_
REQUIRED, this will be the address the datagram should be
indicated to.
Return Value:
NTSTATUS - status of operation.
--*/
{
PTP_ADDRESS Address;
KIRQL oldirql;
NTSTATUS status;
PLIST_ENTRY Flink;
BOOLEAN MatchedAddress;
PUCHAR MatchName;
//
// Verify that this frame is long enough to examine.
//
if (StLength < sizeof(ST_HEADER)) {
return STATUS_ABANDONED; // frame too small.
}
//
// We have a valid connectionless protocol frame that's not a
// datagram, so deliver it to every address which matches the
// destination name in the frame.
//
MatchedAddress = FALSE;
//
// Search for the address; for broadcast datagrams we
// search for the special "broadcast" address.
//
if ((StHeader->Command == ST_CMD_DATAGRAM) &&
(StHeader->Flags & ST_FLAGS_BROADCAST)) {
MatchName = NULL;
} else {
MatchName = StHeader->Destination;
}
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
for (Flink = DeviceContext->AddressDatabase.Flink;
Flink != &DeviceContext->AddressDatabase;
Flink = Flink->Flink) {
Address = CONTAINING_RECORD (
Flink,
TP_ADDRESS,
Linkage);
if ((Address->Flags & ADDRESS_FLAGS_STOPPING) != 0) {
continue;
}
if (StMatchNetbiosAddress (Address, MatchName)) {
StReferenceAddress ("UI Frame", Address); // prevent address from being destroyed.
MatchedAddress = TRUE;
break;
}
}
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
if (MatchedAddress) {
//
// Deliver the frame to the current address.
//
switch (StHeader->Command) {
case ST_CMD_CONNECT:
status = StProcessConnect (
DeviceContext,
Address,
StHeader,
SourceAddress,
SourceRouting,
SourceRoutingLength);
break;
case ST_CMD_DATAGRAM:
//
// Reference the datagram so it sticks around until the
// ReceiveComplete, when it is processed.
//
StReferenceAddress ("Datagram indicated", Address);
*DatagramAddress = Address;
status = STATUS_MORE_PROCESSING_REQUIRED;
break;
default:
ASSERT(FALSE);
} /* switch on frame command code */
StDereferenceAddress ("Done", Address); // done with previous address.
} else {
status = STATUS_ABANDONED;
}
return status;
} /* StProcessConnectionless */