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.
1778 lines
56 KiB
1778 lines
56 KiB
/*++
|
|
|
|
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 ) ;
|
|
}
|
|
}
|