mirror of https://github.com/lianthony/NT4.0
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
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;
|
|
}
|