Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

5569 lines
154 KiB

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
Comm.c
Abstract:
This module contains COMSYS's internal functions. These functions
are called by commapi functions.
Functions:
CommCreatePorts
CommInit
MonTcp
MonUdp
HandleMsg
CommReadStream
ProcTcpMsg
CommCreateTcpThd
CommCreateUdpThd
CreateThd
CommConnect
CommSend
CommSendAssoc
CommDisc
CommSendUdp
ParseMsg
CommAlloc
CommDealloc
CompareNbtReq
CommEndAssoc
DelAssoc
CommLockBlock
CommUnlockBlock
InitMem
ChkNtfSock
RecvData
Portability:
This module is portable
Author:
Pradeep Bahl (pradeepb) 18-Nov-1992
Revision History:
--*/
/*
Includes
*/
//
// The max. number of connections that can be there to/from WINS.
//
// NOTE NOTE NOTE
//
// We specify a RCVBUF size, based on this value, for the notification socket.
//
#define FD_SETSIZE 300
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include "wins.h"
//
// pragma to disable duplicate definition message
//
#pragma warning (disable : 4005)
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma warning (default : 4005)
#include <nb30.h>
#include <nbtioctl.h>
#include <tdi.h>
#include "nms.h"
#include "rpl.h"
#include "comm.h"
#include "assoc.h"
#include "winsthd.h"
#include "winsque.h"
#include "winsmsc.h"
#include "winsevt.h"
#include "winscnf.h"
#if MCAST > 0
#include "rnraddrs.h"
#endif
/*
defines
*/
#define TCP_QUE_LEN 5 /*Max # of backlog connections that can be
*existent at any time. NOTE: WinsSock
*api can keep a max. of 5 connection req
*in the queue. So, even if we specified
* a higher number, that wouldn't help.
*For our purposes 5 is enough.
*/
#define SPX_QUE_LEN 5 /*Max # of backlog connections that can be*/
//
// These specify the timeout value for select call that is made when
// message/data is expected on a connection
//
//
// We keep the timeout 5 mts for now to give the WINS server we are
// communicating enough time to respond (in case it has been asked to send
// a huge number of records.
//
#define SECS_TO_WAIT 300 //5 mts
#define MICRO_SECS_TO_WAIT 0
#define TWENTY_MTS 1200 //20 mts
#define FIVE_MTS TWENTY_MTS/4 //5 mts
//
// The max. number of bytes we can expect in a message from another WINS over
// a tcp connection
//
#define MAX_BYTES_IN_MSG (RPL_MAX_LIMIT_FOR_RPL * (sizeof(RPL_REC_ENTRY_T) + NMSDB_MAX_NAM_LEN + (RPL_MAX_GRP_MEMBERS * sizeof(COMM_ADD_T))) + 10000 /*pad*/)
#define MCAST_PKT_LEN_M(NoOfIpAdd) (COMM_MCAST_MSG_SZ -1 + (COMM_IP_ADD_SIZE * (NoOfIpAdd)))
//
// This is the string used for getting the port pertaining to a nameserver
// from the etc\services file (via getserverbyname)
//
#define NAMESERVER "nameserver"
/*
Globals
*/
RTL_GENERIC_TABLE CommUdpNbtDlgTable; /*table for dialogue blocks created as
*a result of nbt requests received
*over the UDP port
*/
BOOL fCommDlgError = FALSE; //set to TRUE in ChkNtfSock() fn.
DWORD CommWinsTcpPortNo = COMM_DEFAULT_IP_PORT;
DWORD WinsClusterIpAddress = 0;
#if SPX > 0
#define WINS_IPX_PORT 100
DWORD CommWinsSpxPortNo;
#endif
/*
Static variables
*/
#ifdef WINSDBG
#define SOCKET_TRACK_BUFFER_SIZE 20000
DWORD CommNoOfDgrms; //for testing purposes only. It counts the
//number of datagrams received
DWORD CommNoOfRepeatDgrms;
PUINT_PTR pTmpW;
BOOL sfMemoryOverrun = FALSE;
LPLONG pEndPtr;
#endif
DWORD CommConnCount = 0; //no of tcp connection from/to this WINS
struct timeval sTimeToWait = {SECS_TO_WAIT, MICRO_SECS_TO_WAIT};
STATIC HANDLE sNetbtSndEvtHdl;
STATIC HANDLE sNetbtRcvEvtHdl;
STATIC HANDLE sNetbtGetAddrEvtHdl;
#if MCAST > 0
#define COMM_MCAST_ADDR IP_S_MEMBERSHIP //just pick one in the allowed range
struct sockaddr_in McastAdd;
#endif
//
// Structures used to store information about partners discovered via
// Multicasting
//
typedef struct _ADD_T {
DWORD NoOfAdds;
COMM_IP_ADD_T IpAdd[1];
} ADD_T, *PADD_T;
typedef struct _MCAST_PNR_STATUS_T {
DWORD NoOfPnrs; //no of pnrs in pPnrStatus buffer
DWORD NoOfPnrSlots; //no of pnr slots in pPnrStatus buffer
BYTE Pnrs[1];
} MCAST_PNR_STATUS_T, *PMCAST_PNR_STATUS_T;
typedef struct _PNR_STATUS_T {
COMM_IP_ADD_T IPAdd;
DWORD State;
} PNR_STATUS_T, *PPNR_STATUS_T;
#define MCAST_PNR_STATUS_SIZE_M(_NoOfPnrs) sizeof(MCAST_PNR_STATUS_T) +\
((_NoOfPnrs) * sizeof(PNR_STATUS_T))
PMCAST_PNR_STATUS_T pPnrStatus;
//
// To store WINS Addresses
//
PADD_T pWinsAddresses=NULL; //stores all the IP addresses returned by netbt
/* local function prototypes */
STATIC
DWORD
MonTcp(
LPVOID
);
STATIC
DWORD
MonUdp(
LPVOID
);
STATIC
VOID
HandleMsg(
SOCKET SockNo,
LPLONG pBytesRead,
LPBOOL pfSockCl
);
STATIC
VOID
ProcTcpMsg(
SOCKET SockNo,
MSG_T pMsg,
MSG_LEN_T MsgLen,
LPBOOL pfSockCl
);
STATIC
VOID
CreateThd(
DWORD (*pStartFunc)(LPVOID),
WINSTHD_TYP_E ThdTyp_e
);
STATIC
VOID
ParseMsg(
MSG_T pMsg,
MSG_LEN_T MsgLen,
COMM_TYP_E MsgType,
struct sockaddr_in *pFromAdd,
PCOMMASSOC_ASSOC_CTX_T pAssocCtx
);
STATIC
VOID
DelAssoc(
SOCKET SockNo,
PCOMMASSOC_ASSOC_CTX_T pAssocCtx
);
STATIC
VOID
InitMem(
VOID
);
STATIC
BOOL
ChkNtfSock(
IN fd_set *pActSocks,
IN fd_set *pRdSocks
);
STATIC
STATUS
RecvData(
SOCKET SockNo,
LPBYTE pBuff,
DWORD BytesToRead,
INT Flags,
DWORD SecsToWait,
LPDWORD pBytesRead
);
STATUS
CommTcp(
IN PCOMM_ADD_T pHostAdd,
IN SOCKET Port,
OUT SOCKET *pSockNo
);
#if SPX > 0
STATUS
CommSpx(
IN PCOMM_ADD_T pHostAdd,
IN SOCKET Port,
OUT SOCKET *pSockNo
);
#endif
STATIC
LPVOID
CommHeapAlloc(
IN PRTL_GENERIC_TABLE pTable,
IN CLONG BuffSize
);
STATIC
VOID
CommHeapDealloc(
IN PRTL_GENERIC_TABLE pTable,
IN PVOID pBuff
);
STATIC
NTSTATUS
DeviceIoCtrl(
IN LPHANDLE pEvtHdl,
IN PVOID pDataBuffer,
IN DWORD DataBufferSize,
IN ULONG Ioctl
);
STATIC
VOID
SendNetbt (
struct sockaddr_in *pDest,
MSG_T pMsg,
MSG_LEN_T MsgLen
);
#if MCAST > 0
VOID
JoinMcastGrp(
VOID
);
BOOL
CheckMcastSock(
IN fd_set *pActSocks,
IN fd_set *pRdSocks
);
#endif
VOID
CreateTcpIpPorts(
VOID
);
VOID
CreateSpxIpxPorts(
VOID
);
BOOL
ChkMyAdd(
COMM_IP_ADD_T IpAdd
);
/*
function definitions start here
*/
VOID
CommCreatePorts(
VOID
)
/*++
Routine Description:
This function creates a TCP and UDP port for the WINS server
It uses the standard WINS server port # to bind to both the TCP and the UDP
sockets.
Arguments:
Qlen - Length of queue for incoming connects on the TCP port
pTcpPortHandle - Ptr to SOCKET for the TCP port
pUdpPortHandle - Ptr to SOCKET for the UDP port
pNtfSockHandle - Ptr to SOCKET for receiving messages carrying socket
handles
pNtfAdd - Address bound to Notification socket
Externals Used:
None
Called by:
ECommInit
Comments:
I might want to create a PassiveSock function that would create
a TCP/UDP port based on its arguments. This function would then
be called from MOnTCP and MonUDP.
Return Value:
None
--*/
{
CreateTcpIpPorts();
#if SPX > 0
CreateSpxIpxPorts();
#endif
}
VOID
CreateTcpIpPorts(
VOID
)
{
int Error;
DWORD AddLen = sizeof(struct sockaddr_in);
struct servent *pServEnt;
struct sockaddr_in sin;
int SockBuffSize;
WINSMSC_FILL_MEMORY_M(&sin, sizeof(sin), 0);
WINSMSC_FILL_MEMORY_M(&CommNtfSockAdd, sizeof(sin), 0);
#if MCAST > 0
/*
Allocate a socket for UDP
*/
if ( (CommUdpPortHandle = socket(
PF_INET,
SOCK_DGRAM,
IPPROTO_UDP
)
) == INVALID_SOCKET
)
{
Error = WSAGetLastError();
WINSEVT_LOG_M(Error, WINS_EVT_CANT_CREATE_UDP_SOCK); //log an event
WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
}
DBGPRINT1(MTCAST, "Udp socket # is (%d)\n", CommUdpPortHandle);
#endif
sin.sin_family = PF_INET; //We are using the Internet
//family
if (WinsClusterIpAddress) {
sin.sin_addr.s_addr = htonl(WinsClusterIpAddress); //Any network
} else {
sin.sin_addr.s_addr = 0; //any network
}
if (CommWinsTcpPortNo == COMM_DEFAULT_IP_PORT)
{
pServEnt = getservbyname( NAMESERVER, NULL);
if (!pServEnt)
{
Error = WSAGetLastError();
WINSEVT_LOG_M(Error, WINS_EVT_CANT_CREATE_UDP_SOCK); //log an event
WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
}
sin.sin_port = pServEnt->s_port;
CommWinsTcpPortNo = ntohs(sin.sin_port);
}
else
{
sin.sin_port = htons((USHORT)CommWinsTcpPortNo);
}
DBGPRINT1(DET, "UDP/TCP port used is (%d)\n", CommWinsTcpPortNo);
#if MCAST > 0
//
// Initialize global with mcast address of WINS. Used by SendMcastMsg
//
// Do this here as against later since sin gets changed later on
//
McastAdd.sin_family = PF_INET; //We are using the Internet
//family
McastAdd.sin_addr.s_addr = ntohl(inet_addr(COMM_MCAST_ADDR));
McastAdd.sin_port = sin.sin_port;
/*
Bind the address to the socket
*/
if ( bind(
CommUdpPortHandle,
(struct sockaddr *)&sin,
sizeof(sin)) == SOCKET_ERROR
)
{
Error = WSAGetLastError();
WINSEVT_LOG_M(Error, WINS_EVT_WINSOCK_BIND_ERR); //log an event
WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
}
#endif
/*
* Allocate a socket for receiving TCP connections
*/
if ( (CommTcpPortHandle = socket(
PF_INET,
SOCK_STREAM,
IPPROTO_TCP
)
) == INVALID_SOCKET
)
{
Error = WSAGetLastError();
WINSEVT_LOG_M(Error, WINS_EVT_CANT_CREATE_TCP_SOCK_FOR_LISTENING);
WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
}
/*
* Bind the address to the socket
*/
#if 0
sin.sin_port = pServEnt->s_port;
CommWinsTcpPortNo = ntohs(pServEnt->s_port);
DBGPRINT1(DET, "TCP port used is (%d)\n", CommWinsTcpPortNo);
#endif
DBGPRINT1(DET, "TCP port used is (%d)\n", ntohs(sin.sin_port));
if ( bind(
CommTcpPortHandle,
(struct sockaddr *)&sin,
sizeof(sin)
) == SOCKET_ERROR
)
{
WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_WINSOCK_BIND_ERR); //log an event
WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
}
// Inform the TCP/IP driver of the queue length for connections
if ( listen(CommTcpPortHandle, TCP_QUE_LEN) == SOCKET_ERROR)
{
WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_WINSOCK_LISTEN_ERR);
WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
}
//
// Create another socket for receiving socket #s of connections
// to be added/removed from the list of sockets monitored by the
// TCP listener thread. An example of a connection added to the
// above list is the one initiated by the PULL thread to push update
// notifications to other WINSs (PULL partners of this thread). An
// example of a connection removed is the one on which a PUSH
// notification (trigger) is received.
//
if ( (CommNtfSockHandle = socket(
PF_INET,
#if 0
SOCK_STREAM,
IPPROTO_TCP,
#endif
SOCK_DGRAM,
IPPROTO_UDP
)
) == INVALID_SOCKET
)
{
Error = WSAGetLastError();
WINSEVT_LOG_M(Error, WINS_EVT_CANT_CREATE_NTF_SOCK); //log an event
WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
}
sin.sin_port = 0; //Use any available port in the range 1024-5000
/*
Bind the address to the socket
*/
if ( bind(
CommNtfSockHandle,
(struct sockaddr *)&sin,
sizeof(sin)) == SOCKET_ERROR
)
{
Error = WSAGetLastError();
WINSEVT_LOG_M(Error, WINS_EVT_WINSOCK_BIND_ERR); //log an event
WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
}
//
// Let us get the address that we have bound the notification socket to
//
if (getsockname(
CommNtfSockHandle,
(struct sockaddr *)&CommNtfSockAdd,
&AddLen
) == SOCKET_ERROR
)
{
Error = WSAGetLastError();
WINSEVT_LOG_M(Error, WINS_EVT_WINSOCK_GETSOCKNAME_ERR); //log an event
WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
}
//
// Set the RCVBUF to FD_SETSIZE * 128. 128 is the # of bytes used up
// per msg by Afd. We can have a max of FD_SETSIZE connections initiated
// to and from WINS. So, making the recv buf this size ensures that msgs
// sent by push thread to the tcp thread will never get dropped.
//
// The above size comes out to be 38.4K for an FD_SETSIZE of 300. This
// is > 8k which the default used by Afd. Note: Specifying this does
// not use up memory. It is just used to set a threshold. pmon will
// show a higher non-paged pool since the number it shows for the same
// indicates the amount of memory charged to the process (not necessarily
// allocated
//
SockBuffSize = FD_SETSIZE * 128;
if (setsockopt(
CommNtfSockHandle,
SOL_SOCKET,
SO_RCVBUF,
(char *)&SockBuffSize,
sizeof(SockBuffSize)) == SOCKET_ERROR)
{
Error = WSAGetLastError();
DBGPRINT1(ERR, "CommCreatePorts: SetSockOpt failed", Error);
}
//
// Initialize the address structure for this notification socket.
// We can't use the address returned by getsockname() if the
// machine we are running on is a multi-homed host.
//
// The IP address is in host byte order since we store all addresses in
// host order. CommNtfSockAdd will be passed to CommSendUdp which expects
// the IP address in it to be in host byte order.
//
// Note: the Port should be in net byte order
//
//
// The statement within #if 0 and #endif does not work.
//
CommNtfSockAdd.sin_addr.s_addr = NmsLocalAdd.Add.IPAdd;
#if 0
CommNtfSockAdd.sin_addr.s_addr = ntohl(INADDR_LOOPBACK);
#endif
#if MCAST > 0
JoinMcastGrp();
CommSendMcastMsg(COMM_MCAST_WINS_UP);
#endif
return;
}
#if SPX > 0
VOID
CreateSpxIpxPorts(
VOID
)
{
int Error;
DWORD AddLen = sizeof(struct sockaddr_ipx);
struct servent *pServEnt;
struct sockaddr_ipx sipx;
struct hostent *pHostEnt;
BYTE HostName[80];
WINSMSC_FILL_MEMORY_M(&sipx, sizeof(sipx), 0);
WINSMSC_FILL_MEMORY_M(CommNtfAdd, sizeof(sipx), 0);
/*
* Allocate a socket for receiving TCP connections
*/
if ( (CommSpxPortHandle = socket(
PF_IPX,
SOCK_STREAM,
NSPROTO_SPX
)
) == INVALID_SOCKET
)
{
Error = WSAGetLastError();
WINSEVT_LOG_M(Error, WINS_EVT_CANT_CREATE_TCP_SOCK_FOR_LISTENING);
WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
}
/*
* Bind the address to the socket
*/
sipx.sa_family = PF_IPX;
sipx.sa_port = ntohs(WINS_IPX_PORT);
CHECK("How do I specify that I want the connection from any interface")
DBGPRINT1(DET, "SPX port used is (%d)\n", WINS_IPX_PORT);
CommWinsSpxPortNo = WINS_IPX_PORT;
if ( bind(
CommSpxPortHandle,
(struct sockaddr *)&sipx,
sizeof(sipx)
) == SOCKET_ERROR
)
{
WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_WINSOCK_BIND_ERR); //log an event
WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
}
// Inform the TCP/IP driver of the queue length for connections
if ( listen(CommSpxPortHandle, SPX_QUE_LEN) == SOCKET_ERROR)
{
WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_WINSOCK_LISTEN_ERR);
WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
}
//
// Create another socket for receiving socket #s of connections
// to be added/removed from the list of sockets monitored by the
// TCP listener thread. An example of a connection added to the
// above list is the one initiated by the PULL thread to push update
// notifications to other WINSs (PULL partners of this thread). An
// example of a connection removed is the one on which a PUSH
// notification (trigger) is received.
//
if ( (CommIpxNtfSockHandle = socket(
PF_IPX,
SOCK_DGRAM,
NSPROTO_IPX
)
) == INVALID_SOCKET
)
{
Error = WSAGetLastError();
WINSEVT_LOG_M(Error, WINS_EVT_CANT_CREATE_NTF_SOCK); //log an event
WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
}
sipx.sa_port = 0; //Use any available port in the range 1024-5000
/*
Bind the address to the socket
*/
if ( bind(
CommIpxNtfSockHandle,
(struct sockaddr *)&sipx,
sizeof(sipx)) == SOCKET_ERROR
)
{
Error = WSAGetLastError();
WINSEVT_LOG_M(Error, WINS_EVT_WINSOCK_BIND_ERR); //log an event
WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
}
//
// Let us get the address that we have bound the notification socket to
//
if (getsockname(
CommIpxNtfSockHandle,
(struct sockaddr *)&CommIpxNtfSockAdd,
&AddLen
) == SOCKET_ERROR
)
{
Error = WSAGetLastError();
WINSEVT_LOG_M(Error, WINS_EVT_WINSOCK_GETSOCKNAME_ERR); //log an event
WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
}
//
// Initialize the address structure for this notification socket.
// We can't use the address returned by getsockname() if the
// machine we are running on is a multi-homed host.
//
// The IP address is in host byte order since we store all addresses in
// host order. *pNtfAdd will be passed to CommSendUdp which expects
// the IP address in it to be in host byte order.
//
// Note: the Port should be in net byte order
//
#if 0
if (gethostname(HostName, sizeof(HostName) == SOCKET_ERROR)
{
Error = WSAGetLastError();
WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
}
if (gethostbyname(HostName, sizeof(HostName) == NULL)
{
Error = WSAGetLastError();
WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
}
#endif
//
// The statement within #if 0 and #endif does not work.
//
CommIpxNtfSockAdd->sin_addr.s_addr = 0;
#if 0
pNtfAdd->sin_addr.s_addr = ntohl(INADDR_LOOPBACK);
#endif
return;
}
#endif
VOID
CommInit(
VOID
)
/*++
Routine Description:
This function initializes all the lists, tables and memory
used by COMSYS.
Arguments:
None
Externals Used:
CommAssocTable
CommUdpNbtDlgTable
CommExNbtDlgHdl
CommUdpBuffHeapHdl
Return Value:
None
Error Handling:
Called by:
ECommInit
Side Effects:
Comments:
None
--*/
{
PCOMMASSOC_DLG_CTX_T pDlgCtx = NULL;
//
// Do all memory initialization
//
InitMem();
/*
* Initialize the table that will store the dialogue context blocks
* for nbt requests received over the UDP port.
*/
WINSMSC_INIT_TBL_M(
&CommUdpNbtDlgTable,
CommCompareNbtReq,
CommHeapAlloc,
CommHeapDealloc,
NULL /* table context*/
);
/*
* Initialize the critical sections and queue heads
*
* The initialization is done in a CommAssoc function instead of here
* to avoid recursive includes
*/
CommAssocInit();
CommExNbtDlgHdl.pEnt = CommAssocAllocDlg();
pDlgCtx = CommExNbtDlgHdl.pEnt;
/*
* Initialize the explicit nbt dialogue handle
*/
pDlgCtx->Typ_e = COMM_E_UDP;
pDlgCtx->AssocHdl.pEnt = NULL;
pDlgCtx->Role_e = COMMASSOC_DLG_E_EXPLICIT;
#if USENETBT > 0
//
// Create two events (one for send and one for rcv to/from netbt)
//
WinsMscCreateEvt(NULL, FALSE, &sNetbtSndEvtHdl);
WinsMscCreateEvt(NULL, FALSE, &sNetbtRcvEvtHdl);
WinsMscCreateEvt(NULL, FALSE, &sNetbtGetAddrEvtHdl);
#endif
return;
} // CommInit()
DWORD
MonTcp(
LPVOID pArg
)
/*++
Routine Description:
This function is the thread startup function for the TCP listener
thread. It monitors the TCP port and the connections that have
been made and received by this process.
If a connection is received, it is accepted. If there is data on
a TCP connection, a function is called to process it.
Arguments:
pArg - Argument (Not used)
Externals Used:
CommTCPPortHandle -- TCP port for the process
CommAssocTable
Called by:
ECommInit
Comments:
None
Return Value:
Sucess status codes -- WINS_SUCCESS
Error status codes -- WINS_FAILURE
--*/
{
struct sockaddr_in fsin; //address of connector
#if SPX > 0
struct sockaddr_ipx fsipx; //address of connector
#endif
SOCKET Port;
LPVOID pRemAdd;
fd_set RdSocks; //The read socket set
fd_set ActSocks; //the active socket set
int AddLen = sizeof(fsin); //length of from address
u_short No; //Counter for iterating over the sock
//array
BOOL fSockCl = FALSE; //Was the socket closed ?
SOCKET NewSock = INVALID_SOCKET;
BOOL fNewAssoc = FALSE;
DWORD BytesRead = 0;
DWORD Error;
int i = 0; //for testing purpose only
SOCKET SockNo;
LONG NoOfSockReady = 0;
PCOMMASSOC_ASSOC_CTX_T pAssocCtx = NULL;
STATUS RetStat = WINS_SUCCESS;
BOOL fLimitReached = FALSE;
#ifdef WINSDBG
PUINT_PTR pTmpSv;
DWORD Index = 0;
#endif
EnterCriticalSection(&NmsTermCrtSec);
NmsTotalTrmThdCnt++;
LeaveCriticalSection(&NmsTermCrtSec);
FD_ZERO(&ActSocks); //init the Active socket array
FD_ZERO(&RdSocks); //init the Read socket array
FD_SET(CommTcpPortHandle, &ActSocks); /*set the TCP listening socket
handle in the Active array */
FD_SET(CommNtfSockHandle, &ActSocks); /*set the Notification socket
*handle in the Active array */
#if MCAST > 0
if (CommUdpPortHandle != INVALID_SOCKET)
{
//
// We want to monitor multicast packets also
//
FD_SET(CommUdpPortHandle, &ActSocks);
WinsMscAlloc(MCAST_PNR_STATUS_SIZE_M(RPL_MAX_OWNERS_INITIALLY),
(LPVOID *)&pPnrStatus
);
pPnrStatus->NoOfPnrs = 0;
pPnrStatus->NoOfPnrSlots = RPL_MAX_OWNERS_INITIALLY;
}
#endif
#if SPX > 0
FD_SET(CommSpxPortHandle, &ActSocks); /*set the TCP listening socket
handle in the Active array */
FD_SET(CommIpxNtfSockHandle, &ActSocks); /*set the Notification socket
*handle in the Active array */
#endif
#ifdef WINSDBG
WinsMscAlloc(SOCKET_TRACK_BUFFER_SIZE, &pTmpSv);
pTmpW = pTmpSv;
pEndPtr = (LPLONG)((LPBYTE)pTmpSv + SOCKET_TRACK_BUFFER_SIZE);
#endif
LOOPTCP:
try {
/*
Loop forever
*/
while(TRUE)
{
BOOL fConnTcp;
BOOL fConnSpx;
fConnTcp = FALSE;
fConnSpx = FALSE;
/*
Copy the the active socket array into the
read socket array. This is done every time before calling
select. This is because select changes the contents of
the read socket array.
*/
WINSMSC_COPY_MEMORY_M(&RdSocks, &ActSocks, sizeof(fd_set));
/*
Do a blocking select on all sockets in the array (for connections
and data)
*/
DBGPRINT1(FLOW, "Rd array count is %d \n", RdSocks.fd_count);
#ifdef WINSDBG
if (!sfMemoryOverrun)
{
if ((ULONG_PTR)(pTmpW + (10 + RdSocks.fd_count)) > (ULONG_PTR)pEndPtr)
{
WinsDbg |= 0x3;
DBGPRINT0(ERR, "MonTcp: Stopping socket tracking to prevent Memory overrun\n")
sfMemoryOverrun = TRUE;
}
else
{
*pTmpW++ = RdSocks.fd_count;
*pTmpW++ = 0xFFFFFFFF;
for(i = 0; i< (int)RdSocks.fd_count; i++)
{
*pTmpW++ = RdSocks.fd_array[i];
DBGPRINT1(FLOW, "Sock no is (%d)\n", RdSocks.fd_array[i]);
}
*pTmpW++ = 0xFFFFFFFF;
}
}
#endif
if (
(
NoOfSockReady = select(
FD_SETSIZE /*ignored arg*/,
&RdSocks,
(fd_set *)0,
(fd_set *)0,
(struct timeval *)0 //Infinite
//timeout
)
) == SOCKET_ERROR
)
{
Error = WSAGetLastError();
#ifdef WINSDBG
if (Error == WSAENOTSOCK)
{
DWORD i;
PUINT_PTR pW;
WinsDbg |= 0x3;
DBGPRINT0(ERR, "MonTcp: Memory dump is\n\n");
for (i=0, pW = pTmpSv; pW < pTmpW; pW++,i++)
{
DBGPRINT1(ERR, "|%x|", *pW);
if (*pW == 0xEFFFFFFE)
{
DBGPRINT1(ERR, "Socket closed = (%x)\n", *++pW);
}
if ((i == 16) || (*pW == 0xFFFFFFFF))
{
DBGPRINT0(ERR, "\n");
}
}
DBGPRINT0(ERR, "Memory Dump End\n");
}
#endif
//
// If state is not terminating, we have an error. If
// it is terminating, then the reason we got an error
// from select is because the main thread closed the
// TCP socket. In the latter case, we pass WINS_SUCCESS
// to WinsMscTermThd so that we don't end up signaling
// the main thread prematurely.
//
if (
(WinsCnf.State_e == WINSCNF_E_RUNNING)
||
(WinsCnf.State_e == WINSCNF_E_PAUSED)
)
{
ASSERT(Error != WSAENOTSOCK);
WINSEVT_LOG_D_M( Error, WINS_EVT_WINSOCK_SELECT_ERR );
RetStat = WINS_FAILURE;
}
else
{
//
// State is terminating. Error should
// be WSENOTSOCK
//
//ASSERT(Error == WSAENOTSOCK);
}
WinsThdPool.CommThds[0].fTaken = FALSE;
WinsMscTermThd(RetStat, WINS_NO_DB_SESSION_EXISTS);
}
else
{
DBGPRINT1(FLOW, "Select returned with success. No of Sockets ready - (%d) \n", NoOfSockReady);
/*
if a connection has been received on the TCP port, accept it
and change the active socket array
*/
if (FD_ISSET(CommTcpPortHandle, &RdSocks))
{
fConnTcp = TRUE;
Port = CommTcpPortHandle;
pRemAdd = &fsin;
}
#if SPX > 0
else
{
if (FD_ISSET(CommSpxPortHandle, &RdSocks))
{
fConnSpx = TRUE;
Port = CommSpxPortHandle;
pRemAdd = &fsipx;
}
}
#endif
if (fConnTcp || fConnSpx)
{
DWORD ConnCount;
//
// Note: FD_SET can fail silently if the fd_set array is
// full. Therefore we should check this. Do it here instead
// of after the accept to save on network traffic.
//
ConnCount = InterlockedExchange(&CommConnCount, CommConnCount);
//if (ActSocks.fd_count >= FD_SETSIZE)
#ifdef WINSDBG
if (ConnCount >= 200)
{
DBGPRINT0(ERR,
"MonTcp: Connection limit of 200 reached. \n");
}
#endif
#if 0
if (ConnCount >= FD_SETSIZE)
{
DBGPRINT1(ERR,
"MonTcp: Connection limit of %d reached. No accept being done\n",
FD_SETSIZE);
WINSEVT_LOG_D_M(ConnCount,
WINS_EVT_CONN_LIMIT_REACHED);
fLimitReached = TRUE;
}
#endif
DBGPRINT0(FLOW, "Going to do an accept now\n");
if ( (NewSock = accept(
Port,
(struct sockaddr *)pRemAdd,
&AddLen
)
) == INVALID_SOCKET
)
{
Error = WSAGetLastError();
if (WinsCnf.State_e != WINSCNF_E_TERMINATING)
{
WINSEVT_LOG_M(
Error,
WINS_EVT_WINSOCK_ACCEPT_ERR,
);
}
WinsThdPool.CommThds[0].fTaken = FALSE;
WinsMscTermThd(
(((Error == WSAEINTR) || (Error == WSAENOTSOCK)) ?
WINS_SUCCESS : WINS_FAILURE),
WINS_NO_DB_SESSION_EXISTS);
}
DBGPRINT1(FLOW, "New Sock value is (%d)\n", NewSock);
if (fLimitReached)
{
FUTURES("Move this into CommDisc -- add a flag to it to indicate abrupt stop")
struct linger Linger;
Linger.l_onoff = 0;
if (setsockopt(
NewSock,
SOL_SOCKET,
SO_DONTLINGER,
(char *)&Linger,
sizeof(Linger)) == SOCKET_ERROR)
{
Error = WSAGetLastError();
DBGPRINT1(ERR,
"MonTcp: SetSockOpt failed", Error);
}
fLimitReached = FALSE;
CommDisc(NewSock, FALSE); //close the socket
continue;
}
FD_SET(NewSock, &ActSocks);
InterlockedIncrement(&CommConnCount);
#ifdef WINSDBG
/*
* Let us see if the assoc. is there or not. It shouldn't be
* but let us check anyway (robust programming).
*/
pAssocCtx = CommAssocLookupAssoc( NewSock );
if (!pAssocCtx)
{
#endif
pAssocCtx = CommAssocCreateAssocInTbl(NewSock);
if (!pAssocCtx)
{
WINSEVT_LOG_D_M(
WINS_OUT_OF_MEM,
WINS_EVT_CANT_ALLOC_RSP_ASSOC,
);
WinsMscTermThd(WINS_FAILURE, WINS_NO_DB_SESSION_EXISTS);
}
#ifdef WINSDBG
}
else
{
DBGPRINT0(ERR, "MonTcp: Not a new assoc. Weird\n");
//
// log an error (Cleanup was not done properly)
//
return(WINS_FAILURE);
}
#endif
pAssocCtx->State_e = COMMASSOC_ASSOC_E_NON_EXISTENT;
pAssocCtx->Role_e = COMMASSOC_ASSOC_E_RESPONDER;
pAssocCtx->DlgHdl.pEnt = NULL;
if (fConnTcp)
{
pAssocCtx->RemoteAdd.sin_addr.s_addr =
ntohl(fsin.sin_addr.s_addr);
pAssocCtx->AddTyp_e = COMM_ADD_E_TCPUDPIP;
}
#if SPX > 0
else
{
RtlCopyMemory(
pAssocCtx->RemoteAddSpx.sa_netnum,
fsipx.netnum,
sizeof(fsipx.netnum);
RtlCopyMemory(
pAssocCtx->RemoteAddSpx.sa_nodenum,
fsipx.nodenum,
sizeof(fsipx.nodenum);
pAssocCtx->AddTyp_e = COMM_ADD_E_SPXIPX;
}
#endif
}
else /* one or more sockets has received data or a disconnect*/
{
#if MCAST > 0
if (CheckMcastSock(&ActSocks, &RdSocks) == TRUE)
{
continue;
}
#endif
//
// Check if the notification socket has data in it
// If yes, continue.
//
if (ChkNtfSock(&ActSocks, &RdSocks))
{
DBGPRINT0(FLOW,
"MonTcp: Notification socket had data in it\n");
continue;
}
/*
* Handle sockets that have been set. These could have
* been set either because there is data on them or
* due to disconnects.
*/
for(No = 0; No < RdSocks.fd_count; ++No)
{
SockNo = RdSocks.fd_array[No];
if (FD_ISSET(SockNo, &RdSocks))
{
BytesRead = 0;
fSockCl = FALSE;
DBGPRINT1(FLOW, "MonTcp: Socket (%d) was signaled. It has either data or a disconnect on it\n",
SockNo);
/*
* Socket has data on it or a disconnect. Call
* HandleMsg to handle either case
*/
(VOID)HandleMsg(SockNo, &BytesRead, &fSockCl);
/*
* if the socket was closed due to a stop message
* having been received, let us clean up the
* socket array
*/
if (fSockCl)
{
DBGPRINT1(FLOW, "MonTcp: Sock (%d) was closed\n",
SockNo);
FD_CLR(SockNo, &ActSocks);
}
else
{
/*
* if bytes read are 0, we have a disconnect
* All the processing for the disconnect should
* have been handled by HandleMsg. We just need
* to close the socket and update the socket
* array appropriately.
*/
if (BytesRead == 0)
{
DBGPRINT0(FLOW,
"MonTcp: Received a disconnect\n");
//CommDisc(SockNo, TRUE);
FD_CLR(SockNo, &ActSocks);
}
}
}
} //for (loop over all sockets)
} //else (one or more sockets has received data or a disconnect
} //else clause (if select () < 0)
} // while (TRUE) loop end
} // end of try {}
except (EXCEPTION_EXECUTE_HANDLER) {
DBGPRINTEXC("MONTCP");
WINSEVT_LOG_M(GetExceptionCode(), WINS_EVT_TCP_LISTENER_EXC);
#if 0
//
// Don't use WinsMscTermThd here
//
ExitThread(WINS_FAILURE);
#endif
}
goto LOOPTCP; //ugly but useful
UNREFERENCED_PARAMETER(NoOfSockReady);
// we should never hit this return
ASSERT(0);
return(WINS_FAILURE);
}
DWORD
MonUdp(
LPVOID pArg
)
/*++
Routine Description:
This function is the thread startup function for the UDP listener
thread. It monitors the UDP port for UDP messages.
Arguments:
pArg - Argument (not used)
Externals Used:
CommUDPPortHandle -- UDP port for the process
Called by:
ECommInit
Comments:
None
Return Value:
Success status codes --
Error status codes --
--*/
{
register LPBYTE pBuffer;
struct sockaddr_in FromAdd;
int AddLen = sizeof(FromAdd);
register PCOMM_BUFF_HEADER_T pBuffHdr = NULL;
DWORD DataBuffLen;
tREM_ADDRESS *pRemAdd;
NTSTATUS NTStatus;
LOOP:
try {
while(TRUE)
{
//
// Allocate a buffer to get the datagram. This buffer is prefixed
// by the COMM_BUFF_HEADER_T and tREM_ADDRESS structure.
//
pBuffHdr = WinsMscHeapAlloc (
CommUdpBuffHeapHdl,
COMM_DATAGRAM_SIZE + sizeof(COMM_BUFF_HEADER_T)
+ COMM_NETBT_REM_ADD_SIZE
);
DBGPRINT2(HEAP, "MonUdp: HeapHdl = (%p), pBuffHdr=(%p)\n",
CommUdpBuffHeapHdl, pRemAdd);
pBuffHdr->Typ_e = COMM_E_UDP;
//
// Adjust pointer to point to the Remote address header
//
pRemAdd = (tREM_ADDRESS *)
((LPBYTE)pBuffHdr + sizeof(COMM_BUFF_HEADER_T));
DataBuffLen = COMM_DATAGRAM_SIZE + COMM_NETBT_REM_ADD_SIZE;
//
// Point to the data portion (passed to ParseMsg)
//
pBuffer = (LPBYTE)pRemAdd + COMM_NETBT_REM_ADD_SIZE;
//
// read a datagram prefixed with the address of the sender from
// nbt
//
NTStatus = DeviceIoCtrl(
&sNetbtRcvEvtHdl,
pRemAdd,
DataBuffLen,
IOCTL_NETBT_WINS_RCV
);
if (!NT_SUCCESS(NTStatus))
{
//
// log the message only if WINS is not terminating
//
if (WinsCnf.State_e != WINSCNF_E_TERMINATING)
{
//
// We do not log the message if the Netbt handle is NULL
// We can have a small window when the handle may be NULL
// This happens when we get an address/device change
// notification. WINS closes the old handle and opens
// a new one after such an event if the machine has a
// a valid address that WINS can bind with. The address
// notification can occur due to ipconfig /release and
// /renew or due to psched being installed/removed.
//
if (WinsCnfNbtHandle != NULL)
{
WINSEVT_LOG_D_M(
NTStatus,
WINS_EVT_NETBT_RECV_ERR
);
}
DBGPRINT1(ERR, "MonUdp: Status = (%x)\n", NTStatus);
WinsMscHeapFree( CommUdpBuffHeapHdl, pBuffHdr);
Sleep(0); //relinquish the processor
continue;
}
else
{
DBGPRINT0(ERR, "MonUdp, Exiting Thread\n");
WinsThdPool.CommThds[1].fTaken = FALSE;
#if TEST_HEAP > 0
WinsMscHeapDestroy(CommUdpBuffHeapHdl);
DBGPRINT0(ERR, "MonUdp: Destroyed udp buff heap\n");
WinsMscHeapDestroy(CommUdpDlgHeapHdl);
DBGPRINT0(ERR, "MonUdp: Destroyed udp dlg buff heap\n");
#endif
return(WINS_FAILURE);
}
}
#ifdef WINSDBG
++CommNoOfDgrms;
DBGPRINT1(FLOW, "UDP listener thread: Got datagram (from NETBT) no = (%d)\n", CommNoOfDgrms);
// DBGPRINT1(SPEC, "UDP listener thread: Got datagram (from NETBT) no = (%d)\n", CommNoOfDgrms);
#endif
//
// NETBT returns the same code as is in winsock.h for the
// internet family. Also, port and IpAddress returned are
// in network order
//
FromAdd.sin_family = pRemAdd->Family;
FromAdd.sin_port = pRemAdd->Port;
FromAdd.sin_addr.s_addr = ntohl(pRemAdd->IpAddress);
// from now on the memory allocated for pBuffHdr is passed down the way so consider it handled there
// There is basically no chance to hit an exception (unless everything is really messed up - like no mem)
// in ParseMsg before having this buffer passed down to a different thread for processing.
pBuffHdr = NULL;
/*
* process message
*/
(void)ParseMsg(
pBuffer,
COMM_DATAGRAM_SIZE,
COMM_E_UDP,
&FromAdd,
NULL
);
} //end of while(TRUE)
} // end of try {..}
except(EXCEPTION_EXECUTE_HANDLER) {
DWORD ExcCode = GetExceptionCode();
DBGPRINT1(EXC, "MonUdp: Got Exception (%X)\n", ExcCode);
if (ExcCode == STATUS_NO_MEMORY)
{
//
//If the exception is due to insufficient resources, it could
// mean that WINS is not able to keep up with the fast arrivel
// rate of the datagrams. In such a case drop the datagram.
//
WINSEVT_LOG_M( WINS_OUT_OF_HEAP, WINS_EVT_CANT_ALLOC_UDP_BUFF);
}
else
{
WINSEVT_LOG_M(ExcCode, WINS_EVT_UDP_LISTENER_EXC);
}
PERF("Check how many cycles try consumes. If negligeble, move try inside the")
PERF("the while loop")
#if 0
//Don't use WinsMscTermThd here
ExitThread(WINS_FAILURE);
#endif
} // end of exception
if (pBuffHdr != NULL)
WinsMscHeapFree(CommUdpBuffHeapHdl, pBuffHdr);
goto LOOP; //ugly but useful
//
// we should never hit this return
//
ASSERT(0);
return(WINS_FAILURE);
}
VOID
HandleMsg(
IN SOCKET SockNo,
OUT LPLONG pBytesRead,
OUT LPBOOL pfSockCl
)
/*++
Routine Description:
This function is called to read in a message or a disconnect from
a socket and handle either appropriately.
If there were no bytes received on the socket, tt
does the cleanup
The bytes read are handed to ProcTcpMsg function.
Arguments:
SockNo - Socket to read data from
pBytesRead - # of bytes that were read
fSockCl - whether the socket is in closed condition
Externals Used:
None
Called by:
MonTcp
Comments:
None
Return Value:
None
--*/
{
MSG_T pMsg;
STATUS RetStat;
/*
* Read in the message from the socket
*/
// ---ft: 06/16/2000---
// The second parameter to the call below has to be TRUE (timed receive).
// If it is not so (it was FALSE before this moment) the following scenario
// could happen.
// An attacher creates a TCP socket and connects it to port 42 to any
// WINS server and then sends 4 or less bytes on that socket. He leaves the
// connection open (doesn't close the socket) and simply unplug his net cable.
// Then he kills his app. Although his end of the connection terminates, WINS
// will have no idea about that (since the cable is disconnected) and will
// remain blocked in the call below (CommReadStrea->RecvData->recv) indefinitely
//
// Consequences:
// - WINS will never be able again to listen on the TCP port 42: push/pull replication
// is brought down along with the consistency checking.
// - WINS will not be able to terminate gracefully (in case the administrator attempts
// to shut down the service and restart it) because the MonTcp thread is in a hung state
//
// The same could happen in more usual cases (not necesarily on an intenional attack):
// While sending Push notification (which happens quite often):
// 1) the pusher is powered down (power outage)
// 2) the pusher hits a PnP event like media disconnect or adapter disabled
// 3) some router is down between the pusher and the receiving WINS.
//
// With this fix the best we can do for now is to have MonTcp thread recover in 20mts.
// Even better would be to log an event.
RetStat = CommReadStream(
SockNo,
TRUE, //don't do timed recv
&pMsg,
pBytesRead
);
//
// if either RetStat is not WINS_SUCCESS or the number of bytes
// read are 0, we need to delete the association and close the
// socket. Further, we need to set *pfSockCl to TRUE to indicate
// to the caller (MonTcp) that it should get rid of the socket
// from its array of sockets.
//
if ((RetStat != WINS_SUCCESS) || (*pBytesRead == 0))
{
/*
* No bytes received. This means that it is a disconnect
* Let us get rid of the context associated with the socket
*/
DelAssoc(
SockNo,
NULL /* we don't have the ptr to assoc block*/
);
CommDisc(SockNo, TRUE);
*pfSockCl = TRUE;
}
else // means (RetStat == WINS_SUCCESS) and (*pBytesRead > 0)
{
ASSERT(*pBytesRead > 0);
/*
* process the message
*/
ProcTcpMsg(
SockNo,
pMsg,
*pBytesRead,
pfSockCl
);
}
return;
} // HandleMsg()
STATUS
CommReadStream(
IN SOCKET SockNo,
IN BOOL fDoTimedRecv,
OUT PMSG_T ppMsg,
OUT LPLONG pBytesRead
)
/*++
Routine Description:
This function reads from a TCP socket. If there are no bytes
there, it means a disconnect was received on that socket.
Arguments:
SockNo - Socket to read data from
fDoTimedRecv - Whether timed receive should be done (set to TRUE only
if we are not sure whether data has arrived or not yet)
ppMsg - Buffer containing data that was read in
pBytesRead - Size of buffer
Return Value:
TBS
--*/
{
u_long MsgLen;
LONG BytesToRead;
INT Flags = 0; /*flags for recv call (PEEK and/or OOB).
* we want neither
*/
WINS_MEM_T WinsMem[2];
PWINS_MEM_T pWinsMem = WinsMem;
STATUS RetStat;
PCOMM_BUFF_HEADER_T pBuffHdr;
DBGENTER("CommReadStream\n");
pWinsMem->pMem = NULL;
#ifdef WINSDBG
try {
#endif
/*
* All TCP messages are preceded by a length word (4 bytes) that
* gives the length of the message that follows. Read the length
* bytes.
*/
RetStat = RecvData(
SockNo,
(LPBYTE)&MsgLen,
sizeof(u_long),
Flags,
fDoTimedRecv ? TWENTY_MTS : fDoTimedRecv,
pBytesRead
);
/*
* Check if there was an error in reading. We will have a RetStat
* of WINS_SUCCESS even if 0 bytes (meaning a disconnect) were read
* in
*/
if (RetStat == WINS_SUCCESS)
{
if (*pBytesRead != 0)
{
COMM_NET_TO_HOST_L_M(MsgLen, MsgLen);
//
// Just making sure that the message length did not get
// corrupted on the way. Also, this is a good guard against
// a process that is trying to bring us down.
//
if (MsgLen <= MAX_BYTES_IN_MSG)
{
/*
* Allocate memory for the buffer. Allocate extra space
* at the top to store the Header for the buffer. This
* header is used to store information about the buffer.
* (See ECommFreeBuff also)
*/
*ppMsg = WinsMscHeapAlloc(
CommAssocTcpMsgHeapHdl,
MsgLen +
#if USENETBT > 0
COMM_NETBT_REM_ADD_SIZE +
#endif
sizeof(COMM_BUFF_HEADER_T) + sizeof(LONG)
);
//
// if *ppMsg is NULL, it means that we received garabage
// in the first 4 bytes. It should have been the length
// of the message.
//
if (*ppMsg == NULL)
{
//
// return with *pBytesRead = 0
//
*pBytesRead = 0;
return(WINS_FAILURE);
}
pWinsMem->pMem = *ppMsg;
(++pWinsMem)->pMem = NULL;
/*
* Increment pointer past the buffer header and field
* storing the length of the message.
*/
pBuffHdr = (PCOMM_BUFF_HEADER_T)(*ppMsg + sizeof(LONG));
*ppMsg = *ppMsg +
#if USENETBT > 0
COMM_NETBT_REM_ADD_SIZE +
#endif
sizeof(COMM_BUFF_HEADER_T) + sizeof(LONG);
#if 0
pBuffHdr =
(PCOMM_BUFF_HEADER_T)(*ppMsg - sizeof(COMM_BUFF_HEADER_T));
#endif
pBuffHdr->Typ_e = COMM_E_TCP; //store type of buffer info
BytesToRead = MsgLen;
/*
* Read the whole message into the allocated buffer
*/
RetStat = RecvData(
SockNo,
*ppMsg,
BytesToRead,
Flags,
fDoTimedRecv ? FIVE_MTS : fDoTimedRecv,
pBytesRead
);
//
// If no bytes were read, deallocate memory
//
if ((*pBytesRead == 0) || (RetStat != WINS_SUCCESS))
{
ECommFreeBuff(*ppMsg);
}
}
else
{
DBGPRINT1(ERR, "CommReadStream: Message size (%x) is TOO BIG\n", MsgLen);
WINSEVT_LOG_M(MsgLen, WINS_EVT_MSG_TOO_BIG);
*pBytesRead = 0;
}
}
} // if (RetStat == WINS_SUCCESS)
#ifdef WINSDBG
else
{
//
// *pBytesRead = 0 is a valid condition. It indicates a
// disconnect from the remote WINS
//
}
#endif
#ifdef WINSDBG
} // end of try { .. }
except (EXCEPTION_EXECUTE_HANDLER) {
DBGPRINTEXC("CommReadStream");
WINS_HDL_EXC_M(WinsMem);
WINS_RERAISE_EXC_M();
}
#endif
DBGLEAVE("CommReadStream\n");
return(RetStat);
} //CommReadStream()
VOID
ProcTcpMsg(
IN SOCKET SockNo,
IN MSG_T pMsg,
IN MSG_LEN_T MsgLen,
OUT LPBOOL pfSockCl
)
/*++
Routine Description:
This function processes a TCP message after it has been read in
Arguments:
SockNo - Socket on which data was received
pMsg - Buffer containing data
MsgLen - Size of buffer
pfSockCl - Flag indicating whether the socket was closed
Externals Used:
None
Return Value:
None
Error Handling:
Called by:
HandleMsg
Side Effects:
Comments:
None
--*/
{
#if SUPPORT612WINS > 0
BYTE AssocMsg[COMMASSOC_POST_BETA1_ASSOC_MSG_SIZE];
#else
BYTE AssocMsg[COMMASSOC_ASSOC_MSG_SIZE];
#endif
DWORD Opc;
DWORD MsgTyp;
DWORD MsgSz = sizeof(AssocMsg);
PCOMMASSOC_ASSOC_CTX_T pAssocCtx;
PCOMMASSOC_DLG_CTX_T pDlgCtx;
BOOL fAssocAV = FALSE;
DBGENTER("ProcTcpMsg\n");
//#ifdef WINSDBG
try {
//#endif
/*
Get the opcode and check whether it is an NBT message or a
message from a WINSS.
*/
if (NMSISNBT_M(pMsg))
{
/*
* Get the assoc. ctx block associated with the socket
*/
if ( (pAssocCtx = CommAssocLookupAssoc(SockNo) ) == NULL )
{
ECommFreeBuff(pMsg);
WINSEVT_LOG_D_M(WINS_FAILURE, WINS_EVT_CANT_LOOKUP_ASSOC);
WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
}
if (pAssocCtx->DlgHdl.pEnt == NULL)
{
pDlgCtx = CommAssocAllocDlg();
//
// The following will initialize the dlg and assoc ctx
// blocks. The association will be marked ACTIVE.
//
COMMASSOC_SETUP_COMM_DS_M(
pDlgCtx,
pAssocCtx,
COMM_E_NBT,
COMMASSOC_DLG_E_IMPLICIT
);
}
/*
* Parse the message
*/
ParseMsg(
pMsg,
MsgLen,
pAssocCtx->Typ_e,
&pAssocCtx->RemoteAdd,
pAssocCtx
);
}
else /*message from WINS */
{
ULONG uLocalAssocCtx;
COMM_GET_HEADER_M(pMsg, Opc, uLocalAssocCtx, MsgTyp);
DBGPRINT1(REPL,"ProcTcpMsg: Got Wins msg with tag %08x.\n", uLocalAssocCtx);
pAssocCtx = (PCOMMASSOC_ASSOC_CTX_T)CommAssocTagMap(&sTagAssoc, uLocalAssocCtx);
/*
If the ptr to my assoc. ctx block is NULL, it means that
this is the "start asssoc req" message from the remote WINS.
We don't need to check MsgTyp but are doing it anyway for more
robust error checking
*/
if ((pAssocCtx == NULL) && (MsgTyp == COMM_START_REQ_ASSOC_MSG))
{
/*
Get the assoc. ctx block associated with the socket
*/
if ( (pAssocCtx = CommAssocLookupAssoc(SockNo)) == NULL )
{
ECommFreeBuff(pMsg);
WINSEVT_LOG_D_M(WINS_FAILURE, WINS_EVT_CANT_LOOKUP_ASSOC);
WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
}
/*
Unformat the assoc. message. This function will return
with an error status if the message received is not
a start assoc. message.
*/
CommAssocUfmStartAssocReq(
pMsg,
&pAssocCtx->Typ_e,
&pAssocCtx->MajVersNo,
&pAssocCtx->MinVersNo,
&pAssocCtx->uRemAssocCtx
);
#if SUPPORT612WINS > 0
if (MsgLen >= (COMMASSOC_POST_BETA1_ASSOC_MSG_SIZE - sizeof(LONG)))
{
pAssocCtx->MajVersNo = WINS_BETA2_MAJOR_VERS_NO;
}
#endif
//
// Free the buffer read in.
//
ECommFreeBuff(pMsg);
/*
check if association set up params specified in the
message are acceptable.
*/
//
// if the version numbers do not match, terminate the association
// and log a message
//
#if SUPPORT612WINS > 0
if (pAssocCtx->MajVersNo != WINS_BETA2_MAJOR_VERS_NO)
{
#endif
if (pAssocCtx->MajVersNo != WINS_MAJOR_VERS)
{
DelAssoc(0, pAssocCtx);
CommDisc(SockNo, TRUE);
*pfSockCl = TRUE;
//CommDecConnCount();
WINSEVT_LOG_M(pAssocCtx->MajVersNo, WINS_EVT_VERS_MISMATCH);
DBGLEAVE("ProcTcpMsg\n");
return;
}
#if SUPPORT612WINS > 0
}
#endif
FUTURES("When we support more sophisticated association set up protocol")
FUTURES("we will check the params. A more sophisticated set up protocol")
FUTURES("is one where there is some negotiation going one. Backward")
FUTURES("compatibility is another item which would require it")
/*
* Format a start assoc. response message.
*
* The address passed to the formatting function is offset
* from the address of the buffer by a LONG so that
* CommSendAssoc can store the length of the message in it.
*/
CommAssocFrmStartAssocRsp(
pAssocCtx,
AssocMsg + sizeof(LONG),
MsgSz - sizeof(LONG)
);
CommSendAssoc(
pAssocCtx->SockNo,
AssocMsg + sizeof(LONG),
MsgSz - sizeof(LONG)
);
//
// Allocate the dlg and initialize the assoc and dlg ctx blocks.
// The association is marked ACTIVE
//
pDlgCtx = CommAssocAllocDlg();
COMMASSOC_SETUP_COMM_DS_M(
pDlgCtx,
pAssocCtx,
pAssocCtx->Typ_e,
COMMASSOC_DLG_E_IMPLICIT
);
}
else /*the assoc has to be in the ACTIVE state */
{
/*
Let us check that this is not the stop assoc message
*/
if (MsgTyp == COMM_STOP_REQ_ASSOC_MSG)
{
fAssocAV = TRUE;
DelAssoc(0, pAssocCtx);
fAssocAV = FALSE;
ECommFreeBuff(pMsg);
CommDisc(SockNo, TRUE);
*pfSockCl = TRUE;
//CommDecConnCount();
}
else
{
PERF("Remove this test")
CHECK("Is there any need for this test")
fAssocAV = TRUE;
if (pAssocCtx->State_e == COMMASSOC_ASSOC_E_NON_EXISTENT)
{
fAssocAV = FALSE;
ECommFreeBuff(pMsg);
WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_BAD_STATE_ASSOC);
DelAssoc(0, pAssocCtx);
CommDisc(SockNo, TRUE);
*pfSockCl = TRUE;
// CommDecConnCount();
WINS_RAISE_EXC_M(WINS_EXC_BAD_STATE_ASSOC);
}
else
{
fAssocAV = FALSE;
}
/*
* Parse the message header to determine what message it is.
*/
ParseMsg(
pMsg,
MsgLen,
pAssocCtx->Typ_e,
&pAssocCtx->RemoteAdd, //not used
pAssocCtx
);
} //else (msg is not stop assoc msg)
} //else (assoc is active)
} // else (message is from a remote wins
//#ifdef WINSDBG
} // end of try block
except(EXCEPTION_EXECUTE_HANDLER) {
DWORD ExcCode = GetExceptionCode();
FUTURES("Distinguish between different exceptions. Handle some. Reraise others")
DBGPRINT1(EXC, "ProcTcpMsg: Got Exception (%x)\n", ExcCode);
WINSEVT_LOG_D_M(ExcCode, WINS_EVT_SFT_ERR);
if (ExcCode == WINS_EXC_COMM_FAIL)
{
DelAssoc(0, pAssocCtx);
CommDisc(SockNo, TRUE);
*pfSockCl = TRUE;
// CommDecConnCount();
}
if (fAssocAV)
{
ECommFreeBuff(pMsg);
// Without the following the assoc and the tcp connection
// will stay until either the tcp connection gets a valid
// message (one with the correct pAssocCtx) or it gets
// terminated
#if 0
DelAssoc(SockNo, NULL);
CommDisc(SockNo, TRUE);
*pfSockCl = TRUE;
// CommDecConnCount();
#endif
}
// WINS_RERAISE_EXC_M();
}
//#endif
DBGLEAVE("ProcTcpMsg\n");
return;
} //ProcTcpMsg()
VOID
CommCreateTcpThd(
VOID
)
/*++
Routine Description:
This function creates the TCP listener thread
Arguments:
None
Externals Used:
None
Called by:
CommInit
Comments:
Return Value:
None
--*/
{
CreateThd(MonTcp, WINSTHD_E_TCP);
return;
}
VOID
CommCreateUdpThd(VOID)
/*++
Routine Description:
This function creates the UDP listener thread
Arguments:
None
Externals Used:
None
Called by:
CommInit
Comments:
Return Value:
None
--*/
{
CreateThd(MonUdp, WINSTHD_E_UDP);
return;
}
VOID
CreateThd(
DWORD (*pStartFunc)(LPVOID),
WINSTHD_TYP_E ThdTyp_e
)
/*++
Routine Description:
This function creates a COMSYS thread and initializes the
context for it.
Arguments:
pStartFunc -- address of startup function for the thread
ThdTyp_e -- Type of thread (TCP listener or UDP listener)
Externals Used:
WinsThdPool
Called by:
CommCreateTCPThd, CommCreateUDPThd
Comments:
None
Return Value:
None
--*/
{
HANDLE ThdHandle;
DWORD ThdId;
INT No;
/*
Create a thread with no sec attributes (i.e. it will take the
security attributes of the process), and default stack size
*/
ThdHandle = WinsMscCreateThd(
pStartFunc,
NULL, /*no arg*/
&ThdId
);
FUTURES("Improve the following to remove knowledge of # of threads in commsys")
/*
Grab the first slot for comm threads (2 slots in total) if available.
Else, use the second one. Initialize the thread context block
*/
No = (WinsThdPool.CommThds[0].fTaken == FALSE) ? 0 : 1;
{
WinsThdPool.CommThds[No].fTaken = TRUE;
WinsThdPool.CommThds[No].ThdId = ThdId;
WinsThdPool.CommThds[No].ThdHdl = ThdHandle;
WinsThdPool.CommThds[No].ThdTyp_e = ThdTyp_e;
}
WinsThdPool.ThdCount++;
return;
}
STATUS
CommConnect(
IN PCOMM_ADD_T pHostAdd,
IN SOCKET Port,
OUT SOCKET *pSockNo
)
/*++
Routine Description:
This function creates a TCP connection to a destination host
Arguments:
pHostAdd --pointer to Host's address
Port -- Port number to connect to
pSockNo -- ptr to a Socket variable
Called by:
Externals Used:
Return Value:
TBS
--*/
{
//struct sockaddr_in sin; //*Internet endpoint address
DWORD ConnCount;
ConnCount = InterlockedExchange(&CommConnCount, CommConnCount);
#ifdef WINSDBG
if (ConnCount >= 200)
{
DBGPRINT0(ERR,
"MonTcp: Connection limit of 200 reached. \n");
}
#endif
#if 0
if (ConnCount >= FD_SETSIZE)
{
DBGPRINT2(EXC, "CommConnect: Socket Limit reached. Current no = (%d). Connection not being made to WINS. Address faimly of WINS is (%s)\n",
ConnCount,
pHostAdd->AddTyp_e == COMM_ADD_E_TCPUDPIP ? "TCPIP" : "SPXIPX"
);
WINSEVT_LOG_D_M(ConnCount, WINS_EVT_CONN_LIMIT_REACHED);
WINS_RAISE_EXC_M(WINS_EXC_COMM_FAIL);
}
#endif
#if SPX == 0
if (CommTcp(pHostAdd, Port, pSockNo) != WINS_SUCCESS)
{
return(WINS_FAILURE);
}
#else
if (pHostAdd->AddTyp_e == COMM_ADD_E_TCPUDPIP)
{
if (CommTcp(pHostAdd, Port, pSockNo) != WINS_SUCCESS)
{
return(WINS_FAILURE);
}
}
else
{
if (CommSpx(pHostAdd, Port, pSockNo) != WINS_SUCCESS)
{
return(WINS_FAILURE);
}
}
//
#endif
// Connection has been made. Let us increment the connection count
//
InterlockedIncrement(&CommConnCount);
return(WINS_SUCCESS);
}
STATUS
CommTcp(
IN PCOMM_ADD_T pHostAdd,
IN SOCKET Port,
OUT SOCKET *pSockNo
)
{
struct sockaddr_in destsin; //*Internet endpoint address
struct sockaddr_in srcsin;
// DWORD ConnCount;
if (pHostAdd->Add.IPAdd == INADDR_NONE)
{
return(WINS_FAILURE);
}
//
// Create a TCP socket and connect it to the target host
//
if ((*pSockNo = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
{
WINSEVT_LOG_M(
WSAGetLastError(),
WINS_EVT_CANT_CREATE_TCP_SOCK_FOR_CONN
);
return(WINS_FAILURE);
}
if (WinsClusterIpAddress) {
WINSMSC_FILL_MEMORY_M(&srcsin, sizeof(srcsin), 0);
srcsin.sin_addr.s_addr = htonl(WinsClusterIpAddress);
srcsin.sin_family = PF_INET;
srcsin.sin_port = 0;
if ( bind(*pSockNo,(struct sockaddr *)&srcsin,sizeof(srcsin)) == SOCKET_ERROR)
{
WINSEVT_LOG_M(WSAGetLastError(), WINS_EVT_WINSOCK_BIND_ERR); //log an event
return(WINS_FAILURE);
}
}
FUTURES("May want to call setsockopt() on it to enable graceful close")
WINSMSC_FILL_MEMORY_M(&destsin, sizeof(destsin), 0);
destsin.sin_addr.s_addr = htonl(pHostAdd->Add.IPAdd);
destsin.sin_family = PF_INET;
destsin.sin_port = (u_short)htons((u_short)Port);
if (
connect(*pSockNo, (struct sockaddr *)&destsin, sizeof(destsin))
== SOCKET_ERROR
)
{
struct in_addr InAddr;
InAddr.s_addr = destsin.sin_addr.s_addr;
WinsEvtLogDetEvt(FALSE, WINS_EVT_WINSOCK_CONNECT_ERR,
NULL, __LINE__, "sd",
COMM_NETFORM_TO_ASCII_M(&InAddr),
WSAGetLastError());
WINS_RAISE_EXC_M(WINS_EXC_COMM_FAIL);
}
return(WINS_SUCCESS);
}
#if SPX > 0
STATUS
CommSpx(
IN PCOMM_ADD_T pHostAdd,
IN SOCKET Port,
OUT SOCKET *pSockNo
)
{
struct sockaddr_ipx sipx; //*SPX/IPX endpoint address
LPVOID pRemAdd;
DWORD SizeOfRemAdd;
//
// Create an SPX socket and connect it to the target host
//
if ((*pSockNo = socket(PF_IPX, SOCK_STREAM, NSPROTO_SPX)) ==
INVALID_SOCKET)
{
WINSEVT_LOG_M(WSAGetLastError(),
WINS_EVT_CANT_CREATE_TCP_SOCK_FOR_CONN);
return(WINS_FAILURE);
}
WINSMSC_FILL_MEMORY_M(&sipx, sizeof(sipx), 0);
sipx.sa_socket = htons(Port);
sipx.sa_family = PF_IPX;
RtlCopyMemory(sipx.sa_netnum, pHostAdd->Add.netnum,
sizeof(pHostAdd->Add.netnum);
RtlCopyMemory(sipx.sa_nodenum, pHostAdd->Add.nodenum,
sizeof(pHostAdd->Add.nodenum);
FUTURES("May want to call setsockopt() on it to enable graceful close")
if (
connect(*pSockNo, (struct sockaddr *)&sipx, sizeof(sipx))
== SOCKET_ERROR
)
{
PERF("Pass address as binary data. Also log WSAGetLastError()")
WinsEvtLogDetEvt(FALSE, WINS_EVT_WINSOCK_CONNECT_ERR,
NULL, __LINE__, "sd",
sipx.sa_nodenum,
WSAGetLastError());
WINS_RAISE_EXC_M(WINS_EXC_COMM_FAIL);
}
return(WINS_SUCCESS);
}
#endif
VOID
CommSend(
COMM_TYP_E CommTyp_e,
PCOMM_HDL_T pAssocHdl,
MSG_T pMsg,
MSG_LEN_T MsgLen
)
/*++
Routine Description:
This function is called to send a TCP message to a WINS server or to
an nbt client
Arguments:
CommTyp_e - Type of communication
pAssocHdl - Handle to association to send message on
pMSg - Message to send
MsgLen - Length of above message
Externals Used:
None
Called by:
Replicator code
Comments:
This function should not be called for sending assoc messages.
Return Value:
None
--*/
{
PCOMM_HEADER_T pCommHdr = NULL;
PCOMMASSOC_ASSOC_CTX_T pAssocCtx = pAssocHdl->pEnt;
LPLONG pLong;
if (!CommLockBlock(pAssocHdl))
{
WINS_RAISE_EXC_M(WINS_EXC_LOCK_ASSOC_ERR);
}
try {
/*
* If it is not an NBT message (i.e. it is a WINS message), we
* need to set the header appropriately
*/
if (CommTyp_e != COMM_E_NBT)
{
pCommHdr = (PCOMM_HEADER_T)(pMsg - COMM_HEADER_SIZE);
pLong = (LPLONG) pCommHdr;
COMM_SET_HEADER_M(
pLong,
WINS_IS_NOT_NBT,
pAssocCtx->uRemAssocCtx,
COMM_RPL_MSG
);
pMsg = (LPBYTE)pCommHdr;
MsgLen = MsgLen + COMM_HEADER_SIZE;
}
/*
send the message
*/
CommSendAssoc(
pAssocCtx->SockNo,
pMsg,
MsgLen
);
}
finally {
CommUnlockBlock(pAssocHdl);
}
return;
}
VOID
CommSendAssoc(
SOCKET SockNo,
MSG_T pMsg,
MSG_LEN_T MsgLen
)
/*++
Routine Description:
This function is called to interface with the TCP/IP code for
sending a message on a TCP link
Arguments:
SockNo - Socket to send message on
pMsg - Message to send
MsgLen - Length of message to send
Externals Used:
None
Called by:
CommAssocSetUpAssoc
Comments:
None
Return Value:
None
--*/
{
int Flags = 0; //flags to indicate OOB or DONTROUTE
INT Error;
int BytesSent;
LONG Len = MsgLen;
LPLONG pLong = (LPLONG)(pMsg - sizeof(LONG));
int NoOfBytesToSend;
//initialize the last four bytes with the length of
//the message
COMM_HOST_TO_NET_L_M(Len, Len);
*pLong = Len;
MsgLen = MsgLen + 4;
while(MsgLen > 0)
{
//
// Since send(...) takes an int for the size of the message, let us
// be conservative (since int could be different on different
// machines) and not specify anything larger than MAXUSHORT.
//
// This strategy is also prudent since winsock may not work
// properly for sizes > 64K
//
if ( MsgLen > MAXUSHORT)
{
NoOfBytesToSend = MAXUSHORT;
}
else
{
NoOfBytesToSend = MsgLen;
}
BytesSent = send(
SockNo,
(LPBYTE)pLong,
NoOfBytesToSend,
Flags
);
if (BytesSent == SOCKET_ERROR)
{
Error = WSAGetLastError();
if (
(Error == WSAENOTCONN) ||
(Error == WSAECONNRESET) ||
(Error == WSAECONNABORTED) ||
(Error == WSAEDISCON)
)
{
DBGPRINT1(ERR, "CommSendAssoc: send returned SOCKET_ERROR due to connection abortion or reset. Error = (%d) \n", Error);
WINSEVT_LOG_D_M(
Error,
WINS_EVT_WINSOCK_SEND_MSG_ERR
);
WINS_RAISE_EXC_M(WINS_EXC_COMM_FAIL);
// break;
}
else
{
DBGPRINT1(ERR, "CommSendAssoc: send returned SOCKET_ERROR due to severe error = (%d) \n", Error);
//
// Some severe error. Raise an exception. We
// don't want the caller to ignore this.
//
WINSEVT_LOG_M(Error, WINS_EVT_WINSOCK_SEND_ERR);
WINS_RAISE_EXC_M(WINS_EXC_COMM_FAIL);
}
}
else
{
if (BytesSent < NoOfBytesToSend)
{
DBGPRINT2(ERR, "CommSendAssoc: Bytes Sent (%d) are < Specified (%d)\n", BytesSent, NoOfBytesToSend);
WINSEVT_LOG_D_M(BytesSent, WINS_EVT_WINSOCK_SEND_MSG_ERR);
/*
* The connection could have gone down because of the
* other side aborting in the middle
*
* We should log an error but not raise an exception.
*/
//WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
WINS_RAISE_EXC_M(WINS_EXC_COMM_FAIL);
// break;
}
else //BytesSent == NoOfBytesToSend
{
//
// Let us update the length left and the pointer into the
// buffer to send.
//
MsgLen -= BytesSent;
pLong = (LPLONG)((LPBYTE)pLong + BytesSent);
}
}
}
return;
} // CommSendAssoc()
VOID
CommDisc(
SOCKET SockNo,
BOOL fDecCnt
)
/*++
Routine Description:
This function closes the connection (socket)
Arguments:
SockNo - Socket that needs to be disconnected
Externals Used:
None
Called by:
MonTcp, HandleMsg, ProcTcpMsg, CommEndAssoc
Comments:
None
Return Value:
None
--*/
{
DBGPRINT1(FLOW, "CommDisc: Closing socket = (%d)\n", SockNo);
if (closesocket(SockNo) == SOCKET_ERROR)
{
WINSEVT_LOG_M(WSAGetLastError(),
WINS_EVT_WINSOCK_CLOSESOCKET_ERR);
//WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
}
#ifdef WINSDBG
if (!sfMemoryOverrun)
{
if ((UINT_PTR)(pTmpW + 2) < (UINT_PTR)pEndPtr)
{
*pTmpW++ = 0xEFFFFFFE;
*pTmpW++ = SockNo;
}
else
{
WinsDbg |= 0x3;
DBGPRINT0(ERR, "CommDisc: Stopping socket close tracking to prevent Memory overrun\n")
sfMemoryOverrun = TRUE;
}
}
#endif
if (fDecCnt)
{
CommDecConnCount();
}
return;
}
VOID
CommSendUdp (
SOCKET SockNo,
struct sockaddr_in *pDest,
MSG_T pMsg,
MSG_LEN_T MsgLen
)
/*++
Routine Description:
This function is called to send a message to an NBT node using the
datagram port
Arguments:
SockNo - Socket to send message on (UDP port)
pDest - Address of node to send message to
pMsg - Message to send
MsgLen - Length of message to send
Externals Used:
None
Called by:
NmsNmh functions
Comments:
None
Return Value:
None
--*/
{
DWORD BytesSent;
DWORD Error;
int Flags = 0;
struct sockaddr_in CopyOfDest;
#if USENETBT > 0
//
// When the address to send the datagram to is CommNtfSockAdd, we
// use sockets, else we send it over NETBT.
//
#if MCAST > 0
if ((pDest != &CommNtfSockAdd) && (SockNo != CommUdpPortHandle))
#else
if (pDest != &CommNtfSockAdd)
#endif
{
SendNetbt(pDest, pMsg, MsgLen);
return;
}
#endif
//
// use copy of the destination so that when we change the byte
// order in it, we don't disturb the source. This is important
// because CommSendUdp can be called multiple times by HdlPushNtf
// in the Push thread with pDest pointing to the address of the
// UDP socket used by the TCP listener thread. This address is
// in host byte order and should not be changed
//
CopyOfDest = *pDest;
CopyOfDest.sin_addr.s_addr = htonl(pDest->sin_addr.s_addr);
BytesSent = (DWORD)sendto(
SockNo,
pMsg,
MsgLen,
Flags,
(struct sockaddr *)&CopyOfDest,
sizeof(struct sockaddr)
);
if ((BytesSent != MsgLen) || (BytesSent == SOCKET_ERROR))
{
Error = WSAGetLastError();
#ifdef WINSDBG
if (BytesSent == SOCKET_ERROR)
{
DBGPRINT1(ERR, "CommSendUdp:SendTo returned socket error. Error = (%d)\n", Error);
}
else
{
DBGPRINT0(ERR, "CommSendUdp:SendTo did not send all the bytes");
}
#endif
if (WinsCnf.State_e != WINSCNF_E_TERMINATING)
{
WINSEVT_LOG_D_M(Error, WINS_EVT_WINSOCK_SENDTO_ERR);
}
//
// Don't raise exception since sendto might have failed as
// a result of wrong address in the RFC name request packet.
//
// For sending responses to name requests, there is no
// possibility of WINS using a wrong address since the
// address it uses is the one that it got from recvfrom
// (stored In FromAdd field of the dlg ctx block.
//
// The possibility of a wrong address being there is
// only there when a WACK/name query/name release is sent
// by WINS. In this case, it takes the address that is
// stored in the database for the conflicting entry (this
// address is ofcourse the one that was passed in the
// RFC packet
//
// WSAEINVAL error is returned by GetLastError if the
// address is invalid (winsock document doesn't list this --
// inform Dave Treadwell about this).
//
FUTURES("At name registration, should WINS make sure that the address in ")
FUTURES("the packet is the same as the address it got from recvfrom")
FUTURES("probably yes")
//WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
}
return;
}
#if USENETBT > 0
VOID
SendNetbt (
struct sockaddr_in *pDest,
MSG_T pMsg,
MSG_LEN_T MsgLen
)
/*++
Routine Description:
This function is called to send a datagram through NETBT
Arguments:
Externals Used:
None
Return Value:
Success status codes --
Error status codes --
Error Handling:
Called by:
Side Effects:
Comments:
None
--*/
{
//
// Point to the address structure prefix
//
tREM_ADDRESS *pRemAdd = (tREM_ADDRESS *)(pMsg -
COMM_NETBT_REM_ADD_SIZE);
#ifdef JIM
{
BYTE TransId = *pMsg;
ASSERT(TransId == 0x80);
}
#endif
pRemAdd->Family = pDest->sin_family;
pRemAdd->Port = pDest->sin_port;
pRemAdd->IpAddress = htonl(pDest->sin_addr.s_addr);
#ifdef JIM
ASSERT(MsgLen > 0x20);
#endif
pRemAdd->LengthOfBuffer = MsgLen;
DeviceIoCtrl(
&sNetbtSndEvtHdl,
pRemAdd,
MsgLen + COMM_NETBT_REM_ADD_SIZE,
IOCTL_NETBT_WINS_SEND
);
return;
}
#endif
VOID
ParseMsg(
MSG_T pMsg,
MSG_LEN_T MsgLen,
COMM_TYP_E MsgTyp_e,
struct sockaddr_in *pFromAdd,
PCOMMASSOC_ASSOC_CTX_T pAssocCtx
)
/*++
Routine Description:
This function is called to process a message received on the
UDP port or a TCP connection.
Arguments:
pMsg - ptr to message received
MsgLen - length of message received
MsgType - type of message
pFromAdd - ptr to who it is from
pAssocHdl - Assoc Handle if it came on an association
Externals Used:
CommUdpNbtDlgTable
Called by:
ProcTcpMsg, MonUdp
Comments:
None
Return Value:
None
--*/
{
COMM_HDL_T DlgHdl;
COMMASSOC_DLG_CTX_T DlgCtx;
register PCOMMASSOC_DLG_CTX_T pDlgCtx;
BOOL fNewElem = FALSE;
try {
/*
* If the assoc handle is NULL, this is a UDP message
*/
if (pAssocCtx == NULL)
{
/*
* Check if this message is a response. If it is, the explicit
* dialogue is used
*/
if (*(pMsg + 2) & NMS_RESPONSE_MASK)
{
ENmsHandleMsg(
&CommExNbtDlgHdl,
pMsg,
MsgLen
);
return;
}
/*
* Initialize the STATIC dlg ctx block with the fields that the
* compare function will use to check if this is a duplicate
*/
WINSMSC_COPY_MEMORY_M(
&DlgCtx.FromAdd,
pFromAdd,
sizeof(struct sockaddr_in)
);
//
// Copy the first four bytes of the message into the FirstWrdOfMsg
// field of the Dlg Ctx block. The first 4 bytes contain the
// transaction id and the opcode. These values along with the
// address of the sender are used by CompareNbtReq to determine
// whether a request is a repeat request or a new one.
//
// Note: The message buffer and the dlg ctx block are deleted
// in different functions, the message buffer getting deleted
// first. We can not use the pointer to the message
// buffer for the purposes of getting at the first word at
// comparison time since then we open ourselves to the possibility
// of two dialogues pointing to the same block for a finite
// window (true, when requests are coming rapidly)
//
FUTURES("Directly assign the value instead of copying it")
WINSMSC_COPY_MEMORY_M(
&DlgCtx.FirstWrdOfMsg,
pMsg,
sizeof(DWORD)
);
/*
create and insert a dlg ctx block into the table of
NBT type Implicit dialogues. The key to searching for
a duplicate inside the table comprises of the Transaction Id
of the message, and the FromAdd of the nbt node that sent the
datagram.
(refer : CheckDlgDuplicate function).
*/
pDlgCtx = CommAssocInsertUdpDlgInTbl(&DlgCtx, &fNewElem);
if (pDlgCtx == NULL)
{
WINS_RAISE_EXC_M(WINS_EXC_OUT_OF_MEM);
}
/*
* If the dialogue for the particular command from the nbt node is
* already there, we will ignore this request, deallocate the
* UDP buffer and return.
*/
if (!fNewElem)
{
DBGPRINT0(FLOW, "Not a new element\n");
ECommFreeBuff(pMsg);
#ifdef WINSDBG
CommNoOfRepeatDgrms++;
#endif
return;
}
/*
* Initialize the dlg ctx block that got inserted
*/
pDlgCtx->Role_e = COMMASSOC_DLG_E_IMPLICIT;
pDlgCtx->Typ_e = COMM_E_UDP;
DlgHdl.pEnt = pDlgCtx;
/*
* Call name space manager to handle the request
*/
ENmsHandleMsg(&DlgHdl, pMsg, MsgLen);
}
else // the request came over an association
{
pDlgCtx = pAssocCtx->DlgHdl.pEnt;
//
// required by the PULL thread (HandlePushNtf).
// and the PUSH thread to print out the address of the WINS
// that sent the push trigger or the Pull request
//
WINSMSC_COPY_MEMORY_M(
&pDlgCtx->FromAdd,
pFromAdd,
sizeof(struct sockaddr_in)
);
/*
* The request came over a TCP connection. Examine the Dlg type
* and then call the appropriate component
*/
if (pAssocCtx->Typ_e == COMM_E_NBT)
{
/*
* It is an nbt request over a TCP connection. Call
* the Name Space Manager
*/
ENmsHandleMsg(
&pAssocCtx->DlgHdl,
pMsg,
MsgLen
);
}
else
{
/*
* Call the replicator component
*
* Note: pMsg points to COMM_HEADER_T on top of the
* data. We strip it off
*/
DBGIF(fWinsCnfRplEnabled)
ERplInsertQue(
WINS_E_COMSYS,
QUE_E_CMD_REPLICATE_MSG,
&pAssocCtx->DlgHdl,
pMsg + COMM_HEADER_SIZE,
MsgLen - COMM_HEADER_SIZE,
NULL, // no context
0 // no magic no.
);
}
}
}
except(EXCEPTION_EXECUTE_HANDLER) {
DBGPRINTEXC("ParseMsg");
/*
* If this dialogue was allocated as a result of an Insert
* get rid of it.
*/
if (fNewElem)
{
CommAssocDeleteUdpDlgInTbl( pDlgCtx );
}
WINS_RERAISE_EXC_M();
}
return;
}
LPVOID
CommAlloc(
IN PRTL_GENERIC_TABLE pTable,
IN CLONG BuffSize
)
/*++
Routine Description:
This function is called to allocate a buffer
Arguments:
pTable - Table where the buffer will be stored
BuffSize - Size of buffer to allocate
Externals Used:
None
Return Value:
Success status codes -- ptr to buffer allocated
Error status codes --
Error Handling:
Called by:
RtlInsertElementGeneric()
Side Effects:
Comments:
This function exists just because the RtlTbl functions require
this prototype for the user specified alloc function.
--*/
{
LPVOID pTmp;
UNREFERENCED_PARAMETER(pTable);
WinsMscAlloc( (DWORD) BuffSize, &pTmp );
return(pTmp);
}
VOID
CommDealloc(
IN PRTL_GENERIC_TABLE pTable,
IN PVOID pBuff
)
/*++
Routine Description:
This function is called to deallocate memory allocated via CommAlloc.
Arguments:
pTable - Table where buffer was stored
pBuff - Buffer to deallocate
Externals Used:
None
Return Value:
None
Error Handling:
Called by:
Side Effects:
Comments:
The pTable argument is required since the address of this function
is passed as an argument to RtlTbl functions
--*/
{
UNREFERENCED_PARAMETER(pTable);
WinsMscDealloc(
pBuff
);
return;
}
#if 0
RTL_GENERIC_COMPARE_RESULTS
CompareAssoc(
IN PRTL_GENERIC_TABLE pTable,
IN PVOID pFirstAssoc,
IN PVOID pSecondAssoc
)
/*++
Routine Description:
The function compares the first and the second assoc. structures
Arguments:
pTable - table where buffer (assoc. ctx block) is to be stored
pFirstAssoc - First assoc ctx block
pSecondAssoc - Second assoc ctx block
Externals Used:
None
Return Value:
Success status codes --
Error status codes --
Error Handling:
Called by:
RtlInsertElementGenericTable (called by MonTcp)
Side Effects:
Comments:
The pTable argument is ignored.
This function was once being used. Due to change in code, it is
no longer being used. It is kept here for potential future use
--*/
{
PCOMMASSOC_ASSOC_CTX_T pFirst = pFirstAssoc;
PCOMMASSOC_ASSOC_CTX_T pSecond = pSecondAssoc;
if (pFirst->SockNo == pSecond->SockNo)
{
return(GenericEqual);
}
if (pFirst->SockNo > pSecond->SockNo)
{
return(GenericGreaterThan);
}
else
{
return(GenericLessThan);
}
}
#endif
RTL_GENERIC_COMPARE_RESULTS
CommCompareNbtReq(
IN PRTL_GENERIC_TABLE pTable,
IN PVOID pFirstDlg,
IN PVOID pSecondDlg
)
/*++
Routine Description:
This function compares two dialogue context blocks. The fields
used for comparison are:
the address of the sender
the first long word of the message (contains transaction id
and opcode)
Arguments:
pTable - Table where the Dialogue for the NBT request will be stored
pFirstDlg - Dlg. ctx. block
pSecondDlg - Dlg. ctx. block
Externals Used:
None
Return Value:
Success status codes -- GenericLessThan or GenericGreaterThan
Error status codes -- GenericEqual
Error Handling:
Called by:
RtlInsertElementGenericTable (called by ParseMsg)
Side Effects:
Comments:
The pTable argument is ignored.
--*/
{
PCOMMASSOC_DLG_CTX_T pFirst = pFirstDlg;
PCOMMASSOC_DLG_CTX_T pSecond = pSecondDlg;
LONG Val = 0;
LONG FirstMsgLong = pFirst->FirstWrdOfMsg;
LONG SecondMsgLong = pSecond->FirstWrdOfMsg;
//
// There seems to be no Rtl function with the functionality of memcmp
// RtlCompareMemory does not tell you which of the comparators is
// smaller/larger
//
CHECK("Is there an Rtl function faster than memcmp in the nt arsenal\n");
if ( (Val = (long)memcmp(
&pFirst->FromAdd,
&pSecond->FromAdd,
sizeof(struct sockaddr_in)
)
) > 0
)
{
return(GenericGreaterThan);
}
else
{
if (Val < 0)
{
return(GenericLessThan);
}
}
/*
if the addresses are the same, compare the first long word of
the message
*/
Val = FirstMsgLong - SecondMsgLong;
if (Val > 0)
{
return(GenericGreaterThan);
}
else
{
if (Val < 0)
{
return(GenericLessThan);
}
}
return(GenericEqual);
} // CommCompareNbtReq()
VOID
CommEndAssoc(
IN PCOMM_HDL_T pAssocHdl
)
/*++
Routine Description:
This function is called to terminate an explicit association. It sends a stop
association response message to the WINS identified by the Address
in the assoc ctx block. It then closes the socket and deallocates the
association
Arguments:
pAssocHdl - Handle to the Association to be terminated
Externals Used:
None
Return Value:
None
Error Handling:
Called by:
ECommEndDlg (only for an explicit assoc)
Side Effects:
Comments:
--*/
{
BYTE Msg[COMMASSOC_ASSOC_MSG_SIZE];
DWORD MsgLen = COMMASSOC_ASSOC_MSG_SIZE;
PCOMMASSOC_ASSOC_CTX_T pAssocCtx = pAssocHdl->pEnt;
SOCKET SockNo;
// no need to lock the association
//
try {
/*
Format the Stop Assoc. Message
The address passed to the formatting function is offset
from the address of the buffer by a LONG so that CommSendAssoc
can store the length of the message in it.
*/
CommAssocFrmStopAssocReq(
pAssocCtx,
Msg + sizeof(LONG),
MsgLen - sizeof(LONG),
COMMASSOC_E_USER_INITIATED
);
CommSendAssoc(
pAssocCtx->SockNo,
Msg + sizeof(LONG),
MsgLen - sizeof(LONG)
);
CommAssocTagFree(&sTagAssoc, pAssocCtx->nTag);
}
except(EXCEPTION_EXECUTE_HANDLER) {
DBGPRINTEXC("CommEndAssoc");
}
//
// The above call might have failed (It will fail if the connection
// is down. This can happen for instance in the case where GetReplicas()
// in rplpull gets a comm. failure due to the connection going down).
//
SockNo = pAssocCtx->SockNo;
CommAssocDeallocAssoc(pAssocCtx);
CommDisc(SockNo, TRUE);
//
// decrement the conn. count
//
//CommDecConnCount();
return;
}
VOID
DelAssoc(
IN SOCKET SockNo,
IN PCOMMASSOC_ASSOC_CTX_T pAssocCtxPassed
)
/*++
Routine Description:
This function is called only by the TCP listener thread. The
socket no. therefore maps to a RESPONDER association. The function
is called when the TCP listener thread gets an error or 0 bytes
on doing a 'recv'.
Arguments:
SockNo - Socket of association that has to be removed
pAssocCtx - Assoc. ctx block to be removed
Externals Used:
None
Return Value:
None
Error Handling:
Called by:
ProcTcpMsg, HandleMsg
Side Effects:
Comments:
This function is called from HandleMsg() which is called
only by the TCP listener thread.
--*/
{
COMM_HDL_T DlgHdl;
PCOMMASSOC_ASSOC_CTX_T pAssocCtx;
DBGPRINT1(FLOW, "ENTER: DelAssoc. Sock No is (%d)\n", SockNo);
if (pAssocCtxPassed == NULL)
{
/*
Lookup the assoc. ctx block associated with the socket
*/
pAssocCtx = CommAssocLookupAssoc(SockNo);
/*
* There is no reason why the assoc. ctx block should not
* be there (a responder association is deleted only via this
* function).
*/
if(!pAssocCtx)
{
WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
}
}
else
{
pAssocCtx = pAssocCtxPassed;
}
/*
* Only, if the association is not in the non-existent state,
* look for a dialogue handle
*/
if (pAssocCtx->State_e != COMMASSOC_ASSOC_E_NON_EXISTENT)
{
/*
* get the dialogue handle
*/
DlgHdl = pAssocCtx->DlgHdl;
/*
* Lock the dialogue
*
* We have to synchronize with thread calling CommSndRsp
*/
CommLockBlock(&pAssocCtx->DlgHdl);
/*
Remove the assoc. from the table. This will also put
the assoc. in the free list.
*/
CommAssocDeleteAssocInTbl( pAssocCtx );
/*
dealloc the dialogue (i.e. put it in the free list)
Note: An implicit dialogue is deleted when the association(s)
it is mapped to terminates. If this dialogue was earlier
passed on to a client, the client will find out that it
has been deleted (via a communications failure exception)
when it tries to use it (which may be never) -- see ECommSndRsp
*/
CommAssocDeallocDlg( DlgHdl.pEnt );
/*
Unlock the dialogue so that other threads can use it
*/
CommUnlockBlock(&DlgHdl);
}
else
{
/*
Remove the assoc. from the table. This will also put
the assoc. in the free list
*/
CommAssocDeleteAssocInTbl(pAssocCtx);
}
DBGLEAVE("DelAssoc\n");
return;
}
#if PRSCONN
BOOL
CommIsBlockValid (
IN PCOMM_HDL_T pEntHdl
)
/*++
Routine Description:
This function is called to check if the hdl is valid
Arguments:
pEntHdl - Handle to entity to lock
Externals Used:
None
Return Value:
Success status codes -- TRUE
Error status codes -- FALSE
Error Handling:
Called by:
Side Effects:
Comments:
None
--*/
{
//
// pEnt will be NULL for a persistent dlg that was never created during
// the lifetime of this WINS instance or one that was ended.
//
if (pEntHdl->pEnt == NULL)
{
ASSERT(pEntHdl->SeqNo == 0);
return (FALSE);
}
//
// If we can lock the block, the dlg hdl is still valid. If not, it means
// that the dlg was terminated earlier.
//
if (CommLockBlock(pEntHdl))
{
(VOID)CommUnlockBlock(pEntHdl);
return(TRUE);
}
return(FALSE);
}
#endif
BOOL
CommLockBlock(
IN PCOMM_HDL_T pEntHdl
)
/*++
Routine Description:
This function is called to lock the COMSYS entity identified by the
handle.
Arguments:
pEntHdl - Handle to entity to lock
Externals Used:
None
Return Value:
Success status codes -- TRUE
Error status codes -- FALSE
Error Handling:
Called by:
Side Effects:
Comments:
None
--*/
{
PCOMM_TOP_T pTop = pEntHdl->pEnt;
//lock before checking
#if 0
WinsMscWaitInfinite(pTop->MutexHdl);
#endif
EnterCriticalSection(&pTop->CrtSec);
if (pEntHdl->SeqNo == pTop->SeqNo)
{
return(TRUE);
}
else
{
CommUnlockBlock(pEntHdl);
return(FALSE);
}
}
__inline
STATUS
CommUnlockBlock(
PCOMM_HDL_T pEntHdl
)
/*++
Routine Description:
This function is called to unlock the COMSYS entity identified by the
handle.
Arguments:
pEntHdl - Handle to entity to unlock
Externals Used:
None
Return Value:
Success status codes -- WINS_SUCCESS
Error status codes -- WINS_FAILURE
Error Handling:
Called by:
Side Effects:
Comments:
None
--*/
{
FUTURES("Change to a macro")
#if 0
BOOL RetVal = TRUE;
BOOL RetStat = WINS_SUCCESS;
#endif
PCOMM_TOP_T pTop = pEntHdl->pEnt;
LeaveCriticalSection(&pTop->CrtSec);
#if 0
RetVal = ReleaseMutex(pTop->MutexHdl);
if (RetVal == FALSE)
{
RetStat = WINS_FAILURE;
}
#endif
return(WINS_SUCCESS);
}
VOID
InitMem(
VOID
)
/*++
Routine Description:
This function is called to do all memory initialization required
by COMSYS.
Arguments:
None
Externals Used:
None
Return Value:
None
Error Handling:
Called by:
CommInit
Side Effects:
Comments:
None
--*/
{
/*
* Create Memory heap for UDP buffers
* We want mutual exclusion and generation of exceptions
*/
DBGPRINT0(HEAP_CRDL,"InitMem: Udp. Buff heap\n");
CommUdpBuffHeapHdl = WinsMscHeapCreate(
HEAP_GENERATE_EXCEPTIONS,
COMMASSOC_UDP_BUFFER_HEAP_SIZE
);
DBGPRINT0(HEAP_CRDL,"InitMem: Udp. Buff heap\n");
CommUdpDlgHeapHdl = WinsMscHeapCreate(
HEAP_GENERATE_EXCEPTIONS,
COMMASSOC_UDP_DLG_HEAP_SIZE
);
/*
* Create Memory heap for Assoc Ctx blocks.
* We want mutual exclusion and generation of exceptions
*/
DBGPRINT0(HEAP_CRDL,"InitMem: Assoc. blocks heap\n");
CommAssocAssocHeapHdl = WinsMscHeapCreate(
HEAP_GENERATE_EXCEPTIONS,
COMMASSOC_ASSOC_BLKS_HEAP_SIZE
);
/*
* Create Memory heap for dlg blocks
* We want mutual exclusion and generation of exceptions
*/
DBGPRINT0(HEAP_CRDL,"InitMem: Dlgs. blocks heap\n");
CommAssocDlgHeapHdl = WinsMscHeapCreate(
HEAP_GENERATE_EXCEPTIONS,
COMMASSOC_DLG_BLKS_HEAP_SIZE
);
/*
* Create Memory heap for messages on tcp connections
*/
DBGPRINT0(HEAP_CRDL,"InitMem: tcp connection message heap\n");
CommAssocTcpMsgHeapHdl = WinsMscHeapCreate(
HEAP_GENERATE_EXCEPTIONS,
COMMASSOC_TCP_MSG_HEAP_SIZE
);
return;
}
BOOL
ChkNtfSock(
IN fd_set *pActSocks,
IN fd_set *pRdSocks
)
/*++
Routine Description:
This function is called to check if there is a notification message
on the Notification socket. If there is one, it reads the message.
The message contains a socket # and a command to add or remove the
socket to/from the list of sockets being monitored by the TCP
listener thread.
Arguments:
pActSocks - Array of active sockets
pRdSocks - Array of sockets returned by select
Externals Used:
CommNtfSockHandle
Return Value:
TRUE - Yes, there was a message. The Active sockets array has been
changed.
FALSE - No. There was no message
Error Handling:
In case of an error, an exception is raised
Called by:
MonTcp
Side Effects:
Comments:
None
--*/
{
DWORD Error;
int RetVal;
COMM_NTF_MSG_T NtfMsg;
PCOMMASSOC_DLG_CTX_T pDlgCtx;
PCOMMASSOC_ASSOC_CTX_T pAssocCtx;
SOCKET Sock;
BOOL fNtfSockSet = TRUE;
if (FD_ISSET(CommNtfSockHandle, pRdSocks))
{
Sock = CommNtfSockHandle;
}
else
{
#if SPX > 0
if (FD_ISSET(CommIpxNtfSockHandle, pRdSocks))
{
Sock = CommIpxNtfSockHandle;
}
#endif
fNtfSockSet = FALSE;
}
if (fNtfSockSet)
{
//do a recvfrom to read in the data.
RetVal = recvfrom(
Sock,
(char *)&NtfMsg,
COMM_NTF_MSG_SZ,
0, //default flags (i.e. no peeking
//or reading OOB message
NULL, //don't want address of sender
0 //length of above arg
);
if (RetVal == SOCKET_ERROR)
{
Error = WSAGetLastError();
if (WinsCnf.State_e != WINSCNF_E_TERMINATING)
{
WINSEVT_LOG_M(
Error,
WINS_EVT_WINSOCK_RECVFROM_ERR
);
}
WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
}
pDlgCtx = NtfMsg.DlgHdl.pEnt;
pAssocCtx = pDlgCtx->AssocHdl.pEnt;
if (NtfMsg.Cmd_e == COMM_E_NTF_START_MON)
{
DBGPRINT1(FLOW, "ChkNtfSock: Adding Socket (%d) to monitor list\n", NtfMsg.SockNo);
//
// We do this since FD_SETSIZE can fail silently
//
if (pActSocks->fd_count < FD_SETSIZE)
{
FD_SET(NtfMsg.SockNo, pActSocks);
}
else
{
DBGPRINT1(ERR,
"ChkNtfSock: Connection limit of %d reached\n",
FD_SETSIZE);
WINSEVT_LOG_M(WINS_FAILURE,
WINS_EVT_CONN_LIMIT_REACHED);
//
//This will cleanup the dlg and assoc. ctx. blk
//
ECommEndDlg(&NtfMsg.DlgHdl);
return(TRUE);
}
//
// Add the association to the table of associations.
// Since this association will be monitored, we change
// the role of the association to RESPONDER. Also,
// change the dialogue role to IMPLICIT. These are
// sleight of hand tactics. The client who
// established the association (Replicator)
// does not care whet we do with the comm data
// structures as long as we monitor the dialogue that
// it initiated with a remote WINS
//
pAssocCtx->Role_e = COMMASSOC_ASSOC_E_RESPONDER;
pDlgCtx->Role_e = COMMASSOC_DLG_E_IMPLICIT;
pDlgCtx->FromAdd = pAssocCtx->RemoteAdd;
CommAssocInsertAssocInTbl(pAssocCtx);
}
else //COMM_NTF_STOP_MON
{
DBGPRINT1(FLOW, "ChkNtfSock: Removing Socket (%d) from monitor list\n", NtfMsg.SockNo);
FD_CLR(NtfMsg.SockNo, pActSocks);
//
//Remove the association from the table of
//associations. Since this association will not be
//monitored by the TCP thread, we change the role of
//the association to INITIATOR. Also, change the
//dialogue role to EXPLICIT. These are sleight of
//hand tactics.
//
if (CommLockBlock(&NtfMsg.DlgHdl))
{
pAssocCtx->Role_e = COMMASSOC_ASSOC_E_INITIATOR;
pDlgCtx->Role_e = COMMASSOC_DLG_E_EXPLICIT;
COMMASSOC_UNLINK_RSP_ASSOC_M(pAssocCtx);
pAssocCtx->RemoteAdd = pDlgCtx->FromAdd;
CommUnlockBlock(&NtfMsg.DlgHdl);
//
// Let us signal the PUSH thread so that it can
// hand over the connection to the PULL thread (See
// HandleUpdNtf in rplpush.c)
//
WinsMscSignalHdl(RplSyncWTcpThdEvtHdl);
}
else
{
//
//The dlg could not be locked. It means that before
//the tcp listener thread started processing this
//message, it had already processed a disconnect.
//
fCommDlgError = TRUE;
WinsMscSignalHdl(RplSyncWTcpThdEvtHdl);
}
}
return(TRUE);
}
return(FALSE);
} // ChkNtfSock()
STATUS
RecvData(
IN SOCKET SockNo,
IN LPBYTE pBuff,
IN DWORD BytesToRead,
IN INT Flags,
IN DWORD SecsToWait,
OUT LPDWORD pBytesRead
)
/*++
Routine Description:
This function is called to do a timed recv on a socket.
Arguments:
SockNo - Socket No.
pBuff - Buffer to read the data into
BytesToRead - The number of bytes to read
Flags - flag arguments for recv
SecsToWait - No of secs to wait for the first read.
pBytesRead - No of Bytes that are read
Externals Used:
None
Return Value:
Success status codes -- WINS_SUCCESS
Error status codes -- WINS_FAILURE or WINS_RECV_TIMED_OUT
Error Handling:
Called by:
CommReadStream
Side Effects:
Comments:
None
--*/
{
fd_set RdSocks;
int NoOfSockReady;
INT BytesRead = 0;
INT BytesLeft = BytesToRead;
DWORD InChars;
DWORD Error;
BOOL fFirst = TRUE;
STATUS RetStat;
FD_ZERO(&RdSocks);
FD_SET(SockNo, &RdSocks);
/*
* Read the whole message into the allocated buffer
*/
for (
InChars = 0;
BytesLeft > 0;
InChars += BytesRead
)
{
//
// Check if we were told to do a timed receive. This will
// never happen in the TCP listener thread
//
if (SecsToWait)
{
//
// Block on a timed select. The first time around we want to
// wait the time specified by caller. The caller expects the other
// side to send something within this much time. For subsequent
// reads we wait a pre-defined interval since the sender has already
// accumulated all that it wants to send and has started sending it
// obviating the need for us to wait long.
//
if (fFirst)
{
sTimeToWait.tv_sec = (long)SecsToWait;
fFirst = FALSE;
}
else
{
sTimeToWait.tv_sec = SECS_TO_WAIT;
}
if (
(
NoOfSockReady = select(
FD_SETSIZE /*ignored arg*/,
&RdSocks,
(fd_set *)0,
(fd_set *)0,
&sTimeToWait
)
) == SOCKET_ERROR
)
{
Error = WSAGetLastError();
DBGPRINT1(ERR,
"RecvData: Timed Select returned SOCKET ERROR. Error = (%d)\n",
Error);
// CommDecConnCount();
return(WINS_FAILURE);
}
else
{
DBGPRINT1(FLOW, "ReceiveData: Timed Select returned with success. No of Sockets ready - (%d) \n", NoOfSockReady);
if (NoOfSockReady == 0)
{
//
// Timing out of RecvData indicates some problem at
// the remote WINS (either it is very slow
// (overloaded) or the TCP listener thread is out of
// commission).
WINSEVT_LOG_INFO_D_M(
WINS_SUCCESS,
WINS_EVT_WINSOCK_SELECT_TIMED_OUT
);
DBGPRINT0(ERR, "ReceiveData: Select TIMED OUT\n");
*pBytesRead = 0;
*pBytesRead = BytesRead;
// CommDecConnCount();
return(WINS_RECV_TIMED_OUT);
}
}
}
//
// Do a blocking recv
//
BytesRead = recv(
SockNo,
(char *)(pBuff + InChars),
BytesLeft,
Flags
);
if (BytesRead == SOCKET_ERROR)
{
Error = WSAGetLastError();
DBGPRINT1(ERR,
"RecvData: recv returned SOCKET_ERROR. Error = (%d)\n",
Error);
/*
* If the connection was aborted or reset from the
* other end, we close the socket and return an error
*/
if (
(Error == WSAECONNABORTED)
||
(Error == WSAECONNRESET)
||
(Error == WSAEDISCON)
)
{
DBGPRINT0(ERR,
"RecvData: Connection aborted\n");
WINSEVT_LOG_INFO_D_M(
WINS_SUCCESS,
WINS_EVT_CONN_ABORTED
);
}
*pBytesRead = BytesRead;
// CommDecConnCount();
return(WINS_FAILURE);
}
if (BytesRead == 0)
{
/*recv returns 0 (normal graceful shutdown from
* either side)
* Note:
* recv returns 0 if the connection terminated with no
* loss of data from either end point of the connection
*/
//
// If we were told to do a non timed receive,
// we must be executing in the TCP listener thread
//
// We don't return an error status here since
// a disconnect is a valid condition (the other
// WINS is terminating its connection normally)
//
if (SecsToWait == 0)
{
RetStat = WINS_SUCCESS;
}
else
{
//
// The fact that we were told to do a
// timed select means that we are in
// a thread of one of the clients of
// COMSYS. We were expecting data but
// got a disconnect instead. Let us
// return an error
//
RetStat = WINS_FAILURE;
}
//
// We are done. Break out of the loop
//
*pBytesRead = BytesRead;
// CommDecConnCount();
return(RetStat);
}
BytesLeft -= BytesRead;
//
//We are here means that BytesRead > 0
//
} // end of for { ... }
*pBytesRead = InChars;
return(WINS_SUCCESS);
} // RecvData()
#if USENETBT > 0
VOID
CommOpenNbt(
DWORD FirstBindingIpAddr
)
/*++
Routine Description:
This function opens the NetBt device for the interface specified by
FirstBindingIpAddr.
Arguments:
path - path to the NETBT driver
oflag - currently ignored. In the future, O_NONBLOCK will be
relevant.
ignored - not used
Return Value:
An NT handle for the stream, or INVALID_HANDLE_VALUE if unsuccessful.
--*/
{
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
// STRING name_string;
UNICODE_STRING uc_name_string;
NTSTATUS status;
PFILE_FULL_EA_INFORMATION pEaBuffer;
ULONG EaBufferSize;
//
// Convert the path into UNICODE_STRING form
//
#ifdef _PNP_POWER_
RtlInitUnicodeString(&uc_name_string, L"\\Device\\NetBt_Wins_Export");
#else
#ifdef UNICODE
RtlInitUnicodeString(&uc_name_string, pWinsCnfNbtPath);
#else
RtlInitString(&name_string, pWinsCnfNbtPath);
RtlAnsiStringToUnicodeString(&uc_name_string, &name_string, TRUE);
#endif
#endif // _PNP_POWER_
InitializeObjectAttributes(
&ObjectAttributes,
&uc_name_string,
OBJ_CASE_INSENSITIVE,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL
);
EaBufferSize = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
strlen(WINS_INTERFACE_NAME) + 1 +
sizeof(FirstBindingIpAddr); // EA length
WinsMscAlloc(EaBufferSize, &pEaBuffer);
if (pEaBuffer == NULL)
{
WINS_RAISE_EXC_M(WINS_EXC_OUT_OF_MEM);
}
pEaBuffer->NextEntryOffset = 0;
pEaBuffer->Flags = 0;
pEaBuffer->EaNameLength = (UCHAR)strlen(WINS_INTERFACE_NAME);
//
// put "WinsInterface" into the name
//
RtlMoveMemory(
pEaBuffer->EaName,
WINS_INTERFACE_NAME,
pEaBuffer->EaNameLength + 1);
pEaBuffer->EaValueLength = sizeof(FirstBindingIpAddr);
*(DWORD UNALIGNED *)(pEaBuffer->EaName + pEaBuffer->EaNameLength + 1) = FirstBindingIpAddr;
status =
NtCreateFile(
&WinsCnfNbtHandle,
SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
&ObjectAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN_IF,
0,
pEaBuffer,
EaBufferSize
);
#ifndef UNICODE
RtlFreeUnicodeString(&uc_name_string);
#endif
WinsMscDealloc(pEaBuffer);
if(!NT_SUCCESS(status))
{
WinsEvtLogDetEvt(
FALSE,
WINS_PNP_FAILURE,
NULL,
__LINE__,
"d",
status);
DBGPRINT1(EXC, "CommOpenNbt: Status from NtCreateFile is (%x)\n",
status);
WINS_RAISE_EXC_M(WINS_EXC_NBT_ERR);
}
return;
} // CommOpenNbt
//------------------------------------------------------------------------
#if NEWNETBTIF == 0
//#include "nbtioctl.sav"
STATUS
CommGetNetworkAdd(
IN OUT PCOMM_ADD_T pAdd
)
/*++
Routine Description:
This procedure does an adapter status query to get the local name table.
It either prints out the local name table or the remote (cache) table
depending on whether WhichNames is NAMES or CACHE .
Arguments:
Return Value:
0 if successful, -1 otherwise.
--*/
{
LONG i;
PVOID pBuffer;
ULONG BufferSize=sizeof(tADAPTERSTATUS);
NTSTATUS Status;
tADAPTERSTATUS *pAdapterStatus;
ULONG QueryType;
PUCHAR pAddr;
ULONG Ioctl;
//
// Get the local name table
//
Ioctl = IOCTL_TDI_QUERY_INFORMATION;
Status = STATUS_BUFFER_OVERFLOW;
while (Status == STATUS_BUFFER_OVERFLOW)
{
WinsMscAlloc(BufferSize, &pBuffer);
Status = DeviceIoCtrl(
&sNetbtGetAddrEvtHdl,
pBuffer,
BufferSize,
Ioctl
);
if (Status == STATUS_BUFFER_OVERFLOW)
{
WinsMscDealloc(pBuffer);
BufferSize *=2;
if (BufferSize == 0xFFFF)
{
WINSEVT_LOG_D_M(BufferSize, WINS_EVT_UNABLE_TO_ALLOCATE_PACKET);
DBGPRINT1(ERR, "CommGetNetworkAdd: Unable to get address from NBT\n", BufferSize);
return(WINS_FAILURE);
}
}
}
pAdapterStatus = (tADAPTERSTATUS *)pBuffer;
if (pAdapterStatus->AdapterInfo.name_count == 0)
{
WINSEVT_LOG_D_M(WINS_FAILURE, WINS_EVT_ADAPTER_STATUS_ERR);
DBGPRINT0(ERR, "CommGetNetworkAdd: No names in NBT cache\n");
return(WINS_FAILURE);
}
//
// print out the Ip Address of this node
//
pAddr = &pAdapterStatus->AdapterInfo.adapter_address[2];
NMSMSGF_RETRIEVE_IPADD_M(pAddr, pAdd->Add.IPAdd);
WinsMscDealloc(pBuffer);
return(WINS_SUCCESS);
}
#else
STATUS
CommGetNetworkAdd(
)
/*++
Routine Description:
This routine gets all the Ip Addresses of Netbt interfaces.
Arguments:
Return Value:
0 if successful, -1 otherwise.
--*/
{
ULONG Buffer[NBT_MAXIMUM_BINDINGS + 1];
ULONG BufferSize=sizeof(Buffer);
NTSTATUS Status;
ULONG Ioctl;
PULONG pBuffer;
PULONG pBufferSv;
DWORD i, Count;
BOOL fAlloc = FALSE;
//
// Get the local addresses
//
Ioctl = IOCTL_NETBT_GET_IP_ADDRS;
//
// NETBT does not support more than 64 adapters and not more than
// one ip address per adapter. So, there can be a max of 64 ip addresses
// which means we don't require more than 65 * 4 = 280 bytes (256 for the
// addresses followed by a delimiter address of 0.
//
Status = DeviceIoCtrl(
&sNetbtGetAddrEvtHdl,
(LPBYTE)Buffer,
BufferSize,
Ioctl
);
if (Status != STATUS_SUCCESS)
{
BufferSize *= 10; //alocate a buffer that is 10 times bigger.
//surely, netbt can not be supporting so many
//addresses. If it is, then the netbt developer
//goofed in that (s)he did not update
//NBT_MAXIMUM_BINDINGS in nbtioctl.h
WinsMscAlloc(BufferSize, &pBuffer);
DBGPRINT1(ERR, "CommGetNetworkAdd: Ioctl - GET_IP_ADDRS failed. Return code = (%x)\n", Status);
Status = DeviceIoCtrl(
&sNetbtGetAddrEvtHdl,
(LPBYTE)pBuffer,
BufferSize,
Ioctl
);
if (Status != STATUS_SUCCESS)
{
DBGPRINT1(ERR, "CommGetNetworkAdd: Ioctl - GET_IP_ADDRS failed AGAIN. Return code = (%x)\n", Status);
WINSEVT_LOG_M(Status, WINS_EVT_UNABLE_TO_GET_ADDRESSES);
WinsMscDealloc(pBuffer); //dealloc the buffer
return(WINS_FAILURE);
}
fAlloc = TRUE;
}
else
{
pBuffer = Buffer;
}
//
// Count the number of addresses returned
// The end of the address table contains -1 and any null addresses
// contain 0
//
pBufferSv = pBuffer;
for(Count=0; *pBuffer != -1; pBuffer++)
{
// Increment Count only if it is a valid address.
if ( *pBuffer ) {
Count++;
}
}
if ( !Count ) {
DBGPRINT0(ERR, "CommGetNetworkAdd: Netbt did not give any valid address\n");
WINSEVT_LOG_M(Status, WINS_EVT_UNABLE_TO_GET_ADDRESSES);
if (fAlloc)
{
WinsMscDealloc(pBufferSv);
}
return(WINS_FAILURE);
}
if (pWinsAddresses)
{
WinsMscDealloc(pWinsAddresses);
}
//
// Allocate space for the addresses
//
WinsMscAlloc(sizeof(ADD_T) + ((Count - 1) * COMM_IP_ADD_SIZE), &pWinsAddresses);
pWinsAddresses->NoOfAdds = Count;
pBuffer = pBufferSv;
// Copy all valid addresses
for (i=0; i<Count; pBuffer++)
{
if ( *pBuffer ) {
pWinsAddresses->IpAdd[i] = *pBuffer;
i++;
}
}
if (fAlloc)
{
WinsMscDealloc(pBufferSv);
}
return(WINS_SUCCESS);
}
#endif
//------------------------------------------------------------------------
NTSTATUS
DeviceIoCtrl(
IN LPHANDLE pEvtHdl,
IN PVOID pDataBuffer,
IN DWORD DataBufferSize,
IN ULONG Ioctl
)
/*++
Routine Description:
This procedure performs an ioctl(I_STR) on a stream.
Arguments:
fd - NT file handle
iocp - pointer to a strioctl structure
Return Value:
0 if successful, -1 otherwise.
--*/
{
NTSTATUS status;
IO_STATUS_BLOCK iosb;
#if NEWNETBTIF == 0
TDI_REQUEST_QUERY_INFORMATION QueryInfo;
#endif
PVOID pInput = NULL;
ULONG SizeInput = 0;
#if NEWNETBTIF == 0
PERF("TDI_QUERY_INFORMATION is used only at WINS initialization")
if (Ioctl == IOCTL_TDI_QUERY_INFORMATION)
{
pInput = &QueryInfo;
QueryInfo.QueryType = TDI_QUERY_ADAPTER_STATUS; // node status
SizeInput = sizeof(TDI_REQUEST_QUERY_INFORMATION);
}
#endif
while (TRUE)
{
status = NtDeviceIoControlFile(
WinsCnfNbtHandle, // Handle
*pEvtHdl, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&iosb, // IoStatusBlock
Ioctl, // IoControlCode
pInput, // InputBuffer
SizeInput, // Buffer Length
pDataBuffer, // Output Buffer
DataBufferSize // Output BufferSize
);
if (status == STATUS_SUCCESS)
{
return(status);
}
else
{
//
// If status is PENDING, do a wait on the event
//
if (status == STATUS_PENDING)
{
status = NtWaitForSingleObject(
*pEvtHdl, // Handle
TRUE, // Alertable
NULL); // Timeout
if (status == STATUS_SUCCESS)
{
return(status);
}
}
}
//
// status returned by NtDeviceIoCtrl or NtWaitForSingleObject is
// a failure code
//
DBGPRINT1(ERR, "DeviceIoCtrl, Status returned is (%x)\n", status);
if (status != STATUS_CANCELLED)
{
//
// If it is insufficient resources, we drop this datagram and
// try again (only for recv)
//
if (Ioctl == IOCTL_NETBT_WINS_RCV)
{
if (status == STATUS_INSUFFICIENT_RESOURCES)
{
continue;
}
}
//
// in case of a send, it can be invalid handle, invalid
// parameter or insufficient resourcesi. If it is INVALID_PARAMETER,
// it means that we passed 0 in the Address field on top of the buffer.
//
// Drop this datagram and return to the caller
//
if (Ioctl == IOCTL_NETBT_WINS_SEND)
{
if (
(status == STATUS_INSUFFICIENT_RESOURCES)
||
(status == STATUS_INVALID_PARAMETER)
)
{
return(STATUS_SUCCESS);
}
else
{
DBGPRINT1(EXC, "NtDeviceIoCtrl returned error = (%x)\n",
status);
WINSEVT_LOG_D_M(status, WINS_EVT_NETBT_SEND_ERR);
//
// If the machine's address has gone away due to some
// reason, WinsCnfNbtHandle will have been changed
// to NULL. In this case, we will get
// STATUS_INVALID_HANDLE error. We do not check for
// handle being NULL prior to making the Nbt call
// to avoid an if check which is of no value for
// 99% of the time.
//
// An error will be logged up above. We should not see
// too many of these since the window where
// WinsCnfNbtHandle is NULL is very small unless WINS
// is terminating in which case this thread will
// just terminate as a result of the exception being
// raised below.
//
// The address can go away due to the following reasons
//
// 1) psched installation (unbind followed by bind)
// 2) Changing from dhcp/static or static/dhcp
// 3) ipconfig release/renew
//
//
// When the main thread has to terminate WINS, it
// closes WinsCnfNetbtHandle. A worker thread or a
// challenge thread might be busy dealing with its
// queue of work items (potentially long on a busy
// WINS) and may not see a termination
// signal from the main thread. This exception will
// terminate it
//
//
// Raise an exception if the wins is terminating
//
if (WinsCnf.State_e == WINSCNF_E_TERMINATING)
{
WINS_RAISE_EXC_M(WINS_EXC_NBT_ERR);
}
}
}
break;
}
break;
} // end of while (TRUE)
return(status);
}
#endif
LPVOID
CommHeapAlloc(
IN PRTL_GENERIC_TABLE pTable,
IN CLONG BuffSize
)
/*++
Routine Description:
This function is called to allocate a buffer
Arguments:
pTable - Table where the buffer will be stored
BuffSize - Size of buffer to allocate
Externals Used:
None
Return Value:
Success status codes -- ptr to buffer allocated
Error status codes --
Error Handling:
Called by:
RtlInsertElementGeneric()
Side Effects:
Comments:
This function exists just because the RtlTbl functions require
this prototype for the user specified alloc function.
--*/
{
LPVOID pTmp;
UNREFERENCED_PARAMETER(pTable);
pTmp = WinsMscHeapAlloc( CommUdpDlgHeapHdl, (DWORD) BuffSize );
return(pTmp);
}
VOID
CommHeapDealloc(
IN PRTL_GENERIC_TABLE pTable,
IN PVOID pBuff
)
/*++
Routine Description:
This function is called to deallocate memory allocated via CommAlloc.
Arguments:
pTable - Table where buffer was stored
pBuff - Buffer to deallocate
Externals Used:
None
Return Value:
None
Error Handling:
Called by:
Side Effects:
Comments:
The pTable argument is required since the address of this function
is passed as an argument to RtlTbl functions
--*/
{
UNREFERENCED_PARAMETER(pTable);
WinsMscHeapFree(
CommUdpDlgHeapHdl,
pBuff
);
return;
}
VOID
CommDecConnCount(
VOID
)
/*++
Routine Description:
This function decrements the conn. count
Arguments:
Externals Used:
None
Return Value:
Success status codes --
Error status codes --
Error Handling:
Called by:
Side Effects:
Comments:
None
--*/
{
DWORD ConnCount;
ConnCount = InterlockedExchange(&CommConnCount, CommConnCount);
if (ConnCount != 0)
{
InterlockedDecrement(&CommConnCount);
}
else
{
DBGPRINT0(ERR, "CommDecConnCount: WEIRD: ConnCount should not have been zero\n");
}
return;
}
#if PRSCONN
__inline
BOOL
CommIsDlgActive (
PCOMM_HDL_T pDlgHdl
)
{
fd_set RdSocks;
int NoOfSockReady;
BOOL fRetStat = TRUE;
DWORD Error;
PCOMMASSOC_DLG_CTX_T pDlgCtx = pDlgHdl->pEnt;
PCOMM_HDL_T pAssocHdl = &pDlgCtx->AssocHdl;
PCOMMASSOC_ASSOC_CTX_T pAssocCtx = pAssocHdl->pEnt;
if (!CommLockBlock(pAssocHdl))
{
return(FALSE);
}
try {
FD_ZERO(&RdSocks);
FD_SET(pAssocCtx->SockNo, &RdSocks);
sTimeToWait.tv_sec = 0;
//
// Pass socket and a win32 event with flag of FD_CLOSE to WSAEventSelect.
// if the socket is disconnected, the event will be set. NOTE, only one
// event select can be active on the socket at any time - vadime 9/2/98
//
FUTURES("Use WSAEventSelect for marginally better performance")
if (NoOfSockReady = select(
FD_SETSIZE /*ignored arg*/,
&RdSocks,
(fd_set *)0,
(fd_set *)0,
&sTimeToWait
) == SOCKET_ERROR)
{
Error = WSAGetLastError();
DBGPRINT1(ERR,
"RecvData: Timed Select returned SOCKET ERROR. Error = (%d)\n",
Error);
// CommDecConnCount();
return(FALSE);
}
else
{
DBGPRINT1(FLOW, "ReceiveData: Timed Select returned with success. No of Sockets ready - (%d) \n", NoOfSockReady);
//
// Either there is data or the socket is disconnected. There
// should never be any data. We will just assume a disconnect
// is there and return FALSE. The client (RPL) will end the dlg.
//
if (NoOfSockReady == 1)
{
fRetStat = FALSE;
}
ASSERT(NoOfSockReady == 0);
}
}
finally {
CommUnlockBlock(pAssocHdl);
}
return(fRetStat);
}
#endif
#if MCAST > 0
VOID
JoinMcastGrp(
VOID
)
/*++
Routine Description:
This function is called by the comm. subsystem to make WINS join
a multicast group
Arguments:
Externals Used:
None
Return Value:
Success status codes --
Error status codes --
Error Handling:
Called by:
CommCreatePorts
Side Effects:
Comments:
None
--*/
{
int Loop = 0; //to disable loopback of multicast messages on the
//same interface
DWORD Error;
struct ip_mreq mreq;
DBGENTER("JoinMcastGrp\n");
#if 0
//
// Open a socket for sending/receiving multicast packets. We open
// a seperate socket instead of using the one for udp datagrams since
// we don't want to impact the client name packet processing with any
// sort of overhead. Also, having a seperate socket keeps things nice
// and clean.
//
if ( (CommMcastPortHandle = socket(
PF_INET,
SOCK_DGRAM,
IPPROTO_UDP
)
) == INVALID_SOCKET
)
{
Error = WSAGetLastError();
DBGPRINT1(MTCAST, "JoinMcastGrp: Can not create MCAST socket\n", Error);
// WINSEVT_LOG_M(Error, WINS_EVT_CANT_CREATE_MCAST_SOCK); //log an event
return;
}
#endif
//
// Set TTL
//
if (setsockopt(
CommUdpPortHandle,
IPPROTO_IP,
IP_MULTICAST_TTL,
(char *)&WinsCnf.McastTtl,
sizeof((int)WinsCnf.McastTtl)) == SOCKET_ERROR)
{
closesocket(CommUdpPortHandle);
CommUdpPortHandle = INVALID_SOCKET;
Error = WSAGetLastError();
DBGPRINT1(MTCAST, "JoinMcastGrp: Can not set TTL option. Error = (%d)\n", Error);
// WINSEVT_LOG_M(Error, WINS_EVT_CANT_CREATE_MCAST_SOCK); //log an event
return;
}
#if 0
//
// Disable loopback of messages
//
if (setsockopt(CommUdpPortHandle, IPPROTO_IP, IP_MULTICAST_LOOP,
(char *)&Loop, sizeof(Loop)) == SOCKET_ERROR)
{
closesocket(CommUdpPortHandle);
CommUdpPortHandle = INVALID_SOCKET;
Error = WSAGetLastError();
DBGPRINT1(MTCAST, "JoinMcastGrp: Can not set DISABLE LOOPBACK option. Error = (%d)\n",
Error);
// WINSEVT_LOG_M(Error, WINS_EVT_CANT_CREATE_MCAST_SOCK); //log an event
return;
}
#endif
//
// Join a multicast grp
//
mreq.imr_multiaddr.s_addr = htonl(McastAdd.sin_addr.s_addr);
mreq.imr_interface.s_addr = INADDR_ANY; //use the default mcast i/f
if (setsockopt(CommUdpPortHandle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&mreq, sizeof(mreq)) == SOCKET_ERROR)
{
Error = WSAGetLastError();
closesocket(CommUdpPortHandle);
CommUdpPortHandle = INVALID_SOCKET;
DBGPRINT1(MTCAST, "JoinMcastGrp: Can not ADD SELF TO MCAST GRP. Error = (%d)\n", Error);
// WINSEVT_LOG_M(Error, WINS_EVT_CANT_CREATE_MCAST_SOCK); //log an event
return;
}
DBGLEAVE("JoinMcastGrp\n");
return;
}
VOID
CommLeaveMcastGrp(
VOID
)
/*++
Routine Description:
This function is called by the comm. subsystem to make WINS join
a multicast group
Arguments:
Externals Used:
None
Return Value:
Success status codes --
Error status codes --
Error Handling:
Called by:
CommCreatePorts
Side Effects:
Comments:
None
--*/
{
DWORD Error;
struct ip_mreq mreq;
//
// Leave a multicast grp
//
mreq.imr_multiaddr.s_addr = htonl(McastAdd.sin_addr.s_addr);
mreq.imr_interface.s_addr = INADDR_ANY; //use the default mcast i/f
if (setsockopt(CommUdpPortHandle, IPPROTO_IP, IP_DROP_MEMBERSHIP,
(char *)&mreq, sizeof(mreq)) == SOCKET_ERROR)
{
Error = WSAGetLastError();
DBGPRINT1(MTCAST, "CommLeaveMcastGrp: Can not DROP MEMBERSHIP TO MCAST GRP. Error = (%d)\n", Error);
return;
}
return;
}
BOOL
CheckMcastSock(
IN fd_set *pActSocks,
IN fd_set *pRdSocks
)
{
DWORD Error;
int RetVal;
BYTE Buff[COMM_DATAGRAM_SIZE];
PCOMM_MCAST_MSG_T pMcastMsg = (PCOMM_MCAST_MSG_T)Buff;
struct sockaddr_in RemWinsAdd;
int RemWinsAddLen = sizeof(RemWinsAdd);
LPBYTE pBody;
COMM_IP_ADD_T IPAdd;
BOOL fFound;
DWORD i, j;
DWORD NoOfAddsInPkt;
struct in_addr InAdd;
LPBYTE pAdd;
DWORD FirstDelEntryIndex;
PPNR_STATUS_T pPnrStatusTmp;
DBGENTER("CheckMcastSock\n");
if (FD_ISSET(CommUdpPortHandle, pRdSocks))
{
//do a recvfrom to read in the data.
RetVal = recvfrom(
CommUdpPortHandle,
(char *)pMcastMsg,
COMM_DATAGRAM_SIZE,
//COMM_MCAST_MSG_SZ + COMM_IP_ADD_SIZE,
0, //default flags (i.e. no peeking
//or reading OOB message
(struct sockaddr *)&RemWinsAdd,
&RemWinsAddLen
);
if (RetVal == SOCKET_ERROR)
{
Error = WSAGetLastError();
DBGPRINT1(MTCAST, "CheckMcastSock: recvfrom failed. Error = (%d)\n", Error);
if (WinsCnf.State_e != WINSCNF_E_TERMINATING)
{
WINSEVT_LOG_M(
Error,
WINS_EVT_WINSOCK_RECVFROM_ERR
);
}
//WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
}
//
// If we were told not to use self found pnrs, return
//
if (!WinsCnf.fUseSelfFndPnrs)
{
DBGLEAVE("ChkMcastSock - 99\n");
return(TRUE);
}
//
// If the sign is not in the valid range, return
//
if ((pMcastMsg->Sign < COMM_MCAST_SIGN_START) || (pMcastMsg->Sign > COMM_MCAST_SIGN_END))
{
DBGPRINT1(MTCAST, "Signature in received message = %d\n", pMcastMsg->Sign);
DBGLEAVE("CheckMcastSock - 1\n");
return(TRUE);
}
//
// Compute the number of addresses in the packet.
//
NoOfAddsInPkt = (RetVal - (COMM_MCAST_MSG_SZ - 1))/COMM_IP_ADD_SIZE;
DBGPRINT2(MTCAST, "ChkMcastSock: RetVal = (%d);NoOfAddsInPkt = (%d)\n", RetVal, NoOfAddsInPkt);
FirstDelEntryIndex = pPnrStatus->NoOfPnrs;
pBody = pMcastMsg->Body;
IPAdd = *(PCOMM_IP_ADD_T)pBody;
pBody += COMM_IP_ADD_SIZE;
//
// Loop until either all ip addresses in packets are
// exhausted or we get an ip. address of 0. If somebody
// sent a 0 address, then it is ok to ignore the rest.
//
for(
;
(IPAdd != 0) && NoOfAddsInPkt;
IPAdd = *(PCOMM_IP_ADD_T)pBody, pBody += COMM_IP_ADD_SIZE,
NoOfAddsInPkt--
)
{
DBGPRINT1(MTCAST, "CheckMcastSock: Processing WINS address = %lx\n", ntohl(IPAdd));
fFound = FALSE;
pPnrStatusTmp = (PPNR_STATUS_T)(pPnrStatus->Pnrs);
if (pMcastMsg->Code == COMM_MCAST_WINS_UP)
{
for (i=0; i < pPnrStatus->NoOfPnrs; i++, pPnrStatusTmp++)
{
if ((FirstDelEntryIndex == pPnrStatus->NoOfPnrs)
&&
(pPnrStatusTmp->State == COMM_MCAST_WINS_DOWN))
{
FirstDelEntryIndex = i;
}
if (IPAdd == pPnrStatusTmp->IPAdd)
{
if (pPnrStatusTmp->State == COMM_MCAST_WINS_DOWN)
{
pPnrStatusTmp->State = COMM_MCAST_WINS_UP;
InAdd.s_addr = IPAdd;
pAdd = inet_ntoa(InAdd);
WinsCnfAddPnr(RPL_E_PULL, pAdd);
WinsCnfAddPnr(RPL_E_PUSH, pAdd);
}
fFound = TRUE;
break;
}
}
if (!fFound && (i <= pPnrStatus->NoOfPnrs))
{
DWORD FirstFreeIndex;
PPNR_STATUS_T pPnr;
//
// since disable loopback is not working we
// have to check for message sent by self
//
FUTURES("Remove the if when winsock is enhanced to allow loopback to be")
FUTURES("disabled")
if (!ChkMyAdd(ntohl(IPAdd)))
{
InAdd.s_addr = IPAdd;
pAdd = inet_ntoa(InAdd);
if (FirstDelEntryIndex < pPnrStatus->NoOfPnrs)
{
FirstFreeIndex = FirstDelEntryIndex;
}
else
{
FirstFreeIndex = pPnrStatus->NoOfPnrs++;
if (pPnrStatus->NoOfPnrs == pPnrStatus->NoOfPnrSlots)
{
WINSMSC_REALLOC_M(MCAST_PNR_STATUS_SIZE_M(pPnrStatus->NoOfPnrSlots * 2), (LPVOID *)&pPnrStatus);
pPnrStatus->NoOfPnrSlots *= 2;
DBGPRINT1(DET, "CheckMcastSock: NO OF PNR SLOTS INCREASED TO (%d)\n", pPnrStatus->NoOfPnrSlots);
}
}
pPnr = (PPNR_STATUS_T)(pPnrStatus->Pnrs);
(pPnr + FirstFreeIndex)->State = COMM_MCAST_WINS_UP;
(pPnr + FirstFreeIndex)->IPAdd = IPAdd;
WinsCnfAddPnr(RPL_E_PULL, pAdd);
WinsCnfAddPnr(RPL_E_PUSH, pAdd);
DBGPRINT1(MTCAST, "CheckMcastSock: ADDED WINS partner with address = %s\n", pAdd);
}
}
}
else //has to be COMM_MCAST_WINS_DOWN
{
for (i=0; i < pPnrStatus->NoOfPnrs; i++, pPnrStatusTmp++)
{
if (IPAdd == pPnrStatusTmp->IPAdd)
{
if (pPnrStatusTmp->State == COMM_MCAST_WINS_DOWN)
{
fFound = TRUE;
}
else
{
pPnrStatusTmp->State = COMM_MCAST_WINS_DOWN;
}
break;
}
}
if (!fFound)
{
InAdd.s_addr = IPAdd;
pAdd = inet_ntoa(InAdd);
DBGPRINT1(MTCAST, "CheckMcastSock: Will REMOVE WINS partner with address = %s IFF Untouched by admin\n", pAdd);
WinsCnfDelPnr(RPL_E_PULL, pAdd);
WinsCnfDelPnr(RPL_E_PUSH, pAdd);
}
}
} // end of for loop
DBGLEAVE("ChkMcastSock - 2\n");
return(TRUE);
}
DBGLEAVE("ChkMcastSock - 3\n");
return(FALSE);
}
BOOL
ChkMyAdd(
COMM_IP_ADD_T IpAdd
)
{
DWORD i;
PCOMM_IP_ADD_T pIpAdd = pWinsAddresses->IpAdd;
for (i=0; i<pWinsAddresses->NoOfAdds; i++)
{
if (IpAdd == *pIpAdd++)
{
return(TRUE);
}
}
return(FALSE);
}
VOID
CommSendMcastMsg(
DWORD Code
)
{
PCOMM_MCAST_MSG_T pMcastMsg;
DWORD McastMsgLen;
LPBYTE pBody;
DWORD i;
COMM_IP_ADD_T Add = 0;
// --ft bug #103361: no need to send CommSendMcastMsg if there
// is no nic card here
if (pWinsAddresses == NULL)
return;
McastMsgLen = MCAST_PKT_LEN_M(pWinsAddresses->NoOfAdds + 1);
WinsMscAlloc(McastMsgLen, &pMcastMsg);
pMcastMsg->Code = Code;
pMcastMsg->Sign = COMM_MCAST_SIGN_START;
pBody = pMcastMsg->Body;
//
// Insert the count in net order.
//
// NMSMSGF_INSERT_ULONG_M(pBody, pWinsAddresses->NoOfAddresses);
//
// Insert the addresses in net order
//
for (i=0; i<pWinsAddresses->NoOfAdds; i++)
{
DBGPRINT1(MTCAST, "CommSendMcastMsg: Inserting Address = (%lx)\n",
pWinsAddresses->IpAdd[i]);
NMSMSGF_INSERT_IPADD_M(pBody, pWinsAddresses->IpAdd[i]);
}
NMSMSGF_INSERT_IPADD_M(pBody, Add);
DBGPRINT1(MTCAST, "CommSendMcastMsg: Sending MCAST msg of length = (%d)\n",
McastMsgLen);
CommSendUdp (CommUdpPortHandle, &McastAdd, (MSG_T)pMcastMsg, McastMsgLen);
WinsMscDealloc(pMcastMsg);
return;
}
#endif