|
|
/****************************************************************************
* (c) Copyright 1993 Micro Computer Systems, Inc. All rights reserved. ***************************************************************************** * * Title: IPX WinSock Helper DLL for Windows NT * * Module: ipx/sockhelp/wshelper.c * * Version: 1.00.00 * * Date: 04-08-93 * * Author: Brian Walker * ***************************************************************************** * * Change Log: * * Date DevSFC Comment * -------- ------ ------------------------------------------------------- * ***************************************************************************** * * Functional Description: * ****************************************************************************/ #include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windef.h>
#include <winbase.h>
#include <tdi.h>
#include <winsock2.h>
#include <wsahelp.h>
#include <basetyps.h>
#include <nspapi.h>
#include <nspapip.h>
#include <wsipx.h>
#include <wsnwlink.h>
#include <isnkrnl.h>
#include <stdio.h>
#if defined(UNICODE)
#define NWLNKSPX_SERVICE_NAME L"nwlnkspx"
#else
#define NWLNKSPX_SERVICE_NAME "nwlnkspx"
#endif
typedef struct _IPX_OLD_ADDRESS_DATA { UINT adapternum; UCHAR netnum[4]; UCHAR nodenum[6]; } IPX_OLD_ADDRESS_DATA, *PIPX_OLD_ADDRESS_DATA;
/** Device names for IPX sockets **/
#define ISNDGRAM_DEVNAME L"\\Device\\NwlnkIpx"
/** Device names for SPX/SPXII sockets **/
#define ISNSTREAM_DEVNAME L"\\Device\\NwlnkSpx\\SpxStream"
#define ISNSEQPKT_DEVNAME L"\\Device\\NwlnkSpx\\Spx"
#define ISNSTREAMII_DEVNAME L"\\Device\\NwlnkSpx\\Stream"
#define ISNSEQPKTII_DEVNAME L"\\Device\\NwlnkSpx"
/** Friendly names for IPX and SPX. **/
#define SPX_NAME L"SPX"
#define SPX2_NAME L"SPX II"
#define IPX_NAME L"IPX"
/** Start for IPX protocol families **/
#define MCSBASE_DGRAM NSPROTO_IPX
#define BUFFER_SIZE 40
/** **/
UCHAR wsh_bcast[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
// SPX Loaded flag, set for each process
BOOLEAN SpxLoaded = FALSE;
//
// IPX/SPX provider GUIDs.
//
GUID IpxProviderGuid = { /* 11058240-be47-11cf-95c8-00805f48a192 */ 0x11058240, 0xbe47, 0x11cf, { 0x95, 0xc8, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92} };
GUID SpxProviderGuid = { /* 11058241-be47-11cf-95c8-00805f48a192 */ 0x11058241, 0xbe47, 0x11cf, { 0x95, 0xc8, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92} };
/** Forward Decls/External Prototypes **/ DWORD WshLoadSpx( VOID);
extern INT do_tdi_action( HANDLE, ULONG, PUCHAR, INT, BOOLEAN, PHANDLE OPTIONAL);
/*page****************************************************************
These are the triples we support. *********************************************************************/ typedef struct _MAPPING_TRIPLE { INT triple_addrfam; INT triple_socktype; INT triple_protocol; } MAPPING_TRIPLE, *PMAPPING_TRIPLE; #define MAPPING_NUM_COLUMNS 3
extern MAPPING_TRIPLE stream_triples[]; extern int stream_num_triples; extern int stream_table_size;
extern MAPPING_TRIPLE dgram_triples[]; extern int dgram_num_triples; extern int dgram_table_size;
/** Forward declarations on internal routines **/
BOOLEAN is_triple_in_list(PMAPPING_TRIPLE, ULONG, INT, INT, INT);
/**
There is one of these structures allocated for every socket that is created for us. **/
typedef struct _WSHIPX_SOCKET_CONTEXT { INT con_addrfam; INT con_socktype; INT con_pcol; INT con_flags; UCHAR con_sendptype; /* Current send packet type */ UCHAR con_recvptype; /* Recv ptype we are filtering on */ UCHAR con_dstype; /* Datastream type */ } WSHIPX_SOCKET_CONTEXT, *PWSHIPX_SOCKET_CONTEXT;
/** Values for con_flags **/
#define WSHCON_FILTER 0x0001 /* We are filtering on recv pkt type */
#define WSHCON_EXTADDR 0x0002 /* Extended addressing is on */
#define WSHCON_SENDHDR 0x0004 /* Send header flag */
#define WSHCON_RCVBCAST 0x0008 /* It does receive broadcasts */
#define WSHCON_IMM_SPXACK 0x0020 /* Immediate spx acks no piggyback */
/*page***************************************************************
W S H O p e n S o c k e t
This is called for the socket call. We make sure that we support the address family/socket type/protocol triple given and then we will allocate some memory to keep track of the socket.
Arguments - addrfam = Entry: Address family from socket call Exit: Filled in address family socktype = Entry: Socket type from socket call Exit: Filled in socket type pcol = Entry: Protocol from socket call Exit: Filled in protocol devname = Ptr to where to store device name pcontext = Where to store context value events = Bitmask for events we want to know about
Returns - NO_ERROR = OK Else = WinSock Error Code *********************************************************************/ INT WSHOpenSocket(PINT addrfam, PINT socktype, PINT pcol, PUNICODE_STRING devname, PVOID *pcontext, PDWORD events) { PWSHIPX_SOCKET_CONTEXT context;
/** Determine whether this is DGRAM or STREAM or SEQPACKET **/
if (is_triple_in_list(stream_triples, stream_num_triples, *addrfam, *socktype, *pcol)) {
if (*socktype == SOCK_SEQPACKET) { if (*pcol == NSPROTO_SPX) RtlInitUnicodeString(devname, ISNSEQPKT_DEVNAME); else RtlInitUnicodeString(devname, ISNSEQPKTII_DEVNAME); } else { if (*pcol == NSPROTO_SPX) RtlInitUnicodeString(devname, ISNSTREAM_DEVNAME); else RtlInitUnicodeString(devname, ISNSTREAMII_DEVNAME); }
if (!SpxLoaded) {
WshLoadSpx();
} }
/** Check for DGRAM **/
else if (is_triple_in_list(dgram_triples, dgram_num_triples, *addrfam, *socktype, *pcol)) {
RtlInitUnicodeString(devname, ISNDGRAM_DEVNAME); }
/**
All others are errors. This should never happen unless the registry information is wrong. **/
else return WSAEINVAL;
/** Allocate context for the socket **/
context = RtlAllocateHeap(RtlProcessHeap(), 0L, sizeof(*context)); if (context == NULL) return WSAENOBUFS;
/** Init the context **/
context->con_addrfam = *addrfam; context->con_socktype = *socktype; context->con_pcol = *pcol; context->con_flags = WSHCON_RCVBCAST; context->con_sendptype = (UCHAR)(*pcol - MCSBASE_DGRAM); context->con_recvptype = 0; context->con_dstype = 0;
/**
Tell the Windows Sockets DLL which state transitions we are interested in. **/
*events = WSH_NOTIFY_CLOSE | WSH_NOTIFY_BIND | WSH_NOTIFY_CONNECT;
/** Give WinSock DLL our context pointer **/
*pcontext = context;
/** Everything OK - return OK **/
return NO_ERROR; }
/*page**************************************************************
W S H G e t S o c k A d d r T y p e
This routine parses a sockaddr to determine the type of machine address and endpoint address portions of the sockaddr. This is called by the WinSock DLL whenever it needs to interpret a sockaddr.
Arguments - sockaddr = Ptr to sockaddr struct to evaluate sockaddrlen = Length of data in the sockaddr sockaddrinfo = Ptr to structure to recv info about the sockaddr
Returns - NO_ERROR = Evaluation OK Else = WinSock error code ********************************************************************/ INT WSHGetSockaddrType(PSOCKADDR sockaddr, DWORD sockaddrlen, PSOCKADDR_INFO sockaddrinfo) { PSOCKADDR_IPX sa = (PSOCKADDR_IPX)sockaddr;
/** Make sure the address family is correct **/
if (sa->sa_family != AF_NS) return WSAEAFNOSUPPORT;
/** Make sure the length is OK **/
if (sockaddrlen < sizeof(SOCKADDR_IPX)) return WSAEFAULT;
/** Looks like a good addr - determine the type **/
if (!memcmp(sa->sa_nodenum, wsh_bcast, 6)) sockaddrinfo->AddressInfo = SockaddrAddressInfoBroadcast; else sockaddrinfo->AddressInfo = SockaddrAddressInfoNormal;
/** Determine the endpoint **/
if (sa->sa_socket == 0) sockaddrinfo->EndpointInfo = SockaddrEndpointInfoWildcard; else if (ntohs(sa->sa_socket) < 2000) sockaddrinfo->EndpointInfo = SockaddrEndpointInfoReserved; else sockaddrinfo->EndpointInfo = SockaddrEndpointInfoNormal;
/** **/
return NO_ERROR; }
/*page**************************************************************
W S H G e t W i n s o c k M a p p i n g
Returns the list of address family/socket type/protocol triples supported by this helper DLL.
Arguments - mapping = Contect ptr from WSAOpenSocket maplen =
Returns - The length in bytes of a eeded OK Else = WinSock error code ********************************************************************/ DWORD WSHGetWinsockMapping(PWINSOCK_MAPPING mapping, DWORD maplen) { DWORD len;
/**
Figure how much data we are going to copy into the user buffer. **/
len = sizeof(WINSOCK_MAPPING) - sizeof(MAPPING_TRIPLE) + dgram_table_size + stream_table_size;
/**
If the buffer passed is too small, then return the size that is needed. The caller should then call us again with a buffer of the correct size. **/
if (len > maplen) return len;
/** Fill in the output buffer **/
mapping->Rows = stream_num_triples + dgram_num_triples; mapping->Columns = MAPPING_NUM_COLUMNS; RtlMoveMemory(mapping->Mapping, stream_triples, stream_table_size);
RtlMoveMemory((PCHAR)mapping->Mapping + stream_table_size, dgram_triples, dgram_table_size);
/** Return the number of bytes we filled in **/
return len; }
/*page***************************************************************
W S H N o t i f y
This routine is called for events that we registered at open socket time.
Arguments - context = Context ptr from WSAOpenSocket handle = Socket handle addrhandle = Datagram Handle connhandle = Connection Handle event = What event happened
Returns - NO_ERROR = Operation succeeded OK Else = WinSock error code *********************************************************************/ INT WSHNotify(PVOID context, SOCKET handle, HANDLE addrhandle, HANDLE connhandle, DWORD event) { INT rc; INT t1; PWSHIPX_SOCKET_CONTEXT ct;
/** Get context pointer **/
ct = (PWSHIPX_SOCKET_CONTEXT)context;
/** On close - just free the context structure **/
if (event == WSH_NOTIFY_CLOSE) { RtlFreeHeap(RtlProcessHeap(), 0L, context); return NO_ERROR; }
/** On bind set the send packet type **/
if (event == WSH_NOTIFY_BIND) { if (ct->con_socktype == SOCK_DGRAM) { /** Set the send packet ptype **/ t1 = (UINT)ct->con_sendptype; rc = WSHSetSocketInformation( context, handle, addrhandle, connhandle, NSPROTO_IPX, IPX_PTYPE, (PCHAR)&t1, sizeof(INT));
if (rc) return rc;
if (ct->con_flags & WSHCON_EXTADDR) { t1 = 1; rc = WSHSetSocketInformation( context, handle, addrhandle, connhandle, NSPROTO_IPX, IPX_EXTENDED_ADDRESS, (PCHAR)&t1, sizeof(INT));
if (rc) return rc; }
/** Set the recv filter packet type **/
if (ct->con_flags & WSHCON_FILTER) { t1 = (UINT)ct->con_recvptype; rc = WSHSetSocketInformation( context, handle, addrhandle, connhandle, NSPROTO_IPX, IPX_FILTERPTYPE, (PCHAR)&t1, sizeof(INT));
if (rc) return rc; }
/** Set up broadcast reception **/
if (ct->con_flags & WSHCON_RCVBCAST) {
t1 = 1; rc = WSHSetSocketInformation( context, handle, addrhandle, connhandle, NSPROTO_IPX, IPX_RECEIVE_BROADCAST, (PCHAR)&t1, sizeof(INT));
if (rc) return rc; }
/** Enable send header if we need to **/ if (ct->con_flags & WSHCON_SENDHDR) { t1 = 1; rc = WSHSetSocketInformation( context, handle, addrhandle, connhandle, NSPROTO_IPX, IPX_RECVHDR, (PCHAR)&t1, sizeof(INT));
if (rc) return rc; } } else if ((ct->con_socktype == SOCK_STREAM) || (ct->con_socktype == SOCK_SEQPACKET)) { if (ct->con_flags & WSHCON_SENDHDR) { t1 = 1; rc = WSHSetSocketInformation( context, handle, addrhandle, connhandle, NSPROTO_IPX, IPX_RECVHDR, (PCHAR)&t1, sizeof(INT));
if (rc) return rc; }
if (ct->con_flags & WSHCON_IMM_SPXACK) { t1 = 1; rc = WSHSetSocketInformation( context, handle, addrhandle, connhandle, NSPROTO_IPX, IPX_IMMEDIATESPXACK, (PCHAR)&t1, sizeof(INT));
if (rc) return rc; } }
/** It is OK - return OK **/ return NO_ERROR; }
/** On connect set things not set already **/ if (event == WSH_NOTIFY_CONNECT) {
/** If on DGRAM - just return OK **/ if (ct->con_socktype == SOCK_DGRAM) return NO_ERROR;
/**
If the datastream type has been set - set it **/
if (ct->con_dstype) { rc = do_tdi_action(connhandle, MSPX_SETDATASTREAM, &ct->con_dstype, 1, FALSE, NULL); if (rc) return rc; }
/** It is OK - return OK **/ return NO_ERROR; }
/** All others are bad **/ return WSAEINVAL; }
/*page**************************************************************
W S H G e t S o c k I n f o r m a t i o n
This routine retrieves information about a socket for those socket options supported in this DLL. The options supported here are SO_KEEPALIVE and SO_DONTROUTE. This routine is called by the WinSock DLL when a level/option name combination is passed to getsockopt that the WinSock DLL does not understand.
Arguments - context = Context ptr from WSAOpenSocket handle = Socket handle addrhandle = Datagram Handle connhandle = Connection Handle level = Level from getsockopt call optname = Option name from getsockopt call optvalue = Option value ptr from getsockopt call optlength = Option length field from getsockopt call
Returns - NO_ERROR = Operation succeeded OK Else = WinSock error code ********************************************************************/ INT WSHGetSocketInformation(PVOID context, SOCKET handle, HANDLE addrhandle, HANDLE connhandle, INT level, INT optname, PCHAR optvalue, PINT optlength) { PWSHIPX_SOCKET_CONTEXT ct; INT rc; INT ibuf[2]; PIPX_ADDRESS_DATA p;
/** Get ptr to context **/
ct = (PWSHIPX_SOCKET_CONTEXT)context;
//
// Check if this is an internal request for context information.
//
if ( level == SOL_INTERNAL && optname == SO_CONTEXT ) {
//
// The Windows Sockets DLL is requesting context information
// from us. If an output buffer was not supplied, the Windows
// Sockets DLL is just requesting the size of our context
// information.
//
if ( optvalue != NULL ) {
//
// Make sure that the buffer is sufficient to hold all the
// context information.
//
if ( *optlength < sizeof(*ct) ) { return WSAEFAULT; }
//
// Copy in the context information.
//
RtlCopyMemory( optvalue, ct, sizeof(*ct) ); }
*optlength = sizeof(*ct);
return NO_ERROR; }
/** The only level we support is NSPROTO_IPX **/
if (level != NSPROTO_IPX) return WSAEINVAL;
/** Fill in the result based on the options name **/
switch (optname) {
/** Get the current send packet type **/
case IPX_PTYPE:
/** Make sure the length is OK **/
if (*optlength < sizeof(INT)) return WSAEFAULT;
/** Make sure this is for a DGRAM socket **/
if (ct->con_socktype != SOCK_DGRAM) return WSAEINVAL;
/** Set the type **/
*(UINT *)optvalue = (UINT)ct->con_sendptype; *optlength = sizeof(UINT); break;
/** Get the current recv packet type filter **/
case IPX_FILTERPTYPE:
/** Make sure length is OK **/
if (*optlength < sizeof(INT)) return WSAEFAULT;
/** Make sure this is for a DGRAM socket **/
if (ct->con_socktype != SOCK_DGRAM) return WSAEINVAL;
/** If option not on - return error **/
if (!(ct->con_flags & WSHCON_FILTER)) return WSAEINVAL;
/** Save the new value **/
*(UINT *)optvalue = (UINT)ct->con_recvptype; *optlength = sizeof(UINT); break;
/** Get the max DGRAM size that can be sent **/
case IPX_MAXSIZE:
/** Make sure length is OK **/
if (*optlength < sizeof(INT)) return WSAEFAULT;
/** Make sure this is for a DGRAM socket **/
if (ct->con_socktype != SOCK_DGRAM) return WSAEINVAL;
/** Get the value from the driver **/
rc = do_tdi_action(addrhandle, MIPX_GETPKTSIZE, (PUCHAR)ibuf, sizeof(INT)*2, TRUE, NULL);
*(INT *)optvalue = ibuf[1]; *optlength = sizeof(int);
/** Return the result **/
return rc;
/** Get the max adapternum that is valid **/
case IPX_MAX_ADAPTER_NUM:
/** Make sure length is OK **/
if (*optlength < sizeof(INT)) return WSAEFAULT;
/** Make sure this is for a DGRAM socket **/
if (ct->con_socktype != SOCK_DGRAM) return WSAEINVAL;
/** Get the value from the driver **/
rc = do_tdi_action(addrhandle, MIPX_ADAPTERNUM, optvalue, sizeof(INT), TRUE, NULL);
*optlength = sizeof(int);
/** Return the result **/
return rc;
/** Get SPX statistics **/
case IPX_SPXGETCONNECTIONSTATUS:
/** Make sure data length OK **/
if (*optlength < sizeof(IPX_SPXCONNSTATUS_DATA)) return WSAEFAULT;
/** Make sure this is for a STREAM socket **/
if ((ct->con_socktype != SOCK_STREAM) && (ct->con_socktype != SOCK_SEQPACKET)) {
return WSAEINVAL; }
/** Send it to the driver **/
rc = do_tdi_action( connhandle, MSPX_GETSTATS, optvalue, *optlength, FALSE, NULL);
if (rc) return rc;
*optlength = sizeof(IPX_SPXCONNSTATUS_DATA);
/** Return OK **/
return NO_ERROR;
/** Get the current datastream type to send pkts with **/
case IPX_DSTYPE:
/** Make sure length is OK **/
if (*optlength < sizeof(INT)) return WSAEFAULT;
/** Make sure this is for a STREAM socket **/
if ((ct->con_socktype != SOCK_STREAM) && (ct->con_socktype != SOCK_SEQPACKET)) {
return WSAEINVAL; }
/** Save the new value **/
*(UINT *)optvalue = (UINT)ct->con_dstype; *optlength = sizeof(UINT); break;
/** Get net information **/
case IPX_GETNETINFO:
/** Make sure data length OK **/
if (*optlength < sizeof(IPX_NETNUM_DATA)) return WSAEFAULT;
/** Make sure this is for a DGRAM socket **/
if (ct->con_socktype != SOCK_DGRAM) return WSAEINVAL;
/** Send it to the driver **/
rc = do_tdi_action( addrhandle, MIPX_GETNETINFO, optvalue, *optlength, TRUE, NULL);
if (rc) { return rc; }
*optlength = sizeof(IPX_NETNUM_DATA);
/** Return OK **/
return NO_ERROR;
/** Get net information without RIPping **/
case IPX_GETNETINFO_NORIP:
/** Make sure data length OK **/
if (*optlength < sizeof(IPX_NETNUM_DATA)) return WSAEFAULT;
/** Make sure this is for a DGRAM socket **/
if (ct->con_socktype != SOCK_DGRAM) return WSAEINVAL;
/** Send it to the driver **/
rc = do_tdi_action( addrhandle, MIPX_GETNETINFO_NR, optvalue, *optlength, TRUE, NULL);
if (rc) { return rc; }
*optlength = sizeof(IPX_NETNUM_DATA);
/** Return OK **/
return NO_ERROR;
/** Like GETNETINFO, but force a re-rip **/
case IPX_RERIPNETNUMBER:
/** Make sure data length OK **/
if (*optlength < sizeof(IPX_NETNUM_DATA)) return WSAEFAULT;
/** Make sure this is for a DGRAM socket **/
if (ct->con_socktype != SOCK_DGRAM) return WSAEINVAL;
/** Send it to the driver **/
rc = do_tdi_action( addrhandle, MIPX_RERIPNETNUM, optvalue, *optlength, TRUE, NULL);
if (rc) { return rc; }
*optlength = sizeof(IPX_NETNUM_DATA);
/** Return OK **/
return NO_ERROR;
/** Get card information **/
case IPX_ADDRESS_NOTIFY:
/** We need the action header, the data, and the event handle **/
if (*optlength < (INT)(FIELD_OFFSET(NWLINK_ACTION, Data[0]) + sizeof(IPX_ADDRESS_DATA) + sizeof(HANDLE))) return WSAEFAULT;
/** Otherwise just fall through **/
case IPX_ADDRESS:
/** Make sure data length OK **/
if (*optlength < sizeof(IPX_OLD_ADDRESS_DATA)) return WSAEFAULT;
/** Make sure this is for a DGRAM socket **/
if (ct->con_socktype != SOCK_DGRAM) return WSAEINVAL;
/** Send it to the driver **/
if (optname == IPX_ADDRESS) {
rc = do_tdi_action( addrhandle, MIPX_GETCARDINFO, optvalue, *optlength, TRUE, NULL);
} else {
rc = do_tdi_action( addrhandle, MIPX_NOTIFYCARDINFO, optvalue, *optlength - sizeof(HANDLE), TRUE, (PHANDLE)(optvalue + FIELD_OFFSET(NWLINK_ACTION, Data[0]) + sizeof(IPX_ADDRESS_DATA))); }
if (rc) { p = (PIPX_ADDRESS_DATA)optvalue; memset(p->netnum, 0xFF, 4); memset(p->nodenum, 0xFF, 6); return rc; }
/** Return OK **/
if (*optlength < sizeof(IPX_ADDRESS_DATA)) { *optlength = sizeof(IPX_OLD_ADDRESS_DATA); } else if (*optlength < sizeof(IPX_ADDRESS_DATA)) { *optlength = sizeof(IPX_ADDRESS_DATA); }
return NO_ERROR;
/** All others are error **/
default: return WSAENOPROTOOPT; }
/** All is OK **/
return NO_ERROR; }
/*page***************************************************************
W S H S e t S o c k e t I n f o r m a t i o n
This routine sets information about a socket for those options supported in this helper DLL. This routine is called when a setsockopt call is made and the option/level passed is unknown to the WinSock DLL.
Arguments - context = Context ptr from WSAOpenSocket handle = Socket handle addrhandle = Datagram Handle connhandle = Connection Handle level = Level from getsockopt call optname = Option name from getsockopt call optvalue = Option value ptr from getsockopt call optlength = Option length field from getsockopt call
Returns - NO_ERROR = Operation succeeded OK Else = WinSock error code *********************************************************************/ INT WSHSetSocketInformation(PVOID context, SOCKET handle, HANDLE addrhandle, HANDLE connhandle, INT level, INT optname, PCHAR optvalue, INT optlength) { PWSHIPX_SOCKET_CONTEXT ct; INT rc;
/** Get ptr to context **/
ct = (PWSHIPX_SOCKET_CONTEXT)context;
//
// Check if this is an internal request for context information.
//
if ( level == SOL_INTERNAL && optname == SO_CONTEXT ) {
//
// The Windows Sockets DLL is requesting that we set context
// information for a new socket. If the new socket was
// accept()'ed, then we have already been notified of the socket
// and HelperDllSocketContext will be valid. If the new socket
// was inherited or duped into this process, then this is our
// first notification of the socket and HelperDllSocketContext
// will be equal to NULL.
//
// Insure that the context information being passed to us is
// sufficiently large.
//
if ( optlength < sizeof(*ct) ) { return WSAEINVAL; }
if ( ct == NULL ) {
//
// This is our notification that a socket handle was
// inherited or duped into this process. Allocate a context
// structure for the new socket.
//
ct = RtlAllocateHeap( RtlProcessHeap( ), 0, sizeof(*ct) ); if ( ct == NULL ) { return WSAENOBUFS; }
//
// Copy over information into the context block.
//
RtlCopyMemory( ct, optvalue, sizeof(*ct) );
//
// Tell the Windows Sockets DLL where our context information is
// stored so that it can return the context pointer in future
// calls.
//
*(PWSHIPX_SOCKET_CONTEXT *)optvalue = ct;
return NO_ERROR;
} else {
PWSHIPX_SOCKET_CONTEXT parentContext; INT one = 1;
//
// The socket was accept()'ed and it needs to have the same
// properties as it's parent. The OptionValue buffer
// contains the context information of this socket's parent.
//
parentContext = (PWSHIPX_SOCKET_CONTEXT)optvalue;
ASSERT( ct->con_addrfam == parentContext->con_addrfam ); ASSERT( ct->con_socktype == parentContext->con_socktype ); ASSERT( ct->con_pcol == parentContext->con_pcol );
return NO_ERROR; } }
/** We only support level NSPROTO_IPX **/
if (level != NSPROTO_IPX) return WSAEINVAL;
/** Handle the options **/
switch (optname) {
/** Set the send packet type **/
case IPX_PTYPE:
/** Make sure length is OK **/
if (optlength < sizeof(INT)) return WSAEFAULT;
/** Make sure this is for a DGRAM socket **/
if (ct->con_socktype != SOCK_DGRAM) return WSAEINVAL;
/** Get the value and check it **/
rc = *(INT *)optvalue; if ((rc < 0) || (rc > 255)) return WSAEINVAL;
/** Save the new value **/
ct->con_sendptype = (UCHAR)rc;
/** Send the new value down to the driver **/
if (addrhandle) rc = do_tdi_action(addrhandle, MIPX_SETSENDPTYPE, &ct->con_sendptype, 1, TRUE, NULL); else rc = NO_ERROR;
return rc;
/** Set the recv filter for packet type **/
case IPX_FILTERPTYPE:
/** Make sure length is OK **/
if (optlength < sizeof(INT)) return WSAEFAULT;
/** Make sure this is for a DGRAM socket **/
if (ct->con_socktype != SOCK_DGRAM) return WSAEINVAL;
/** Get the value and check it **/
rc = *(INT *)optvalue; if ((rc < 0) || (rc > 255)) return WSAEINVAL;
/** Save the new value **/
ct->con_recvptype = (UCHAR)rc; ct->con_flags |= WSHCON_FILTER;
/** Send the new value down to the driver **/
if (addrhandle) rc = do_tdi_action(addrhandle, MIPX_FILTERPTYPE, &ct->con_recvptype, 1, TRUE, NULL); else rc = NO_ERROR;
/** **/
return rc;
/** Stop filtering recv on pkt type **/
case IPX_STOPFILTERPTYPE:
/** Make sure this is for a DGRAM socket **/
if (ct->con_socktype != SOCK_DGRAM) return WSAEINVAL;
/** Turn off the flag **/
ct->con_flags &= ~WSHCON_FILTER;
/** Tell the driver **/
if (addrhandle) rc = do_tdi_action(addrhandle, MIPX_NOFILTERPTYPE, NULL, 0, TRUE, NULL); else rc = NO_ERROR; break;
/** Set piggyback wait for backtraffic flag **/ case IPX_IMMEDIATESPXACK:
/** Get the optvalue as an INT **/
rc = *(INT *)optvalue;
/** **/
if (rc) { /** Turn it ON **/ rc = WSAEINVAL; if ((ct->con_socktype == SOCK_STREAM) || (ct->con_socktype == SOCK_SEQPACKET)) { rc = NO_ERROR;
ct->con_flags |= WSHCON_IMM_SPXACK;
if (addrhandle) rc = do_tdi_action(addrhandle, MSPX_NOACKWAIT, NULL, 0, TRUE, NULL); } } else { /** Turn it OFF **/ rc = WSAEINVAL; if ((ct->con_socktype == SOCK_STREAM) || (ct->con_socktype == SOCK_SEQPACKET)) { rc = NO_ERROR;
ct->con_flags &= ~WSHCON_IMM_SPXACK;
if (addrhandle) rc = do_tdi_action(addrhandle, MSPX_ACKWAIT, NULL, 0, TRUE, NULL); } }
/** Return the result **/ return rc;
/** Set to recv pcol hdrs with data **/
case IPX_RECVHDR:
/** Get the optvalue as an INT **/ rc = *(INT *)optvalue;
if (rc) { /** Turn it ON **/ ct->con_flags |= WSHCON_SENDHDR;
/** Send it to the driver **/ rc = WSAEINVAL; if (ct->con_socktype == SOCK_DGRAM) { rc = NO_ERROR; if (addrhandle) rc = do_tdi_action(addrhandle, MIPX_SENDHEADER, NULL, 0, TRUE, NULL); } else if ((ct->con_socktype == SOCK_STREAM) || (ct->con_socktype == SOCK_SEQPACKET)) { /** Do this on address handle **/ rc = NO_ERROR; if (addrhandle) rc = do_tdi_action(addrhandle, MSPX_SENDHEADER, NULL, 0, TRUE, NULL); } } else {
/** Turn it OFF **/ ct->con_flags &= ~WSHCON_SENDHDR;
/** Send it to the driver **/ rc = WSAEINVAL; if (ct->con_socktype == SOCK_DGRAM) { rc = NO_ERROR; if (addrhandle) rc = do_tdi_action(addrhandle, MIPX_NOSENDHEADER, NULL, 0, TRUE, NULL); } else if ((ct->con_socktype == SOCK_STREAM) || (ct->con_socktype == SOCK_SEQPACKET)) { rc = NO_ERROR; if (addrhandle) rc = do_tdi_action(addrhandle, MSPX_NOSENDHEADER, NULL, 0, TRUE, NULL); } }
/** Return the result **/ return rc;
/** Set the Datastream type to send pkts with **/
case IPX_DSTYPE:
/** Make sure length is OK **/
if (optlength < sizeof(INT)) return WSAEFAULT;
/** Make sure this is for a STREAM socket **/
if ((ct->con_socktype != SOCK_STREAM) && (ct->con_socktype != SOCK_SEQPACKET)) {
return WSAEINVAL; }
/** Get the value and check it **/
rc = *(INT *)optvalue; if ((rc < 0) || (rc > 255)) return WSAEINVAL;
/** Save the new value **/
ct->con_dstype = (UCHAR)rc;
/** Send the new value down to the driver **/
if (connhandle) rc = do_tdi_action(connhandle, MSPX_SETDATASTREAM, &ct->con_dstype, 1, FALSE, NULL); else rc = 0;
/** **/
return rc;
/** Set the extended address option **/
case IPX_EXTENDED_ADDRESS:
/** Make sure length is OK **/
if (optlength < sizeof(INT)) return WSAEFAULT;
/** Make sure this is for a DGRAM socket **/
if (ct->con_socktype != SOCK_DGRAM) return WSAEINVAL;
/** Get the optvalue as an INT **/
rc = *(INT *)optvalue;
/** **/
if (rc) {
/** Send the option down to the driver **/
ct->con_flags |= WSHCON_EXTADDR; if (addrhandle) rc = do_tdi_action(addrhandle, MIPX_SENDADDROPT, NULL, 0, TRUE, NULL); else rc = NO_ERROR; } else {
/** Send the option down to the driver **/
ct->con_flags &= ~WSHCON_EXTADDR; if (addrhandle) rc = do_tdi_action(addrhandle, MIPX_NOSENDADDROPT, NULL, 0, TRUE, NULL); else rc = NO_ERROR; } return rc;
/** Set the broadcast reception **/
case IPX_RECEIVE_BROADCAST:
/** Make sure length is OK **/
if (optlength < sizeof(INT)) return WSAEFAULT;
/** Make sure this is for a DGRAM socket **/
if (ct->con_socktype != SOCK_DGRAM) return WSAEINVAL;
/** Get the optvalue as an INT **/
rc = *(INT *)optvalue;
/** **/
if (rc) {
/** Send the option down to the driver **/
ct->con_flags |= WSHCON_RCVBCAST; if (addrhandle) rc = do_tdi_action(addrhandle, MIPX_RCVBCAST, NULL, 0, TRUE, NULL); else rc = NO_ERROR; } else {
/** Send the option down to the driver **/
ct->con_flags &= ~WSHCON_RCVBCAST; if (addrhandle) rc = do_tdi_action(addrhandle, MIPX_NORCVBCAST, NULL, 0, TRUE, NULL); else rc = NO_ERROR; } return rc;
/** All others return error **/
default: return WSAENOPROTOOPT; }
/** All Done OK **/
return NO_ERROR; }
/*page***************************************************************
W S H G e t W i l d c a r d S o c k a d d r
This routing returns a wilcard socket address for the sockets DLL to use.
Arguments - context = Context ptr from WSAOpenSocket addrp = Ptr to where to store the address addrlen = Ptr to where to store length of address
Returns - NO_ERROR = Operation succeeded OK Else = WinSock error code *********************************************************************/ INT WSHGetWildcardSockaddr(PVOID context, PSOCKADDR addrp, PINT addrlen) {
/**
Setup the address as the address family + all 0's for the rest. **/
memset(addrp, 0, sizeof(SOCKADDR)); addrp->sa_family = AF_NS;
/** Set the address length **/
*addrlen = sizeof(SOCKADDR);
/** Return OK **/
return NO_ERROR; }
/*page***************************************************************
i s _ t r i p l e _ i n _ l i s t
Check to see if the given triple is in the given triple list.
Arguments - tlist = Ptr to the triple list tlen = Num entries in the triple list addrfam = Address family to look for socktype = Socket Type to look for pcol = Protocol to look for
Returns - TRUE = Yes FALSE = No *********************************************************************/ BOOLEAN is_triple_in_list(PMAPPING_TRIPLE tlist, ULONG tlen, INT addrfam, INT socktype, INT pcol) { ULONG i;
/**
Go thru the list and search to see if we can find the given triple in the list. **/
for (i = 0 ; i < tlen ; i++,tlist++) {
/** If it matches - return OK **/
if ((addrfam == tlist->triple_addrfam) && (socktype == tlist->triple_socktype) && (pcol == tlist->triple_protocol))
return TRUE; }
/** Not Found **/
return FALSE; }
/*page***************************************************************
W S H E n u m P r o t o c o l s
Enumerates IPX/SPX protocols.
Returns - NO_ERROR or an error code. *********************************************************************/ INT WSHEnumProtocols ( IN LPINT lpiProtocols, IN LPTSTR lpTransportKeyName, IN OUT LPVOID lpProtocolBuffer, IN OUT LPDWORD lpdwBufferLength ) { DWORD bytesRequired; PPROTOCOL_INFOW protocolInfo; BOOL useSpx = FALSE; BOOL useSpx2 = FALSE; BOOL useIpx = FALSE; BOOL spxString; DWORD i; PWCHAR namePtr; INT entriesReturned = 0;
//
// Determine whether we should return information for IPX or SPX.
//
if ( _wcsicmp( L"NwlnkIpx", (LPWSTR)lpTransportKeyName ) == 0 ) { spxString = FALSE; } else { spxString = TRUE; }
//
// Make sure that the caller cares about SPX, SPX2, and/or IPX.
//
if ( ARGUMENT_PRESENT( lpiProtocols ) ) {
for ( i = 0; lpiProtocols[i] != 0; i++ ) { if ( lpiProtocols[i] == NSPROTO_SPX && spxString ) { useSpx = TRUE; } if ( lpiProtocols[i] == NSPROTO_SPXII && spxString ) { useSpx2 = TRUE; } if ( lpiProtocols[i] == NSPROTO_IPX && !spxString ) { useIpx = TRUE; } }
} else {
useSpx = FALSE; useSpx2 = spxString; useIpx = !spxString; }
if ( !useSpx && !useSpx2 && !useIpx ) { *lpdwBufferLength = 0; return 0; }
//
// Make sure that the caller has specified a sufficiently large
// buffer.
//
bytesRequired = (DWORD)((sizeof(PROTOCOL_INFO) * 3) + ( (wcslen( SPX_NAME ) + 1) * sizeof(WCHAR)) + ( (wcslen( SPX2_NAME ) + 1) * sizeof(WCHAR)) + ( (wcslen( IPX_NAME ) + 1) * sizeof(WCHAR)));
if ( bytesRequired > *lpdwBufferLength ) { *lpdwBufferLength = bytesRequired; return -1; }
//
// Initialize local variables.
//
protocolInfo = lpProtocolBuffer; namePtr = (PWCHAR)( (PCHAR)lpProtocolBuffer + *lpdwBufferLength );
//
// Fill in SPX info, if requested.
//
if ( useSpx ) {
entriesReturned += 1;
protocolInfo->dwServiceFlags = XP_GUARANTEED_DELIVERY | XP_MESSAGE_ORIENTED | XP_PSEUDO_STREAM | XP_GUARANTEED_ORDER | XP_FRAGMENTATION; protocolInfo->iAddressFamily = AF_IPX; protocolInfo->iMaxSockAddr = 0x10; protocolInfo->iMinSockAddr = 0xE; protocolInfo->iSocketType = SOCK_SEQPACKET; protocolInfo->iProtocol = NSPROTO_SPX; protocolInfo->dwMessageSize = 0xFFFFFFFF;
namePtr = namePtr - (wcslen( SPX_NAME) + 1); protocolInfo->lpProtocol = namePtr; wcscpy( protocolInfo->lpProtocol, SPX_NAME );
protocolInfo += 1; }
//
// Fill in SPX II info, if requested.
//
if ( useSpx2 ) {
entriesReturned += 1;
protocolInfo->dwServiceFlags = XP_GUARANTEED_DELIVERY | XP_MESSAGE_ORIENTED | XP_PSEUDO_STREAM | XP_GRACEFUL_CLOSE | XP_GUARANTEED_ORDER | XP_FRAGMENTATION; protocolInfo->iAddressFamily = AF_IPX; protocolInfo->iMaxSockAddr = 0x10; protocolInfo->iMinSockAddr = 0xE; protocolInfo->iSocketType = SOCK_SEQPACKET; protocolInfo->iProtocol = NSPROTO_SPXII; protocolInfo->dwMessageSize = 0xFFFFFFFF;
namePtr = namePtr - (wcslen( SPX2_NAME) + 1); protocolInfo->lpProtocol = namePtr; wcscpy( protocolInfo->lpProtocol, SPX2_NAME );
protocolInfo += 1; }
//
// Fill in IPX info, if requested.
//
if ( useIpx ) {
entriesReturned += 1;
protocolInfo->dwServiceFlags = XP_CONNECTIONLESS | XP_MESSAGE_ORIENTED | XP_SUPPORTS_BROADCAST | XP_SUPPORTS_MULTICAST | XP_FRAGMENTATION; protocolInfo->iAddressFamily = AF_IPX; protocolInfo->iMaxSockAddr = 0x10; protocolInfo->iMinSockAddr = 0xE; protocolInfo->iSocketType = SOCK_DGRAM; protocolInfo->iProtocol = NSPROTO_IPX; protocolInfo->dwMessageSize = 576;
namePtr = namePtr - (wcslen( IPX_NAME) + 1); protocolInfo->lpProtocol = namePtr; wcscpy( protocolInfo->lpProtocol, IPX_NAME ); }
*lpdwBufferLength = bytesRequired;
return entriesReturned;
} // WSHEnumProtocols
#define _IPX_CONTROL_CODE(request,method) \
CTL_CODE(FILE_DEVICE_TRANSPORT, request, method, FILE_ANY_ACCESS) #define IOCTL_IPX_LOAD_SPX _IPX_CONTROL_CODE( 0x5678, METHOD_BUFFERED )
DWORD WshLoadSpx( VOID ) /*++
Routine Description:
Starts the nwlnkspx.sys driver by submitting a special ioctl to ipx, which calls ZwLoadDriver() for us.
Arguments:
none
Returns:
Error return from the load operation.
++*/ { DWORD err = NO_ERROR; HANDLE FileHandle; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING FileString; WCHAR FileName[] = L"\\Device\\NwlnkIpx"; NTSTATUS Status;
RtlInitUnicodeString (&FileString, FileName);
InitializeObjectAttributes( &ObjectAttributes, &FileString, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = NtOpenFile( &FileHandle, SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT);
if (!NT_SUCCESS(Status)) {
err = ERROR_FILE_NOT_FOUND;
} else {
Status = NtDeviceIoControlFile( FileHandle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_IPX_LOAD_SPX, NULL, 0, NULL, 0);
if (Status == STATUS_IMAGE_ALREADY_LOADED) {
err = ERROR_SERVICE_ALREADY_RUNNING;
//
// #36451
// If the service controller loads SPX ("net start nwlnkspx", or due to dependency of RPC on SPX)
// then we get this error the first time too. Keep a note of that.
//
// NOTE: we still leak a handle per process since the handle to the driver is actually created
// in the system process' context. The ideal way to fix this should be to have IPX associate the
// handle with the current process (so handle is destroyed when the process dies) or to have the
// dll tell IPX to close the handle it opened earlier.
//
SpxLoaded = TRUE;
} else if (!NT_SUCCESS(Status)) {
err = ERROR_IO_DEVICE;
} else { SpxLoaded = TRUE; }
NtClose (FileHandle);
}
return(err); }
/*page***************************************************************
W S H G e t P r o v i d e r G u i d
Queries the GUID identifier for this protocol.
Returns - NO_ERROR or an error code. *********************************************************************/ INT WINAPI WSHGetProviderGuid ( IN LPWSTR ProviderName, OUT LPGUID ProviderGuid ) {
if( ProviderName == NULL || ProviderGuid == NULL ) {
return WSAEFAULT;
}
if( _wcsicmp( ProviderName, L"NwlnkIpx" ) == 0 ) {
RtlCopyMemory( ProviderGuid, &IpxProviderGuid, sizeof(GUID) );
return NO_ERROR;
}
if( _wcsicmp( ProviderName, L"NwlnkSpx" ) == 0 ) {
RtlCopyMemory( ProviderGuid, &SpxProviderGuid, sizeof(GUID) );
return NO_ERROR;
}
return WSAEINVAL;
} // WSHGetProviderGuid
INT WINAPI WSHAddressToString ( IN LPSOCKADDR Address, IN INT AddressLength, IN LPWSAPROTOCOL_INFOW ProtocolInfo, OUT LPWSTR AddressString, IN OUT LPDWORD AddressStringLength )
/*++
Routine Description:
Converts a SOCKADDR to a human-readable form.
Arguments:
Address - The SOCKADDR to convert.
AddressLength - The length of Address.
ProtocolInfo - The WSAPROTOCOL_INFOW for a particular provider.
AddressString - Receives the formatted address string.
AddressStringLength - On input, contains the length of AddressString. On output, contains the number of characters actually written to AddressString.
Return Value:
INT - 0 if successful, WinSock error code if not.
--*/
{ WCHAR string[BUFFER_SIZE]; INT length; LPSOCKADDR_IPX addr;
//
// Quick sanity checks.
//
if( Address == NULL || AddressLength < sizeof(SOCKADDR_IPX) || AddressString == NULL || AddressStringLength == NULL ) {
return WSAEFAULT;
}
addr = (LPSOCKADDR_IPX)Address;
if( addr->sa_family != AF_NS ) {
return WSAEINVAL;
}
length = swprintf( string, L"%2.2x%2.2x%2.2x%2.2x.%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", (UCHAR) addr->sa_netnum[0], (UCHAR) addr->sa_netnum[1], (UCHAR) addr->sa_netnum[2], (UCHAR) addr->sa_netnum[3], (UCHAR) addr->sa_nodenum[0], (UCHAR) addr->sa_nodenum[1], (UCHAR) addr->sa_nodenum[2], (UCHAR) addr->sa_nodenum[3], (UCHAR) addr->sa_nodenum[4], (UCHAR) addr->sa_nodenum[5] );
if( addr->sa_socket != 0 ) {
length += swprintf( string + length, L":%hu", ntohs( addr->sa_socket ) );
}
length++; // account for terminator
if ( length > BUFFER_SIZE ) { DbgPrint("length exceeded internal buffer in wshisn.dll.\n"); return WSAEFAULT; }
if( *AddressStringLength < (DWORD)length ) { DbgPrint("AddressStringLength %lu < length %lu\n",*AddressStringLength, length); return WSAEFAULT; }
*AddressStringLength = (DWORD)length;
RtlCopyMemory( AddressString, string, length * sizeof(WCHAR) );
return NO_ERROR;
} // WSHAddressToString
|