1898 lines
48 KiB
C
1898 lines
48 KiB
C
|
/****************************************************************************
|
||
|
* (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
|
||
|
|
||
|
|