1779 lines
55 KiB
C
1779 lines
55 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-1993 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Udpsend.c
|
||
|
||
Abstract:
|
||
|
||
|
||
This file handles building udp(and Tcp) requests, formated to the Tdi specification
|
||
to pass to Tdiout. Tdiout formats the request in an Os specific manner and
|
||
passes it on to the transport.
|
||
|
||
This file handles name service type functions such as query name or
|
||
register name, datagram sends. It also handles building Tcp packets.
|
||
|
||
Author:
|
||
|
||
Jim Stewart (Jimst) 10-2-92
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
|
||
#include "precomp.h" // procedure headings
|
||
#include <ipinfo.h>
|
||
|
||
#include "udpsend.tmh"
|
||
|
||
VOID
|
||
SessionRespDone(
|
||
IN PVOID pContext,
|
||
IN NTSTATUS status,
|
||
IN ULONG lInfo);
|
||
VOID
|
||
NDgramSendCompleted(
|
||
PVOID pContext,
|
||
NTSTATUS status,
|
||
ULONG lInfo
|
||
);
|
||
|
||
//----------------------------------------------------------------------------
|
||
NTSTATUS
|
||
UdpSendNSBcast(
|
||
IN tNAMEADDR *pNameAddr,
|
||
IN PCHAR pScope,
|
||
IN tDGRAM_SEND_TRACKING *pTrackerRequest,
|
||
IN PVOID pTimeoutRoutine,
|
||
IN PVOID pClientContext,
|
||
IN PVOID pClientCompletion,
|
||
IN ULONG Retries,
|
||
IN ULONG Timeout,
|
||
IN enum eNSTYPE eNsType,
|
||
IN BOOL SendFlag
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sends a name registration or a name query
|
||
as a broadcast on the subnet or directed to the name server.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - success or not
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
tNAMEHDR *pNameHdr;
|
||
ULONG uLength;
|
||
CTELockHandle OldIrq;
|
||
ULONG UNALIGNED *pHdrIpAddress;
|
||
ULONG IpAddress;
|
||
USHORT Port;
|
||
USHORT NameType;
|
||
tDGRAM_SEND_TRACKING *pTrackerDgram;
|
||
tTIMERQENTRY *pTimerQEntry;
|
||
PFILE_OBJECT pFileObject;
|
||
tDEVICECONTEXT *pDeviceContext = pTrackerRequest->pDeviceContext;
|
||
COMPLETIONCLIENT pOldCompletion;
|
||
PVOID pOldContext;
|
||
|
||
|
||
if (pNameAddr->NameTypeState & (NAMETYPE_GROUP | NAMETYPE_INET_GROUP))
|
||
{
|
||
NameType = NBT_GROUP;
|
||
}
|
||
else
|
||
{
|
||
NameType = NBT_UNIQUE;
|
||
}
|
||
|
||
// build the correct type of pdu depending on the request type
|
||
|
||
status = GetTracker (&pTrackerDgram, NBT_TRACKER_SEND_NSBCAST);
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
pHdrIpAddress = (ULONG UNALIGNED *)CreatePdu(pNameAddr->Name,
|
||
pScope,
|
||
0L, // we don't know the IP address yet
|
||
NameType,
|
||
eNsType,
|
||
(PVOID)&pNameHdr,
|
||
&uLength,
|
||
pTrackerRequest);
|
||
|
||
if (!pHdrIpAddress)
|
||
{
|
||
IF_DBG(NBT_DEBUG_NAMESRV)
|
||
KdPrint(("Nbt:Failed to Create Pdu to send to WINS PduType= %X\n", eNsType));
|
||
|
||
FreeTracker (pTrackerDgram, RELINK_TRACKER);
|
||
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
//
|
||
// change the dgram header for name refreshes
|
||
//
|
||
if (eNsType == eNAME_REFRESH)
|
||
{
|
||
pNameHdr->OpCodeFlags = NbtConfig.OpRefresh;
|
||
}
|
||
else
|
||
if ( (eNsType == eNAME_QUERY)
|
||
#ifdef VXD
|
||
|| (eNsType == eDNS_NAME_QUERY)
|
||
#endif
|
||
)
|
||
{
|
||
pHdrIpAddress = NULL;
|
||
}
|
||
|
||
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
||
|
||
|
||
// fill in the Datagram hdr info in the tracker structure.
|
||
// There is never a client buffer to send.
|
||
//
|
||
// Set the fields here instead of after the timer is started
|
||
// since they may be accessed by the Timer completion function
|
||
//
|
||
pTrackerRequest->pNameAddr = pNameAddr;
|
||
pTrackerRequest->TransactionId = pNameHdr->TransactId; // save for response checks.
|
||
pTrackerDgram->SendBuffer.pDgramHdr = NULL; // to catch erroneous free's
|
||
|
||
pTrackerDgram->SendBuffer.pDgramHdr = pNameHdr;
|
||
pTrackerDgram->SendBuffer.HdrLength = uLength;
|
||
pTrackerDgram->SendBuffer.pBuffer = NULL;
|
||
pTrackerDgram->SendBuffer.Length = 0;
|
||
pTrackerDgram->pNameAddr = pNameAddr;
|
||
pTrackerDgram->pDeviceContext = pDeviceContext;
|
||
|
||
// start the timer now...We didn't start it before because it could
|
||
// have expired during the dgram setup, perhaps before the Tracker was
|
||
// fully setup.
|
||
//
|
||
if (Timeout)
|
||
{
|
||
//
|
||
// Before we over-write the current pTimer field in pNameAddr below,
|
||
// we need to check if there is any timer running, and if so, we will
|
||
// have to stop it right now
|
||
//
|
||
while (pTimerQEntry = pNameAddr->pTimer)
|
||
{
|
||
pNameAddr->pTimer = NULL;
|
||
status = StopTimer(pTimerQEntry, &pOldCompletion, &pOldContext);
|
||
if (pOldCompletion)
|
||
{
|
||
CTESpinFree(&NbtConfig.JointLock, OldIrq);
|
||
(*pOldCompletion) (pOldContext, STATUS_TIMEOUT);
|
||
CTESpinLock(&NbtConfig.JointLock, OldIrq);
|
||
}
|
||
}
|
||
|
||
status = StartTimer(pTimeoutRoutine,
|
||
Timeout,
|
||
(PVOID)pTrackerRequest, // context value
|
||
NULL,
|
||
pClientContext,
|
||
pClientCompletion,
|
||
pDeviceContext,
|
||
&pTimerQEntry,
|
||
(USHORT)Retries,
|
||
TRUE);
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
// we need to differentiate the timer failing versus lack
|
||
// of resources
|
||
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
||
CTEMemFree(pNameHdr);
|
||
|
||
FreeTracker(pTrackerDgram,RELINK_TRACKER);
|
||
|
||
return(STATUS_INVALID_PARAMETER_6);
|
||
}
|
||
//
|
||
// Cross link the nameaddr and the timer so we can stop the timer
|
||
// when the name query response occurs
|
||
//
|
||
pTimerQEntry->pCacheEntry = pNameAddr;
|
||
pNameAddr->pTimer = pTimerQEntry;
|
||
}
|
||
|
||
//
|
||
// Check the Flag value in the tracker and see if we should do a broadcast
|
||
// or a directed send to the name server
|
||
//
|
||
if (pTrackerRequest->Flags & NBT_BROADCAST)
|
||
{
|
||
//
|
||
// set the broadcast bit in the header to be ON since this may be
|
||
// an M or MS node that is changing to broadcast from directed sends.
|
||
//
|
||
((PUCHAR)pTrackerDgram->SendBuffer.pDgramHdr)[3] |= FL_BROADCAST_BYTE;
|
||
|
||
Port = NBT_NAMESERVICE_UDP_PORT;
|
||
|
||
IpAddress = pDeviceContext->BroadcastAddress;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// turn off the broadcast bit in the header since this may be
|
||
// an M or MS node that is changing to directed sends from broadcasts.
|
||
//
|
||
((PUCHAR)pTrackerDgram->SendBuffer.pDgramHdr)[3] &= ~FL_BROADCAST_BYTE;
|
||
|
||
// check for a zero first byte in the name passed to the name server
|
||
ASSERT(((PUCHAR)pTrackerDgram->SendBuffer.pDgramHdr)[12]);
|
||
|
||
//
|
||
// for Multihomed hosts, UNIQUE name registrations use a special new
|
||
// code (0x0F) to tell the name server this is a multihomed name that
|
||
// will have several ip addresses
|
||
//
|
||
if (NbtConfig.MultiHomed && ((eNsType == eNAME_REGISTRATION) && (NameType == NBT_UNIQUE)))
|
||
{
|
||
//
|
||
// if it is a multihomed host, then use a new special registration opcode (0xF)
|
||
//
|
||
((PUCHAR)pTrackerDgram->SendBuffer.pDgramHdr)[2] |= OP_REGISTER_MULTI;
|
||
}
|
||
|
||
Port = NbtConfig.NameServerPort;
|
||
|
||
// name srvr, backup name srvr, dns srvr, backup dnr srvr:which one?
|
||
|
||
if (pTrackerRequest->Flags & NBT_NAME_SERVER)
|
||
{
|
||
IpAddress = pDeviceContext->lNameServerAddress;
|
||
}
|
||
#ifdef MULTIPLE_WINS
|
||
//
|
||
// IMPORTANT: Check for NAME_SERVER_OTHERS flag has to be before check
|
||
// for NAME_SERVER_BACKUP flag, since both flags will be set when we
|
||
// we are querying "other" servers
|
||
//
|
||
else if (pTrackerRequest->Flags & NBT_NAME_SERVER_OTHERS) // Try "other" servers
|
||
{
|
||
if (0 == pTrackerRequest->NSOthersLeft) // Do LOOP_BACK
|
||
{
|
||
IpAddress = LOOP_BACK;
|
||
}
|
||
else
|
||
{
|
||
IpAddress = pTrackerRequest->pDeviceContext->lOtherServers[pTrackerRequest->NSOthersIndex];
|
||
}
|
||
}
|
||
#endif
|
||
else
|
||
#ifndef VXD
|
||
{
|
||
IpAddress = pDeviceContext->lBackupServer;
|
||
}
|
||
#else
|
||
if (pTrackerRequest->Flags & NBT_NAME_SERVER_BACKUP)
|
||
{
|
||
IpAddress = pDeviceContext->lBackupServer;
|
||
}
|
||
else
|
||
if (pTrackerRequest->Flags & NBT_DNS_SERVER)
|
||
{
|
||
IpAddress = pDeviceContext->lDnsServerAddress;
|
||
Port = NbtConfig.DnsServerPort;
|
||
}
|
||
else // ----- if (pTrackerRequest->Flags & NBT_DNS_SERVER_BACKUP) ----
|
||
{
|
||
IpAddress = pDeviceContext->lDnsBackupServer;
|
||
Port = NbtConfig.DnsServerPort;
|
||
}
|
||
#endif
|
||
|
||
|
||
//
|
||
// is it is a send to WINS on this machine
|
||
//
|
||
if (pNameHdr->AnCount == (UCHAR)WINS_SIGNATURE)
|
||
{
|
||
//
|
||
// on RAS links, we don't want to register with the local wins
|
||
// but with the wins that RAS told us about.
|
||
// (of course, if RAS didn't give us a wins address, at least
|
||
// register with the local guy!)
|
||
//
|
||
if ((pDeviceContext->IpInterfaceFlags & IP_INTFC_FLAG_P2P) && // Check for PointToPoint
|
||
(pDeviceContext->lNameServerAddress != LOOP_BACK))
|
||
{
|
||
// Don't do anything;
|
||
}
|
||
else
|
||
{
|
||
IpAddress = pDeviceContext->IpAddress;
|
||
}
|
||
}
|
||
}
|
||
|
||
ASSERT(pTrackerRequest->Flags);
|
||
|
||
// each adapter has a different source Ip address for registrations
|
||
// pHdrIpAddress will be NULL for NameQueries
|
||
if (pHdrIpAddress)
|
||
{
|
||
// If the Source IP address is to be different from the device we are
|
||
// sending the Datagram on, fill it in!
|
||
if (pTrackerRequest->Flags & NBT_USE_UNIQUE_ADDR)
|
||
{
|
||
*pHdrIpAddress = htonl(pTrackerRequest->RemoteIpAddress);
|
||
}
|
||
else
|
||
{
|
||
*pHdrIpAddress = htonl(pDeviceContext->IpAddress);
|
||
}
|
||
}
|
||
|
||
//
|
||
// in the event that DHCP has just removed the IP address, use a null
|
||
// FileObject to signal UdpSendDatagram not to do the send
|
||
// Also, if the device has been destroyed, dont send anything.
|
||
//
|
||
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
||
status = UdpSendDatagram(pTrackerDgram,
|
||
IpAddress,
|
||
NDgramSendCompleted,
|
||
pTrackerDgram,
|
||
Port,
|
||
(SendFlag ? NBT_NAME_SERVICE : 0));
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
//
|
||
// Since pTrackerDgram is associated only with the Datagram send,
|
||
// it should be free'ed here only!
|
||
//
|
||
FreeTracker (pTrackerDgram, FREE_HDR | RELINK_TRACKER);
|
||
}
|
||
|
||
return(status);
|
||
}
|
||
//----------------------------------------------------------------------------
|
||
PVOID
|
||
CreatePdu(
|
||
IN PCHAR pName,
|
||
IN PCHAR pScope,
|
||
IN ULONG IpAddress,
|
||
IN USHORT NameType,
|
||
IN enum eNSTYPE eNsType,
|
||
OUT PVOID *pHdrs,
|
||
OUT PULONG pLength,
|
||
IN tDGRAM_SEND_TRACKING *pTrackerRequest
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine builds a registration pdu
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
PULONG - a ptr to the ip address in the pdu so it can be filled in later
|
||
|
||
--*/
|
||
{
|
||
tNAMEHDR *pNameHdr;
|
||
ULONG uLength;
|
||
ULONG uScopeSize;
|
||
tGENERALRR *pGeneral;
|
||
CTELockHandle OldIrq;
|
||
|
||
|
||
#ifdef VXD
|
||
if ( (eNsType == eDNS_NAME_QUERY) || (eNsType == eDIRECT_DNS_NAME_QUERY) )
|
||
{
|
||
uScopeSize = domnamelen(pTrackerRequest->pchDomainName) + 1; // +1 for len byte
|
||
if (uScopeSize > 1)
|
||
{
|
||
uScopeSize++; // for the null byte
|
||
}
|
||
}
|
||
else
|
||
#endif
|
||
uScopeSize = strlen(pScope) +1; // +1 for null too
|
||
|
||
|
||
// size is size of the namehdr structure -1 for the NetbiosName[1]
|
||
// + the 32 bytes for the half ascii name +
|
||
// scope + size of the General RR structure
|
||
uLength = sizeof(tNAMEHDR) - 1
|
||
+ (NETBIOS_NAME_SIZE << 1)
|
||
+ uScopeSize;
|
||
|
||
if (eNsType == eNAME_QUERY)
|
||
{
|
||
uLength = uLength + sizeof(ULONG);
|
||
}
|
||
#ifdef VXD
|
||
// there is no half-ascii conversion in DNS. we added 32 bytes above, but
|
||
// we need only 16. so, subtract 16.
|
||
else if (eNsType == eDNS_NAME_QUERY)
|
||
{
|
||
uLength = uLength - NETBIOS_NAME_SIZE + sizeof(ULONG);
|
||
}
|
||
// This is a "raw" DNS name query. Substitute raw string length of pName
|
||
// for NETBIOS_NAME_SIZE.
|
||
else if (eNsType == eDIRECT_DNS_NAME_QUERY)
|
||
{
|
||
uLength = uLength - (NETBIOS_NAME_SIZE << 1) + sizeof(ULONG) + strlen(pName) + 1;
|
||
}
|
||
#endif
|
||
else
|
||
{
|
||
uLength += sizeof(tGENERALRR);
|
||
}
|
||
|
||
// Note that this memory must be deallocated when the send completes in
|
||
// tdiout.DgramSendCompletion
|
||
pNameHdr = NbtAllocMem((USHORT)uLength ,NBT_TAG('X'));
|
||
|
||
if (!pNameHdr)
|
||
{
|
||
return(NULL);
|
||
}
|
||
|
||
CTEZeroMemory((PVOID)pNameHdr,uLength);
|
||
|
||
//
|
||
// for resends of the same name query or name registration, do not increment
|
||
// the transaction id
|
||
//
|
||
if (pTrackerRequest->TransactionId)
|
||
{
|
||
pNameHdr->TransactId = pTrackerRequest->TransactionId;
|
||
}
|
||
else
|
||
{
|
||
pNameHdr->TransactId = htons(GetTransactId());
|
||
}
|
||
|
||
pNameHdr->QdCount = 1;
|
||
pNameHdr->AnCount = 0;
|
||
pNameHdr->NsCount = 0;
|
||
|
||
|
||
#ifdef VXD
|
||
if ((eNsType != eDNS_NAME_QUERY)&&(eNsType != eDIRECT_DNS_NAME_QUERY))
|
||
{
|
||
#endif
|
||
// Convert the name to half ascii and copy!! ... adding the scope too
|
||
pGeneral = (tGENERALRR *)ConvertToHalfAscii(
|
||
(PCHAR)&pNameHdr->NameRR.NameLength,
|
||
pName,
|
||
pScope,
|
||
uScopeSize);
|
||
|
||
pGeneral->Question.QuestionTypeClass = htonl(QUEST_NBINTERNET);
|
||
#ifdef VXD
|
||
}
|
||
#endif
|
||
|
||
*pHdrs = (PVOID)pNameHdr;
|
||
*pLength = uLength;
|
||
|
||
switch (eNsType)
|
||
|
||
{
|
||
|
||
#ifdef VXD
|
||
case eDNS_NAME_QUERY:
|
||
case eDIRECT_DNS_NAME_QUERY:
|
||
|
||
// copy the netbios name ... adding the scope too
|
||
pGeneral = (tGENERALRR *)DnsStoreName(
|
||
(PCHAR)&pNameHdr->NameRR.NameLength,
|
||
pName,
|
||
pTrackerRequest->pchDomainName,
|
||
eNsType);
|
||
|
||
pGeneral->Question.QuestionTypeClass = htonl(QUEST_DNSINTERNET);
|
||
|
||
pNameHdr->OpCodeFlags = (FL_RECURDESIRE);
|
||
|
||
pNameHdr->ArCount = 0;
|
||
|
||
// we just need to return something non-null to succeed.
|
||
return((PULONG)pNameHdr);
|
||
#endif
|
||
|
||
case eNAME_QUERY:
|
||
|
||
if (NodeType & BNODE)
|
||
{
|
||
pNameHdr->OpCodeFlags = (FL_BROADCAST | FL_RECURDESIRE);
|
||
}
|
||
else
|
||
pNameHdr->OpCodeFlags = (FL_RECURDESIRE);
|
||
|
||
pNameHdr->ArCount = 0;
|
||
|
||
// we just need to return something non-null to succeed.
|
||
return((PULONG)pNameHdr);
|
||
break;
|
||
|
||
case eNAME_REGISTRATION_OVERWRITE:
|
||
case eNAME_REFRESH:
|
||
case eNAME_REGISTRATION:
|
||
//
|
||
// The broadcast bit is set in UdpSendNSBcast so we don't
|
||
// need to set it here. - just set the op code, since the broadcast
|
||
// bit is a function of whether we are talking to the nameserver or doing
|
||
// a broadcast. This code handles the multi-homed case with a new
|
||
// opcode for registration, and that opcode is set in the routine that
|
||
//
|
||
// The final name registration in Broadcast is called an Overwrite request
|
||
// and it does not have the FL_RECURSION Desired bit set.
|
||
//
|
||
if (eNsType == eNAME_REGISTRATION_OVERWRITE)
|
||
{
|
||
pNameHdr->OpCodeFlags = (OP_REGISTRATION);
|
||
}
|
||
else
|
||
{
|
||
pNameHdr->OpCodeFlags = (FL_RECURDESIRE | OP_REGISTRATION);
|
||
}
|
||
|
||
pGeneral->Ttl = htonl(DEFAULT_TTL);
|
||
|
||
// *** NOTE: There is no BREAK here by DESIGN!!
|
||
|
||
case eNAME_RELEASE:
|
||
|
||
// this code sets the Broadcast bit based on the node type rather than the
|
||
// type of send....UdpSendNSBcast, resets the code according to the type of
|
||
// name, so this code may not need to set the Broadcast bit
|
||
//
|
||
if (eNsType == eNAME_RELEASE)
|
||
{
|
||
pNameHdr->OpCodeFlags = OP_RELEASE;
|
||
//
|
||
// TTL for release is zero
|
||
//
|
||
pGeneral->Ttl = 0;
|
||
}
|
||
|
||
pNameHdr->ArCount = 1; // 1 additional resource record included
|
||
//
|
||
// If WINS is on the same machine adjust the PDU to be able to tell
|
||
// WINS that this pdu came from the local machine
|
||
//
|
||
#ifndef VXD
|
||
if (pWinsInfo && (pTrackerRequest->Flags & NBT_NAME_SERVER))
|
||
{
|
||
pNameHdr->AnCount = (UCHAR)WINS_SIGNATURE;
|
||
}
|
||
#endif
|
||
|
||
pGeneral->RrName.uSizeLabel = PTR_TO_NAME; // set top two bits to signify ptr
|
||
|
||
// the offset ptr to the name added above
|
||
pGeneral->RrName.pLabel[0] = sizeof(tNAMEHDR) - sizeof(tNETBIOS_NAME);
|
||
pGeneral->RrTypeClass = htonl(QUEST_NBINTERNET);
|
||
|
||
|
||
pGeneral->Length = htons(6);
|
||
pGeneral->Flags = htons((USHORT)((NameType << 15) | NbtConfig.PduNodeType));
|
||
pGeneral->IpAddress = htonl(IpAddress);
|
||
|
||
break;
|
||
}
|
||
|
||
// return the ptr to the IP address so this can be filled in later if necessary
|
||
return((PVOID)&pGeneral->IpAddress);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
VOID
|
||
NameDgramSendCompleted(
|
||
PVOID pContext,
|
||
NTSTATUS status,
|
||
ULONG lInfo
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine frees the name service datagram that was allocated for
|
||
this name query or name registration in UdpSendNsBcast.
|
||
|
||
Arguments:
|
||
|
||
pContext = ptr to datagram header
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
tDGRAM_SEND_TRACKING *pTracker;
|
||
CTELockHandle OldIrq;
|
||
|
||
pTracker = (tDGRAM_SEND_TRACKING *)pContext;
|
||
|
||
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
||
CTEMemFree(pTracker->SendBuffer.pDgramHdr);
|
||
pTracker->SendBuffer.pDgramHdr = NULL;
|
||
NBT_DEREFERENCE_TRACKER(pTracker, TRUE);
|
||
|
||
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
||
}
|
||
//----------------------------------------------------------------------------
|
||
VOID
|
||
NDgramSendCompleted(
|
||
PVOID pContext,
|
||
NTSTATUS status,
|
||
ULONG lInfo
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine frees the name service datagram that was allocated for
|
||
this name query or name registration in UdpSendNsBcast.
|
||
|
||
Arguments:
|
||
|
||
pContext = ptr to datagram header
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
tDGRAM_SEND_TRACKING *pTracker;
|
||
CTELockHandle OldIrq;
|
||
|
||
pTracker = (tDGRAM_SEND_TRACKING *)pContext;
|
||
FreeTracker(pTracker, FREE_HDR | RELINK_TRACKER);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
NTSTATUS
|
||
UdpSendResponse(
|
||
IN ULONG lNameSize,
|
||
IN tNAMEHDR UNALIGNED *pNameHdrIn,
|
||
IN tNAMEADDR *pNameAddr,
|
||
IN PTDI_ADDRESS_IP pDestIpAddress,
|
||
IN tDEVICECONTEXT *pDeviceContext,
|
||
IN ULONG Rcode,
|
||
IN enum eNSTYPE NsType,
|
||
IN CTELockHandle OldIrq
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine builds a Name Release/Registration/Query response pdu and
|
||
sends it with the specified Rcode.
|
||
|
||
Arguments:
|
||
|
||
lSize - number of bytes in the name including scope in half ascii
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - success or not
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
tNAMEHDR *pNameHdr;
|
||
ULONG uLength;
|
||
tDGRAM_SEND_TRACKING *pTracker;
|
||
tQUERYRESP UNALIGNED *pQuery;
|
||
ULONG ToCopy;
|
||
LONG i;
|
||
BOOLEAN RespondWithOneAddr = TRUE;
|
||
ULONG MultiHomedSize = 0;
|
||
ULONG in_addr;
|
||
USHORT in_port;
|
||
ULONG IpAddress = 0;
|
||
USHORT NameType = 0; // Assume we are Unique by default!
|
||
BOOLEAN DoNonProxyCode = TRUE;
|
||
|
||
in_addr = ntohl(pDestIpAddress->in_addr);
|
||
in_port = ntohs(pDestIpAddress->sin_port);
|
||
|
||
// a multihomed node can have the SingleResponse registry value set so
|
||
// that it never returns a list of ip addresses. This allows multihoming
|
||
// in disjoint WINS server domains. - for name Query responses only
|
||
//
|
||
|
||
if ((NbtConfig.MultiHomed) && (!pNameAddr || pNameAddr->Verify != REMOTE_NAME) &&
|
||
(!NbtConfig.SingleResponse) &&
|
||
(NsType == eNAME_QUERY_RESPONSE))
|
||
{
|
||
// if (SrcIsNameServer(in_addr,in_port))
|
||
{
|
||
RespondWithOneAddr = FALSE;
|
||
MultiHomedSize = (NbtConfig.AdapterCount-1)*sizeof(tADDSTRUCT);
|
||
}
|
||
}
|
||
|
||
// size is size of the namehdr structure -1 for NetBiosName[1]
|
||
// + the 32 bytes for the half ascii name + the Query response record
|
||
// + any scope size (including the null on the end of the name)
|
||
// ( part of the lNameSize) + the number of extra adapters * the size
|
||
// of the address structure (multihomed case).
|
||
uLength = sizeof(tNAMEHDR)
|
||
+ sizeof(tQUERYRESP)
|
||
+ lNameSize
|
||
- 1
|
||
+ MultiHomedSize;
|
||
|
||
// Note that this memory must be deallocated when the send completes in
|
||
// tdiout.DgramSendCompletion
|
||
pNameHdr = NbtAllocMem((USHORT)uLength ,NBT_TAG('Y'));
|
||
if (!pNameHdr)
|
||
{
|
||
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
||
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
CTEZeroMemory((PVOID)pNameHdr,uLength);
|
||
|
||
pNameHdr->QdCount = 0;
|
||
pNameHdr->AnCount = 1;
|
||
|
||
//
|
||
// fill in the rest of the PDU explicitly
|
||
//
|
||
pQuery = (tQUERYRESP *)&pNameHdr->NameRR.NetBiosName[lNameSize];
|
||
|
||
pQuery->RrTypeClass = htonl(QUEST_NBINTERNET);
|
||
pQuery->Ttl = 0;
|
||
pQuery->Length = htons(sizeof(tADDSTRUCT));
|
||
pQuery->Flags = htons((USHORT)(NbtConfig.PduNodeType));
|
||
|
||
// set the name type to 1 if it is a group so we can shift the 1 to the 16th
|
||
// bit position
|
||
// pNameAddr may not be set if we are sending a -ve NameQuery response, in which case, the field
|
||
// is never looked at, or if we are sending a release response, which holds sends only
|
||
// for a unique name, in which case we have already initialized the value to 0
|
||
//
|
||
if (pNameAddr != NULL)
|
||
{
|
||
NameType = (pNameAddr->NameTypeState & (NAMETYPE_GROUP | NAMETYPE_INET_GROUP)) ? 1 : 0;
|
||
}
|
||
pQuery->Flags = htons((USHORT)((NameType << 15) | NbtConfig.PduNodeType));
|
||
|
||
// convert Rcode to network order
|
||
Rcode = htons(Rcode);
|
||
|
||
switch (NsType)
|
||
{
|
||
|
||
case eNAME_RELEASE:
|
||
case eNAME_REGISTRATION_RESPONSE:
|
||
|
||
// copy the source name and the 12 bytes preceeding it to complete the
|
||
// response pdu
|
||
//
|
||
ToCopy = sizeof(tNAMEHDR) + lNameSize -1;
|
||
CTEMemCopy((PVOID)pNameHdr,
|
||
(PVOID)pNameHdrIn,
|
||
ToCopy);
|
||
|
||
if (NsType == eNAME_RELEASE)
|
||
{
|
||
// setup the fields in the response.
|
||
pNameHdr->OpCodeFlags = (USHORT)(OP_RESPONSE | OP_RELEASE
|
||
| FL_AUTHORITY
|
||
| Rcode);
|
||
|
||
}
|
||
else
|
||
{
|
||
// setup the fields in the response.
|
||
pNameHdr->OpCodeFlags = (USHORT)(OP_RESPONSE | OP_REGISTRATION |
|
||
FL_RECURDESIRE | FL_RECURAVAIL | FL_AUTHORITY
|
||
| Rcode);
|
||
|
||
}
|
||
|
||
// these two lines must be here because the memcopy above sets
|
||
// them to wrong values.
|
||
pNameHdr->QdCount = 0;
|
||
pNameHdr->AnCount = 1;
|
||
pNameHdr->ArCount = 0;
|
||
pNameHdr->NsCount = 0;
|
||
|
||
// this code will run in the proxy case where another node does a
|
||
// registration of a unique name that conflicts with an internet
|
||
// group name in the remote table. There are never any internet group
|
||
// names in the local table - at least if there are, they are flagged
|
||
// as simple groups.
|
||
//
|
||
if (pNameAddr)
|
||
{
|
||
if (pNameAddr->NameTypeState & NAMETYPE_INET_GROUP)
|
||
{
|
||
if (pNameAddr->pLmhSvcGroupList)
|
||
{
|
||
IpAddress = pNameAddr->pLmhSvcGroupList[0];
|
||
}
|
||
else
|
||
{
|
||
IpAddress = 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// an ipaddress of 0 and a group name means it is a local name
|
||
// table entry, where the 0 ipaddress should be switched to the
|
||
// ipaddress of this adapter.
|
||
//
|
||
if ((pNameAddr->IpAddress == 0) &&
|
||
(pNameAddr->NameTypeState & NAMETYPE_GROUP))
|
||
{
|
||
IpAddress = pDeviceContext->IpAddress;
|
||
}
|
||
else
|
||
{
|
||
IpAddress = pNameAddr->IpAddress;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
IpAddress = 0;
|
||
}
|
||
break;
|
||
|
||
case eNAME_QUERY_RESPONSE:
|
||
|
||
pNameHdr->OpCodeFlags = ( OP_RESPONSE | FL_AUTHORITY | FL_RECURDESIRE );
|
||
|
||
pNameHdr->TransactId = pNameHdrIn->TransactId;
|
||
|
||
// add 1 for the name length byte on the front of the name - scope is already
|
||
// included in lNameSize
|
||
//
|
||
CTEMemCopy(&pNameHdr->NameRR.NameLength, (PVOID)&pNameHdrIn->NameRR.NameLength, lNameSize+1);
|
||
|
||
if (pNameAddr == NULL)
|
||
{
|
||
// this is a negative query response record since there is no
|
||
// local name to be found
|
||
//
|
||
pNameHdr->OpCodeFlags |= htons(NAME_ERROR);
|
||
pQuery->Length = 0;
|
||
IpAddress = 0;
|
||
}
|
||
else
|
||
{
|
||
tDEVICECONTEXT *pDevContext;
|
||
PLIST_ENTRY pHead;
|
||
PLIST_ENTRY pEntry;
|
||
|
||
// do not send name query responses for names not registered on
|
||
// this net card, unless it is the name server for that net
|
||
// card requesting the name query, since for Multihomed nodes
|
||
// when it registers a name, WINS will do a query, which may
|
||
// come in on the other net card that the name is not active on
|
||
// yet - so we want to respond to this sort of query. Do not do
|
||
// this check for a proxy since it is responding for a name
|
||
// in the remote name table and it is not bound to an adapter.
|
||
//
|
||
if (!(NodeType & PROXY) &&
|
||
!(pNameAddr->AdapterMask & pDeviceContext->AdapterMask) &&
|
||
(!((in_port == NbtConfig.NameServerPort) &&
|
||
(pDeviceContext->lNameServerAddress == in_addr) ||
|
||
(pDeviceContext->lBackupServer == in_addr))))
|
||
{
|
||
//
|
||
// Only return an address to the requestor if the
|
||
// name is registered on that adapter
|
||
//
|
||
CTEMemFree(pNameHdr);
|
||
|
||
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
||
return(STATUS_UNSUCCESSFUL);
|
||
}
|
||
|
||
pQuery->Ttl = htonl(DEFAULT_TTL);
|
||
//
|
||
// In case of PROXY, we send one IP address as response to an
|
||
// internet group query. Note: there should not be any INET_GROUP
|
||
// names in the local hash table, hence a non-proxy should not execute
|
||
// this code
|
||
//
|
||
#ifdef PROXY_NODE
|
||
//
|
||
// When the proxy responds, the source node will see that it is a
|
||
// group name and convert it to a broadcast, so the Ip address doesn't
|
||
// really matter since the sender will not use it. Note that the
|
||
// source node send may not actually reach any members of the
|
||
// internet group since they may all be off the local subnet.
|
||
//
|
||
IF_PROXY(NodeType)
|
||
{
|
||
DoNonProxyCode = FALSE;
|
||
|
||
if (pNameAddr->NameTypeState & (NAMETYPE_INET_GROUP))
|
||
{
|
||
IpAddress = 0;
|
||
PickBestAddress (pNameAddr, pDeviceContext, &IpAddress);
|
||
}
|
||
else if (pNameAddr->Verify == LOCAL_NAME)
|
||
{
|
||
//
|
||
// if this name is local and if this is a multihomed machine
|
||
// we should treat it like a regular multihomed machine, even
|
||
// though this is a Proxy node
|
||
//
|
||
DoNonProxyCode = TRUE;
|
||
}
|
||
else
|
||
{
|
||
IpAddress = pNameAddr->IpAddress;
|
||
}
|
||
|
||
if (IpAddress == 0)
|
||
{
|
||
// don't return 0, return the broadcast address
|
||
//
|
||
IpAddress = pDeviceContext->BroadcastAddress;
|
||
}
|
||
|
||
}
|
||
|
||
if (DoNonProxyCode)
|
||
#endif
|
||
{
|
||
// the node could be multihomed, but we are saying, only
|
||
// respond with one address when this flag is set.
|
||
if (RespondWithOneAddr)
|
||
{
|
||
// for multihomed hosts, SelectAdapter can be set to TRUE
|
||
//
|
||
if (NbtConfig.SelectAdapter)
|
||
{
|
||
CTESystemTime TimeValue;
|
||
LONG Index;
|
||
ULONG Count=0;
|
||
|
||
// we are only going to return one address, but we
|
||
// can randomly select it from the available adapters
|
||
// Try to find a valid ip address 5 times.
|
||
//
|
||
IpAddress = 0;
|
||
while ((IpAddress == 0) && (Count < 5))
|
||
{
|
||
Count++;
|
||
CTEQuerySystemTime(TimeValue);
|
||
Index = RandomizeFromTime( TimeValue, NbtConfig.AdapterCount ) ;
|
||
|
||
pHead = &NbtConfig.DeviceContexts;
|
||
pEntry = pHead->Flink;
|
||
|
||
for (i = 0;i< Index;i++)
|
||
pEntry = pEntry->Flink;
|
||
|
||
pDevContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage);
|
||
IpAddress = pDevContext->IpAddress;
|
||
}
|
||
|
||
//
|
||
// if this adapter still has a null IpAddress then respond
|
||
// with the adapter the request came in on, since the
|
||
// other adapters could be idle RAS or waiting for a DHCP
|
||
// address just now...
|
||
//
|
||
if (IpAddress == 0)
|
||
{
|
||
IpAddress = pDeviceContext->IpAddress;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
IpAddress = pDeviceContext->IpAddress;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
tADDSTRUCT *pAddStruct;
|
||
USHORT Flags;
|
||
ULONG Count = 0;
|
||
|
||
// multihomed case - go through all the adapters making
|
||
// up a structure of all adapters that the name is
|
||
// registered against. Enough memory was allocated up
|
||
// front to have the name registered against all adapeters
|
||
// on this node.
|
||
//
|
||
Flags = pQuery->Flags;
|
||
|
||
// set to zero so we don't try to set pQuery->IpAddress
|
||
// below
|
||
IpAddress = 0;
|
||
|
||
pAddStruct = (tADDSTRUCT *)&pQuery->Flags;
|
||
pHead = &NbtConfig.DeviceContexts;
|
||
pEntry = pHead->Flink;
|
||
while (pEntry != pHead)
|
||
{
|
||
pDevContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage);
|
||
|
||
//
|
||
// only pass back addresses registered on this adapter
|
||
// that are not null(i.e. not RAS adapters after a disconnect)
|
||
//
|
||
if ((pDevContext->AdapterMask & pNameAddr->AdapterMask) &&
|
||
(pDevContext->IpAddress))
|
||
{
|
||
pAddStruct->NbFlags = Flags;
|
||
pAddStruct->IpAddr = htonl(pDevContext->IpAddress);
|
||
Count++;
|
||
pAddStruct++;
|
||
}
|
||
pEntry = pEntry->Flink;
|
||
|
||
}
|
||
// re-adjust the length of the pdu if the name is not registered
|
||
// against all adapters...
|
||
//
|
||
if (Count != NbtConfig.AdapterCount)
|
||
{
|
||
uLength -= (NbtConfig.AdapterCount - Count)*sizeof(tADDSTRUCT);
|
||
}
|
||
pQuery->Length = (USHORT)htons(Count*sizeof(tADDSTRUCT));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (IpAddress)
|
||
{
|
||
pQuery->IpAddress = htonl(IpAddress);
|
||
}
|
||
|
||
// get a tracker structure, which has a SendInfo structure in it
|
||
status = GetTracker(&pTracker, NBT_TRACKER_SEND_RESPONSE_DGRAM);
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
CTEMemFree((PVOID)pNameHdr);
|
||
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
||
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
// fill in the connection information
|
||
pTracker->SendBuffer.HdrLength = uLength;
|
||
pTracker->SendBuffer.pDgramHdr = (PVOID)pNameHdr;
|
||
pTracker->SendBuffer.Length = 0;
|
||
pTracker->SendBuffer.pBuffer = NULL;
|
||
pTracker->pDeviceContext = pDeviceContext;
|
||
|
||
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
||
|
||
status = UdpSendDatagram (pTracker,
|
||
in_addr,
|
||
QueryRespDone,
|
||
pTracker,
|
||
in_port,
|
||
NBT_NAME_SERVICE);
|
||
|
||
return(status);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
VOID
|
||
QueryRespDone(
|
||
IN PVOID pContext,
|
||
IN NTSTATUS status,
|
||
IN ULONG lInfo)
|
||
/*++
|
||
Routine Description
|
||
|
||
This routine handles cleaning up various data blocks used in conjunction
|
||
with the sending the Query response.
|
||
|
||
Arguments:
|
||
|
||
pContext - ptr to the DGRAM_TRACKER block
|
||
NTSTATUS - completion status
|
||
|
||
Return Values:
|
||
|
||
VOID
|
||
|
||
--*/
|
||
|
||
{
|
||
tDGRAM_SEND_TRACKING *pTracker;
|
||
CTELockHandle OldIrq;
|
||
|
||
pTracker = (tDGRAM_SEND_TRACKING *)pContext;
|
||
|
||
FreeTracker(pTracker,RELINK_TRACKER | FREE_HDR);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
NTSTATUS
|
||
UdpSendDatagram(
|
||
IN tDGRAM_SEND_TRACKING *pDgramTracker,
|
||
IN ULONG IpAddress,
|
||
IN PVOID pCompletionRoutine,
|
||
IN PVOID CompletionContext,
|
||
IN USHORT Port,
|
||
IN ULONG Service
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sends a datagram across the TDI to be sent by Udp.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - success or not
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
TDI_REQUEST TdiRequest;
|
||
ULONG uSentSize;
|
||
TDI_CONNECTION_INFORMATION *pSendInfo;
|
||
PTRANSPORT_ADDRESS pTransportAddr;
|
||
ULONG Length;
|
||
PFILE_OBJECT TransportFileObject = NULL;
|
||
CTELockHandle OldIrq;
|
||
tDEVICECONTEXT *pDeviceContext = NULL;
|
||
tFILE_OBJECTS *pFileObjectsContext;
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
||
|
||
if (NBT_REFERENCE_DEVICE (pDgramTracker->pDeviceContext, REF_DEV_UDP_SEND, TRUE))
|
||
{
|
||
pDeviceContext = pDgramTracker->pDeviceContext; // Assigned => referenced!
|
||
|
||
if ((pDgramTracker->pDeviceContext->IpAddress) &&
|
||
(pFileObjectsContext = pDgramTracker->pDeviceContext->pFileObjects))
|
||
{
|
||
switch (Service)
|
||
{
|
||
case (NBT_NAME_SERVICE):
|
||
TransportFileObject = pDgramTracker->pDeviceContext->pFileObjects->pNameServerFileObject;
|
||
break;
|
||
|
||
case (NBT_DATAGRAM_SERVICE):
|
||
TransportFileObject = pDgramTracker->pDeviceContext->pFileObjects->pDgramFileObject;
|
||
break;
|
||
|
||
default:
|
||
;
|
||
}
|
||
|
||
//
|
||
// an address of 0 means do a broadcast. When '1C' internet group
|
||
// names are built either from the Lmhost file or from the network
|
||
// the broadcast address is inserted in the list as 0.
|
||
//
|
||
if (IpAddress == 0)
|
||
{
|
||
IpAddress = pDgramTracker->pDeviceContext->BroadcastAddress;
|
||
}
|
||
|
||
// when there is no WINS server set in the registry we set the WINS
|
||
// ip address to LOOP_BACK, so if it is set to that here, do not send
|
||
// the datagram. If There is no Ip Address then the Transport Handle
|
||
// will be null and we do not do the send in that case either.
|
||
//
|
||
if (IpAddress == LOOP_BACK)
|
||
{
|
||
TransportFileObject = NULL ;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Dereference the Device if the request is going to fail, or
|
||
// there is no completion routine!
|
||
//
|
||
if ((!TransportFileObject) || (!pCompletionRoutine))
|
||
{
|
||
NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_UDP_SEND, TRUE);
|
||
}
|
||
}
|
||
|
||
if (!TransportFileObject)
|
||
{
|
||
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
||
|
||
if (pCompletionRoutine)
|
||
{
|
||
(*(NBT_COMPLETION) pCompletionRoutine) (CompletionContext, STATUS_UNSUCCESSFUL, 0);
|
||
}
|
||
return(status);
|
||
}
|
||
|
||
pFileObjectsContext->RefCount++; // Dereferenced after the Send has completed
|
||
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
||
|
||
// the completion routine is setup to free the pDgramTracker memory block
|
||
TdiRequest.Handle.AddressHandle = (PVOID)TransportFileObject;
|
||
TdiRequest.RequestNotifyObject = pCompletionRoutine;
|
||
TdiRequest.RequestContext = (PVOID)CompletionContext;
|
||
|
||
// the send length is the client dgram length + the size of the dgram header
|
||
Length = pDgramTracker->SendBuffer.HdrLength + pDgramTracker->SendBuffer.Length;
|
||
|
||
// fill in the connection information
|
||
pSendInfo = pDgramTracker->pSendInfo;
|
||
pSendInfo->RemoteAddressLength = sizeof(TRANSPORT_ADDRESS) -1 + pNbtGlobConfig->SizeTransportAddress;
|
||
|
||
// fill in the remote address
|
||
pTransportAddr = (PTRANSPORT_ADDRESS)pSendInfo->RemoteAddress;
|
||
pTransportAddr->TAAddressCount = 1;
|
||
pTransportAddr->Address[0].AddressLength = pNbtGlobConfig->SizeTransportAddress;
|
||
pTransportAddr->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
|
||
((PTDI_ADDRESS_IP)pTransportAddr->Address[0].Address)->sin_port = htons(Port);
|
||
((PTDI_ADDRESS_IP)pTransportAddr->Address[0].Address)->in_addr = htonl(IpAddress);
|
||
|
||
status = TdiSendDatagram (&TdiRequest, pSendInfo, Length, &uSentSize, pDgramTracker);
|
||
|
||
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
||
if (--pFileObjectsContext->RefCount == 0)
|
||
{
|
||
NTQueueToWorkerThread(
|
||
&pFileObjectsContext->WorkItemCleanUp,
|
||
DelayedNbtCloseFileHandles,
|
||
NULL,
|
||
pFileObjectsContext,
|
||
NULL,
|
||
NULL,
|
||
TRUE
|
||
);
|
||
}
|
||
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
||
|
||
return(status);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
NTSTATUS
|
||
TcpSessionStart(
|
||
IN tDGRAM_SEND_TRACKING *pTracker,
|
||
IN ULONG IpAddress,
|
||
IN tDEVICECONTEXT *pDeviceContext,
|
||
IN PVOID pCompletionRoutine,
|
||
IN ULONG Port
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets up a tcp connection by passing a connect through TDI to
|
||
TCP.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - success or not
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
TDI_REQUEST TdiRequest;
|
||
TDI_CONNECTION_INFORMATION *pSendInfo;
|
||
PTRANSPORT_ADDRESS pTransportAddr;
|
||
tCONNECTELE *pConnEle;
|
||
CTELockHandle OldIrq;
|
||
tLOWERCONNECTION *pLowerConn;
|
||
|
||
pSendInfo = pTracker->pSendInfo;
|
||
|
||
// we need to pass the file handle of the connection to TCP.
|
||
pConnEle = (tCONNECTELE *)pTracker->pConnEle;
|
||
|
||
CTESpinLock(pConnEle,OldIrq);
|
||
pLowerConn = pConnEle->pLowerConnId;
|
||
if (pLowerConn)
|
||
{
|
||
TdiRequest.Handle.AddressHandle = (PVOID)((tLOWERCONNECTION *)pConnEle->pLowerConnId)->pFileObject;
|
||
|
||
// the completion routine is setup to free the pTracker memory block
|
||
TdiRequest.RequestNotifyObject = pCompletionRoutine;
|
||
TdiRequest.RequestContext = (PVOID)pTracker;
|
||
|
||
// fill in the connection information
|
||
pSendInfo->RemoteAddressLength = sizeof(TRANSPORT_ADDRESS) -1 + pNbtGlobConfig->SizeTransportAddress;
|
||
|
||
pTransportAddr = (PTRANSPORT_ADDRESS)pSendInfo->RemoteAddress;
|
||
|
||
// fill in the remote address
|
||
pTransportAddr->TAAddressCount = 1;
|
||
pTransportAddr->Address[0].AddressLength = pNbtGlobConfig->SizeTransportAddress;
|
||
pTransportAddr->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
|
||
((PTDI_ADDRESS_IP)pTransportAddr->Address[0].Address)->sin_port = htons((USHORT)Port);
|
||
((PTDI_ADDRESS_IP)pTransportAddr->Address[0].Address)->in_addr = htonl(IpAddress);
|
||
|
||
CTESpinFree(pConnEle,OldIrq);
|
||
|
||
// pass through the TDI I/F on the bottom of NBT, to the transport
|
||
// pass in the original irp from the client so that the client can
|
||
// cancel it ok...rather than use one of NBT's irps
|
||
//
|
||
status = TdiConnect (&TdiRequest, (ULONG_PTR)pTracker->pTimeout, pSendInfo, pConnEle->pIrp);
|
||
}
|
||
else
|
||
{
|
||
CTESpinFree(pConnEle,OldIrq);
|
||
//
|
||
// Complete the request through the completion routine so it
|
||
// cleans up correctly
|
||
//
|
||
(*(NBT_COMPLETION)pCompletionRoutine)( (PVOID)pTracker, STATUS_CANCELLED, 0L );
|
||
status = STATUS_CANCELLED;
|
||
}
|
||
|
||
NbtTrace(NBT_TRACE_OUTBOUND, ("\tpTracker %p: %!status!", pTracker, status));
|
||
return(status);
|
||
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
NTSTATUS
|
||
TcpSendSessionResponse(
|
||
IN tLOWERCONNECTION *pLowerConn,
|
||
IN ULONG lStatusCode,
|
||
IN ULONG lSessionStatus
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sends a session PDU corresponding to the lStatusCode. This
|
||
could be a KeepAlive, PositiveSessionResponse, NegativeSessionResponse or
|
||
a Retarget (not implemented yet). For the Keep Alive case the completion
|
||
routine passed in is used rather than SessionRespDone, as is the case
|
||
for all other messages.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - success or not
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
tDGRAM_SEND_TRACKING *pTracker;
|
||
tSESSIONERROR *pSessionHdr;
|
||
|
||
pSessionHdr = (tSESSIONERROR *)NbtAllocMem(sizeof(tSESSIONERROR),NBT_TAG('Z'));
|
||
if (!pSessionHdr)
|
||
{
|
||
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
// get a tracker structure, which has a SendInfo structure in it
|
||
status = GetTracker(&pTracker, NBT_TRACKER_SEND_RESPONSE_SESSION);
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
pTracker->SendBuffer.pDgramHdr = (PVOID)pSessionHdr;
|
||
pTracker->SendBuffer.pBuffer = NULL;
|
||
pTracker->SendBuffer.Length = 0;
|
||
|
||
pSessionHdr->Flags = NBT_SESSION_FLAGS;
|
||
pSessionHdr->Type = (UCHAR)lStatusCode;
|
||
|
||
switch (lStatusCode)
|
||
{
|
||
case NBT_NEGATIVE_SESSION_RESPONSE:
|
||
pTracker->SendBuffer.HdrLength = sizeof(tSESSIONERROR);
|
||
// this length is one byte longer for the error code - different type used here
|
||
pSessionHdr->Length = htons(1); // one error code byte
|
||
pSessionHdr->ErrorCode = (UCHAR)lSessionStatus;
|
||
break;
|
||
|
||
case NBT_POSITIVE_SESSION_RESPONSE:
|
||
pTracker->SendBuffer.HdrLength = sizeof(tSESSIONHDR);
|
||
pSessionHdr->Length = 0; // no data following the length byte
|
||
break;
|
||
|
||
}
|
||
NbtTrace(NBT_TRACE_INBOUND, ("pTracker %p: Session Response %d", pTracker, lStatusCode));
|
||
|
||
status = TcpSendSession(pTracker,
|
||
pLowerConn,
|
||
SessionRespDone);
|
||
}
|
||
else
|
||
{
|
||
CTEMemFree((PVOID)pSessionHdr);
|
||
}
|
||
|
||
return(status);
|
||
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
NTSTATUS
|
||
TcpSendSession(
|
||
IN tDGRAM_SEND_TRACKING *pTracker,
|
||
IN tLOWERCONNECTION *pLowerConn,
|
||
IN PVOID pCompletionRoutine
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sends a message on a tcp connection.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - success or not
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
TDI_REQUEST TdiRequest;
|
||
ULONG lSentLength;
|
||
|
||
// we need to pass the file handle of the connection to TCP.
|
||
TdiRequest.Handle.AddressHandle = (PVOID)pLowerConn->pFileObject;
|
||
|
||
// the completion routine is setup to free the pTracker memory block
|
||
TdiRequest.RequestContext = (PVOID)pTracker;
|
||
|
||
// this completion routine just puts the tracker back on its list and
|
||
// frees the memory associated with the UserData buffer.
|
||
TdiRequest.RequestNotifyObject = pCompletionRoutine;
|
||
|
||
// pass through the TDI I/F on the bottom of NBT, to the transport
|
||
status = TdiSend(
|
||
&TdiRequest,
|
||
0, // no send flags
|
||
(ULONG)pTracker->SendBuffer.HdrLength +
|
||
(ULONG)pTracker->SendBuffer.Length ,
|
||
&lSentLength,
|
||
&pTracker->SendBuffer,
|
||
0); // no send flags set
|
||
|
||
NbtTrace(NBT_TRACE_OUTBOUND, ("\tpTracker %p: %!status!", pTracker, status));
|
||
|
||
return(status);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
VOID
|
||
SessionRespDone(
|
||
IN PVOID pContext,
|
||
IN NTSTATUS status,
|
||
IN ULONG lInfo)
|
||
/*++
|
||
Routine Description
|
||
|
||
This routine handles cleaning up various data blocks used in conjunction
|
||
sending a session response at session startup time. If the session
|
||
response was negative, then kill the connection.
|
||
|
||
Arguments:
|
||
|
||
pContext - ptr to the DGRAM_TRACKER block
|
||
NTSTATUS - completion status
|
||
|
||
Return Values:
|
||
|
||
VOID
|
||
|
||
--*/
|
||
|
||
{
|
||
tDGRAM_SEND_TRACKING *pTracker;
|
||
|
||
pTracker = (tDGRAM_SEND_TRACKING *)pContext;
|
||
|
||
FreeTracker(pTracker,FREE_HDR | RELINK_TRACKER);
|
||
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
NTSTATUS
|
||
SendTcpDisconnect(
|
||
IN tLOWERCONNECTION *pLowerConnId
|
||
)
|
||
/*++
|
||
Routine Description
|
||
|
||
This routine disconnects a TCP connection in a graceful manner which
|
||
insures that any data still in the pipe gets to the other side. Mostly
|
||
it calls TcpDisconnect which does the work. This routine just gets a
|
||
tracker for the send.
|
||
|
||
Arguments:
|
||
|
||
pLowerConnID - ptr to the lower connection that has the file object in it
|
||
|
||
Return Values:
|
||
NTSTATUS - completion status
|
||
|
||
VOID
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
tDGRAM_SEND_TRACKING *pTracker;
|
||
|
||
status = GetTracker(&pTracker, NBT_TRACKER_SEND_DISCONNECT);
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
pTracker->pConnEle = (PVOID)pLowerConnId;
|
||
|
||
status = TcpDisconnect(pTracker,NULL,TDI_DISCONNECT_RELEASE,FALSE);
|
||
}
|
||
return(status);
|
||
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
NTSTATUS
|
||
TcpDisconnect(
|
||
IN tDGRAM_SEND_TRACKING *pTracker,
|
||
IN PVOID Timeout,
|
||
IN ULONG Flags,
|
||
IN BOOLEAN Wait
|
||
)
|
||
/*++
|
||
Routine Description
|
||
|
||
This routine disconnects a TCP connection in a graceful manner which
|
||
insures that any data still in the pipe gets to the other side.
|
||
|
||
Arguments:
|
||
|
||
pTracker - ptr to the DGRAM_TRACKER block
|
||
|
||
Return Values:
|
||
NTSTATUS - completion status
|
||
|
||
VOID
|
||
|
||
--*/
|
||
|
||
{
|
||
TDI_REQUEST TdiRequest;
|
||
NTSTATUS status;
|
||
|
||
// we need to pass the file handle of the connection to TCP.
|
||
TdiRequest.Handle.AddressHandle =
|
||
(PVOID)((tLOWERCONNECTION *)pTracker->pConnEle)->pFileObject;
|
||
|
||
// the completion routine is setup to free the pTracker memory block
|
||
TdiRequest.RequestContext = (PVOID)pTracker;
|
||
|
||
// this completion routine just puts the tracker back on its list and
|
||
// frees the memory associated with the UserData buffer.
|
||
TdiRequest.RequestNotifyObject = DisconnectDone;
|
||
pTracker->Flags = (USHORT)Flags;
|
||
|
||
status = TdiDisconnect(&TdiRequest,
|
||
Timeout,
|
||
Flags,
|
||
pTracker->pSendInfo,
|
||
((tLOWERCONNECTION *)pTracker->pConnEle)->pIrp,
|
||
Wait);
|
||
|
||
NbtTrace(NBT_TRACE_DISCONNECT, ("pTracker %p: %!status!", pTracker, status));
|
||
|
||
return(status);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
VOID
|
||
DisconnectDone(
|
||
IN PVOID pContext,
|
||
IN NTSTATUS status,
|
||
IN ULONG lInfo)
|
||
/*++
|
||
Routine Description
|
||
|
||
This routine handles cleaning up after a disconnect is sent to the transport.
|
||
|
||
Arguments:
|
||
|
||
pContext - ptr to the DGRAM_TRACKER block
|
||
|
||
Return Values:
|
||
|
||
VOID
|
||
|
||
--*/
|
||
|
||
{
|
||
tDGRAM_SEND_TRACKING *pTracker;
|
||
tLOWERCONNECTION *pLowerConn;
|
||
CTELockHandle OldIrq;
|
||
PCTE_IRP pIrp;
|
||
BOOLEAN CleanupLower = FALSE;
|
||
NTSTATUS DiscWaitStatus;
|
||
tCONNECTELE *pConnEle;
|
||
PCTE_IRP pIrpClose;
|
||
tDEVICECONTEXT *pDeviceContext = NULL;
|
||
|
||
pTracker = (tDGRAM_SEND_TRACKING *)pContext;
|
||
pLowerConn = (tLOWERCONNECTION *)pTracker->pConnEle;
|
||
|
||
NbtTrace(NBT_TRACE_DISCONNECT, ("pTracker %p: pLowerConn %p pConnEle %p %!status!",
|
||
pTracker, pLowerConn, pLowerConn->pUpperConnection, status));
|
||
|
||
CTESpinLock(&NbtConfig.JointLock,OldIrq);
|
||
CTESpinLockAtDpc(pLowerConn);
|
||
|
||
ASSERT (NBT_VERIFY_HANDLE (pLowerConn, NBT_VERIFY_LOWERCONN));
|
||
|
||
IF_DBG(NBT_DEBUG_DISCONNECT)
|
||
KdPrint(("Nbt.DisconnectDone: Disconnect Irp has been returned...pLowerConn %X,state %X\n",
|
||
pLowerConn,pLowerConn->State));
|
||
//
|
||
// if the state is disconnected, then a disconnect indication
|
||
// has come from the transport.. . if still disconnecting,
|
||
// then we have not had a disconnect indication yet, so
|
||
// wait for the indication to go through DisconnectHndlrNotOs which
|
||
// will do the cleanup.
|
||
//
|
||
|
||
// Streams TCP always indicates before completing the disconnect request,
|
||
// so we always cleanup here for the Streams stack.
|
||
//
|
||
//
|
||
// If the disconnect was abortive, then there will not be a disconnect
|
||
// indication, so do the cleanup now.
|
||
//
|
||
if ((!StreamsStack) &&
|
||
(NT_SUCCESS (status)) &&
|
||
(pTracker->Flags == TDI_DISCONNECT_RELEASE) &&
|
||
(pLowerConn->State == NBT_DISCONNECTING))
|
||
{
|
||
SET_STATE_LOWER (pLowerConn, NBT_DISCONNECTED);
|
||
}
|
||
else if (pLowerConn->State != NBT_IDLE)
|
||
{
|
||
//
|
||
// change the state to idle so that the Disconnect handler will
|
||
// not attempt to do anything with it if for some reason the transport
|
||
// indicates a disconnect after this point.
|
||
//
|
||
ASSERT((pLowerConn->State == NBT_DISCONNECTED) || (pLowerConn->State == NBT_DISCONNECTING));
|
||
SET_STATE_LOWER (pLowerConn, NBT_IDLE);
|
||
|
||
CleanupLower = TRUE;
|
||
}
|
||
|
||
//
|
||
// there may be a disconnect wait irp, so return that first if there
|
||
// is one waiting around.
|
||
//
|
||
pConnEle = pLowerConn->pUpperConnection;
|
||
if (pConnEle && pConnEle->pIrpClose)
|
||
{
|
||
pIrpClose = pConnEle->pIrpClose;
|
||
CHECK_PTR(pConnEle);
|
||
pConnEle->pIrpClose = NULL ;
|
||
if (pConnEle->DiscFlag == TDI_DISCONNECT_ABORT)
|
||
{
|
||
DiscWaitStatus = STATUS_CONNECTION_RESET;
|
||
}
|
||
else
|
||
{
|
||
DiscWaitStatus = STATUS_GRACEFUL_DISCONNECT;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
pIrpClose = NULL;
|
||
}
|
||
|
||
//
|
||
// This is the disconnect requesting Irp
|
||
//
|
||
pLowerConn->bDisconnectIrpPendingInTCP = FALSE;
|
||
if (pLowerConn->pIrp)
|
||
{
|
||
pIrp = pLowerConn->pIrp;
|
||
pLowerConn->pIrp = NULL ;
|
||
}
|
||
else
|
||
{
|
||
pIrp = NULL;
|
||
}
|
||
|
||
CTESpinFreeAtDpc(pLowerConn);
|
||
|
||
if (CleanupLower)
|
||
{
|
||
ASSERT(pLowerConn->RefCount > 1);
|
||
|
||
if (NBT_VERIFY_HANDLE (pLowerConn->pDeviceContext, NBT_VERIFY_DEVCONTEXT))
|
||
{
|
||
pDeviceContext = pLowerConn->pDeviceContext;
|
||
}
|
||
|
||
// this either puts the lower connection back on its free
|
||
// queue if inbound, or closes the connection with the transport
|
||
// if out bound. (it can't be done at dispatch level).
|
||
//
|
||
status = NTQueueToWorkerThread(
|
||
&pLowerConn->WorkItemCleanUpAndWipeOut,
|
||
DelayedCleanupAfterDisconnect,
|
||
NULL,
|
||
pLowerConn,
|
||
NULL,
|
||
pDeviceContext,
|
||
TRUE
|
||
);
|
||
}
|
||
|
||
CTESpinFree(&NbtConfig.JointLock,OldIrq);
|
||
|
||
FreeTracker(pTracker,RELINK_TRACKER);
|
||
|
||
if (pIrpClose)
|
||
{
|
||
CTEIoComplete( pIrpClose, DiscWaitStatus, 0 ) ;
|
||
}
|
||
|
||
if (pIrp)
|
||
{
|
||
CTEIoComplete( pIrp, status, 0 ) ;
|
||
}
|
||
}
|