summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/tcpip/tcp/info.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/tdi/tcpip/tcp/info.c917
1 files changed, 917 insertions, 0 deletions
diff --git a/private/ntos/tdi/tcpip/tcp/info.c b/private/ntos/tdi/tcpip/tcp/info.c
new file mode 100644
index 000000000..99fff25bf
--- /dev/null
+++ b/private/ntos/tdi/tcpip/tcp/info.c
@@ -0,0 +1,917 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1993 **/
+/********************************************************************/
+/* :ts=4 */
+
+//** INFO.C - TDI Query/SetInformation routines.
+//
+// This file contains the code for dealing with TDI Query/Set information
+// calls.
+//
+
+#include "oscfg.h"
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "tdi.h"
+#ifdef VXD
+#include "tdivxd.h"
+#include "tdistat.h"
+#endif
+#ifdef NT
+#include "tdint.h"
+#include "tdistat.h"
+#endif
+#include "queue.h"
+#include "addr.h"
+#include "tcp.h"
+#include "tcb.h"
+#include "tcpconn.h"
+#include "tlcommon.h"
+#include "info.h"
+#include "tdiinfo.h"
+#include "tcpcfg.h"
+#include "udp.h"
+#include "tcpsend.h"
+
+#ifndef UDP_ONLY
+#define MY_SERVICE_FLAGS (TDI_SERVICE_CONNECTION_MODE | \
+ TDI_SERVICE_ORDERLY_RELEASE | \
+ TDI_SERVICE_CONNECTIONLESS_MODE | \
+ TDI_SERVICE_ERROR_FREE_DELIVERY | \
+ TDI_SERVICE_BROADCAST_SUPPORTED | \
+ TDI_SERVICE_DELAYED_ACCEPTANCE | \
+ TDI_SERVICE_EXPEDITED_DATA | \
+ TDI_SERVICE_NO_ZERO_LENGTH)
+#else
+#define MY_SERVICE_FLAGS (TDI_SERVICE_CONNECTIONLESS_MODE | \
+ TDI_SERVICE_BROADCAST_SUPPORTED)
+#endif
+
+extern uint StartTime;
+EXTERNAL_LOCK(AddrObjTableLock)
+
+#ifndef UDP_ONLY
+TCPStats TStats;
+#endif
+
+UDPStats UStats;
+
+struct ReadTableStruct {
+ uint (*rts_validate)(void *Context, uint *Valid);
+ uint (*rts_readnext)(void *Context, void *OutBuf);
+};
+
+struct ReadTableStruct ReadAOTable =
+ {ValidateAOContext, ReadNextAO};
+
+#ifndef UDP_ONLY
+
+struct ReadTableStruct ReadTCBTable =
+ {ValidateTCBContext, ReadNextTCB};
+
+EXTERNAL_LOCK(TCBTableLock)
+#endif
+
+EXTERNAL_LOCK(AddrObjTableLock)
+
+extern IPInfo LocalNetInfo;
+
+struct TDIEntityID *EntityList;
+uint EntityCount;
+
+//* TdiQueryInformation - Query Information handler.
+//
+// The TDI QueryInformation routine. Called when the client wants to
+// query information on a connection, the provider as a whole, or to
+// get statistics.
+//
+// Input: Request - The request structure for this command.
+// QueryType - The type of query to be performed.
+// Buffer - Buffer to place data into.
+// BufferSize - Pointer to size in bytes of buffer. On return,
+// filled in with bytes copied.
+// IsConn - Valid only for TDI_QUERY_ADDRESS_INFO. TRUE
+// if we are querying the address info on
+// a connection.
+//
+// Returns: Status of attempt to query information.
+//
+TDI_STATUS
+TdiQueryInformation(PTDI_REQUEST Request, uint QueryType, PNDIS_BUFFER Buffer,
+ uint *BufferSize, uint IsConn)
+{
+ union {
+ TDI_CONNECTION_INFO ConnInfo;
+ TDI_ADDRESS_INFO AddrInfo;
+ TDI_PROVIDER_INFO ProviderInfo;
+ TDI_PROVIDER_STATISTICS ProviderStats;
+ } InfoBuf;
+
+ uint InfoSize;
+ CTELockHandle ConnTableHandle, TCBHandle, AddrHandle, AOHandle;
+#ifndef UDP_ONLY
+ TCPConn *Conn;
+ TCB *InfoTCB;
+#endif
+ AddrObj *InfoAO;
+ void *InfoPtr = NULL;
+ uint Offset;
+ uint Size;
+ uint BytesCopied;
+
+ switch (QueryType) {
+
+ case TDI_QUERY_BROADCAST_ADDRESS:
+ return TDI_INVALID_QUERY;
+ break;
+
+ case TDI_QUERY_PROVIDER_INFO:
+ InfoBuf.ProviderInfo.Version = 0x100;
+#ifndef UDP_ONLY
+ InfoBuf.ProviderInfo.MaxSendSize = 0xffffffff;
+#else
+ InfoBuf.ProviderInfo.MaxSendSize = 0;
+#endif
+ InfoBuf.ProviderInfo.MaxConnectionUserData = 0;
+ InfoBuf.ProviderInfo.MaxDatagramSize = 0xffff - sizeof(UDPHeader);
+ InfoBuf.ProviderInfo.ServiceFlags = MY_SERVICE_FLAGS;
+ InfoBuf.ProviderInfo.MinimumLookaheadData = 1;
+ InfoBuf.ProviderInfo.MaximumLookaheadData = 0xffff;
+ InfoBuf.ProviderInfo.NumberOfResources = 0;
+ InfoBuf.ProviderInfo.StartTime.LowPart = StartTime;
+ InfoBuf.ProviderInfo.StartTime.HighPart = 0;
+ InfoSize = sizeof(TDI_PROVIDER_INFO);
+ InfoPtr = &InfoBuf.ProviderInfo;
+ break;
+
+ case TDI_QUERY_ADDRESS_INFO:
+ InfoSize = sizeof(TDI_ADDRESS_INFO) - sizeof(TRANSPORT_ADDRESS) +
+ TCP_TA_SIZE;
+ CTEMemSet(&InfoBuf.AddrInfo, 0, TCP_TA_SIZE);
+ InfoBuf.AddrInfo.ActivityCount = 1; // Since noone knows what
+ // this means, we'll set
+ // it to one.
+
+ if (IsConn) {
+#ifdef UDP_ONLY
+ return TDI_INVALID_QUERY;
+#else
+
+ CTEGetLock(&AddrObjTableLock, &AddrHandle);
+ CTEGetLock(&ConnTableLock, &ConnTableHandle);
+ Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext);
+
+ if (Conn != NULL) {
+ CTEStructAssert(Conn, tc);
+
+ InfoTCB = Conn->tc_tcb;
+ // If we have a TCB we'll
+ // return information about that TCB. Otherwise we'll return
+ // info about the address object.
+ if (InfoTCB != NULL) {
+ CTEStructAssert(InfoTCB, tcb);
+ CTEGetLock(&InfoTCB->tcb_lock, &TCBHandle);
+ CTEFreeLock(&ConnTableLock, TCBHandle);
+ CTEFreeLock(&AddrObjTableLock, ConnTableHandle);
+ BuildTDIAddress((uchar *)&InfoBuf.AddrInfo.Address,
+ InfoTCB->tcb_saddr, InfoTCB->tcb_sport);
+ CTEFreeLock(&InfoTCB->tcb_lock, AddrHandle);
+ InfoPtr = &InfoBuf.AddrInfo;
+ break;
+ } else {
+ // No TCB, return info on the AddrObj.
+ InfoAO = Conn->tc_ao;
+ if (InfoAO != NULL) {
+ // We have an AddrObj.
+ CTEStructAssert(InfoAO, ao);
+ CTEGetLock(&InfoAO->ao_lock, &AOHandle);
+ BuildTDIAddress((uchar *)&InfoBuf.AddrInfo.Address,
+ InfoAO->ao_addr, InfoAO->ao_port);
+ CTEFreeLock(&InfoAO->ao_lock, AOHandle);
+ CTEFreeLock(&ConnTableLock, ConnTableHandle);
+ CTEFreeLock(&AddrObjTableLock, AddrHandle);
+ InfoPtr = &InfoBuf.AddrInfo;
+ break;
+ }
+ }
+
+ }
+
+ // Fall through to here when we can't find the connection, or
+ // the connection isn't associated.
+ CTEFreeLock(&ConnTableLock, ConnTableHandle);
+ CTEFreeLock(&AddrObjTableLock, AddrHandle);
+ return TDI_INVALID_CONNECTION;
+ break;
+
+#endif
+ } else {
+ // Asking for information on an addr. object.
+#ifdef VXD
+ InfoAO = GetIndexedAO((uint)Request->Handle.AddressHandle);
+
+ if (InfoAO == NULL)
+ return TDI_ADDR_INVALID;
+#else
+ InfoAO = Request->Handle.AddressHandle;
+#endif
+
+ CTEStructAssert(InfoAO, ao);
+
+ CTEGetLock(&InfoAO->ao_lock, &AOHandle);
+
+ if (!AO_VALID(InfoAO)) {
+ CTEFreeLock(&InfoAO->ao_lock, AOHandle);
+ return TDI_ADDR_INVALID;
+ }
+
+ BuildTDIAddress((uchar *)&InfoBuf.AddrInfo.Address,
+ InfoAO->ao_addr, InfoAO->ao_port);
+ CTEFreeLock(&InfoAO->ao_lock, AOHandle);
+ InfoPtr = &InfoBuf.AddrInfo;
+ break;
+ }
+
+ break;
+
+ case TDI_QUERY_CONNECTION_INFO:
+#ifndef UDP_ONLY
+ InfoSize = sizeof(TDI_CONNECTION_INFO);
+ CTEGetLock(&ConnTableLock, &ConnTableHandle);
+ Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext);
+
+ if (Conn != NULL) {
+ CTEStructAssert(Conn, tc);
+
+ InfoTCB = Conn->tc_tcb;
+ // If we have a TCB we'll return the information. Otherwise
+ // we'll error out.
+ if (InfoTCB != NULL) {
+
+ ulong TotalTime;
+ ulong BPS, PathBPS;
+ IP_STATUS IPStatus;
+ CTEULargeInt TempULargeInt;
+
+ CTEStructAssert(InfoTCB, tcb);
+ CTEGetLock(&InfoTCB->tcb_lock, &TCBHandle);
+ CTEFreeLock(&ConnTableLock, TCBHandle);
+ CTEMemSet(&InfoBuf.ConnInfo, 0, sizeof(TDI_CONNECTION_INFO));
+ InfoBuf.ConnInfo.State = (ulong)InfoTCB->tcb_state;
+ IPStatus = (*LocalNetInfo.ipi_getpinfo)(InfoTCB->tcb_daddr,
+ InfoTCB->tcb_saddr, NULL, &PathBPS);
+
+ if (IPStatus != IP_SUCCESS) {
+ InfoBuf.ConnInfo.Throughput.LowPart = 0xFFFFFFFF;
+ InfoBuf.ConnInfo.Throughput.HighPart = 0xFFFFFFFF;
+ } else {
+ InfoBuf.ConnInfo.Throughput.HighPart = 0;
+ TotalTime = InfoTCB->tcb_totaltime /
+ (1000 / MS_PER_TICK);
+ if (TotalTime != 0) {
+ TempULargeInt.LowPart = InfoTCB->tcb_bcountlow;
+ TempULargeInt.HighPart = InfoTCB->tcb_bcounthi;
+
+ BPS = CTEEnlargedUnsignedDivide(TempULargeInt,
+ TotalTime, NULL);
+ InfoBuf.ConnInfo.Throughput.LowPart =
+ MIN(BPS, PathBPS);
+ } else
+ InfoBuf.ConnInfo.Throughput.LowPart = PathBPS;
+ }
+
+
+
+ // To figure the delay we use the rexmit timeout. Our
+ // rexmit timeout is roughly the round trip time plus
+ // some slop, so we use half of that as the one way delay.
+#ifdef VXD
+ InfoBuf.ConnInfo.Delay.LowPart =
+ (REXMIT_TO(InfoTCB) * MS_PER_TICK) / 2;
+ InfoBuf.ConnInfo.Throughput.HighPart = 0;
+#else // VXD
+ InfoBuf.ConnInfo.Delay.LowPart =
+ (REXMIT_TO(InfoTCB) * MS_PER_TICK) / 2;
+ InfoBuf.ConnInfo.Delay.HighPart = 0;
+ //
+ // Convert milliseconds to 100ns and negate for relative
+ // time.
+ //
+ InfoBuf.ConnInfo.Delay =
+ RtlExtendedIntegerMultiply(
+ InfoBuf.ConnInfo.Delay,
+ 10000
+ );
+
+ CTEAssert(InfoBuf.ConnInfo.Delay.HighPart == 0);
+
+ InfoBuf.ConnInfo.Delay.QuadPart =
+ -InfoBuf.ConnInfo.Delay.QuadPart;
+
+#endif // VXD
+ CTEFreeLock(&InfoTCB->tcb_lock, ConnTableHandle);
+ InfoPtr = &InfoBuf.ConnInfo;
+ break;
+ }
+
+ }
+
+ // Come through here if we can't find the connection or it has
+ // no TCB.
+ CTEFreeLock(&ConnTableLock, ConnTableHandle);
+ return TDI_INVALID_CONNECTION;
+ break;
+
+#else // UDP_ONLY
+ return TDI_INVALID_QUERY;
+ break;
+#endif // UDP_ONLY
+ case TDI_QUERY_PROVIDER_STATISTICS:
+ CTEMemSet(&InfoBuf.ProviderStats, 0, sizeof(TDI_PROVIDER_STATISTICS));
+ InfoBuf.ProviderStats.Version = 0x100;
+ InfoSize = sizeof(TDI_PROVIDER_STATISTICS);
+ InfoPtr = &InfoBuf.ProviderStats;
+ break;
+ default:
+ return TDI_INVALID_QUERY;
+ break;
+ }
+
+ // When we get here, we've got the pointers set up and the information
+ // filled in.
+
+ CTEAssert(InfoPtr != NULL);
+ Offset = 0;
+ Size = *BufferSize;
+ (void)CopyFlatToNdis(Buffer, InfoPtr, MIN(InfoSize, Size), &Offset,
+ &BytesCopied);
+ if (Size < InfoSize)
+ return TDI_BUFFER_OVERFLOW;
+ else {
+ *BufferSize = InfoSize;
+ return TDI_SUCCESS;
+ }
+}
+
+//* TdiSetInformation - Set Information handler.
+//
+// The TDI SetInformation routine. Currently we don't allow anything to be
+// set.
+//
+// Input: Request - The request structure for this command.
+// SetType - The type of set to be performed.
+// Buffer - Buffer to set from.
+// BufferSize - Size in bytes of buffer.
+// IsConn - Valid only for TDI_QUERY_ADDRESS_INFO. TRUE
+// if we are setting the address info on
+// a connection.
+//
+// Returns: Status of attempt to set information.
+//
+TDI_STATUS
+TdiSetInformation(PTDI_REQUEST Request, uint SetType, PNDIS_BUFFER Buffer,
+ uint BufferSize, uint IsConn)
+{
+ return TDI_INVALID_REQUEST;
+}
+
+//* TdiAction - Action handler.
+//
+// The TDI Action routine. Currently we don't support any actions.
+//
+// Input: Request - The request structure for this command.
+// ActionType - The type of action to be performed.
+// Buffer - Buffer of action info.
+// BufferSize - Size in bytes of buffer.
+//
+// Returns: Status of attempt to perform action.
+//
+TDI_STATUS
+TdiAction(PTDI_REQUEST Request, uint ActionType, PNDIS_BUFFER Buffer,
+ uint BufferSize)
+{
+ return TDI_INVALID_REQUEST;
+}
+
+//* TdiQueryInfoEx - Extended TDI query information.
+//
+// This is the new TDI query information handler. We take in a TDIObjectID
+// structure, a buffer and length, and some context information, and return
+// the requested information if possible.
+//
+// Input: Request - The request structure for this command.
+// ID - The object ID
+// Buffer - Pointer to buffer to be filled in.
+// Size - Pointer to size in bytes of Buffer. On exit,
+// filled in with bytes written.
+// Context - Pointer to context buffer.
+//
+// Returns: Status of attempt to get information.
+//
+TDI_STATUS
+TdiQueryInformationEx(PTDI_REQUEST Request, TDIObjectID *ID,
+ PNDIS_BUFFER Buffer, uint *Size, void *Context)
+{
+ uint BufferSize = *Size;
+ uint InfoSize;
+ void *InfoPtr;
+ uint Fixed;
+ CTELockHandle Handle;
+#ifndef VXD
+ CTELock *LockPtr;
+#else
+#ifdef DEBUG
+ CTELock *LockPtr;
+#endif
+#endif
+ uint Offset = 0;
+ uchar InfoBuffer[sizeof(TCPConnTableEntry)];
+ uint BytesRead;
+ uint Valid;
+ uint Entity;
+ uint BytesCopied;
+
+ // First check to see if he's querying for list of entities.
+ Entity = ID->toi_entity.tei_entity;
+ if (Entity == GENERIC_ENTITY) {
+ *Size = 0;
+
+ if (ID->toi_class != INFO_CLASS_GENERIC ||
+ ID->toi_type != INFO_TYPE_PROVIDER ||
+ ID->toi_id != ENTITY_LIST_ID) {
+ return TDI_INVALID_PARAMETER;
+ }
+
+ // Make sure we have room for it the list in the buffer.
+ InfoSize = EntityCount * sizeof(TDIEntityID);
+
+ if (BufferSize < InfoSize) {
+ // Not enough room.
+ return TDI_BUFFER_TOO_SMALL;
+ }
+
+ *Size = InfoSize;
+
+ // Copy it in, free our temp. buffer, and return success.
+ (void)CopyFlatToNdis(Buffer, (uchar *)EntityList, InfoSize, &Offset,
+ &BytesCopied);
+ return TDI_SUCCESS;
+ }
+
+
+ //* Check the level. If it can't be for us, pass it down.
+#ifndef UDP_ONLY
+ if (Entity != CO_TL_ENTITY && Entity != CL_TL_ENTITY) {
+#else
+ if (Entity != CL_TL_ENTITY) {
+#endif
+
+ // When we support multiple lower entities at this layer we'll have
+ // to figure out which one to dispatch to. For now, just pass it
+ // straight down.
+ return (*LocalNetInfo.ipi_qinfo)(ID, Buffer, Size, Context);
+ }
+
+ if (ID->toi_entity.tei_instance != TL_INSTANCE) {
+ // We only support a single instance.
+ return TDI_INVALID_REQUEST;
+ }
+
+ // Zero returned parameters in case of an error below.
+ *Size = 0;
+
+ if (ID->toi_class == INFO_CLASS_GENERIC) {
+ // This is a generic request.
+ if (ID->toi_type == INFO_TYPE_PROVIDER && ID->toi_id == ENTITY_TYPE_ID) {
+ if (BufferSize >= sizeof(uint)) {
+ *(uint *)&InfoBuffer[0] = (Entity == CO_TL_ENTITY) ? CO_TL_TCP
+ : CL_TL_UDP;
+ (void)CopyFlatToNdis(Buffer, InfoBuffer, sizeof(uint), &Offset,
+ &BytesCopied);
+ return TDI_SUCCESS;
+ } else
+ return TDI_BUFFER_TOO_SMALL;
+ }
+ return TDI_INVALID_PARAMETER;
+ }
+
+ if (ID->toi_class == INFO_CLASS_PROTOCOL) {
+ // Handle protocol specific class of information. For us, this is
+ // the MIB-2 stuff or the minimal stuff we do for oob_inline support.
+
+#ifndef UDP_ONLY
+ if (ID->toi_type == INFO_TYPE_CONNECTION) {
+ TCPConn *Conn;
+ TCB *QueryTCB;
+ TCPSocketAMInfo *AMInfo;
+ CTELockHandle TCBHandle;
+
+ if (BufferSize < sizeof(TCPSocketAMInfo) ||
+ ID->toi_id != TCP_SOCKET_ATMARK)
+ return TDI_INVALID_PARAMETER;
+
+ AMInfo = (TCPSocketAMInfo *)InfoBuffer;
+ CTEGetLock(&ConnTableLock, &Handle);
+
+ Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext);
+
+ if (Conn != NULL) {
+ CTEStructAssert(Conn, tc);
+
+ QueryTCB = Conn->tc_tcb;
+ if (QueryTCB != NULL) {
+ CTEStructAssert(QueryTCB, tcb);
+ CTEGetLock(&QueryTCB->tcb_lock, &TCBHandle);
+ if ((QueryTCB->tcb_flags & (URG_INLINE | URG_VALID)) ==
+ (URG_INLINE | URG_VALID)) {
+ // We're in inline mode, and the urgent data fields are
+ // valid.
+ AMInfo->tsa_size = QueryTCB->tcb_urgend -
+ QueryTCB->tcb_urgstart + 1;
+ // Rcvnext - pendingcnt is the sequence number of the
+ // next byte of data that will be delivered to the
+ // client. Urgend - that value is the offset in the
+ // data stream of the end of urgent data.
+ AMInfo->tsa_offset = QueryTCB->tcb_urgend -
+ (QueryTCB->tcb_rcvnext - QueryTCB->tcb_pendingcnt);
+ } else {
+ AMInfo->tsa_size = 0;
+ AMInfo->tsa_offset = 0;
+ }
+ CTEFreeLock(&QueryTCB->tcb_lock, TCBHandle);
+ *Size = sizeof(TCPSocketAMInfo);
+ CopyFlatToNdis(Buffer, InfoBuffer, sizeof(TCPSocketAMInfo),
+ &Offset, &BytesCopied);
+ return TDI_SUCCESS;
+ }
+ }
+ return TDI_INVALID_PARAMETER;
+
+ }
+
+#endif
+ if (ID->toi_type != INFO_TYPE_PROVIDER)
+ return TDI_INVALID_PARAMETER;
+
+ switch (ID->toi_id) {
+
+ case UDP_MIB_STAT_ID:
+#if UDP_MIB_STAT_ID != TCP_MIB_STAT_ID
+ case TCP_MIB_STAT_ID:
+#endif
+ Fixed = TRUE;
+ if (Entity == CL_TL_ENTITY) {
+ InfoSize = sizeof(UDPStats);
+ InfoPtr = &UStats;
+ } else {
+#ifndef UDP_ONLY
+ InfoSize = sizeof(TCPStats);
+ InfoPtr = &TStats;
+#else
+ return TDI_INVALID_PARAMETER;
+#endif
+ }
+ break;
+ case UDP_MIB_TABLE_ID:
+#if UDP_MIB_TABLE_ID != TCP_MIB_TABLE_ID
+ case TCP_MIB_TABLE_ID:
+#endif
+ Fixed = FALSE;
+ if (Entity == CL_TL_ENTITY) {
+ InfoSize = sizeof(UDPEntry);
+ InfoPtr = &ReadAOTable;
+ CTEGetLock(&AddrObjTableLock, &Handle);
+#ifndef VXD
+ LockPtr = &AddrObjTableLock;
+#else
+#ifdef DEBUG
+ LockPtr = &AddrObjTableLock;
+#endif
+#endif
+ } else {
+#ifndef UDP_ONLY
+ InfoSize = sizeof(TCPConnTableEntry);
+ InfoPtr = &ReadTCBTable;
+ CTEGetLock(&TCBTableLock, &Handle);
+#ifndef VXD
+ LockPtr = &TCBTableLock;
+#else
+#ifdef DEBUG
+ LockPtr = &TCBTableLock;
+#endif
+#endif
+
+#else
+ return TDI_INVALID_PARAMETER;
+#endif
+ }
+ break;
+ default:
+ return TDI_INVALID_PARAMETER;
+ break;
+ }
+
+ if (Fixed) {
+ if (BufferSize < InfoSize)
+ return TDI_BUFFER_TOO_SMALL;
+
+ *Size = InfoSize;
+
+ (void)CopyFlatToNdis(Buffer, InfoPtr, InfoSize, &Offset,
+ &BytesCopied);
+ return TDI_SUCCESS;
+ } else {
+ struct ReadTableStruct *RTSPtr;
+ uint ReadStatus;
+
+ // Have a variable length (or mult-instance) structure to copy.
+ // InfoPtr points to the structure describing the routines to
+ // call to read the table.
+ // Loop through up to CountWanted times, calling the routine
+ // each time.
+ BytesRead = 0;
+
+ RTSPtr = InfoPtr;
+
+ ReadStatus = (*(RTSPtr->rts_validate))(Context, &Valid);
+
+ // If we successfully read something we'll continue. Otherwise
+ // we'll bail out.
+ if (!Valid) {
+ CTEFreeLock(LockPtr, Handle);
+ return TDI_INVALID_PARAMETER;
+ }
+
+ while (ReadStatus) {
+ // The invariant here is that there is data in the table to
+ // read. We may or may not have room for it. So ReadStatus
+ // is TRUE, and BufferSize - BytesRead is the room left
+ // in the buffer.
+ if ((int)(BufferSize - BytesRead) >= (int)InfoSize) {
+ ReadStatus = (*(RTSPtr->rts_readnext))(Context,
+ InfoBuffer);
+ BytesRead += InfoSize;
+ Buffer = CopyFlatToNdis(Buffer, InfoBuffer, InfoSize,
+ &Offset, &BytesCopied);
+ } else
+ break;
+
+ }
+
+ *Size = BytesRead;
+ CTEFreeLock(LockPtr, Handle);
+ return (!ReadStatus ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW);
+ }
+
+ }
+
+ if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) {
+ // We want to return implementation specific info. For now, error out.
+ return TDI_INVALID_PARAMETER;
+ }
+
+ return TDI_INVALID_PARAMETER;
+
+}
+
+//* TdiSetInfoEx - Extended TDI set information.
+//
+// This is the new TDI set information handler. We take in a TDIObjectID
+// structure, a buffer and length. We set the object specifed by the ID
+// (and possibly by the Request) to the value specified in the buffer.
+//
+// Input: Request - The request structure for this command.
+// ID - The object ID
+// Buffer - Pointer to buffer containing value to set.
+// Size - Size in bytes of Buffer.
+//
+// Returns: Status of attempt to get information.
+//
+TDI_STATUS
+TdiSetInformationEx(PTDI_REQUEST Request, TDIObjectID *ID, void *Buffer,
+ uint Size)
+{
+ TCPConnTableEntry *TCPEntry;
+ CTELockHandle TableHandle, TCBHandle;
+ TCB *SetTCB;
+ uint Entity;
+ TCPConn *Conn;
+ TDI_STATUS Status;
+
+
+ //* Check the level. If it can't be for us, pass it down.
+ Entity = ID->toi_entity.tei_entity;
+
+ if (Entity != CO_TL_ENTITY && Entity != CL_TL_ENTITY) {
+ // Someday we'll have to figure out how to dispatch. For now, just pass
+ // it down.
+ return (*LocalNetInfo.ipi_setinfo)(ID, Buffer, Size);
+ }
+
+ if (ID->toi_entity.tei_instance != TL_INSTANCE)
+ return TDI_INVALID_REQUEST;
+
+ if (ID->toi_class == INFO_CLASS_GENERIC) {
+ // Fill this in when we have generic class defines.
+ return TDI_INVALID_PARAMETER;
+ }
+
+ //* Now look at the rest of it.
+ if (ID->toi_class == INFO_CLASS_PROTOCOL) {
+ // Handle protocol specific class of information. For us, this is
+ // the MIB-2 stuff, as well as common sockets options,
+ // and in particular the setting of the state of a TCP connection.
+
+ if (ID->toi_type == INFO_TYPE_CONNECTION) {
+ TCPSocketOption *Option;
+ uint Flag;
+ uint Value;
+
+#ifndef UDP_ONLY
+ // A connection type. Get the connection, and then figure out
+ // what to do with it.
+ Status = TDI_INVALID_PARAMETER;
+
+ if (Size < sizeof(TCPSocketOption))
+ return Status;
+
+ CTEGetLock(&ConnTableLock, &TableHandle);
+
+ Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext);
+
+ if (Conn != NULL) {
+ CTEStructAssert(Conn, tc);
+
+ Status = TDI_SUCCESS;
+
+ if (ID->toi_id == TCP_SOCKET_WINDOW) {
+ // This is a funny option, because it doesn't involve
+ // flags. Handle this specially.
+ Option = (TCPSocketOption *)Buffer;
+
+ // We don't allow anyone to shrink the window, as this
+ // gets too weird from a protocol point of view. Also,
+ // make sure they don't try and set anything too big.
+ if (Option->tso_value > 0xffff)
+ Status = TDI_INVALID_PARAMETER;
+ else if (Option->tso_value > Conn->tc_window ||
+ Conn->tc_tcb == NULL) {
+ Conn->tc_flags |= CONN_WINSET;
+ Conn->tc_window = Option->tso_value;
+ SetTCB = Conn->tc_tcb;
+
+ if (SetTCB != NULL) {
+ CTEStructAssert(SetTCB, tcb);
+ CTEGetLock(&SetTCB->tcb_lock, &TCBHandle);
+ CTEAssert(Option->tso_value > SetTCB->tcb_defaultwin);
+ if (DATA_RCV_STATE(SetTCB->tcb_state) &&
+ !CLOSING(SetTCB)) {
+ SetTCB->tcb_flags |= WINDOW_SET;
+ SetTCB->tcb_defaultwin = Option->tso_value;
+ SetTCB->tcb_refcnt++;
+ CTEFreeLock(&SetTCB->tcb_lock, TCBHandle);
+ SendACK(SetTCB);
+ CTEGetLock(&SetTCB->tcb_lock, &TCBHandle);
+ DerefTCB(SetTCB, TCBHandle);
+ } else {
+ CTEFreeLock(&SetTCB->tcb_lock, TCBHandle);
+ }
+ }
+ }
+ CTEFreeLock(&ConnTableLock, TableHandle);
+ return Status;
+ }
+
+ Flag = 0;
+ Option = (TCPSocketOption *)Buffer;
+ Value = Option->tso_value;
+ // We have the connection, so figure out which flag to set.
+ switch (ID->toi_id) {
+
+ case TCP_SOCKET_NODELAY:
+ Value = !Value;
+ Flag = NAGLING;
+ break;
+ case TCP_SOCKET_KEEPALIVE:
+ Flag = KEEPALIVE;
+ break;
+ case TCP_SOCKET_BSDURGENT:
+ Flag = BSD_URGENT;
+ break;
+ case TCP_SOCKET_OOBINLINE:
+ Flag = URG_INLINE;
+ break;
+ default:
+ Status = TDI_INVALID_PARAMETER;
+ break;
+ }
+
+ if (Status == TDI_SUCCESS) {
+ if (Value)
+ Conn->tc_tcbflags |= Flag;
+ else
+ Conn->tc_tcbflags &= ~Flag;
+
+ SetTCB = Conn->tc_tcb;
+ if (SetTCB != NULL) {
+ CTEStructAssert(SetTCB, tcb);
+ CTEGetLock(&SetTCB->tcb_lock, &TCBHandle);
+ if (Value)
+ SetTCB->tcb_flags |= Flag;
+ else
+ SetTCB->tcb_flags &= ~Flag;
+
+ if (ID->toi_id == TCP_SOCKET_KEEPALIVE) {
+ SetTCB->tcb_alive = TCPTime;
+ SetTCB->tcb_kacount = 0;
+ }
+
+ CTEFreeLock(&SetTCB->tcb_lock, TCBHandle);
+ }
+ }
+ }
+
+ CTEFreeLock(&ConnTableLock, TableHandle);
+ return Status;
+#else
+ return TDI_INVALID_PARAMETER;
+#endif
+ }
+
+ if (ID->toi_type == INFO_TYPE_ADDRESS_OBJECT) {
+ // We're setting information on an address object. This is
+ // pretty simple.
+
+ return SetAddrOptions(Request, ID->toi_id, Size, Buffer);
+
+ }
+
+
+ if (ID->toi_type != INFO_TYPE_PROVIDER)
+ return TDI_INVALID_PARAMETER;
+
+#ifndef UDP_ONLY
+ if (ID->toi_id == TCP_MIB_TABLE_ID) {
+ if (Size != sizeof(TCPConnTableEntry))
+ return TDI_INVALID_PARAMETER;
+
+ TCPEntry = (TCPConnTableEntry *)Buffer;
+
+ if (TCPEntry->tct_state != TCP_DELETE_TCB)
+ return TDI_INVALID_PARAMETER;
+
+ // We have an apparently valid request. Look up the TCB.
+ CTEGetLock(&TCBTableLock, &TableHandle);
+ SetTCB = FindTCB(TCPEntry->tct_localaddr,
+ TCPEntry->tct_remoteaddr, (ushort)TCPEntry->tct_remoteport,
+ (ushort)TCPEntry->tct_localport);
+
+ // We found him. If he's not closing or closed, close him.
+ if (SetTCB != NULL) {
+ CTEGetLock(&SetTCB->tcb_lock, &TCBHandle);
+ CTEFreeLock(&TCBTableLock, TCBHandle);
+
+ // We've got him. Bump his ref. count, and call TryToCloseTCB
+ // to mark him as closing. Then notify the upper layer client
+ // of the disconnect.
+ SetTCB->tcb_refcnt++;
+ if (SetTCB->tcb_state != TCB_CLOSED && !CLOSING(SetTCB)) {
+ SetTCB->tcb_flags |= NEED_RST;
+ TryToCloseTCB(SetTCB, TCB_CLOSE_ABORTED, TableHandle);
+ CTEGetLock(&SetTCB->tcb_lock, &TableHandle);
+
+ if (SetTCB->tcb_state != TCB_TIME_WAIT) {
+ // Remove him from the TCB, and notify the client.
+ CTEFreeLock(&SetTCB->tcb_lock, TableHandle);
+ RemoveTCBFromConn(SetTCB);
+ NotifyOfDisc(SetTCB, NULL, TDI_CONNECTION_RESET);
+ CTEGetLock(&SetTCB->tcb_lock, &TableHandle);
+ }
+
+ }
+
+ DerefTCB(SetTCB, TableHandle);
+ return TDI_SUCCESS;
+ } else {
+ CTEFreeLock(&TCBTableLock, TableHandle);
+ return TDI_INVALID_PARAMETER;
+ }
+ } else
+ return TDI_INVALID_PARAMETER;
+#else
+ return TDI_INVALID_PARAMETER;
+#endif
+
+ }
+
+ if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) {
+ // We want to return implementation specific info. For now, error out.
+ return TDI_INVALID_REQUEST;
+ }
+
+ return TDI_INVALID_REQUEST;
+
+
+}