Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4427 lines
121 KiB

/*++
Copyright (c) 1989, 1990, 1991 Microsoft Corporation
Module Name:
connobj.c
Abstract:
This module contains code which implements the CONNECTION_FILE object.
Routines are provided to create, destroy, reference, and dereference,
transport connection objects.
All routines which perform operations like connect/listen etc., on
the connection objects are also included here.
BUGBUG:
Should creation of address/connection objects increment ref counts
on device objects (currently they are not referenced). It is not
being done with the assumption that NT will call NtClose for all
existing handles before the Unload routine is called.
Notes:
There are two important states during the death of a connection object.
STOPPING- This happens when the associated address object is being
closed. It also happens when the Cleanup irp is received
for the connection object.
When in this state, the connection object will be
deactivated (connections, listens etc., cancelled),
and brought back into the OPEN state. This can happen at
any point in the life of the connection object.
CLOSING- This happens when a TdiClose on the connection object is
called. This can happen and should be accepted *at any point*
in the connection objects life. It will be preceeded by the
connection object entering the STOPPING state.
More Notes:
These are the states a connection object can be in -
CONNECTION_FLAGS_OPEN - Open
CONNECTION_FLAGS_ASSOCIATED - Open and is associated with an AO
CONNECTION_FLAGS_LISTENPOSTING - A listen is being posted
CONNECTION_FLAGS_CONNECTPOSTING - A connect is being posted
CONNECTION_FLAGS_LISTENPOSTED - A listen has been posted
CONNECTION_FLAGS_CONNECTPOSTED - A connect has been posted
CONNECTION_FLAGS_LISTENCOMPLETEINDICATE - A listen completed, wait for accept
CONNECTION_FLAGS_ACCEPTPOSTING - An accept is being posted
CONNECTION_FLAGS_ACCEPTPOSTED - An accept has been posted
CONNECTION_FLAGS_ACTIVE - The connection is active
CONNECTION_FLAGS_DISCONNECTING - The connection is being disconnected
CONNECTION_FLAGS_DEFERREDDISC - A disconnect request has been deferred
CONNECTION_FLAGS_STOPPING - The connection is stopping (disassoc)
CONNECTION_FLAGS_CLOSING - The connection is closing
The states - *POSTING/*POSTED exist to avoid holding the spinlock while the
request is actually being posted with the portable stack. Since a close/disc
can come in while that is happening, we have race conditions. A disconnect
request needs to complete before a stop request (and hence a close request)
can complete. A disconnect request works in the following way -
if the connection is active, disconnect proceeds normally.
if the connection has listen/accept/connect being posted,
it gets deferred, until the posting actually completes.
at that point, in the case of listen, a cancel is attempted.
if it passes, disconnect is complete, else it will be completed
during the activation in listen completion.
in the case of accept/connect, a deferred disconnect will
complete during completion.
if the connection is in the listencompleteindicate state,
then the disconnect translates to a deny/disconnect.
We will depend on the transition of the secondary reference count from 1 -> 0
to indicate there are no requests pending on the connection, so a disconnect
can happen at that point. Listen presents the unique case - there is no
guarantee it will complete within a finite amount of time, so we need to
check during the posting->posted transition for a deferred disconnect, and
we need to explicitly call disconnect ourselves.
We call AtalkConnDisconnect with the retry flag set to true from within the
dereference connection routine and from the explicit case for listen mentioned
above. Note again, that the connection gets dereferenced when the tdi request
gets dereferenced.
All send/receive etc., requests will not be valid if the connection is
in a stopping/closing/disconnecting/deferreddisc state. A disconnect request
will prove to be a NOP in such cases (i.e. if already disconnecting) except
for a tdi disconnect request, which will remember the disconnect irp for
completion.
Author:
Nikhil Kamkolkar ([email protected])
Environment:
Kernel mode
Revision History:
--*/
#include "atalknt.h"
#include "connobj.h"
//
// NOTE: All worker routines *must* be passed valid connection/address
// objects. The references on the connection object and the tdi request
// block will be removed by the completion routines, if STATUS_PENDING
// is returned. If any other status is returned, the caller must remove
// these references.
//
VOID
AtalkAllocateConnection(
IN PATALK_DEVICE_CONTEXT Context,
OUT PCONNECTION_FILE *Connection
)
/*++
Routine Description:
This routine allocates storage for a transport connection. Some
minimal initialization is done.
Arguments:
Context - Currently unused, could be used later for memory
limitations/free list maintainance etc.
Connection - Pointer to a place where this routine will return a
pointer to a transport connection structure. Returns
NULL if the storage cannot be allocated.
Return Value:
None.
--*/
{
PCONNECTION_FILE connection;
connection = (PCONNECTION_FILE)AtalkCallocNonPagedMemory(
sizeof(CONNECTION_FILE), sizeof(char));
if (connection != NULL) {
//
// Initialize the Type and Size fields. These are used during verification.
//
connection->Type = ATALK_CONNECTION_SIGNATURE;
connection->Size = sizeof (CONNECTION_FILE);
} else {
//
// BUGBUG: LOG ERROR
//
}
*Connection = connection;
return;
}
VOID
AtalkDeallocateConnection(
IN PATALK_DEVICE_CONTEXT Context,
IN PCONNECTION_FILE Connection
)
/*++
Routine Description:
This routine frees the allocated connection structure.
Arguments:
Context - Currently unused, could be used later for memory
limitations/free list maintainance etc.
Connection - Pointer to a place where this routine will return a
pointer to a transport connection structure. Returns
NULL if the storage cannot be allocated.
Return Value:
None.
--*/
{
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_INFOCLASS1,
("INFO1: AtalkDeallocateConnection- Freeing Connection: %lx\n", Connection));
AtalkFreeNonPagedMemory(Connection);
return;
} /* AtalkDeallocateConnection */
NTSTATUS
AtalkVerifyConnectionObject (
IN PCONNECTION_FILE 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.
Arguments:
Connection - potential pointer to a PCONNECTION_FILE object.
Return Value:
STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise
--*/
{
NTSTATUS status = STATUS_SUCCESS;
//
// try to verify the connection signature. If the signature is valid,
// reference it. Note that being in the stopping state is an OK place
// to be and reference the connection;
//
try {
if ((Connection->Size == sizeof (CONNECTION_FILE)) &&
(Connection->Type == ATALK_CONNECTION_SIGNATURE)) {
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
if ((Connection->Flags & CONNECTION_FLAGS_CLOSING) == 0) {
AtalkReferenceConnection ("VerifyConn", Connection,
CREF_VERIFY, SECONDARY_REFSET);
} else {
//
// Connection is closing down...
//
status = STATUS_INVALID_CONNECTION;
}
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
} else {
status = STATUS_INVALID_CONNECTION;
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_ERROR,
("ERROR: AtalkVerifyConnectionObject- sig %lx\n", Connection));
DBGBRK(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_ERROR);
}
} except(EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
return status;
}
VOID
AtalkRefConnection(
IN PCONNECTION_FILE Connection,
IN REFERENCE_SET ReferenceSet
)
/*++
Routine Description:
This routine increments the reference count on a transport connection.
The primary reference set consists of the Creation reference, the
secondary reference set consists of all the other references.
Arguments:
Connection - Pointer to a transport connection object.
ReferenceSet- Reference type to be incremented (PRIMARY_REFSET or
SECONDARY_REFSET).
Return Value:
none.
--*/
{
ULONG count;
if (ReferenceSet == PRIMARY_REFSET) {
count = NdisInterlockedAddUlong (
(PULONG)&Connection->PrimaryReferenceCount,
(ULONG)1,
&AtalkGlobalRefLock);
} else {
//
// Secondary ref set
//
count = NdisInterlockedAddUlong (
(PULONG)&Connection->SecondaryReferenceCount,
(ULONG)1,
&AtalkGlobalRefLock);
}
ASSERT (count >= 0);
return;
} /* AtalkRefConnection */
#define CONNDEREF_ACTIONNONE 0x00
#define CONNDEREF_CLEANUP 0x01
#define CONNDEREF_DISCCOMP 0x02
#define CONNDEREF_DEFDISC 0x03
VOID
AtalkDerefConnection(
IN PCONNECTION_FILE Connection,
IN REFERENCE_SET ReferenceSet
)
/*++
Routine Description:
This routine dereferences a transport connection by decrementing the
reference count contained in the structure. If, after being
decremented, both the primary and the secondary reference counts are
zero, then this routine calls AtalkDestroyConnection to remove it from
the system.
Arguments:
Connection - Pointer to a transport connection object.
ReferenceSet- Reference type to be decremented (PRIMARY_REFSET or
SECONDARY_REFSET).
Locks:
This must be called with no locks held. This obtains the ConnectionLock
and then the AtalkGlobalRefLock to avoid deadlock situations with AtalkRef
being called with the ConnectionLock held.
ConnectionLock
AtalkGlobalRefLock
Return Value:
none.
--*/
{
UCHAR action = CONNDEREF_ACTIONNONE;
//
// If all references (including the creation reference are removed
// for this object, then destroy it
//
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
ACQUIRE_SPIN_LOCK(&AtalkGlobalRefLock);
//
// Decrement the indicated reference
//
if (ReferenceSet == PRIMARY_REFSET) {
Connection->PrimaryReferenceCount--;
} else {
Connection->SecondaryReferenceCount--;
}
ASSERT(Connection->PrimaryReferenceCount >= 0 &&
Connection->SecondaryReferenceCount >= 0);
if ((Connection->PrimaryReferenceCount == 0) &&
(Connection->SecondaryReferenceCount == 0)) {
action = CONNDEREF_CLEANUP;
} else if ((Connection->SecondaryReferenceCount == 0) &&
(ReferenceSet == SECONDARY_REFSET)) {
//
// For the 1->0 transition of the SecondaryReferenceCount, we know
// that all requests on the connection (if any) have completed, and
// we can complete the disconnection/stopping phases.
//
// If we were disconnecting, we are now done
// Each of these call could potentially result in the CO
// being destroyed. Make sure we do not try to reference it
// after the call.
//
if (Connection->Flags & CONNECTION_FLAGS_DISCONNECTING) {
action = CONNDEREF_DISCCOMP;
} else if (Connection->Flags & CONNECTION_FLAGS_DEFERREDDISC) {
//
// The connection had either a listen/connect being posted
// when a disconnect needed to happen. Call disconnect now
// and since there are no requests, the disconnect complete
// routine should be called due to either immediate or
// delayed completion of the Portable stack's disconnect
// routine.
//
action = CONNDEREF_DEFDISC;
}
}
RELEASE_SPIN_LOCK(&AtalkGlobalRefLock);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
switch (action) {
case CONNDEREF_CLEANUP :
// If we have deleted all references to this connection, 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 connection any longer.
//
AtalkDestroyConnection (Connection);
break;
case CONNDEREF_DISCCOMP :
AtalkConnDisconnectComplete(Connection);
break;
case CONNDEREF_DEFDISC :
AtalkConnDisconnect(
Connection,
Connection->DisconnectStatus,
Connection->DisconnectIrp,
TRUE); // Is this a retry?
break;
default:
break;
}
return;
} /* AtalkDerefConnection */
NTSTATUS
AtalkCreateConnection(
IN CONNECTION_CONTEXT ConnectionContext,
OUT PCONNECTION_FILE *Connection,
IN PATALK_DEVICE_CONTEXT AtalkDeviceContext
)
/*++
Routine Description:
This routine creates a transport connection. The connection is referenced
for the PRIMARY_REFSET (creation belongs to the primary ref set).
Arguments:
ConnectionContext - Connection context to be associated with created connection
Connection - Pointer to a place where this routine will return a pointer
to a transport connection structure
AtalkDeviceContext - Device context of the device
Return Value:
NTSTATUS - status of operation.
--*/
{
PCONNECTION_FILE connection;
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_INFOCLASS0,
("INFO0: AtalkCreateConnection - Entered\n"));
AtalkAllocateConnection (AtalkDeviceContext, &connection);
if (connection == NULL) {
return(STATUS_INSUFFICIENT_RESOURCES);
}
#if DBG
{
UINT Counter;
for (Counter = 0; Counter < NUMBER_OF_CREFS; Counter++) {
connection->RefTypes[Counter] = 0;
}
}
#endif
//
// IMPORTANT NOTE:
// This reference is removed by AtalkCloseConnection
//
AtalkReferenceConnection("ConnCreation", connection,
CREF_CREATION, PRIMARY_REFSET);
//
// Initialize the request queues & components of this connection.
//
connection->Flags = CONNECTION_FLAGS_OPEN;
connection->DisconnectStatus = STATUS_SUCCESS;
connection->DeviceContext = AtalkDeviceContext;
connection->OwningDevice = AtalkDeviceContext->DeviceType;
connection->ConnectionContext = ConnectionContext;
connection->Destroyed = FALSE;
NdisAllocateSpinLock(&connection->ConnectionLock);
InitializeListHead(&connection->Linkage);
InitializeListHead(&connection->RequestLinkage);
connection->AssociatedAddress = NULL;
*Connection = connection; // return the connection.
return STATUS_SUCCESS;
} /* AtalkCreateConnection */
NTSTATUS
AtalkDestroyConnection(
IN PCONNECTION_FILE Connection
)
/*++
Routine Description:
This routine is only called by AtalkDereferenceConnection. The reason for
this is that there may be multiple streams of execution which are
simultaneously referencing the same connection object, and it should
not be deleted out from under an interested stream of execution.
Arguments:
Connection - Pointer to a transport connection structure to
be destroyed.
Return Value:
NTSTATUS - status of operation.
--*/
{
PATALK_DEVICE_CONTEXT atalkDeviceContext;
PIRP closeIrp;
PFILE_OBJECT fileObject;
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_INFOCLASS0,
("INFO0: AtalkDestroyConnection - %lx\n", Connection));
if (Connection->Destroyed) {
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_IMPOSSIBLE,
("IMPOSSIBLE: AtalkDestroyConnection - destroyed %lx\n", Connection));
DBGBRK(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_IMPOSSIBLE);
return(STATUS_FILE_CLOSED);
}
if ((Connection->Flags & CONNECTION_FLAGS_CLOSING) == 0){
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_IMPOSSIBLE,
("IMPOSSIBLE: AtalkDestroyConnection - Unstopped %lx\n", Connection));
DBGBRK(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_IMPOSSIBLE);
return(STATUS_UNSUCCESSFUL);
}
Connection->Destroyed = TRUE;
atalkDeviceContext = Connection->DeviceContext;
//
// Now complete the close IRP. This will be set to non-null
// when CloseConnection was called.
//
closeIrp = Connection->CloseIrp;
fileObject = Connection->FileObject;
Connection->CloseIrp = (PIRP)NULL;
if (closeIrp != (PIRP)NULL) {
//
// Set the iostatus in the irp
//
closeIrp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(closeIrp, IO_NETWORK_INCREMENT );
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_INFOCLASS1,
("INFO1: AtalkDestroyConnection - Connection %x destroyed\n", Connection));
} else {
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_IMPOSSIBLE,
("IMPOSSIBLE: AtalkDestroyConnection - Conn %x no irp!\n", Connection));
DBGBRK(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_IMPOSSIBLE);
}
//
// Free the connection
//
AtalkDeallocateConnection (atalkDeviceContext, Connection);
return STATUS_SUCCESS;
} /* AtalkDestroyConnection */
VOID
AtalkStopConnection(
IN PCONNECTION_FILE Connection
)
/*++
Routine Description:
This routine is called to STOP the connection. That implies that
all activity on the connection will end and the connection will
be disassociated from the address object (if it is associated with
one).
NOTES:
This is called from two places.
TdiClose(AO) processing (AtalkStopAddress) &
TdiClose(CO) processing (AtalkCloseConnection).
Note that in both instances, there is no dependency on the synchronous
completion of this routines activities. TdiClose(AO) will only be done
when the associate reference goes away. And that happens when this routine
(in a potentially asynchronous manner) removes that reference. And also
in the TdiClose(CO) case, if there are any requests on the connection
object, the connection close irp will be completed only when the
references due to those requests go away. The CloseConnection routines
can go ahead and remove the creation reference after return from this
routine. But that will not complete the close irp if there are any
secondary references on the connection object.
IMPORTANT: Caller of this routine *must* have a reference to the connection
object that is removed after returning from this routine. Otherwise, there
is the potential that the disconnect process etc., would free up the object.
Arguments:
Connection - Pointer to a PCONNECTION_FILE object.
Return Value:
none.
--*/
{
NTSTATUS status;
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_INFOCLASS0,
("INFO0: AtalkStopConnection - Entered for connection %lx \n", Connection));
ACQUIRE_SPIN_LOCK (&Connection->ConnectionLock);
do {
//
// if we are already stopping just return
//
if (Connection->Flags & CONNECTION_FLAGS_STOPPING) {
break;
}
//
// If already effectively stopped, just return.
//
if ((Connection->Flags & ~(CONNECTION_FLAGS_OPEN |
CONNECTION_FLAGS_CLOSING)) == 0) {
break;
}
Connection->Flags |= CONNECTION_FLAGS_STOPPING;
//
// Have a primary reference for the stop connection
// This reference will go in StopConnectionComplete.
//
AtalkReferenceConnection("StopPRef", Connection,
CREF_TEMP, PRIMARY_REFSET);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
status = AtalkConnDisconnect(Connection, STATUS_FILE_CLOSED, NULL, FALSE);
if (status == STATUS_DEVICE_NOT_CONNECTED) {
//
// We were already disconnected. Call stop completion
// Callers reference will still be around until this routine
// returns.
//
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_INFOCLASS0,
("INFO1: AtalkStopConnection - Connection already inactive"));
AtalkStopConnectionComplete(Connection);
}
ACQUIRE_SPIN_LOCK (&Connection->ConnectionLock);
} while (FALSE);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
return;
} /* AtalkStopConnection */
VOID
AtalkStopConnectionComplete(
IN PCONNECTION_FILE Connection
)
/*++
Routine Description:
Completion routine for the STOP. This is either called from
DisconnectComplete or from StopConnection.
Arguments:
Connection - Pointer to a PCONNECTION_FILE object.
Return Value:
none.
--*/
{
if (Connection->Flags & CONNECTION_FLAGS_STOPPING) {
//
// Call the disassociate routine, then reset the stopping flag
//
AtalkConnDisassociateAddress(Connection);
ACQUIRE_SPIN_LOCK (&Connection->ConnectionLock);
Connection->Flags &= ~CONNECTION_FLAGS_STOPPING;
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
AtalkDereferenceConnection("StopPRefC", Connection,
CREF_TEMP, PRIMARY_REFSET);
} else {
ASSERT(0);
}
return;
}
NTSTATUS
AtalkCleanupConnection(
IN OUT PIO_STATUS_BLOCK IoStatus,
IN PCONNECTION_FILE Connection,
IN PIRP Irp,
IN PATALK_DEVICE_CONTEXT Context
)
/*++
Routine Description:
This routine is called when the cleanup irp is submitted for this connection
object.
Arguments:
IoStatus - The io status block for the request (pointer to the one in the irp)
Connection - The connection object's fscontext
Irp - The cleanup irp
Context - The device context for the device the object belongs to
Return Value:
STATUS_SUCCESS
--*/
{
UNREFERENCED_PARAMETER(IoStatus);
UNREFERENCED_PARAMETER(Irp);
UNREFERENCED_PARAMETER(Context);
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_INFOCLASS0,
("INFO0: AtalkCleanupConnection - Entered for connection %lx\n", Connection));
AtalkStopConnection(Connection);
return(STATUS_SUCCESS);
} /* AtalkCleanupConnection */
NTSTATUS
AtalkCloseConnection(
IN OUT PIO_STATUS_BLOCK IoStatus,
IN PCONNECTION_FILE Connection,
IN PIRP Irp,
IN PATALK_DEVICE_CONTEXT Context
)
/*++
Routine Description:
This routine is called when a close irp is received.
Arguments:
IoStatus - The io status block for the request (pointer to the one in the irp)
Connection - The connection object's fscontext
Irp - The close irp
Context - The device context for the device the object belongs to
Return Value:
STATUS_SUCCESS if all is well, STATUS_PENDING if close is to pend.
--*/
{
NTSTATUS status;
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_INFOCLASS1,
("INFO1: AtalkCloseConnection - Closing Connection: %lx\n", Connection));
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
do {
if (Connection->Flags & CONNECTION_FLAGS_CLOSING) {
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_IMPOSSIBLE,
("IMPOSSIBLE: AtalkCloseConnection - Close TWICE!: %lx\n", Connection));
DBGBRK(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_IMPOSSIBLE);
status = STATUS_TOO_MANY_COMMANDS;
break;
}
Connection->Flags |= CONNECTION_FLAGS_CLOSING;
Connection->CloseIrp = Irp;
//
// If we are not currently stopping, make sure we are
// completely stopped. Note that some requests could have
// sneaked in, between the time of CLEANUP irp completion
// and STOPPING->IDLE transition, and the actual close
// irp coming in.
//
if ((Connection->Flags & CONNECTION_FLAGS_STOPPING) == 0) {
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
AtalkStopConnection(Connection);
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
}
} while (FALSE);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
//
// Remove the creation reference
//
AtalkDereferenceConnection("ClosingConn", Connection,
CREF_CREATION, PRIMARY_REFSET);
return STATUS_PENDING;
} /* AtalkCloseConnection */
NTSTATUS
AtalkConnVerifyAssocAddress (
IN PCONNECTION_FILE Connection
)
/*++
Routine Description:
Verfies that there is an association present, and if so verifies the
address referencing it in the process.
Arguments:
Connection - pointer to a PCONNECTION_FILE object.
Return Value:
STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise
--*/
{
NTSTATUS status = STATUS_INVALID_ADDRESS;
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
if (Connection->Flags & CONNECTION_FLAGS_ASSOCIATED) {
//
// Verify is being called with the ConnectionLock held so that
// a close request on the address object will not lead to a null
// associated address race condition
//
status = AtalkVerifyAddressObject(Connection->AssociatedAddress);
} else {
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_WARNING,
("WARNING: AtalkConnVerifyAssocAddr - unassoc %lx\n", Connection->Flags));
DBGBRK(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_WARNING);
}
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
return status;
}
VOID
AtalkConnDereferenceAssocAddress(
PCONNECTION_FILE Connection
)
/*++
Routine Description:
Removes a reference on the associated address.
Arguments:
Connection - pointer to a PCONNECTION_FILE object.
Return Value:
None
--*/
{
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
if (Connection->Flags & CONNECTION_FLAGS_ASSOCIATED) {
//
// Call dereference with the connection spinlock held, to
// avoid race conditions where the association is broken
//
AtalkDereferenceAddress("AssocAddrDeref", Connection->AssociatedAddress,
AREF_VERIFY, SECONDARY_REFSET);
} else {
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_IMPOSSIBLE,
("IMPOSSIBLE: AtalkConnDerefAssocAddr - unassoc %lx\n", Connection->Flags));
DBGBRK(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_IMPOSSIBLE);
}
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
return;
}
//
// All routines which perform TDI functions on connection objects
//
NTSTATUS
AtalkConnAssociateAddress(
IN PCONNECTION_FILE Connection,
IN PADDRESS_FILE Address
)
/*++
Routine Description:
Associates the connection with the address specified. Both of these objects
must be verified by caller. Associating increments the primary reference
counts on both the address and the connection objects.
Arguments:
Connection - pointer to a PCONNECTION_FILE object
Address - pointer to a PADDRESS_FILE object
Return Value:
STATUS_SUCCESS - if successful
STATUS_CONNECTION_ESTABLISHED - if association already exists
STATUS_FILE_CLOSED - if address object is closing
STATUS_INVALID_CONNECTION - if connection object is stopping/closing
--*/
{
NTSTATUS status;
//
// Reference the Address for this association
//
AtalkReferenceAddress("AssociateConnection", Address,
AREF_ASSOCIATE, PRIMARY_REFSET);
//
// Acquire the connection lock and then the address lock
// NOTE: Avoid deadlock scenarios- always try to acquire co-lock then ao-lock
//
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
ACQUIRE_SPIN_LOCK(&Address->AddressLock);
do {
if (Address->Flags & ADDRESS_FLAGS_CLOSING) {
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_ERROR,
("ERROR: AtalkConnAssociateAddress - Address closing\n"));
status = STATUS_FILE_CLOSED;
break;
}
//
// check the connection state here
//
if (Connection->Flags & CONNECTION_FLAGS_ASSOCIATED) {
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_ERROR,
("ERROR: AtalkConnAssociateAddress - Connection already assoc\n"));
status = STATUS_CONNECTION_ESTABLISHED;
break;
} else if (Connection->Flags & CONNECTION_FLAGS_STOPPING) {
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_ERROR,
("ERROR: AtalkConnAssocAddr - conn stop %lx\n", Connection->Flags));
status = STATUS_INVALID_CONNECTION;
break;
}
InsertTailList (
&Address->ConnectionLinkage,
&Connection->Linkage);
Connection->AssociatedAddress = Address;
Connection->Flags |= CONNECTION_FLAGS_ASSOCIATED;
//
// Reference the connection for this association
//
AtalkReferenceConnection("AssocRefConn", Connection,
CREF_ASSOCIATE, PRIMARY_REFSET);
status = STATUS_SUCCESS;
} while ( FALSE );
RELEASE_SPIN_LOCK(&Address->AddressLock);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
if (!NT_SUCCESS(status)) {
//
// Dereference the Address as association did not succeed
//
AtalkDereferenceAddress("AssoFailed", Address,
AREF_ASSOCIATE, PRIMARY_REFSET);
}
return(status);
}
NTSTATUS
AtalkConnDisassociateAddress(
IN PCONNECTION_FILE Connection
)
/*++
Routine Description:
This routine destroys the association between a transport connection and
the address it is associated with.
This should be called when the OPEN and ASSOCIATED flags are the only
flags set.
Arguments:
Connection - Pointer to a transport connection structure
Return Value:
NTSTATUS - status of operation.
--*/
{
NTSTATUS status;
PADDRESS_FILE address;
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_INFOCLASS0,
("INFO0: AtalkDisassociateAddress - Entered for connection %lx\n", Connection));
//
// CO-lock first, then the AO-lock
//
ACQUIRE_SPIN_LOCK (&Connection->ConnectionLock);
if ((Connection->Flags & CONNECTION_FLAGS_ASSOCIATED) == 0) {
//
// Connection already disassociated
//
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_WARNING,
("WARNING: AtalkConnDisassociateAddress - Connection Disassociated!\n"));
status = STATUS_INVALID_CONNECTION;
} else {
address = Connection->AssociatedAddress;
ASSERT(address != NULL);
ACQUIRE_SPIN_LOCK (&address->AddressLock);
Connection->Flags &= ~(CONNECTION_FLAGS_ASSOCIATED);
//
// Delink this connection from its associated address connection
// database.
//
RemoveEntryList (&Connection->Linkage);
InitializeListHead (&Connection->Linkage);
//
// remove the association between the address and the connection.
//
Connection->AssociatedAddress = NULL;
RELEASE_SPIN_LOCK (&address->AddressLock);
status = STATUS_SUCCESS;
}
//
// Release co spinlock
//
RELEASE_SPIN_LOCK (&Connection->ConnectionLock);
if (status == STATUS_SUCCESS) {
//
// and remove a reference to the address and connection
//
AtalkDereferenceAddress ("Disassociate", address,
AREF_CONNECTION, PRIMARY_REFSET);
AtalkDereferenceConnection("DissocRefConn", Connection,
CREF_ASSOCIATE, PRIMARY_REFSET);
}
return status;
} /* AtalkConnDisassociateAddress */
NTSTATUS
AtalkConnPortableListenCancel(
IN PCONNECTION_FILE Connection,
IN PATALK_TDI_REQUEST Request
)
/*++
Routine Description:
This is called to cancel a posted listen with the portable code base.
Arguments:
Connection - pointer to a PCONNECTION_FILE object.
Request - the tdi request structure containing the posted-listen request
Return Value:
STATUS_SUCCESS - if the listen was cancelled successfully by the portable stack
error otherwise (listen possibly completed before listen cancel could happen)
--*/
{
NTSTATUS status;
PORTABLE_ERROR errorCode;
//
// Try to cancel the listen with the portable stack. If
// successful, return success, else return pending. irp will
// be completed when the cancel/disconnect completes.
//
switch (Connection->OwningDevice) {
case ATALK_DEVICE_ADSP:
//
// BUGBUG: Portable stack needs to supply cancel routine
//
status = STATUS_PENDING;
break;
case ATALK_DEVICE_ASP:
errorCode = AspCancelGetSession(
Connection->AssociatedAddress->ListenerRefNum,
Request->Listen.ListenRefNum);
status = ConvertToNTStatus(errorCode, SYNC_REQUEST);
break;
case ATALK_DEVICE_PAP:
errorCode = PapCancelGetNextJob(
Connection->AssociatedAddress->ListenerRefNum,
Request->Listen.ListenRefNum);
status = ConvertToNTStatus(errorCode, SYNC_REQUEST);
break;
default:
KeBugCheck(0);
}
return(status);
}
NTSTATUS
AtalkConnPortableDisconnect(
IN PCONNECTION_FILE Connection
)
/*++
Routine Description:
This routine is used to cancel an active connection with the portable
code base.
Arguments:
Connection - pointer to a PCONNECTION_FILE object.
Return Value:
STATUS_SUCCESS - successfully disconnected
STATUS_PENDING - successfully started disconnection process
(DisconnectComplete will be called when done...)
STATUS_REMOTE_DISCONNECT - remote already disconnected the connection
STATUS_NO_SUCH_DEVICE - internal error
--*/
{
NTSTATUS status;
PORTABLE_ERROR errorCode;
//
// Connection is active. Call the portable stack's disconnect
// routine. Note that currently disconnect is synchronous, but
// will need to become asynchronous later on when the atp reference
// count stuff comes in.
//
switch (Connection->OwningDevice) {
case ATALK_DEVICE_ADSP:
errorCode = AdspCloseConnection(
Connection->ConnectionRefNum,
TRUE); // SendCloseAdvice to remote
status = ConvertToNTStatus(errorCode, SYNC_REQUEST);
break;
case ATALK_DEVICE_ASP:
errorCode = AspCloseSession(
Connection->ConnectionRefNum,
FALSE);
status = ConvertToNTStatus(errorCode, SYNC_REQUEST);
break;
case ATALK_DEVICE_PAP:
errorCode = PapCloseJob(
Connection->ConnectionRefNum,
FALSE,
FALSE);
status = ConvertToNTStatus(errorCode, SYNC_REQUEST);
break;
default:
KeBugCheck(0);
}
return(status);
}
NTSTATUS
AtalkConnDisconnect(
IN PCONNECTION_FILE Connection,
IN NTSTATUS DisconnectStatus,
IN PIRP DisconnectIrp,
IN BOOLEAN Retry
)
/*++
Routine Description:
This routine is called when to disconnect a connection. It can be called
as a result of TdiClose(AO)/TdiClose(CO)/TdiDisconnect(CO)/RemoteDisconnect.
NOTES:
Our DISCONNECTING->ASSOCIATED only transition happens in the DeRef code.
We depend on the caller to perform a deref on the connection to touch
the deref code. In the case of remote disconnects, it could happen when
a posted request is complete. In that case, during the deref of the request
the connection will also be deref'd and we are cool. In the case of a new
request being posted, and it returns with no-such-connection error, again
the deref of the request should deref the connection.
NOTES2:
Only if we set the DISCONNECTING flag, or if it is already set are we
guaranteed that (at some point), DisconnectComplete will be called.
StopConnection will need to call StopComplete if STATUS_DEVICE_NOT_CONNECTED
returned.
Disconnect() is called from all over the place, but all are TDI request
related (posting or completion) and so we have a guaranteed dereference
of the TDI request implying a dereference of the connection object.
Arguments:
Connection - Pointer to a transport connection structure
DisconnectStatus - Why is disconnect happening (STATUS_FILE_CLOSED,
STATUS_REMOTE_DISCONNECT,
STATUS_LOCAL_DISCONNECT)
DisconnectIrp - if STATUS_LOCAL_DISCONNECT, then disconnect irp needs to
be specified
Retry - Is a deferred disconnect request being handled now?
Return Value:
STATUS_SUCCESS - if completed successfully (State will now be OPEN+ASSOCIATED)
STATUS_TOO_MANY_COMMANDS - a second TdiDisconnect posted when one is already active
STATUS_DEVICE_NOT_CONNECTED - if device is already disconnected
STATUS_PENDING - if started successfully
--*/
{
PORTABLE_ERROR errorCode;
NTSTATUS status = STATUS_PENDING;
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_INFOCLASS0,
("INFO0: AtalkDisconnect - Entered\n"));
//
// We are here if called with STATUS_FILE_CLOSED (TdiClose(AO/CO)) or
// STATUS_LOCAL_DISCONNECT (TdiDisconnect(CO)), or STATUS_REMOTE_DISCONNECT
// (if remote shutdown the connection)
//
do {
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
if (Connection->Flags & CONNECTION_FLAGS_DISCONNECTING) {
if ((DisconnectStatus == STATUS_LOCAL_DISCONNECT) && (!Retry)) {
//
// We are being called as a result of TdiDisconnect()
// Remember the irp
//
ASSERT((DisconnectIrp != NULL) &&
(Connection->DisconnectIrp == NULL));
if (Connection->DisconnectIrp != NULL) {
//
// A previous TdiDisconnect is still waiting to be done!
//
status = STATUS_TOO_MANY_COMMANDS;
break;
}
Connection->DisconnectIrp = DisconnectIrp;
}
//
// Return STATUS_PENDING
//
break;
}
//
// Are we in a state to accept a disconnect request?
//
if ((Connection->Flags & (CONNECTION_FLAGS_LISTENPOSTING |
CONNECTION_FLAGS_LISTENPOSTED |
CONNECTION_FLAGS_CONNECTPOSTING|
CONNECTION_FLAGS_CONNECTPOSTED |
CONNECTION_FLAGS_ACCEPTPOSTING |
CONNECTION_FLAGS_ACCEPTPOSTED |
CONNECTION_FLAGS_LISTENCOMPLETEINDICATE |
CONNECTION_FLAGS_DEFERREDDISC |
CONNECTION_FLAGS_ACTIVE)) == 0) {
status = STATUS_DEVICE_NOT_CONNECTED;
break;
}
//
// If this is a TdiDisconnect call, then remember the irp
//
if ((DisconnectStatus == STATUS_LOCAL_DISCONNECT) && (!Retry)) {
ASSERT((DisconnectIrp != NULL) && (Connection->DisconnectIrp == NULL));
if (Connection->DisconnectIrp == NULL) {
Connection->DisconnectIrp = DisconnectIrp;
Connection->DisconnectStatus = DisconnectStatus;
} else {
//
// BUGBUG:
// This would be a bug in the caller's code if it happens
//
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_ERROR,
("ERROR: AtalkConnDisconnect - Disconnect irp already present!"));
DBGBRK(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_ERROR);
status = STATUS_TOO_MANY_COMMANDS;
break;
}
}
if (Connection->Flags & CONNECTION_FLAGS_ACTIVE) {
//
// We are active, call the disconnect of the stack if not remote
// disconnect, as in that case we are being notified by the stack.
// Set disconnecting, remove deferreddisc flag
//
Connection->Flags |= CONNECTION_FLAGS_DISCONNECTING;
Connection->Flags &= ~CONNECTION_FLAGS_DEFERREDDISC;
Connection->DisconnectStatus = DisconnectStatus;
AtalkReferenceConnection("StackDisc", Connection,
CREF_TEMP, PRIMARY_REFSET);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
if (DisconnectStatus != STATUS_REMOTE_DISCONNECT) {
status = AtalkConnPortableDisconnect(
Connection);
}
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
break;
} else if ((Connection->Flags & (CONNECTION_FLAGS_CONNECTPOSTING |
CONNECTION_FLAGS_CONNECTPOSTED)) ||
(Connection->Flags & (CONNECTION_FLAGS_ACCEPTPOSTING |
CONNECTION_FLAGS_ACCEPTPOSTED))) {
//
// Complete disconnect during the completion routines
//
Connection->Flags |= CONNECTION_FLAGS_DEFERREDDISC;
Connection->DisconnectStatus = DisconnectStatus;
break;
} else if (Connection->Flags & CONNECTION_FLAGS_LISTENPOSTING) {
//
// Complete disconnect during the POSTING->POSTED transition
//
Connection->Flags |= CONNECTION_FLAGS_DEFERREDDISC;
Connection->DisconnectStatus = DisconnectStatus;
break;
} else if (Connection->Flags & CONNECTION_FLAGS_LISTENPOSTED) {
PLIST_ENTRY p;
PATALK_TDI_REQUEST request;
//
// Try to cancel the posted listen - if it succeeds, the listen
// completion routine will reset the LISTENPOSTED flag.
//
Connection->Flags |= CONNECTION_FLAGS_DEFERREDDISC;
Connection->DisconnectStatus = DisconnectStatus;
p = Connection->RequestLinkage.Flink;
if (p == &Connection->RequestLinkage) {
//
// This should never happen, as we will never take the request out
// of the request queue, until we change the state from LISTENPOSTED
// to either ACTIVE/ or just plain OPEN.
//
KeBugCheck(0);
break;
}
//
// Request is there, try to cancel it with the portable
// stack
//
request = CONTAINING_RECORD(p, ATALK_TDI_REQUEST, Linkage);
AtalkReferenceTdiRequest("TempDiscLCncl", request, RQREF_TEMP);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
status = AtalkConnPortableListenCancel(
Connection,
request);
//
// Dereference the tdi request
//
AtalkDereferenceTdiRequest("TempDiscLCnclComp", request, RQREF_TEMP);
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
if (status != STATUS_SUCCESS) {
//
// This means that the cancel listen failed. This will happen if
// the listen completes in the meantime. Since we have the deferred
// disconnect flag set, and since listen completion will have the
// connection go from LISTENPOSTED to either ACTIVE/or inactive,
// let the deferred disconnect processing during the deref for the
// listen request take care of the rest.
//
status = STATUS_PENDING;
break;
} else {
//
// We keep the deferred disconnect flag. This will mean
// that during the deref we will find that the deferred disc
// is already complete and will set the disconnecting flag
// and allow disconnect completion to be called.
//
//
// Reset the LISTEN_POSTED flag, listen completion is not entered
// when this cancel succeeds
//
Connection->Flags &= ~CONNECTION_FLAGS_LISTENPOSTED;
//
// Dequeue the request
//
RemoveEntryList(&request->Linkage);
InitializeListHead(&request->Linkage);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
//
// Complete the tdi request
//
AtalkCompleteTdiRequest(request, STATUS_LOCAL_DISCONNECT);
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
status = STATUS_PENDING;
break;
}
} else if (Connection->Flags & CONNECTION_FLAGS_LISTENCOMPLETEINDICATE) {
//
// LISTENINDICATECOMPLETE state- no request, but translates to a Deny
// connection
//
Connection->Flags |= CONNECTION_FLAGS_DISCONNECTING;
AtalkReferenceConnection("DenyDisc", Connection, CREF_TEMP, PRIMARY_REFSET);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
//
// NOTES:
// #1. We can use the associated address pointer as we are
// still associated and guaranteed to be until disconnection is done.
// #2. The listener could potentially be closed if the address
// is shutting down.
//
// We don't care about #2 as if that is the case, the connection
// request would already have been denied anyway by the portable
// stack.
//
// This state will only be entered for an ADSP/PAP device
//
ASSERT((Connection->OwningDevice == ATALK_DEVICE_ADSP) ||
(Connection->OwningDevice == ATALK_DEVICE_PAP));
status = STATUS_SUCCESS;
if (Connection->OwningDevice == ATALK_DEVICE_ADSP) {
errorCode = AdspDenyConnectionRequest(
Connection->AssociatedAddress->ListenerRefNum,
Connection->ConnectionRefNum);
} else {
errorCode = PapCloseJob(
Connection->ConnectionRefNum,
FALSE, // Closed by remote
FALSE); // Closed by timer
}
status = ConvertToNTStatus(errorCode, SYNC_REQUEST);
ASSERT(status == STATUS_SUCCESS);
if (status != STATUS_SUCCESS) {
//
// Either NOLISTENER, NOCONNECTION, NOMEMORY
// Assume deny completed in all three cases
//
status = STATUS_SUCCESS;
}
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
break;
} else {
ASSERT(((Connection->Flags & ~(CONNECTION_FLAGS_OPEN |
CONNECTION_FLAGS_ASSOCIATED |
CONNECTION_FLAGS_DEFERREDDISC |
CONNECTION_FLAGS_STOPPING |
CONNECTION_FLAGS_CLOSING)) == 0));
//
// Well, we are trying to satisfy a deferred disconnect request,
// but we are already disconnected. So set the disconnecting flag
// and then allow DisconnectComplete to be called. This could happen
// when a posted listen is cancelled due to a TdiDisconnect. In the
// deref, we call Disconnect again, and end up here.
//
// NOTE: Wherever you set the disconnecting flag, make the disconnecting
// reference.
//
Connection->Flags |= CONNECTION_FLAGS_DISCONNECTING;
Connection->Flags &= ~CONNECTION_FLAGS_DEFERREDDISC;
Connection->DisconnectStatus = DisconnectStatus;
AtalkReferenceConnection("StackDisc", Connection,
CREF_TEMP, PRIMARY_REFSET);
status = STATUS_SUCCESS;
break;
}
} while ( FALSE );
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
//
// BUGBUG:
// This will move to disconnect completion when that comes in for the
// portable code base- when the portable disconnect routines become
// asynchronous.
//
if (status == STATUS_SUCCESS) {
AtalkConnDisconnectComplete(Connection);
status = STATUS_PENDING;
}
return status;
} /* AtalkConnDisconnect */
VOID
AtalkConnDisconnectComplete(
IN PCONNECTION_FILE Connection
)
/*++
Routine Description:
This routine is called when the disconnect completes. It will remove
the DISCONNECTING state. It will also remove the reference added in
the disconnect routine. That will check for the STOPPING flag etc. This
routine will complete the disconnect irp if present.
Arguments:
Connection - pointer to a PCONNECTION_FILE object.
Return Value:
None
--*/
{
BOOLEAN stopping;
//
// If disconnecting is not set, just return. This can be called
// from two places, Disconnect() and Dereference when S(1->0).
//
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
if (Connection->Flags & CONNECTION_FLAGS_DISCONNECTING) {
//
// Reset connection flags to ASSOCIATED (remove ACTIVE/DISCONNECTING etc)
//
Connection->Flags &= ~(CONNECTION_FLAGS_ACTIVE |
CONNECTION_FLAGS_CONNECTPOSTED |
CONNECTION_FLAGS_LISTENPOSTED |
CONNECTION_FLAGS_LISTENCOMPLETEINDICATE |
CONNECTION_FLAGS_ACCEPTPOSTED);
Connection->Flags &= ~(CONNECTION_FLAGS_DISCONNECTING);
if (Connection->Flags & CONNECTION_FLAGS_SETCOOKIE) {
//
// Dereference for setting the cookie reference. We never
// close the address until we reach this point anyways, so
// it is ok, to let this reference hang around until here.
//
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
AtalkDereferenceConnection("ForCookieSet", Connection,
CREF_COOKIE, PRIMARY_REFSET);
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
Connection->Flags &= ~CONNECTION_FLAGS_SETCOOKIE;
}
//
// Complete the disconnect irp if present.
//
if (Connection->DisconnectIrp != NULL) {
PIRP disconnectIrp;
disconnectIrp = Connection->DisconnectIrp;
Connection->DisconnectIrp = NULL;
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
//
// If the disconnect is completing due to a local disconnect, then
// complete with STATUS_SUCCESS instead.
//
TdiCompleteRequest(
disconnectIrp,
((Connection->DisconnectStatus == STATUS_LOCAL_DISCONNECT) ?
STATUS_SUCCESS : Connection->DisconnectStatus));
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
}
stopping= ((Connection->Flags & CONNECTION_FLAGS_STOPPING) != 0);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
if (stopping) {
AtalkStopConnectionComplete(Connection);
}
//
// This reference was added in Disconnect routine when setting the DISCONNECTING
// flag. This reference could potentially destroy the connection.
//
AtalkDereferenceConnection("StackDiscComp", Connection,
CREF_TEMP, PRIMARY_REFSET);
} else {
ASSERT(0);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
}
return;
}
NTSTATUS
AtalkConnCreateListenerOnAssocAddr(
IN PCONNECTION_FILE Connection
)
/*++
Routine Description:
This routine will create a listener on the associated address object if
not already created.
Arguments:
Connection - pointer to a PCONNECTION_FILE object.
Return Value:
None
--*/
{
NTSTATUS status;
//
// Call CreateListener with the connection lock held. CreateListener
// acquires the address lock - note we are still following the sequence
// of obtaining the connection lock followed by the associated address's
// lock
//
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
status = AtalkCreateListener(Connection->AssociatedAddress);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
return(status);
}
NTSTATUS
AtalkConnPostListen(
IN PCONNECTION_FILE Connection,
IN PATALK_TDI_REQUEST Request
)
/*++
Routine Description:
Posts a listen request on the connection
Arguments:
Connection - pointer to a PCONNECTION_FILE object.
Request - the listen request block
Return Value:
STATUS_INVALID_ADDRESS - if associated address verify fails
CreateListener errors - if listener could not be created
DisconnectError - if connection is already disconnected
STATUS_INVALID_DEVICE_REQUEST - if device does not support CO requests
STATUS_PENDING - if request successfully posted
Portable code base errors
--*/
{
NTSTATUS status;
PADDRESS_FILE address;
PORTABLE_ERROR errorCode;
PTDI_REQUEST_KERNEL_LISTEN parameters;
if (!NT_SUCCESS(status = AtalkConnCreateListenerOnAssocAddr(Connection))) {
return(status);
}
address = Connection->AssociatedAddress;
//
// Assert that the listener is created
//
ASSERT(address->Flags & ADDRESS_FLAGS_LISTENER);
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
//
// If any flags other than OPEN/ASSOCIATED are set, we cannot
// post the listen. This includes the STOPPING/CLOSING flags also
//
if (Connection->Flags & ~(CONNECTION_FLAGS_OPEN |
CONNECTION_FLAGS_ASSOCIATED)) {
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_ERROR,
("ERROR: AtalkConnPostListen - flags: %lx DisconnectStatus %lx\n",
Connection->Flags, Connection->DisconnectStatus));
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
return(STATUS_INVALID_CONNECTION);
}
//
// Queue the request into the connection list
//
InsertTailList(&Connection->RequestLinkage, &Request->Linkage);
Connection->Flags |= CONNECTION_FLAGS_LISTENPOSTING;
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
//
// Have a temporary reference for the connection. We need this
// as we touch the connection object even if status returned is
// STATUS_PENDING
//
AtalkReferenceConnection("TempListenPost", Connection, CREF_TEMP, SECONDARY_REFSET);
//
// BUGBUG:
// parameters will be used later when remoteAddress specification etc.,
// is in
//
parameters = (PTDI_REQUEST_KERNEL_LISTEN)Request->Parameters;
switch (Connection->OwningDevice) {
case ATALK_DEVICE_ADSP:
Request->CompletionRoutine = AdspConnPostListenComplete;
errorCode = AdspGetConnectionRequest(
address->ListenerRefNum,
&Request->Listen.ListenRefNum,
Request->CompletionRoutine,
(ULONG)Request);
status = ConvertToNTStatus(errorCode, ASYNC_REQUEST);
break;
case ATALK_DEVICE_ASP:
Request->CompletionRoutine = AspConnPostListenComplete;
errorCode = AspGetSession(
address->ListenerRefNum,
FALSE, // privateSocket
&Request->Listen.ListenRefNum,
Request->CompletionRoutine,
(ULONG)Request);
status = ConvertToNTStatus(errorCode, ASYNC_REQUEST);
break;
case ATALK_DEVICE_PAP:
Request->CompletionRoutine = PapConnPostListenComplete;
errorCode = PapGetNextJob(
address->ListenerRefNum,
&Request->Listen.ListenRefNum,
Request->CompletionRoutine,
(ULONG)Request,
NULL, // Close job completion
(ULONG)0); // Context for above
status = ConvertToNTStatus(errorCode, ASYNC_REQUEST);
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
if (status == STATUS_PENDING) {
//
// NOTE Listen could already have been completed!
// There is still a creation reference on the request so it the
// listen irp cannot be completed.
//
if (Connection->Flags & CONNECTION_FLAGS_LISTENPOSTING) {
//
// Listen has not completed, otherwise this flag would have
// been reset
//
Connection->Flags &= ~CONNECTION_FLAGS_LISTENPOSTING;
Connection->Flags |= CONNECTION_FLAGS_LISTENPOSTED;
//
// Now since a listen does not complete within some time
// even if there are no incoming requests, we need to check
// for pending disconnect requests.
//
if (Connection->Flags & CONNECTION_FLAGS_DEFERREDDISC) {
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
status = AtalkConnDisconnect(
Connection,
Connection->DisconnectStatus,
Connection->DisconnectIrp, // Use the already set irp
TRUE); // retry?
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
}
}
//
// Its also possible that the listen completed before we could even
// change flags to LISTEN_POSTED. Just return pending in that case.
//
} else {
//
// An error occurred, dequeue the request and free it...
//
RemoveEntryList(&Request->Linkage);
InitializeListHead(&Request->Linkage);
}
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_INFOCLASS0,
("INFO0: AtalkPostListen - status %lx- errorCode %lx\n", status, errorCode));
AtalkDereferenceConnection("TempListenPostC", Connection, CREF_TEMP, SECONDARY_REFSET);
return(status);
}
NTSTATUS
AtalkConnPostAccept(
PCONNECTION_FILE Connection,
PATALK_TDI_REQUEST Request
)
/*++
Routine Description:
This routine is used to accept a connection for ADSP. This can also be called
from ListenCompletion for ADSP.
Arguments:
Request - the listen request which completed
Return Value:
None
--*/
{
NTSTATUS status = STATUS_INVALID_CONNECTION;
PORTABLE_ERROR errorCode;
PADDRESS_FILE address;
//
// First check if the connection is in the LISTEN_INDICATED state
//
address = (PADDRESS_FILE)Connection->AssociatedAddress;
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
switch (Connection->OwningDevice) {
case ATALK_DEVICE_ADSP:
if (Connection->Flags & CONNECTION_FLAGS_LISTENCOMPLETEINDICATE) {
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
errorCode = AdspAcceptConnectionRequest(
Connection->AssociatedAddress->ListenerRefNum,
Connection->ConnectionRefNum,
NULL, // BUGBUG: Multiplexing
DEFAULT_PORT, // Only for a new socket
NULL, // Any node
0, // dynamic socket if new socket
AdspConnAcceptConnectionComplete,
(ULONG)Request,
(address->RegisteredReceiveHandler ?\
&NTAdspReceiveEventHandler : NULL),
(ULONG)Connection,
(address->RegisteredExpeditedDataHandler ?\
&NTAdspReceiveAttnEventHandler : NULL),
(ULONG)Connection,
(address->RegisteredSendPossibleHandler ?\
&NTGenericSendPossibleEventHandler : NULL),
(ULONG)Connection,
(address->RegisteredDisconnectHandler ?\
&NTAdspDisconnectEventHandler : NULL),
(ULONG)Connection);
status = ConvertToNTStatus(errorCode, ASYNC_REQUEST);
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
if (status == STATUS_PENDING) {
//
// Request successfully queued
//
Connection->Flags &= ~(CONNECTION_FLAGS_LISTENCOMPLETEINDICATE);
Connection->Flags |= CONNECTION_FLAGS_ACCEPTPOSTED;
}
}
break;
case ATALK_DEVICE_PAP:
if (Connection->Flags & CONNECTION_FLAGS_LISTENCOMPLETEINDICATE) {
Connection->Flags &= ~CONNECTION_FLAGS_LISTENCOMPLETEINDICATE;
Connection->Flags |= CONNECTION_FLAGS_ACTIVE;
errorCode = PapAcceptJob(
Connection->ConnectionRefNum,
(address->RegisteredSendPossibleHandler ?\
&NTGenericSendPossibleEventHandler : NULL),
(ULONG)Connection,
(address->RegisteredDisconnectHandler ?\
&NTPapDisconnectEventHandler : NULL),
(ULONG)Connection);
status = ConvertToNTStatus(errorCode, SYNC_REQUEST);
} else {
ASSERT(0);
}
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
return(status);
}
NTSTATUS
AtalkConnPostConnect(
IN PCONNECTION_FILE Connection,
IN PATALK_TDI_REQUEST Request
)
/*++
Routine Description:
Posts a connect request on the connection
Arguments:
Connection - pointer to a PCONNECTION_FILE object.
Request - the connect request block
Return Value:
STATUS_INVALID_ADDRESS - if associated address verify fails/or listen type address
STATUS_INVALID_DEVICE_REQUEST - if device does not support CO requests
STATUS_PENDING - if request successfully posted
Portable code base errors
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PADDRESS_FILE address;
PORTABLE_ERROR errorCode;
PTDI_REQUEST_KERNEL_LISTEN parameters;
PTA_APPLETALK_ADDRESS remoteAddress;
PORTABLE_ADDRESS portableAddress;
address = Connection->AssociatedAddress;
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
ACQUIRE_SPIN_LOCK(&address->AddressLock);
do {
//
// if the address is not listener type then we are ok
//
if (address->Flags & ADDRESS_FLAGS_LISTENER) {
status = STATUS_INVALID_ADDRESS;
break;
}
//
// If any flags other than OPEN/ASSOCIATED are set, we cannot
// post the connect. This includes the STOPPING/CLOSING flags also
//
if (Connection->Flags & ~(CONNECTION_FLAGS_OPEN |
CONNECTION_FLAGS_ASSOCIATED)) {
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_ERROR,
("ERROR: AtalkConnPostConnect - flags: %lx\n", Connection->Flags));
status = STATUS_INVALID_CONNECTION;
break;
}
//
// Queue the request into the connection list
//
InsertTailList(&Connection->RequestLinkage, &Request->Linkage);
Connection->Flags |= CONNECTION_FLAGS_CONNECTPOSTING;
address->Flags |= ADDRESS_FLAGS_CONNECT;
} while (FALSE);
RELEASE_SPIN_LOCK(&address->AddressLock);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
if (NT_SUCCESS(status)) {
parameters = (PTDI_REQUEST_KERNEL_CONNECT)Request->Parameters;
remoteAddress = (PTA_APPLETALK_ADDRESS)parameters->RequestConnectionInformation->RemoteAddress;
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_INFOCLASS1,
("INFO1: AtalkConnPostConnect - Net %x Node %x Socket %x\n",
remoteAddress->Address[0].Address[0].Network,
remoteAddress->Address[0].Address[0].Node,
remoteAddress->Address[0].Address[0].Socket));
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_INFOCLASS1,
("INFO1: AtalkConnPostConnect - Cnt: %x\n", remoteAddress->TAAddressCount));
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_INFOCLASS1,
("INFO1: AtalkConnPostConnect - Type %x\n Length %d\n",
remoteAddress->Address[0].AddressType,
remoteAddress->Address[0].AddressLength));
portableAddress.networkNumber = remoteAddress->Address[0].Address[0].Network;
portableAddress.nodeNumber = remoteAddress->Address[0].Address[0].Node;
portableAddress.socketNumber = remoteAddress->Address[0].Address[0].Socket;
//
// Have a temporary reference for the connection. We need this
// as we touch the connection object even if status returned is
// STATUS_PENDING
//
AtalkReferenceConnection("TempConnectPost", Connection, CREF_TEMP, SECONDARY_REFSET);
switch (Connection->OwningDevice) {
case ATALK_DEVICE_ADSP:
Request->CompletionRoutine = AdspConnPostConnectComplete;
errorCode = AdspOpenConnectionOnNode(
AdspActiveOpen,
&address->SocketRefNum, // Socket to use
DEFAULT_PORT,
NULL, // Desired node
0, // Desired socket
portableAddress,
&Connection->ConnectionRefNum,
Request->CompletionRoutine,
(ULONG)Request,
(address->RegisteredReceiveHandler ?\
&NTAdspReceiveEventHandler : NULL),
(ULONG)Connection,
(address->RegisteredExpeditedDataHandler ?\
&NTAdspReceiveAttnEventHandler : NULL),
(ULONG)Connection,
(address->RegisteredSendPossibleHandler ?\
&NTGenericSendPossibleEventHandler : NULL),
(ULONG)Connection,
(address->RegisteredDisconnectHandler ?\
&NTAdspDisconnectEventHandler : NULL),
(ULONG)Connection);
status = ConvertToNTStatus(errorCode, ASYNC_REQUEST);
break;
case ATALK_DEVICE_ASP:
Request->CompletionRoutine = AspConnPostConnectComplete;
errorCode = AspOpenSessionOnNode(
DEFAULT_PORT,
address->SocketRefNum, // use this socket
0, // desired socket
portableAddress,
&Connection->SocketRefNum,
Request->CompletionRoutine,
(ULONG)Request);
status = ConvertToNTStatus(errorCode, ASYNC_REQUEST);
break;
case ATALK_DEVICE_PAP:
Request->CompletionRoutine = PapConnPostConnectComplete;
errorCode = PapOpenJobOnNode(
DEFAULT_PORT,
address->SocketRefNum, // use this socket
0, // desired socket
&Connection->ConnectionRefNum,
&portableAddress,
NULL, // nbp lookup object
NULL, // type
NULL, // zone
8, // workstation quantum
NULL, // opaque status buffer
(address->RegisteredSendPossibleHandler ?\
&NTGenericSendPossibleEventHandler : NULL),
(ULONG)Connection,
(address->RegisteredDisconnectHandler ?\
&NTPapDisconnectEventHandler : NULL),
(ULONG)Connection,
Request->CompletionRoutine,
(ULONG)Request);
status = ConvertToNTStatus(errorCode, ASYNC_REQUEST);
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
if (status == STATUS_PENDING) {
//
// NOTE Connect could already have been completed!
//
if (Connection->Flags & CONNECTION_FLAGS_CONNECTPOSTING) {
//
// Connect has not completed, otherwise this flag would have
// been reset
//
Connection->Flags &= ~CONNECTION_FLAGS_CONNECTPOSTING;
Connection->Flags |= CONNECTION_FLAGS_CONNECTPOSTED;
}
//
// Its also possible that the connect completed before we could even
// change flags to CONNECT_POSTED. Just return pending in that case.
//
} else {
//
// An error occurred, dequeue the request and free it...
//
RemoveEntryList(&Request->Linkage);
InitializeListHead(&Request->Linkage);
}
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_INFOCLASS0,
("INFO0: AtalkPostConnect - status %lx- error %lx\n", status, errorCode));
AtalkDereferenceConnection("TempConnectPostC", Connection, CREF_TEMP, SECONDARY_REFSET);
}
return(status);
}
NTSTATUS
AtalkConnPostDisconnect(
IN PCONNECTION_FILE Connection,
IN PATALK_TDI_REQUEST Request
)
/*++
Routine Description:
Posts a connect request on the connection
Arguments:
Connection - pointer to a PCONNECTION_FILE object.
Request - the connect request block
Return Value:
STATUS_INVALID_ADDRESS - if associated address verify fails/or listen type address
STATUS_INVALID_DEVICE_REQUEST - if device does not support CO requests
STATUS_PENDING - if request successfully posted
Portable code base errors
--*/
{
NTSTATUS status = STATUS_PENDING;
do {
if (Request->Disconnect.DisconnectFlags != TDI_DISCONNECT_WAIT) {
//
// Any other flags implicitly translate to an ABORT. We do not support
// graceful shutdown
//
status = AtalkConnDisconnect(
Connection,
STATUS_LOCAL_DISCONNECT,
Request->IoRequestIrp,
FALSE);
break;
}
//
// This request is being posted merely to be completed when either
// a local or a remote disconnect happens. Set the irp in the
// DisconnectWaitIrp field and return
//
// The connection *must* be in an active state or we return an error
//
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
if (Connection->Flags & CONNECTION_FLAGS_ACTIVE) {
Connection->DisconnectWaitIrp = Request->IoRequestIrp;
} else {
status = STATUS_INVALID_CONNECTION;
}
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
break;
} while (FALSE);
return(status);
}
NTSTATUS
AtalkConnSend(
IN PCONNECTION_FILE Connection,
IN PATALK_TDI_REQUEST Request
)
/*++
Routine Description:
This routine is used to send data on a connection
Arguments:
Connection - pointer to a PCONNECTION_FILE object
Request - the request block containing the send request
Return Value:
STATUS_SUCCESS - if successfully sent (ADSP only)
STATUS_PENDING - if successfully queued
Error otherwise
--*/
{
NTSTATUS status;
PORTABLE_ERROR errorCode;
BOOLEAN endOfMessage;
//
// The send request could have been received through a TdiSend
// call or a NtWrite primitive (in which case SystemWrite will be
// true). For a NtWrite primitive, the sendFlags are not sent in.
//
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
if (((Connection->Flags & CONNECTION_FLAGS_ACTIVE) == 0) ||
(Connection->Flags & (CONNECTION_FLAGS_DEFERREDDISC |
CONNECTION_FLAGS_DISCONNECTING |
CONNECTION_FLAGS_STOPPING))) {
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_ERROR,
("ERROR: AtalkConnSend - flags: %lx DisconnectStatus %lx\n",
Connection->Flags, Connection->DisconnectStatus));
return((Connection->Flags & CONNECTION_FLAGS_ACTIVE) ? \
Connection->DisconnectStatus : STATUS_INVALID_CONNECTION);
}
//
// Queue the request into the connection list
//
InsertTailList(&Connection->RequestLinkage, &Request->Linkage);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
switch (Connection->OwningDevice) {
case ATALK_DEVICE_ADSP:
//
// Sends are synchronous for ADSP, portable stack makes
// a copy
//
// BUGBUG: We do not care about whether the send is a blocking send
// or a non-blocking send. Winsock will be changed by DavidTr
// (AFD actually) to retry partial sends on a blocking socket.
// So we always support a non-blocking send.
//
if ((Request->Send.SendFlags & TDI_SEND_EXPEDITED) == 0) {
//
// Check for stream socket - no eom for stream sockets
//
endOfMessage =
((Connection->AssociatedAddress->SocketType == SOCKET_TYPE_STREAM) ?\
FALSE : \
(BOOLEAN)((Request->Send.SendFlags & TDI_SEND_PARTIAL) == 0));
//
// BUGBUG: Portable code should return the number of bytes actually sent.
// If zero, then we should be guaranteed that the SendPossible event
// handler (if set), will be called when the send window allows more
// sends. Set this value in the information field.
//
// BUGBUG: Flush flag is set to TRUE. This really should be false, and a
// timer-based scheme should be used to optimise small-sized
// sends. If this is set to false, ADSP level deferrel queue will
// fill up when the sends are internal to a node (loopback).
//
errorCode = AdspSend(
Connection->ConnectionRefNum,
(PVOID)Request->Send.MdlAddress,
Request->Send.SendBufferLength,
endOfMessage,
TRUE, // Flush flag
&Request->IoStatus->Information);
status = ConvertToNTStatus(errorCode, SYNC_REQUEST);
break;
}
//
// Expedited data
//
{
PMDL newCurrentMdl;
ULONG newByteOffset ;
ULONG trueLength;
PUSHORT mdlBuffer;
LONG size = Request->Send.SendBufferLength - ATTENTIONCODE_SIZE;
//
// First two bytes of buffer make up the attention code and rest of the buffer
// will be the attention data
//
if (size < 0) {
status = STATUS_INVALID_PARAMETER;
break;
}
//
// Get the system address for the MDL and figure out the Attention Code
//
mdlBuffer = (PUSHORT)MmGetSystemAddressForMdl(Request->Send.MdlAddress);
//
// Build an mdl for the rest of the buffer, could be zero length
// Mdl will be freed automatically during request completion
//
status = BuildMdlChainFromMdlChain (
Request->Send.MdlAddress, // MasterMdl
ATTENTIONCODE_SIZE, // ByteOffset,
(ULONG)size, // Size of mdl
&Request->MdlChain[0], // subsetMdl,
&newCurrentMdl,
&newByteOffset,
&trueLength);
ASSERT(trueLength == (ULONG)size);
if (!NT_SUCCESS(status)) {
break;
}
errorCode = AdspSendAttention(
Connection->ConnectionRefNum,
(USHORT)(*mdlBuffer),
(PVOID)Request->MdlChain[0],
size,
NULL, // No completion routine
(ULONG)0); // No context either
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_INFOCLASS1,
("INFO1: AtalkConnSend - SendAttention error %lx attncode %lx\n",
errorCode, *mdlBuffer));
status = ConvertToNTStatus(errorCode, SYNC_REQUEST);
if (NT_SUCCESS(status)) {
Request->IoStatus->Information = size+ATTENTIONCODE_SIZE;
}
}
break;
case ATALK_DEVICE_PAP:
//
// Non-blocking sends for PAP:
// Pap uses a binary event - send data credit thats sent to the remote
// end. ATP remembers the actual size of the remote entitys response
// buffer. In any case, if we do not have send credit the call would
// block, and we dont want that to happen. Also, there should be no
// pending writes on the connection to begin with.
//
if (Request->Send.SendFlags & TDI_SEND_EXPEDITED) {
status = STATUS_INVALID_PARAMETER;
break;
}
if (Request->Send.SendFlags & TDI_SEND_NON_BLOCKING) {
if (!PapSendCreditAvailable(Connection->ConnectionRefNum)) {
status = STATUS_DEVICE_NOT_READY; // This is what AFD needs
break;
}
}
endOfMessage =
(BOOLEAN)((Request->Send.SendFlags & TDI_SEND_PARTIAL) == 0);
errorCode = PapWrite(
Connection->ConnectionRefNum,
(PVOID)Request->Send.MdlAddress,
Request->Send.SendBufferLength,
endOfMessage, // Actually, PAP's endoffile
PapConnSendComplete,
(ULONG)Request);
status = ConvertToNTStatus(errorCode, ASYNC_REQUEST);
break;
case ATALK_DEVICE_ASP:
case ATALK_DEVICE_ATP:
case ATALK_DEVICE_DDP:
status = STATUS_NOT_SUPPORTED;
break;
default:
KeBugCheck(0);
}
if (status != STATUS_PENDING) {
//
// An error occurred, dequeue the request and free it...
//
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
RemoveEntryList(&Request->Linkage);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
InitializeListHead(&Request->Linkage);
//
// Disconnect the connection if remote disconnect
//
if (status == STATUS_REMOTE_DISCONNECT) {
DBGPRINT(ATALK_DEBUG_ALL, DEBUG_LEVEL_ERROR,
("ERROR: AtalkConnSend - RemoteDisconnect! Could not post receive\n"));
AtalkConnDisconnect(Connection, STATUS_REMOTE_DISCONNECT, NULL, FALSE);
}
}
return(status);
}
NTSTATUS
AtalkConnReceive(
IN PCONNECTION_FILE Connection,
IN PATALK_TDI_REQUEST Request
)
/*++
Routine Description:
This routine is called to post a receive request
Arguments:
Connection - pointer to a PCONNECTION_FILE object
Request - receive request
Return Value:
None
--*/
{
NTSTATUS status;
PORTABLE_ERROR errorCode;
//
// The receive request could have been received through a TdiReceive
// call or a NtRead primitive (in which case SystemRead will be
// true).
//
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
if (((Connection->Flags & CONNECTION_FLAGS_ACTIVE) == 0) ||
(Connection->Flags & (CONNECTION_FLAGS_DEFERREDDISC |
CONNECTION_FLAGS_DISCONNECTING |
CONNECTION_FLAGS_STOPPING))) {
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_ERROR,
("ERROR: AtalkConnReceive - flags: %lx DisconnectStatus %lx\n",
Connection->Flags, Connection->DisconnectStatus));
return((Connection->Flags & CONNECTION_FLAGS_ACTIVE) ? \
Connection->DisconnectStatus : STATUS_INVALID_CONNECTION);
}
//
// Queue the request into the connection list
//
InsertTailList(&Connection->RequestLinkage, &Request->Linkage);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
switch (Connection->OwningDevice) {
case ATALK_DEVICE_ADSP:
if (*Request->Receive.ReceiveFlags & TDI_RECEIVE_PEEK) {
BOOLEAN endOfMessage;
LONG bufferSize = Request->Receive.ReceiveBufferLength;
//
// BUGBUG: This should call a routine that will not
// actually consume the data...
//
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_ERROR,
("ERROR: AtalkConnReceive - need to implement peek!\n"));
errorCode = AdspPeek(
Connection->ConnectionRefNum,
(PVOID)Request->Receive.MdlAddress,
&bufferSize,
&endOfMessage);
status = ConvertToNTStatus(errorCode, SYNC_REQUEST);
Request->IoStatus->Information = bufferSize;
break;
}
errorCode = AdspGetAnything(
Connection->ConnectionRefNum,
(PVOID)Request->Receive.MdlAddress,
Request->Receive.ReceiveBufferLength,
AdspConnReceiveComplete,
(ULONG)Request);
status = ConvertToNTStatus(errorCode, ASYNC_REQUEST);
break;
case ATALK_DEVICE_PAP:
if (*Request->Receive.ReceiveFlags & TDI_RECEIVE_PEEK) {
status = STATUS_NOT_SUPPORTED;
break;
}
errorCode = PapRead(
Connection->ConnectionRefNum,
(PVOID)Request->Receive.MdlAddress,
Request->Receive.ReceiveBufferLength,
PapConnReceiveComplete,
(ULONG)Request);
status = ConvertToNTStatus(errorCode, ASYNC_REQUEST);
break;
case ATALK_DEVICE_ASP:
case ATALK_DEVICE_ATP:
case ATALK_DEVICE_DDP:
status = STATUS_NOT_SUPPORTED;
break;
default:
KeBugCheck(0);
}
if (status != STATUS_PENDING) {
//
// An error occurred, dequeue the request and free it...
//
ACQUIRE_SPIN_LOCK(&Connection->ConnectionLock);
RemoveEntryList(&Request->Linkage);
RELEASE_SPIN_LOCK(&Connection->ConnectionLock);
InitializeListHead(&Request->Linkage);
//
// Disconnect the connection if remote disconnect
//
if (status == STATUS_REMOTE_DISCONNECT) {
DBGPRINT(ATALK_DEBUG_ALL, DEBUG_LEVEL_ERROR,
("ERROR: AtalkConnReceive - RemoteDisconnect! Could not post recv\n"));
AtalkConnDisconnect(Connection, STATUS_REMOTE_DISCONNECT, NULL, FALSE);
}
}
return(status);
}
NTSTATUS
AtalkConnQueryStatistics(
IN PCONNECTION_FILE Connection,
OUT PTDI_CONNECTION_INFO ConnectionInfo
)
{
NTSTATUS status = STATUS_SUCCESS;
ACQUIRE_SPIN_LOCK (&Connection->ConnectionLock);
if ((Connection->Flags & (CONNECTION_FLAGS_CLOSING |
CONNECTION_FLAGS_STOPPING)) != 0) {
//
// Connection closing/stopping
//
DBGPRINT(ATALK_DEBUG_CONNOBJ, DEBUG_LEVEL_WARNING,
("WARNING: AtalkConnQueryStatistics - Connection Stopping/closing %lx!\n",
Connection->Flags));
status = STATUS_INVALID_CONNECTION;
}
//
// Release co spinlock
//
RELEASE_SPIN_LOCK (&Connection->ConnectionLock);
if (NT_SUCCESS(status)) {
//
// Get the statistics and put them in ConnectionInfo
//
RtlZeroMemory ((PVOID)ConnectionInfo, sizeof(TDI_CONNECTION_INFO));
}
return(status);
}
//
// COMPLETION ROUTINES for the different providers
//
// LISTEN Completion Routines
//
// BUGBUG:
// If possible, club as much code as possible into one generic completion routine
//
VOID
AspConnPostListenComplete(
PORTABLE_ERROR Error,
ULONG UserData,
LONG SocketRefNum,
LONG SessionRefNum
)
/*++
Routine Description:
This is the ASP listen completion routine.
Arguments:
Error - the portable code base errors declared in atdcls.h
UserData - the cookie we passed to the portable code (PCONNECTION_FILE object)
SocketRefNum - socket created for the new connection
SessionRefNum - session reference number for the new connection
Return Value:
None
--*/
{
NTSTATUS status;
PORTABLE_ERROR errorCode;
PATALK_TDI_REQUEST request;
PCONNECTION_FILE connection;
PTDI_REQUEST_KERNEL_LISTEN parameters;
PTDI_CONNECTION_INFORMATION returnConnInfo;
POPTIONS_CONNINF options;
#if !TDI_SPEC_ISSUE_RESOLVED
UNREFERENCED_PARAMETER(parameters);
UNREFERENCED_PARAMETER(returnConnInfo);
UNREFERENCED_PARAMETER(options);
#endif
request = (PATALK_TDI_REQUEST)UserData;
//
// Connection object should still be verified, i.e. a reference exists
// for this request
//
do {
status = ConvertToNTStatus(Error, SYNC_REQUEST);
DBGPRINT(ATALK_DEBUG_ASP, DEBUG_LEVEL_INFOCLASS1,
("INFO1: AspConnListenComplete - complete %lx nt %lx request! %lx\n",
Error, status, request));
connection = (PCONNECTION_FILE)request->FileObject->FsContext;
connection->SocketRefNum = SocketRefNum;
connection->ConnectionRefNum = SessionRefNum;
if (NT_SUCCESS(status)) {
//
// Copy information into the return information structure
// OK to have the spinlock released at this point.
//
#if TDI_SPEC_ISSUE_RESOLVED
//
// Set some return values in the listen parameters structure
// if STATUS was success
//
// BUGBUG: TDI SPEC IS BROKEN ON THIS!
// BUG #8123
//
parameters = (PTDI_REQUEST_KERNEL_LISTEN)request->Parameters;
ASSERT(parameters != NULL);
returnConnInfo = (PTDI_CONNECTION_INFORMATION)parameters->ReturnConnectionInformation;
ASSERT(returnConnInfo != NULL);
options = (POPTIONS_CONNINF)returnConnInfo->Options;
ASSERT(options != NULL);
#endif
//
// Try to set the cookie for the session, we need this for GetAnyRequests
// If it fails, it implies that the remote end disconnected the session.
//
// BUGBUG: This should not fail if the remote end has disconnected
// the session. The whole take-off-the-list design flaw.
// This should only fail after the last request has been
// completed, *and* a GAR has been completed for the conn.
//
errorCode = AspSetCookieForSession(SessionRefNum, (ULONG)connection);
status = ConvertToNTStatus(errorCode, SYNC_REQUEST);
if (!NT_SUCCESS(status)) {
DBGPRINT(ATALK_DEBUG_ASP, DEBUG_LEVEL_SEVERE,
("ERROR: AspPostListenComplete - setcook conn %lx\n", connection));
DBGBRK(ATALK_DEBUG_ASP, DEBUG_LEVEL_SEVERE);
break;
} else {
//
// Reference the connection one more time for this. This reference
// is removed only in the AtalkDisconnectComplete routine.
//
AtalkReferenceConnection("SettingAsCookie", connection,
CREF_COOKIE, PRIMARY_REFSET);
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
connection->Flags |= CONNECTION_FLAGS_SETCOOKIE;
RELEASE_SPIN_LOCK(&connection->ConnectionLock);
DBGPRINT(ATALK_DEBUG_ASP, DEBUG_LEVEL_INFOCLASS1,
("INFO1: AspPostListenComplete - CookieSet: %lx\n", connection));
status = STATUS_SUCCESS;
}
//
// Now change the state to ACTIVE
//
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
connection->Flags &= ~(CONNECTION_FLAGS_LISTENPOSTED);
connection->Flags |= CONNECTION_FLAGS_ACTIVE;
status = STATUS_SUCCESS;
break;
} else {
//
// Connection was not established.
//
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
connection->Flags &= ~CONNECTION_FLAGS_LISTENPOSTED;
break;
}
} while (FALSE);
//
// Spinlock should be acquired at this point- if we set the state to
// active, we really do not want any more requests on this connection
// until we remove/complete the listen request.
//
// Dequeue the request from the connection object
//
RemoveEntryList(&request->Linkage);
InitializeListHead(&request->Linkage);
RELEASE_SPIN_LOCK(&connection->ConnectionLock);
//
// Complete the request
//
AtalkCompleteTdiRequest(request, status);
return;
}
VOID
AdspConnPostListenComplete(
PORTABLE_ERROR Error,
ULONG UserData,
PORTABLE_ADDRESS SourceAddress,
LONG ListenerRefNum,
LONG ConnectionRefNum
)
/*++
Routine Description:
This is the ADSP listen completion routine called by the portable
stack
Arguments:
Error - the portable code base errors declared in atdcls.h
UserData - the cookie we passed to the portable code (PCONNECTION_FILE object)
SourceAddress - address of remote client
ListenerRefNum - the listener reference number on which the connection completed
ConnectionRefNum - the value to use for Accept/Deny connection
Return Value:
None
--*/
{
NTSTATUS status;
PATALK_TDI_REQUEST request;
PCONNECTION_FILE connection;
PTDI_REQUEST_KERNEL_LISTEN parameters;
PTDI_CONNECTION_INFORMATION returnConnInfo;
POPTIONS_CONNINF options;
#if !TDI_SPEC_ISSUE_RESOLVED
UNREFERENCED_PARAMETER(parameters);
UNREFERENCED_PARAMETER(returnConnInfo);
UNREFERENCED_PARAMETER(options);
#endif
request = (PATALK_TDI_REQUEST)UserData;
//
// Connection object should still be verified, i.e. a reference exists
// for this request
//
do {
status = ConvertToNTStatus(Error, SYNC_REQUEST);
DBGPRINT(ATALK_DEBUG_ADSP, DEBUG_LEVEL_INFOCLASS1,
("INFO1: AdspPostListenComplete - complete %lx nt %lx request %lx\n",
Error, status, request));
connection = (PCONNECTION_FILE)request->FileObject->FsContext;
//
// Connection->SocketRefNum obtained after Accept
//
connection->ConnectionRefNum = ConnectionRefNum;
if (NT_SUCCESS(status)) {
//
// If we are to automatically accept the connection, do it now
// before changing the state to ACTIVE
//
if ((request->Listen.ListenFlags & TDI_QUERY_ACCEPT) == 0) {
//
// Call the accept request
//
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
connection->Flags &= ~(CONNECTION_FLAGS_LISTENPOSTED);
connection->Flags |= CONNECTION_FLAGS_LISTENCOMPLETEINDICATE;
RELEASE_SPIN_LOCK(&connection->ConnectionLock);
status = AtalkConnPostAccept(
connection,
request);
break;
}
//
// Copy information into the return information structure
// OK to have the spinlock released at this point.
//
#if TDI_SPEC_ISSUE_RESOLVED
//
// Set some return values in the listen parameters structure
// if STATUS was success
//
// BUGBUG: TDI SPEC IS BROKEN ON THIS!
// BUG #8123
//
parameters = (PTDI_REQUEST_KERNEL_LISTEN)request->Parameters;
ASSERT(parameters != NULL);
returnConnInfo = (PTDI_CONNECTION_INFORMATION)parameters->ReturnConnectionInformation;
ASSERT(returnConnInfo != NULL);
options = (POPTIONS_CONNINF)returnConnInfo->Options;
ASSERT(options != NULL);
#endif
//
// Now change the state to LISTENCOMPLETEINDICATE
//
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
connection->Flags &= ~(CONNECTION_FLAGS_LISTENPOSTED);
connection->Flags |= CONNECTION_FLAGS_LISTENCOMPLETEINDICATE;
status = STATUS_SUCCESS;
break;
} else {
//
// Connection was not established.
//
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
connection->Flags &= ~CONNECTION_FLAGS_LISTENPOSTED;
}
} while (FALSE);
//
// Spinlock should be acquired at this point
// Dequeue the request from the connection object if we are not waiting
// for an autoaccept to complete
//
if (status != STATUS_PENDING) {
RemoveEntryList(&request->Linkage);
InitializeListHead(&request->Linkage);
}
RELEASE_SPIN_LOCK(&connection->ConnectionLock);
if (status != STATUS_PENDING) {
//
// Complete the request if not pending
//
AtalkCompleteTdiRequest(request, status);
}
return;
}
VOID
AdspConnAcceptConnectionComplete(
PORTABLE_ERROR Error,
ULONG UserData,
LONG ConnectionRefNum,
LONG SocketRefNum,
PORTABLE_ADDRESS RemoteAddress
)
/*++
Routine Description:
This is the ADSP accept completion routine called by the portable
stack
Arguments:
Error - the portable code base errors declared in atdcls.h
UserData - the cookie we passed to the portable code (PCONNECTION_FILE object)
ConnectionRefNum - the value of the active connection
SocketRefNum - the socket value used for the connection
RemoteAddress - address of remote client
Return Value:
None
--*/
{
NTSTATUS status;
PATALK_TDI_REQUEST request;
PCONNECTION_FILE connection;
PTDI_REQUEST_KERNEL_LISTEN parameters;
PTDI_CONNECTION_INFORMATION returnConnInfo;
POPTIONS_CONNINF options;
#if !TDI_SPEC_ISSUE_RESOLVED
UNREFERENCED_PARAMETER(parameters);
UNREFERENCED_PARAMETER(returnConnInfo);
UNREFERENCED_PARAMETER(options);
#endif
request = (PATALK_TDI_REQUEST)UserData;
//
// Connection object should still be verified, i.e. a reference exists
// for this request
//
do {
status = ConvertToNTStatus(Error, SYNC_REQUEST);
DBGPRINT(ATALK_DEBUG_ADSP, DEBUG_LEVEL_INFOCLASS1,
("INFO1: AdspAcceptConnectionComplete - complete %lx nt %lx request %lx\n",
Error, status, request));
connection = (PCONNECTION_FILE)request->FileObject->FsContext;
connection->SocketRefNum = SocketRefNum;
connection->ConnectionRefNum = ConnectionRefNum;
if (NT_SUCCESS(status)) {
//
// Copy information into the return information structure
// OK to have the spinlock released at this point.
//
#if TDI_SPEC_ISSUE_RESOLVED
//
// Set some return values in the listen parameters structure
// if STATUS was success
//
// BUGBUG: TDI SPEC IS BROKEN ON THIS!
// BUG #8123
//
parameters = (PTDI_REQUEST_KERNEL_LISTEN)request->Parameters;
ASSERT(parameters != NULL);
returnConnInfo = (PTDI_CONNECTION_INFORMATION)parameters->ReturnConnectionInformation;
ASSERT(returnConnInfo != NULL);
options = (POPTIONS_CONNINF)returnConnInfo->Options;
ASSERT(options != NULL);
#endif
//
// Now change the state to ACTIVE
//
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
connection->Flags &= ~(CONNECTION_FLAGS_LISTENPOSTED | CONNECTION_FLAGS_ACCEPTPOSTED);
connection->Flags |= CONNECTION_FLAGS_ACTIVE;
status = STATUS_SUCCESS;
break;
} else {
//
// Connection was not established.
//
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
connection->Flags &= ~(CONNECTION_FLAGS_LISTENPOSTED | CONNECTION_FLAGS_ACCEPTPOSTED);
break;
}
} while (FALSE);
//
// Spinlock should be acquired at this point- if the state is set to
// active, we should not have any requests posted, until we dequeu
// the listen request.
//
RemoveEntryList(&request->Linkage);
InitializeListHead(&request->Linkage);
RELEASE_SPIN_LOCK(&connection->ConnectionLock);
//
// Complete the request
//
AtalkCompleteTdiRequest(request, status);
return;
}
VOID
PapConnPostListenComplete(
PORTABLE_ERROR Error,
ULONG UserData,
LONG JobRefNum,
SHORT WorkstationQuantum,
SHORT WaitTime
)
/*++
Routine Description:
This is listen complete routine for PAP
Arguments:
Error - the portable code base errors declared in atdcls.h
UserData - the cookie we passed to the portable code (the request block)
JobRefNum - the connection reference number
WorkstationQuantum - the workstation quantum of the remote client
WaitTime - the wait time that the connection took to be accepted
Return Value:
None
--*/
{
NTSTATUS status;
PATALK_TDI_REQUEST request;
PCONNECTION_FILE connection;
PTDI_REQUEST_KERNEL_LISTEN parameters;
PTDI_CONNECTION_INFORMATION returnConnInfo;
POPTIONS_CONNINF options;
#if !TDI_SPEC_ISSUE_RESOLVED
UNREFERENCED_PARAMETER(parameters);
UNREFERENCED_PARAMETER(returnConnInfo);
UNREFERENCED_PARAMETER(options);
#endif
request = (PATALK_TDI_REQUEST)UserData;
//
// Connection object should still be verified, i.e. a reference exists
// for this request
//
do {
status = ConvertToNTStatus(Error, SYNC_REQUEST);
DBGPRINT(ATALK_DEBUG_PAP, DEBUG_LEVEL_INFOCLASS1,
("INFO1: PapPostListenComplete - complete %lx nt %lx for request! %lx\n",
Error, status, request));
connection = (PCONNECTION_FILE)request->FileObject->FsContext;
if (NT_SUCCESS(status)) {
//
// BUGBUG:
// Portable stack should be consistent about returning the socket number
// Connection->SocketRefNum not available for PAP
//
connection->ConnectionRefNum = JobRefNum;
//
// Copy information into the return information structure
// OK to have the spinlock released at this point.
//
#if TDI_SPEC_ISSUE_RESOLVED
//
// Set some return values in the listen parameters structure
// if STATUS was success
//
// BUGBUG: TDI SPEC IS BROKEN ON THIS!
// BUG #8123
//
parameters = (PTDI_REQUEST_KERNEL_LISTEN)request->Parameters;
ASSERT(parameters != NULL);
returnConnInfo = (PTDI_CONNECTION_INFORMATION)parameters->ReturnConnectionInformation;
ASSERT(returnConnInfo != NULL);
options = (POPTIONS_CONNINF)returnConnInfo->Options;
ASSERT(options != NULL);
options->PapInfo.RemoteAddress =
options->PapInfo.WorkstationQuantum = WorkstationQuantum;
DBGPRINT(ATALK_DEBUG_PAP, DEBUG_LEVEL_INFOCLASS1,
("INFO1: PapPostListenComplete - WorkstationQuantum: %lx\n",
WorkstationQuantum));
#endif
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
//
// Now change the state to ACTIVE
//
connection->Flags &= ~(CONNECTION_FLAGS_LISTENPOSTED);
connection->Flags |= CONNECTION_FLAGS_ACTIVE;
status = STATUS_SUCCESS;
break;
} else {
//
// Connection was not established.
//
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
connection->Flags &= ~CONNECTION_FLAGS_LISTENPOSTED;
}
} while (FALSE);
//
// Spinlock should be acquired at this point- if we set the state to
// active, we really do not want any more requests on this connection
// until we remove/complete the listen request.
//
// Dequeue the request from the connection object
//
RemoveEntryList(&request->Linkage);
InitializeListHead(&request->Linkage);
RELEASE_SPIN_LOCK(&connection->ConnectionLock);
//
// Complete the request
//
AtalkCompleteTdiRequest(request, status);
return;
}
//
// POST CONNECT COMPLETION ROUTINES
//
VOID
AdspConnPostConnectComplete(
PORTABLE_ERROR Error,
ULONG UserData,
LONG ConnectionRefNum,
LONG SocketRefNum,
PORTABLE_ADDRESS RemoteAddress
)
/*++
Routine Description:
This is connect complete routine for ADSP
Arguments:
Error - the portable code base errors declared in atdcls.h
UserData - the cookie we passed to the portable code (the request block)
JobRefNum - the connection reference number
SocketRefNum - socket on which connection is now open
RemoteAddress - the actual remote address
Return Value:
None
--*/
{
NTSTATUS status;
PATALK_TDI_REQUEST request;
PCONNECTION_FILE connection;
PTDI_REQUEST_KERNEL_LISTEN parameters;
PTDI_CONNECTION_INFORMATION returnConnInfo;
POPTIONS_CONNINF options;
#if !TDI_SPEC_ISSUE_RESOLVED
UNREFERENCED_PARAMETER(parameters);
UNREFERENCED_PARAMETER(returnConnInfo);
UNREFERENCED_PARAMETER(options);
#endif
request = (PATALK_TDI_REQUEST)UserData;
//
// Connection object should still be verified, i.e. a reference exists
// for this request
//
do {
status = ConvertToNTStatus(Error, SYNC_REQUEST);
DBGPRINT(ATALK_DEBUG_ADSP, DEBUG_LEVEL_INFOCLASS1,
("INFO1: AdspPostConnectComplete - complete %lx nt %lx request %lx\n",
Error, status, request));
connection = (PCONNECTION_FILE)request->FileObject->FsContext;
if (NT_SUCCESS(status)) {
connection->ConnectionRefNum = ConnectionRefNum;
connection->SocketRefNum = SocketRefNum;
//
// Copy information into the return information structure
// OK to have the spinlock released at this point.
//
#if TDI_SPEC_ISSUE_RESOLVED
//
// Set some return values in the Connect parameters structure
// if STATUS was success
//
// BUGBUG: TDI SPEC IS BROKEN ON THIS!
// BUG #8123
//
parameters = (PTDI_REQUEST_KERNEL_Connect)request->Parameters;
ASSERT(parameters != NULL);
returnConnInfo = (PTDI_CONNECTION_INFORMATION)parameters->ReturnConnectionInformation;
ASSERT(returnConnInfo != NULL);
options = (POPTIONS_CONNINF)returnConnInfo->Options;
ASSERT(options != NULL);
DBGPRINT(ATALK_DEBUG_ADSP, DEBUG_LEVEL_INFOCLASS1,
("INFO1: AdspPostConnectComplete - WorkstationQuantum: %lx\n",
WorkstationQuantum));
#endif
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
//
// Now change the state to ACTIVE
//
connection->Flags &= ~(CONNECTION_FLAGS_CONNECTPOSTED);
connection->Flags |= CONNECTION_FLAGS_ACTIVE;
status = STATUS_SUCCESS;
break;
} else {
//
// Connection was not established.
//
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
connection->Flags &= ~CONNECTION_FLAGS_CONNECTPOSTED;
break;
}
} while (FALSE);
//
// Spinlock should be acquired at this point- if we set the state to
// active, we really do not want any more requests on this connection
// until we remove/complete the Connect request.
//
// Dequeue the request from the connection object
//
RemoveEntryList(&request->Linkage);
InitializeListHead(&request->Linkage);
RELEASE_SPIN_LOCK(&connection->ConnectionLock);
//
// Complete the request
//
AtalkCompleteTdiRequest(request, status);
return;
}
VOID
AspConnPostConnectComplete(
PORTABLE_ERROR Error,
ULONG UserData,
LONG SessionRefNum
)
/*++
Routine Description:
This is connect complete routine for ASP
Arguments:
Error - the portable code base errors declared in atdcls.h
UserData - the cookie we passed to the portable code (the request block)
SessionRefNum - the connection reference number
Return Value:
None
--*/
{
NTSTATUS status;
PATALK_TDI_REQUEST request;
PCONNECTION_FILE connection;
PTDI_REQUEST_KERNEL_LISTEN parameters;
PTDI_CONNECTION_INFORMATION returnConnInfo;
POPTIONS_CONNINF options;
#if !TDI_SPEC_ISSUE_RESOLVED
UNREFERENCED_PARAMETER(parameters);
UNREFERENCED_PARAMETER(returnConnInfo);
UNREFERENCED_PARAMETER(options);
#endif
request = (PATALK_TDI_REQUEST)UserData;
//
// Connection object should still be verified, i.e. a reference exists
// for this request
//
do {
status = ConvertToNTStatus(Error, SYNC_REQUEST);
DBGPRINT(ATALK_DEBUG_ASP, DEBUG_LEVEL_INFOCLASS1,
("INFO1: PapPostConnectComplete - complete %lx nt %lx request! %lx\n",
Error, status, request));
connection = (PCONNECTION_FILE)request->FileObject->FsContext;
if (NT_SUCCESS(status)) {
//
// BUGBUG:
// Portable stack should be consistent about returning the socket
// number Connection->SocketRefNum not available for ASP
//
connection->ConnectionRefNum = SessionRefNum;
//
// Copy information into the return information structure
// OK to have the spinlock released at this point.
//
#if TDI_SPEC_ISSUE_RESOLVED
//
// Set some return values in the Connect parameters structure
// if STATUS was success
//
// BUGBUG: TDI SPEC IS BROKEN ON THIS!
// BUG #8123
//
parameters = (PTDI_REQUEST_KERNEL_Connect)request->Parameters;
ASSERT(parameters != NULL);
returnConnInfo = (PTDI_CONNECTION_INFORMATION)parameters->ReturnConnectionInformation;
ASSERT(returnConnInfo != NULL);
options = (POPTIONS_CONNINF)returnConnInfo->Options;
ASSERT(options != NULL);
#endif
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
//
// Now change the state to ACTIVE
//
connection->Flags &= ~(CONNECTION_FLAGS_CONNECTPOSTED);
connection->Flags |= CONNECTION_FLAGS_ACTIVE;
status = STATUS_SUCCESS;
break;
} else {
//
// Connection was not established.
//
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
connection->Flags &= ~CONNECTION_FLAGS_CONNECTPOSTED;
}
} while (FALSE);
//
// Spinlock should be acquired at this point- if we set the state to
// active, we really do not want any more requests on this connection
// until we remove/complete the Connect request.
//
// Dequeue the request from the connection object
//
RemoveEntryList(&request->Linkage);
InitializeListHead(&request->Linkage);
RELEASE_SPIN_LOCK(&connection->ConnectionLock);
//
// Complete the request
//
AtalkCompleteTdiRequest(request, status);
return;
}
VOID
PapConnPostConnectComplete(
PORTABLE_ERROR Error,
ULONG UserData,
LONG JobRefNum,
SHORT ServerQuantum,
PVOID OpaqueStatusBuffer,
INT StatusBufferSize
)
/*++
Routine Description:
This is connect complete routine for PAP
Arguments:
Error - the portable code base errors declared in atdcls.h
UserData - the cookie we passed to the portable code (the request block)
JobRefNum - the connection reference number
ServerQuantum - the quantum of the server for this connection
OpaqueStatusBuffer - the buffer we passed for the status string
StatusBufferSize - the size of the string copied into the buffer
Return Value:
None
--*/
{
NTSTATUS status;
PATALK_TDI_REQUEST request;
PCONNECTION_FILE connection;
PTDI_REQUEST_KERNEL_LISTEN parameters;
PTDI_CONNECTION_INFORMATION returnConnInfo;
POPTIONS_CONNINF options;
#if !TDI_SPEC_ISSUE_RESOLVED
UNREFERENCED_PARAMETER(parameters);
UNREFERENCED_PARAMETER(returnConnInfo);
UNREFERENCED_PARAMETER(options);
#endif
request = (PATALK_TDI_REQUEST)UserData;
//
// Connection object should still be verified, i.e. a reference exists
// for this request
//
do {
status = ConvertToNTStatus(Error, SYNC_REQUEST);
DBGPRINT(ATALK_DEBUG_PAP, DEBUG_LEVEL_INFOCLASS1,
("INFO1: PapPostConnectComplete - complete %lx nt %lx request! %lx\n",
Error, status, request));
connection = (PCONNECTION_FILE)request->FileObject->FsContext;
if (NT_SUCCESS(status)) {
//
// BUGBUG:
// Portable stack should be consistent about returning the socket
// number Connection->SocketRefNum not available for PAP
//
connection->ConnectionRefNum = JobRefNum;
//
// Copy information into the return information structure
// OK to have the spinlock released at this point.
//
#if TDI_SPEC_ISSUE_RESOLVED
//
// Set some return values in the Connect parameters structure
// if STATUS was success
//
// BUGBUG: TDI SPEC IS BROKEN ON THIS!
// BUG #8123
//
parameters = (PTDI_REQUEST_KERNEL_CONNECT)request->Parameters;
ASSERT(parameters != NULL);
returnConnInfo = (PTDI_CONNECTION_INFORMATION)parameters->ReturnConnectionInformation;
ASSERT(returnConnInfo != NULL);
options = (POPTIONS_CONNINF)returnConnInfo->Options;
ASSERT(options != NULL);
options->PapInfo.RemoteAddress =
options->PapInfo.WorkstationQuantum = WorkstationQuantum;
DBGPRINT(ATALK_DEBUG_PAP, DEBUG_LEVEL_INFOCLASS1,
("INFO1: PapPostConnectComplete - WorkstationQuantum: %lx\n",
WorkstationQuantum));
#endif
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
//
// Now change the state to ACTIVE
//
connection->Flags &= ~(CONNECTION_FLAGS_CONNECTPOSTED);
connection->Flags |= CONNECTION_FLAGS_ACTIVE;
status = STATUS_SUCCESS;
break;
} else {
//
// Connection was not established.
//
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
connection->Flags &= ~CONNECTION_FLAGS_CONNECTPOSTED;
}
} while (FALSE);
//
// Spinlock should be acquired at this point- if we set the state to
// active, we really do not want any more requests on this connection
// until we remove/complete the Connect request.
//
// Dequeue the request from the connection object
//
RemoveEntryList(&request->Linkage);
InitializeListHead(&request->Linkage);
RELEASE_SPIN_LOCK(&connection->ConnectionLock);
//
// Complete the request
//
AtalkCompleteTdiRequest(request, status);
return;
}
//
// SEND Completion routines
//
VOID
PapConnSendComplete(
PORTABLE_ERROR Error,
ULONG UserData,
LONG ConnectionRefNum
)
/*++
Routine Description:
This is the send completion routine for PAP
Arguments:
Error - the portable code base errors declared in atdcls.h
UserData - the cookie we passed to the portable code (the request block)
ConnectionRefNum - the connection reference number
Return Value:
None
--*/
{
NTSTATUS status;
PATALK_TDI_REQUEST request;
PCONNECTION_FILE connection;
request = (PATALK_TDI_REQUEST)UserData;
status = ConvertToNTStatus(Error, SYNC_REQUEST);
DBGPRINT(ATALK_DEBUG_PAP, DEBUG_LEVEL_INFOCLASS1,
("INFO1: PapConnSendComplete - %lx nt %lx req %lx\n", Error, status, request));
//
// Connection object should still be verified, i.e. a reference exists
// for this request
//
connection = (PCONNECTION_FILE)request->FileObject->FsContext;
//
// If the send completed due to the connection being torndown by the
// remote end, let our structures know about it
//
if (status == STATUS_REMOTE_DISCONNECT) {
AtalkConnDisconnect(connection, STATUS_REMOTE_DISCONNECT, NULL, FALSE);
} else if (NT_SUCCESS(status)) {
//
// PAP is a read driven protocol, if send succeeded, then all the data was sent
//
request->IoRequestIrp->IoStatus.Information = request->Send.SendBufferLength;
}
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
//
// Dequeue the request from the connection object
//
RemoveEntryList(&request->Linkage);
InitializeListHead(&request->Linkage);
RELEASE_SPIN_LOCK(&connection->ConnectionLock);
//
// Complete the request
//
AtalkCompleteTdiRequest(request, status);
return;
}
//
// RECEIVE Completion routines
//
VOID
PapConnReceiveComplete(
PORTABLE_ERROR Error,
ULONG UserData,
LONG ConnectionRefNum,
PVOID OpaqueBuffer,
LONG BufferSize,
BOOLEAN EndOfMessage
)
/*++
Routine Description:
This is the receive completion routine used by PAP/ADSP
Arguments:
Error - the portable code base errors declared in atdcls.h
UserData - the cookie we passed to the portable code (the request block)
ConnectionRefNum - the connection reference number
OpaqueBuffer - receive buffer
BufferSize - number of bytes written into buffer
EndOfMessage - end of message
Return Value:
None
--*/
{
NTSTATUS status;
PATALK_TDI_REQUEST request;
PCONNECTION_FILE connection;
request = (PATALK_TDI_REQUEST)UserData;
status = ConvertToNTStatus(Error, SYNC_REQUEST);
DBGPRINT(ATALK_DEBUG_PAP, DEBUG_LEVEL_INFOCLASS1,
("INFO1: PapConnReceiveComplete %lx nt %lx EOM %lx request! %lx\n",
Error, status, EndOfMessage, request));
//
// Connection object should still be verified, i.e. a reference exists
// for this request
//
connection = (PCONNECTION_FILE)request->FileObject->FsContext;
//
// If the read completed due to the connection being torndown by the
// remote end, let our structures know about it
//
if (status == STATUS_REMOTE_DISCONNECT) {
AtalkConnDisconnect(connection, STATUS_REMOTE_DISCONNECT, NULL, FALSE);
} else if (NT_SUCCESS(status)) {
request->IoRequestIrp->IoStatus.Information = (ULONG)BufferSize;
//
// BUGBUG:
// Until STATUS_RECEIVE_PARTIAL etc., are defined, set the high bit
// of the information field to indicate end of file...
//
}
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
//
// Dequeue the request from the connection object
//
RemoveEntryList(&request->Linkage);
InitializeListHead(&request->Linkage);
//
// Release the spinlock
//
RELEASE_SPIN_LOCK(&connection->ConnectionLock);
//
// Complete the request
//
AtalkCompleteTdiRequest(request, status);
return;
}
VOID
AdspConnReceiveComplete(
PORTABLE_ERROR Error,
ULONG UserData,
LONG ConnectionRefNum,
BOOLEAN ExpeditedData,
PVOID OpaqueBuffer,
LONG BufferSize,
BOOLEAN EndOfMessage
)
/*++
Routine Description:
This is the receive completion routine used by ADSP
Arguments:
Error - the portable code base errors declared in atdcls.h
UserData - the cookie we passed to the portable code (the request block)
ConnectionRefNum - the connection reference number
ExpeditedData - is the data attention data?
OpaqueBuffer - receive buffer
BufferSize - number of bytes written into buffer
EndOfMessage - end of message
Return Value:
None
--*/
{
NTSTATUS status;
PATALK_TDI_REQUEST request;
PCONNECTION_FILE connection;
request = (PATALK_TDI_REQUEST)UserData;
status = ConvertToNTStatus(Error, SYNC_REQUEST);
DBGPRINT(ATALK_DEBUG_ADSP, DEBUG_LEVEL_INFOCLASS1,
("INFO1: AdspConnReceiveComplete %lx nt %lx EOM %lx Expedited %d request! %lx\n",
Error, status, EndOfMessage, ExpeditedData, request));
//
// Connection object should still be verified, i.e. a reference exists
// for this request
//
connection = (PCONNECTION_FILE)request->FileObject->FsContext;
//
// If the read completed due to the connection being torndown by the
// remote end, let our structures know about it
//
if (status == STATUS_REMOTE_DISCONNECT) {
AtalkConnDisconnect(connection, STATUS_REMOTE_DISCONNECT, NULL, FALSE);
} else if (NT_SUCCESS(status)) {
request->IoRequestIrp->IoStatus.Information = (ULONG)BufferSize;
//
// BUGBUG:
// Until STATUS_RECEIVE_PARTIAL etc., are defined, set the high bit
// of the information field to indicate end of file...
// Also, wait on status codes for indicating Expedited data etc.
// We also handle STREAM socket stuff here, where we ignore the EndOfMessage
// flag.
//
// if (EndOfMessage) {
// request->IoRequestIrp->IoStatus.Information |= (ULONG)0x80000000;
// }
//
//
}
ACQUIRE_SPIN_LOCK(&connection->ConnectionLock);
//
// Dequeue the request from the connection object
//
RemoveEntryList(&request->Linkage);
InitializeListHead(&request->Linkage);
//
// Release the spinlock
//
RELEASE_SPIN_LOCK(&connection->ConnectionLock);
//
// Complete the request
//
AtalkCompleteTdiRequest(request, status);
return;
}