/*++

Copyright (c) 1992 Microsoft Corporation

Module Name:

    D:\nt\private\net\sockets\wshatm\wshatm.c

Abstract:

    This module contains necessary routines for the ATM Windows Sockets
    Helper DLL.  This DLL provides the transport-specific support necessary
    for the Windows Sockets DLL to use ATM as a transport.

Revision History:

    arvindm              20-May-1997    Created based on TCP/IP's helper DLL, wshtcpip

--*/

#define UNICODE
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>

#include <windows.h>
#include <stdio.h>
#include <ctype.h>
#include <wchar.h>
#include <tdi.h>

#include <winsock2.h>
#include <mswsock.h>
#include <ws2atm.h>
#include <wsahelp.h>

#include <tdistat.h>
#include <tdiinfo.h>

#include <rwanuser.h>

typedef unsigned long   ulong;
typedef unsigned short  ushort;
typedef unsigned int    uint;
typedef unsigned char   uchar;
#define TL_INSTANCE 0

#include <ws2atmsp.h>

#define NT // temporarily needed by tdiinfo.h...

#include <tdiinfo.h>

#include <basetyps.h>
#include <nspapi.h>

#define ATM_NAME L"ATM"
#define RWAN_NAME L"RawWan"

#define ATM_ADDR_SIZE                           20
#define ATM_ADDR_BLANK_CHAR                     L' '
#define ATM_ADDR_PUNCTUATION_CHAR       L'.'
#define ATM_ADDR_E164_START_CHAR        '+'

#define ATM_AAL5_SOCK_TYPE                      SOCK_RAW


#define ATM_WSA_MULTIPOINT_FLAGS        (WSA_FLAG_MULTIPOINT_C_ROOT |   \
                                                                         WSA_FLAG_MULTIPOINT_C_LEAF |   \
                                                                         WSA_FLAG_MULTIPOINT_D_ROOT |   \
                                                                         WSA_FLAG_MULTIPOINT_D_LEAF)

//
// Define valid flags for WSHOpenSocket2().
//
#define VALID_ATM_FLAGS                         (WSA_FLAG_OVERLAPPED |                  \
                                                                         ATM_WSA_MULTIPOINT_FLAGS)

//
// Maximum expected size of ATM Connection Options: this includes the
// base QOS structure, plus all possible IEs.
//
#if 0
#define MAX_ATM_OPTIONS_LENGTH          \
                                        sizeof(QOS) + \
                                        sizeof(Q2931_IE) + sizeof(AAL_PARAMETERS_IE) + \
                                        sizeof(Q2931_IE) + sizeof(ATM_TRAFFIC_DESCRIPTOR_IE) + \
                                        sizeof(Q2931_IE) + sizeof(ATM_BROADBAND_BEARER_CAPABILITY_IE) + \
                                        (3 * (sizeof(Q2931_IE) + sizeof(ATM_BLLI_IE))) + \
                                        sizeof(Q2931_IE) + sizeof(ATM_CALLED_PARTY_NUMBER_IE) + \
                                        sizeof(Q2931_IE) + sizeof(ATM_CALLED_PARTY_SUBADDRESS_IE) + \
                                        sizeof(Q2931_IE) + sizeof(ATM_CALLING_PARTY_SUBADDRESS_IE) + \
                                        sizeof(Q2931_IE) + sizeof(ATM_CAUSE_IE) + \
                                        sizeof(Q2931_IE) + sizeof(ATM_QOS_CLASS_IE) + \
                                        sizeof(Q2931_IE) + sizeof(ATM_TRANSIT_NETWORK_SELECTION_IE)

#else
//
//  Need much more with ATMUNI 4.0...
//
#define MAX_ATM_OPTIONS_LENGTH  1024
#endif

#if DBG1
#define DBGPRINT(stmt)          { DbgPrint ("WSHATM: "); DbgPrint stmt; }
#else
#define DBGPRINT(stmt)
#endif // DBG1

#if DBG
extern
PVOID
MyRtlAllocateHeap(
        IN      PVOID   HeapHandle,
        IN      ULONG   Flags,
        IN      ULONG   Size,
        IN      ULONG   LineNumber
        );
extern
VOID
MyRtlFreeHeap(
        IN PVOID        HeapHandle,
        IN ULONG        Flags,
        IN PVOID        MemPtr,
        IN ULONG        LineNumber
        );
#define RTL_ALLOCATE_HEAP(_Handle, _Flags, _Size)       MyRtlAllocateHeap(_Handle, _Flags, _Size, __LINE__)
#define RTL_FREE_HEAP(_Handle, _Flags, _Memptr) MyRtlFreeHeap(_Handle, _Flags, _Memptr, __LINE__)
#else
#define RTL_ALLOCATE_HEAP(_Handle, _Flags, _Size)       RtlAllocateHeap(_Handle, _Flags, _Size)
#define RTL_FREE_HEAP(_Handle, _Flags, _Memptr) RtlFreeHeap(_Handle, _Flags, _Memptr)
#endif

#define ATM_AAL5_PACKET_SIZE            65535

//
// Structure and variables to define the triples supported by ATM. The
// first entry of each array is considered the canonical triple for
// that socket type; the other entries are synonyms for the first.
//

typedef struct _MAPPING_TRIPLE {
    INT AddressFamily;
    INT SocketType;
    INT Protocol;
} MAPPING_TRIPLE, *PMAPPING_TRIPLE;

MAPPING_TRIPLE AtmMappingTriples[] = {
                                       AF_ATM,    ATM_AAL5_SOCK_TYPE, ATMPROTO_AAL5,
                                       AF_ATM,    ATM_AAL5_SOCK_TYPE, 0,
                                                                           AF_ATM,    SOCK_RAW,       ATMPROTO_AAL5,
                                       AF_ATM,    SOCK_RAW,       0,
                                       AF_ATM,    0,              ATMPROTO_AAL5,
                                       AF_UNSPEC, 0,              ATMPROTO_AAL5,
                                       AF_UNSPEC, ATM_AAL5_SOCK_TYPE, ATMPROTO_AAL5,
                                       AF_UNSPEC, SOCK_RAW,       ATMPROTO_AAL5
                                                                         };


//
// Winsock 2 WSAPROTOCOL_INFO structures for all supported protocols.
//

#define ATM_UNI_VERSION                 0x00030001      // For UNI 3.1

WSAPROTOCOL_INFOW Winsock2Protocols[] =
    {
        //
        // ATM AAL5
        //

        {
            XP1_GUARANTEED_ORDER                    // dwServiceFlags1
                | XP1_MESSAGE_ORIENTED
                // | XP1_PARTIAL_MESSAGE
                | XP1_IFS_HANDLES
                | XP1_SUPPORT_MULTIPOINT
                | XP1_MULTIPOINT_DATA_PLANE
                | XP1_MULTIPOINT_CONTROL_PLANE
                | XP1_QOS_SUPPORTED,
            0,                                      // dwServiceFlags2
            0,                                      // dwServiceFlags3
            0,                                      // dwServiceFlags4
            PFL_MATCHES_PROTOCOL_ZERO,              // dwProviderFlags
            {                                       // gProviderId
                0, 0, 0,
                { 0, 0, 0, 0, 0, 0, 0, 0 }
            },
            0,                                      // dwCatalogEntryId
            {                                       // ProtocolChain
                BASE_PROTOCOL,                          // ChainLen
                { 0, 0, 0, 0, 0, 0, 0 }                 // ChainEntries
            },
            ATM_UNI_VERSION,                        // iVersion
            AF_ATM,                                 // iAddressFamily
            sizeof(sockaddr_atm),                   // iMaxSockAddr
            sizeof(sockaddr_atm),                   // iMinSockAddr
            ATM_AAL5_SOCK_TYPE,                     // iSocketType
            ATMPROTO_AAL5,                          // iProtocol
            0,                                      // iProtocolMaxOffset
            BIGENDIAN,                              // iNetworkByteOrder
            SECURITY_PROTOCOL_NONE,                 // iSecurityScheme
            ATM_AAL5_PACKET_SIZE,                   // dwMessageSize
            0,                                      // dwProviderReserved
            L"MSAFD ATM AAL5"                       // szProtocol
        }
    };

#define NUM_WINSOCK2_PROTOCOLS  \
            ( sizeof(Winsock2Protocols) / sizeof(Winsock2Protocols[0]) )

//
// The GUID identifying this provider.
//

GUID AtmProviderGuid = { /* {C3656046-3AAF-11d1-A8C3-00C04FC99C9C} */
    0xC3656046,
    0x3AAF,
    0x11D1,
    {0xA8, 0xC3, 0x00, 0xC0, 0x4F, 0xC9, 0x9C, 0x9C}
    };

//
// Given a digit (0-9) represented in ANSI, return its WCHAR representation
//
#define ANSI_TO_WCHAR(_AnsiDigit)       \
                        (L'0' + (WCHAR)((_AnsiDigit) - '0'))


//
// Given a hex digit value (0-15), return its WCHAR representation
// (i.e. 0 -> L'0', 12 -> L'C')
//
#define DIGIT_TO_WCHAR(_Value)          \
                        (((_Value) > 9)? (L'A' + (WCHAR)((_Value) - 10)) :      \
                                                         (L'0' + (WCHAR)((_Value) - 0 )))

//
// The socket context structure for this DLL.  Each open ATM socket
// will have one of these context structures, which is used to maintain
// information about the socket.
//

typedef struct _WSHATM_SOCKET_CONTEXT {
    INT                         AddressFamily;
    INT                         SocketType;
    INT                         Protocol;
    INT                         ReceiveBufferSize;
    SOCKET                              SocketHandle;
    DWORD                       Flags;
    DWORD                       LocalFlags;
    ATM_CONNECTION_ID   ConnectionId;

} WSHATM_SOCKET_CONTEXT, *PWSHATM_SOCKET_CONTEXT;

#define DEFAULT_RECEIVE_BUFFER_SIZE ATM_AAL5_PACKET_SIZE

//
// LocalFlags in WSHATM_SOCKET_CONTEXT:
//
#define WSHATM_SOCK_IS_BOUND                            0x00000001
#define WSHATM_SOCK_IS_PVC                                      0x00000004
#define WSHATM_SOCK_ASSOCIATE_PVC_PENDING       0x00000008


//
// Forward declarations of internal routines.
//

BOOLEAN
IsTripleInList (
    IN PMAPPING_TRIPLE List,
    IN ULONG ListLength,
    IN INT AddressFamily,
    IN INT SocketType,
    IN INT Protocol
    );

INT
WSHAtmSetQoS(
    IN PVOID HelperDllSocketContext,
    IN SOCKET SocketHandle,
    IN LPVOID InputBuffer,
    IN DWORD InputBufferLength
    );

INT
WSHAtmGetQoS(
    IN PVOID HelperDllSocketContext,
    IN SOCKET SocketHandle,
    IN LPVOID OutputBuffer,
    IN DWORD OutputBufferLength,
    OUT LPDWORD NumberOfBytesReturned
    );

INT
AtmQueryAtmGlobalInformation(
        IN ATM_OBJECT_ID ObjectId,
        IN LPVOID pContext,
        IN DWORD ContextLength,
        IN LPVOID OutputBuffer,
        IN DWORD OutputBufferLength,
        OUT LPDWORD NumberOfBytesReturned
        );

INT
AtmSetGenericObjectInformation (
    IN HANDLE TdiObjectHandle,
    IN ULONG IoControlCode,
    IN RWAN_OBJECT_ID ObjectId,
    IN PVOID InputBuffer,
    IN ULONG InputBufferLength
    );

INT
AtmGetGenericObjectInformation (
    IN HANDLE TdiObjectHandle,
    IN ULONG IoControlCode,
    IN RWAN_OBJECT_ID ObjectId,
    IN PVOID InputBuffer,
    IN ULONG InputBufferLength,
    OUT PVOID OutputBuffer,
    IN ULONG OutputBufferLength,
        OUT LPDWORD NumberOfBytesReturned
    );

INT
AtmSetAtmObjectInformation (
    IN HANDLE TdiObjectHandle,
    IN ULONG IoControlCode,
    IN ATM_OBJECT_ID ObjectId,
    IN PVOID InputBuffer,
    IN ULONG InputBufferLength
    );

INT
AtmGetAtmObjectInformation (
    IN HANDLE TdiObjectHandle,
    IN ULONG IoControlCode,
    IN ATM_OBJECT_ID ObjectId,
    IN PVOID InputBuffer,
    IN ULONG InputBufferLength,
    OUT PVOID OutputBuffer,
    IN ULONG OutputBufferLength,
        OUT LPDWORD NumberOfBytesReturned
    );

INT
AtmAssociatePVC(
    IN SOCKET SocketHandle,
        IN PVOID HelperDllSocketContext,
        IN HANDLE TdiAddressObjectHandle,
        IN HANDLE TdiConnectionObjectHandle,
        IN LPVOID InputBuffer,
        IN DWORD InputBufferLength
        );

INT
AtmDoAssociatePVC(
        IN PWSHATM_SOCKET_CONTEXT Context,
        IN HANDLE TdiAddressObjectHandle
        );



BOOLEAN
DllInitialize (
    IN PVOID DllHandle,
    IN ULONG Reason,
    IN PVOID Context OPTIONAL
    )
{

    DBGPRINT(("DllInitialize, Reason %d\n", Reason));

    switch ( Reason ) {

    case DLL_PROCESS_ATTACH:

        //
        // We don't need to receive thread attach and detach
        // notifications, so disable them to help application
        // performance.
        //

        DisableThreadLibraryCalls( DllHandle );

        return TRUE;

    case DLL_THREAD_ATTACH:

        break;

    case DLL_PROCESS_DETACH:

        break;

    case DLL_THREAD_DETACH:

        break;
    }

    return TRUE;

} // SockInitialize

INT
WSHGetSockaddrType (
    IN PSOCKADDR Sockaddr,
    IN DWORD SockaddrLength,
    OUT PSOCKADDR_INFO SockaddrInfo
    )

/*++

Routine Description:

    This routine parses a sockaddr to determine the type of the
    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 - a pointer to the sockaddr structure to evaluate.

    SockaddrLength - the number of bytes in the sockaddr structure.

    SockaddrInfo - a pointer to a structure that will receive information
        about the specified sockaddr.


Return Value:

    INT - a winsock error code indicating the status of the operation, or
        NO_ERROR if the operation succeeded.

--*/

{
    UNALIGNED SOCKADDR_ATM *sockaddr = (PSOCKADDR_ATM)Sockaddr;
    ULONG i;

        DBGPRINT(("GetSockaddrType: SockaddrLength %d, satm_family %d, AddrType x%x\n",
                                        SockaddrLength, sockaddr->satm_family, sockaddr->satm_number.AddressType));

    //
    // Make sure that the address family is correct.
    //

    if ( sockaddr->satm_family != AF_ATM ) {
        return WSAEAFNOSUPPORT;
    }

    //
    // Make sure that the length is correct.
    //

    if ( SockaddrLength < sizeof(SOCKADDR_ATM) ) {
        return WSAEFAULT;
    }

#if 0
    //
    // The ATM address part cannot be "absent".
    //
    if ( sockaddr->satm_number.AddressType == SAP_FIELD_ABSENT ) {
        return WSAEINVAL;
    }
#endif

    if ( sockaddr->satm_number.NumofDigits > ATM_ADDR_SIZE ) {
        return WSAEINVAL;
    }

    //
    // The address passed the tests, looks like a good address.
    // Determine the type of the address and endpoint portions
        // of the sockaddr.
    //

    if ( sockaddr->satm_number.AddressType == SAP_FIELD_ANY &&
                 sockaddr->satm_blli.Layer2Protocol == SAP_FIELD_ANY &&
                 sockaddr->satm_blli.Layer3Protocol == SAP_FIELD_ANY &&
                 sockaddr->satm_bhli.HighLayerInfoType == SAP_FIELD_ANY ) {

        SockaddrInfo->AddressInfo = SockaddrAddressInfoWildcard;
        SockaddrInfo->EndpointInfo = SockaddrEndpointInfoWildcard;

    } else if ( sockaddr->satm_number.AddressType == SAP_FIELD_ABSENT ) {

        SockaddrInfo->AddressInfo = SockaddrAddressInfoWildcard;
        SockaddrInfo->EndpointInfo = SockaddrEndpointInfoWildcard;

        } else {

        SockaddrInfo->AddressInfo = SockaddrAddressInfoNormal;
        SockaddrInfo->EndpointInfo = SockaddrEndpointInfoNormal;

    }

    return NO_ERROR;

} // WSHGetSockaddrType


INT
WSHGetSocketInformation (
    IN PVOID HelperDllSocketContext,
    IN SOCKET SocketHandle,
    IN HANDLE TdiAddressObjectHandle,
    IN HANDLE TdiConnectionObjectHandle,
    IN INT Level,
    IN INT OptionName,
    OUT PCHAR OptionValue,
    OUT PINT OptionLength
    )

/*++

Routine Description:

    This routine retrieves information about a socket for those socket
    options supported in this helper DLL. Currently there are none
    supported.

    This is called by the winsock DLL when a level/option name
        combination is passed to getsockopt() that the winsock DLL does
        not understand.

Arguments:

    HelperDllSocketContext - the context pointer returned from
        WSHOpenSocket().

    SocketHandle - the handle of the socket for which we're getting
        information.

    TdiAddressObjectHandle - the TDI address object of the socket, if
        any.  If the socket is not yet bound to an address, then
        it does not have a TDI address object and this parameter
        will be NULL.

    TdiConnectionObjectHandle - the TDI connection object of the socket,
        if any.  If the socket is not yet connected, then it does not
        have a TDI connection object and this parameter will be NULL.

    Level - the level parameter passed to getsockopt().

    OptionName - the optname parameter passed to getsockopt().

    OptionValue - the optval parameter passed to getsockopt().

    OptionLength - the optlen parameter passed to getsockopt().

Return Value:

    INT - a winsock error code indicating the status of the operation, or
        NO_ERROR if the operation succeeded.

--*/

{
    PWSHATM_SOCKET_CONTEXT context = HelperDllSocketContext;
    INT err;

        DBGPRINT(("GetSocketInformation: Level %d, OptionName %d, OptionLength %d\n",
                        Level, OptionName, *OptionLength));

    UNREFERENCED_PARAMETER( SocketHandle );
    UNREFERENCED_PARAMETER( TdiAddressObjectHandle );
    UNREFERENCED_PARAMETER( TdiConnectionObjectHandle );

    //
    // Check if this is an internal request for context information.
    //

    if ( Level == SOL_INTERNAL && OptionName == 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 ( OptionValue != NULL ) {

            //
            // Make sure that the buffer is sufficient to hold all the
            // context information.
            //

            if ( *OptionLength < sizeof(*context) ) {
                return WSAEFAULT;
            }

            //
            // Copy in the context information.
            //

            RtlCopyMemory( OptionValue, context, sizeof(*context) );
        }

        *OptionLength = sizeof(*context);

        err = NO_ERROR;

    } else {

        switch ( OptionName ) {

        case SO_MAX_MSG_SIZE:

                        if ( TdiConnectionObjectHandle == NULL ) {

                                DbgPrint("wshatm: SO_MAX_MSG_SIZE: immed return\n");
                                if ( *OptionLength >= sizeof(DWORD) ) {
                                
                                        *(LPDWORD)OptionValue = DEFAULT_RECEIVE_BUFFER_SIZE;
                                        *OptionLength = sizeof(DWORD);
                                        
                                        err = NO_ERROR;
                                
                                } else {

                                        err = WSAEFAULT;
                                
                                }

                        } else {

                                DbgPrint("wshatm: SO_MAX_MSG_SIZE: querying driver\n");
                                err = AtmGetGenericObjectInformation(
                                                TdiConnectionObjectHandle,
                                                IOCTL_RWAN_GENERIC_CONN_HANDLE_QUERY,
                                                RWAN_OID_CONN_OBJECT_MAX_MSG_SIZE,
                                                NULL,   // No Input buffer
                                                0,              // Input Buffer length
                                                OptionValue,    // Output buffer
                                                *OptionLength,  // Output buffer length
                                                OptionLength    // NumberOfBytesReturned
                                                );

                        }
                        break;
                
                default:

                        err = WSAENOPROTOOPT;
                        break;

                }
        }

        return err;

} // WSHGetSocketInformation


INT
WSHGetWildcardSockaddr (
    IN PVOID HelperDllSocketContext,
    OUT PSOCKADDR Sockaddr,
    OUT PINT SockaddrLength
    )

/*++

Routine Description:

    This routine returns a wildcard socket address.  A wildcard address
    is one which will bind the socket to an endpoint of the transport's
    choosing.  For ATM, a wildcard address has AddressType and BHLI and
    BLLI Type fields set to SAP_FIELD_ANY.

Arguments:

    HelperDllSocketContext - the context pointer returned from
        WSHOpenSocket() for the socket for which we need a wildcard
        address.

    Sockaddr - points to a buffer which will receive the wildcard socket
        address.

    SockaddrLength - receives the length of the wioldcard sockaddr.

Return Value:

    INT - a winsock error code indicating the status of the operation, or
        NO_ERROR if the operation succeeded.

--*/

{
        PSOCKADDR_ATM   sockaddr;

        DBGPRINT(("GetWildcardAddress\n"));

        sockaddr = (PSOCKADDR_ATM)Sockaddr;

    if ( *SockaddrLength < sizeof(SOCKADDR_ATM) ) {
        return WSAEFAULT;
    }

    *SockaddrLength = sizeof(SOCKADDR_ATM);


        //
        // Prepare the ATM wild card address.
        //
    RtlZeroMemory( sockaddr, sizeof(SOCKADDR_ATM) );

    sockaddr->satm_family = AF_ATM;
    sockaddr->satm_number.AddressType = SAP_FIELD_ABSENT;
        sockaddr->satm_blli.Layer2Protocol = SAP_FIELD_ANY;
        sockaddr->satm_blli.Layer3Protocol = SAP_FIELD_ANY;
        sockaddr->satm_bhli.HighLayerInfoType = SAP_FIELD_ANY;

    return NO_ERROR;

} // WSAGetWildcardSockaddr


DWORD
WSHGetWinsockMapping (
    OUT PWINSOCK_MAPPING Mapping,
    IN DWORD MappingLength
    )

/*++

Routine Description:

    Returns the list of address family/socket type/protocol triples
    supported by this helper DLL.

Arguments:

    Mapping - receives a pointer to a WINSOCK_MAPPING structure that
        describes the triples supported here.

    MappingLength - the length, in bytes, of the passed-in Mapping buffer.

Return Value:

    DWORD - the length, in bytes, of a WINSOCK_MAPPING structure for this
        helper DLL.  If the passed-in buffer is too small, the return
        value will indicate the size of a buffer needed to contain
        the WINSOCK_MAPPING structure.

--*/

{
    DWORD mappingLength;

        DBGPRINT(("GetWinsockMapping\n"));

    mappingLength = sizeof(WINSOCK_MAPPING) - sizeof(MAPPING_TRIPLE) +
                        sizeof(AtmMappingTriples);

    //
    // If the passed-in buffer is too small, return the length needed
    // now without writing to the buffer.  The caller should allocate
    // enough memory and call this routine again.
    //

    if ( mappingLength > MappingLength ) {
        return mappingLength;
    }

    //
    // Fill in the output mapping buffer with the list of triples
    // supported in this helper DLL.
    //

    Mapping->Rows = sizeof(AtmMappingTriples) / sizeof(AtmMappingTriples[0]);
    Mapping->Columns = sizeof(MAPPING_TRIPLE) / sizeof(DWORD);
    RtlMoveMemory(
        Mapping->Mapping,
        AtmMappingTriples,
        sizeof(AtmMappingTriples)
        );

    //
    // Return the number of bytes we wrote.
    //

    return mappingLength;

} // WSHGetWinsockMapping


INT
WSHOpenSocket (
    IN OUT PINT AddressFamily,
    IN OUT PINT SocketType,
    IN OUT PINT Protocol,
    OUT PUNICODE_STRING TransportDeviceName,
    OUT PVOID *HelperDllSocketContext,
    OUT PDWORD NotificationEvents
    )
{
    return WSHOpenSocket2(
               AddressFamily,
               SocketType,
               Protocol,
               0,           // Group
               0,           // Flags
               TransportDeviceName,
               HelperDllSocketContext,
               NotificationEvents
               );

} // WSHOpenSocket


INT
WSHOpenSocket2 (
    IN OUT PINT AddressFamily,
    IN OUT PINT SocketType,
    IN OUT PINT Protocol,
    IN GROUP Group,
    IN DWORD Flags,
    OUT PUNICODE_STRING TransportDeviceName,
    OUT PVOID *HelperDllSocketContext,
    OUT PDWORD NotificationEvents
    )

/*++

Routine Description:

    Does the necessary work for this helper DLL to open a socket and is
    called by the winsock DLL in the socket() routine.  This routine
    verifies that the specified triple is valid, determines the NT
    device name of the TDI provider that will support that triple,
    allocates space to hold the socket's context block, and
    canonicalizes the triple.

Arguments:

    AddressFamily - on input, the address family specified in the
        socket() call.  On output, the canonicalized value for the
        address family.

    SocketType - on input, the socket type specified in the socket()
        call.  On output, the canonicalized value for the socket type.

    Protocol - on input, the protocol specified in the socket() call.
        On output, the canonicalized value for the protocol.

    Group - Identifies the group for the new socket.

    Flags - Zero or more WSA_FLAG_* flags as passed into WSASocket().

    TransportDeviceName - receives the name of the TDI provider that
        will support the specified triple.

    HelperDllSocketContext - receives a context pointer that the winsock
        DLL will return to this helper DLL on future calls involving
        this socket.

    NotificationEvents - receives a bitmask of those state transitions
        this helper DLL should be notified on.

Return Value:

    INT - a winsock error code indicating the status of the operation, or
        NO_ERROR if the operation succeeded.

--*/

{
    PWSHATM_SOCKET_CONTEXT context;
    DWORD multipointFlags;
    UNICODE_STRING atmDeviceName;

    //
    // Determine whether this is an ATM socket.
    //

    DBGPRINT(("WSHOpenSocket2: AF %d, Type %d, Proto %d\n",
                        *AddressFamily, *SocketType, *Protocol));

    if ( IsTripleInList(
             AtmMappingTriples,
             sizeof(AtmMappingTriples) / sizeof(AtmMappingTriples[0]),
             *AddressFamily,
             *SocketType,
             *Protocol ) ) {

        //
        // It's an ATM socket. Check the flags.
        //

        if ( ( Flags & ~VALID_ATM_FLAGS ) != 0 ) {

            DBGPRINT(("WSHOpenSocket2: Bad flags x%x\n", Flags));
            return WSAEINVAL;

        }

        if ( ( Flags & ATM_WSA_MULTIPOINT_FLAGS ) != 0 ) {

                //
                //  The only multipoint combinations allowed are:
                //
                //  1. C_ROOT|D_ROOT
                //  2. C_LEAF|D_LEAF
                //

                multipointFlags = ( Flags & ATM_WSA_MULTIPOINT_FLAGS );

                if ( ( multipointFlags != (WSA_FLAG_MULTIPOINT_C_ROOT | WSA_FLAG_MULTIPOINT_D_ROOT) ) &&
                         ( multipointFlags != (WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF) ) ) {

                    DBGPRINT(("WSHOpenSocket2: Bad multipoint flags x%x\n",
                                        Flags));
                        
                        return WSAEINVAL;

                } else {

                        DBGPRINT(("WSHOpenSocket2: Good multipoint flags x%x\n", Flags));

                }
        }

        //
        // Return the canonical form of a ATM socket triple.
        //

        *AddressFamily = AtmMappingTriples[0].AddressFamily;
        *SocketType = AtmMappingTriples[0].SocketType;
        *Protocol = AtmMappingTriples[0].Protocol;

        //
        // Prepare the name of the TDI device.
        //

        RtlInitUnicodeString( &atmDeviceName, DD_ATM_DEVICE_NAME );

        TransportDeviceName->Buffer = RTL_ALLOCATE_HEAP( RtlProcessHeap( ), 0, atmDeviceName.MaximumLength );

        if ( TransportDeviceName->Buffer == NULL ) {
                
                return WSAEFAULT;

        }

        TransportDeviceName->MaximumLength = atmDeviceName.MaximumLength;
        TransportDeviceName->Length = 0;

        RtlCopyUnicodeString(TransportDeviceName, &atmDeviceName);

    } else {

        //
        // This should never happen if the registry information about this
        // helper DLL is correct.  If somehow this did happen, just return
        // an error.
        //

        DBGPRINT(("WSHOpenSocket2: Triple not found!\n"));

        if ( *Protocol != ATMPROTO_AAL5 ) {
        
                return WSAEPROTONOSUPPORT;
        
        }

        return WSAEINVAL;
    }

    //
    // Allocate context for this socket.  The Windows Sockets DLL will
    // return this value to us when it asks us to get/set socket options.
    //

    context = RTL_ALLOCATE_HEAP( RtlProcessHeap( ), 0, sizeof(*context) );
    if ( context == NULL ) {
        return WSAENOBUFS;
    }

    //
    // Initialize the context for the socket.
    //

    context->AddressFamily = *AddressFamily;
    context->SocketType = *SocketType;
    context->Protocol = *Protocol;
    context->ReceiveBufferSize = DEFAULT_RECEIVE_BUFFER_SIZE;
    context->Flags = Flags;
    context->LocalFlags = 0;

    //
    // Tell the Windows Sockets DLL which state transitions we're
    // interested in being notified of.
    //

    *NotificationEvents =
            WSH_NOTIFY_BIND | WSH_NOTIFY_LISTEN | WSH_NOTIFY_CLOSE;

    //
    // Everything worked, return success.
    //

    *HelperDllSocketContext = context;

    DBGPRINT(("WSHOpenSocket2 success: AF %d, Type %d, Proto %d\n",
                        *AddressFamily, *SocketType, *Protocol));

    return NO_ERROR;

} // WSHOpenSocket


INT
WSHNotify (
    IN PVOID HelperDllSocketContext,
    IN SOCKET SocketHandle,
    IN HANDLE TdiAddressObjectHandle,
    IN HANDLE TdiConnectionObjectHandle,
    IN DWORD NotifyEvent
    )

/*++

Routine Description:

    This routine is called by the winsock DLL after a state transition
    of the socket.  Only state transitions returned in the
    NotificationEvents parameter of WSHOpenSocket() are notified here.
    This routine allows a winsock helper DLL to track the state of
    socket and perform necessary actions corresponding to state
    transitions.

    If we see a LISTEN event, we call setsockopt() to force AFD to
    allocate data buffers (for options) for incoming connection requests
    on this socket.

    If we see a BIND event for a socket to be used for multipoint
    activity, we tell RAWWAN that the associated address object is
    of multipoint type.

Arguments:

    HelperDllSocketContext - the context pointer given to the winsock
        DLL by WSHOpenSocket().

    SocketHandle - the handle for the socket.

    TdiAddressObjectHandle - the TDI address object of the socket, if
        any.  If the socket is not yet bound to an address, then
        it does not have a TDI address object and this parameter
        will be NULL.

    TdiConnectionObjectHandle - the TDI connection object of the socket,
        if any.  If the socket is not yet connected, then it does not
        have a TDI connection object and this parameter will be NULL.

    NotifyEvent - indicates the state transition for which we're being
        called.

Return Value:

    INT - a winsock error code indicating the status of the operation, or
        NO_ERROR if the operation succeeded.

--*/

{
    PWSHATM_SOCKET_CONTEXT context = HelperDllSocketContext;
    INT err;
    INT Option;
    INT OptionLength = sizeof(Option);
    PCHAR pOption = (PCHAR)&Option;

        DBGPRINT(("Notify: Event = %d\n", NotifyEvent));

        err = NO_ERROR;

        switch (NotifyEvent)
        {
                case WSH_NOTIFY_BIND:

                        DBGPRINT(("NotifyBind: context %x, Flags %x, LocalFlags %x\n",
                                        context, context->Flags, context->LocalFlags));

                        //
                        //  Request the Winsock DLL to set the options.
                        //
                        Option = MAX_ATM_OPTIONS_LENGTH;

                        err = setsockopt(
                                        SocketHandle,
                                        SOL_SOCKET,
                                        SO_CONNOPTLEN,
                                        pOption,
                                        OptionLength
                                        );

                        if ( err != NO_ERROR ) {

                                DBGPRINT(("WSHATM: NotifyBind: setsockopt SO_CONNOPTLEN err %x\n", err));
                                break;
                        }

                        context->LocalFlags |= WSHATM_SOCK_IS_BOUND;

                        if ( ( context->Flags & ATM_WSA_MULTIPOINT_FLAGS ) != 0 ) {
                                
                                //
                                //  Prepare flags for RAWWAN.
                                //
                                Option = 0;

                                if ( context->Flags & WSA_FLAG_MULTIPOINT_C_ROOT ) {

                                        Option |= RWAN_AOFLAG_C_ROOT;
                                
                                }
                        
                                if ( context->Flags & WSA_FLAG_MULTIPOINT_C_LEAF ) {

                                        Option |= RWAN_AOFLAG_C_LEAF;
                                
                                }

                                if ( context->Flags & WSA_FLAG_MULTIPOINT_D_ROOT ) {

                                        Option |= RWAN_AOFLAG_D_ROOT;
                                
                                }

                                if ( context->Flags & WSA_FLAG_MULTIPOINT_D_LEAF ) {

                                        Option |= RWAN_AOFLAG_D_LEAF;
                                
                                }

                                //
                                //  Inform RAWWAN about the Multipoint nature of this
                                //  Address Object.
                                //
                                err = AtmSetGenericObjectInformation(
                                                TdiAddressObjectHandle,
                                                IOCTL_RWAN_GENERIC_ADDR_HANDLE_SET,
                                                RWAN_OID_ADDRESS_OBJECT_FLAGS,
                                                &Option,
                                                sizeof(Option)
                                                );

                                DBGPRINT(("Notify: Bind Notify on PMP endpoint, Option x%x, ret = %d\n",
                                                Option, err));
                        }
                        else if ( ( context->LocalFlags & WSHATM_SOCK_ASSOCIATE_PVC_PENDING ) ) {

                                DBGPRINT(("Notify: Bind Notify will Associate PVC\n"));
                                err = AtmDoAssociatePVC(
                                                context,
                                                TdiAddressObjectHandle
                                                );
                        }

                        break;
                                
                case WSH_NOTIFY_CLOSE:

                        RTL_FREE_HEAP( RtlProcessHeap( ), 0, context );
                        break;
                
                case WSH_NOTIFY_LISTEN:
                        //
                        //  Request the Winsock DLL to set the options.
                        //
                        Option = MAX_ATM_OPTIONS_LENGTH;

                        err = setsockopt(
                                        SocketHandle,
                                        SOL_SOCKET,
                                        SO_CONNOPTLEN,
                                        pOption,
                                        OptionLength
                                        );
                        break;

                default:
                        err = WSAEINVAL;
                        break;
        
        }

    return err;

} // WSHNotify


INT
WSHSetSocketInformation (
    IN PVOID HelperDllSocketContext,
    IN SOCKET SocketHandle,
    IN HANDLE TdiAddressObjectHandle,
    IN HANDLE TdiConnectionObjectHandle,
    IN INT Level,
    IN INT OptionName,
    IN PCHAR OptionValue,
    IN INT OptionLength
    )

/*++

Routine Description:

    This routine sets information about a socket for those socket
    options supported in this helper DLL.  We don't support any
    options at present.

    This routine is called by the winsock DLL when a level/option
        name combination is passed to setsockopt() that the winsock DLL
        does not understand.

Arguments:

    HelperDllSocketContext - the context pointer returned from
        WSHOpenSocket().

    SocketHandle - the handle of the socket for which we're getting
        information.

    TdiAddressObjectHandle - the TDI address object of the socket, if
        any.  If the socket is not yet bound to an address, then
        it does not have a TDI address object and this parameter
        will be NULL.

    TdiConnectionObjectHandle - the TDI connection object of the socket,
        if any.  If the socket is not yet connected, then it does not
        have a TDI connection object and this parameter will be NULL.

    Level - the level parameter passed to setsockopt().

    OptionName - the optname parameter passed to setsockopt().

    OptionValue - the optval parameter passed to setsockopt().

    OptionLength - the optlen parameter passed to setsockopt().

Return Value:

    INT - a winsock error code indicating the status of the operation, or
        NO_ERROR if the operation succeeded.

--*/

{
    PWSHATM_SOCKET_CONTEXT context = HelperDllSocketContext;
    INT error;
    INT optionValue;

    UNREFERENCED_PARAMETER( SocketHandle );
    UNREFERENCED_PARAMETER( TdiAddressObjectHandle );
    UNREFERENCED_PARAMETER( TdiConnectionObjectHandle );

        DBGPRINT(("SetSocketInformation: Level %d, Option x%x\n", Level, OptionName));

    //
    // Check if this is an internal request for context information.
    //

    if ( Level == SOL_INTERNAL && OptionName == 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 ( OptionLength < sizeof(*context) ) {
            return WSAEINVAL;
        }

        if ( HelperDllSocketContext == NULL ) {

            //
            // This is our notification that a socket handle was
            // inherited or duped into this process.  Allocate a context
            // structure for the new socket.
            //

            context = RTL_ALLOCATE_HEAP( RtlProcessHeap( ), 0, sizeof(*context) );
            if ( context == NULL ) {
                return WSAENOBUFS;
            }

            //
            // Copy over information into the context block.
            //

            RtlCopyMemory( context, OptionValue, sizeof(*context) );
            context->SocketHandle = SocketHandle;

            //
            // Tell the Windows Sockets DLL where our context information is
            // stored so that it can return the context pointer in future
            // calls.
            //

            *(PWSHATM_SOCKET_CONTEXT *)OptionValue = context;

            return NO_ERROR;

        } else {

            PWSHATM_SOCKET_CONTEXT parentContext;
            INT one = 1;
            INT zero = 0;

            //
            // 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 = (PWSHATM_SOCKET_CONTEXT)OptionValue;

            ASSERT( context->AddressFamily == parentContext->AddressFamily );
            ASSERT( context->SocketType == parentContext->SocketType );
            ASSERT( context->Protocol == parentContext->Protocol );


            return NO_ERROR;
        }
    }


        return WSAENOPROTOOPT;

#if 0
    //
    // Handle socket-level options.
    //
    optionValue = *OptionValue;

    switch ( OptionName ) {

    case SO_RCVBUF:

        context->ReceiveBufferSize = optionValue;

        break;

    default:

        return WSAENOPROTOOPT;
    }

    return NO_ERROR;
#endif

} // WSHSetSocketInformation


INT
WSHEnumProtocols (
    IN LPINT lpiProtocols,
    IN LPWSTR lpTransportKeyName,
    IN OUT LPVOID lpProtocolBuffer,
    IN OUT LPDWORD lpdwBufferLength
    )

/*++

Routine Description:

    Enumerates the protocols supported by this helper.

Arguments:

    lpiProtocols - Pointer to a NULL-terminated array of protocol
        identifiers. Only protocols specified in this array will
        be returned by this function. If this pointer is NULL,
        all protocols are returned.

    lpTransportKeyName -

    lpProtocolBuffer - Pointer to a buffer to fill with PROTOCOL_INFO
        structures.

    lpdwBufferLength - Pointer to a variable that, on input, contains
        the size of lpProtocolBuffer. On output, this value will be
        updated with the size of the data actually written to the buffer.

Return Value:

    INT - The number of protocols returned if successful, -1 if not.

--*/

{
    DWORD bytesRequired;
    PPROTOCOL_INFO atmProtocolInfo;
    PPROTOCOL_INFO udpProtocolInfo;
    BOOL useAtm = FALSE;
    DWORD i;

    lpTransportKeyName;         // Avoid compiler warnings.

        DBGPRINT(("EnumProtocols\n"));

    //
    // Make sure that the caller cares about ATM.
    //

    if ( ARGUMENT_PRESENT( lpiProtocols ) ) {

        for ( i = 0; lpiProtocols[i] != 0; i++ ) {
            if ( lpiProtocols[i] == ATMPROTO_AAL5 ) {
                useAtm = TRUE;
            }
        }

    } else {

        useAtm = TRUE;
    }

    if ( !useAtm ) {
        *lpdwBufferLength = 0;
        return 0;
    }

    //
    // Make sure that the caller has specified a sufficiently large
    // buffer.
    //

    bytesRequired = (DWORD)((sizeof(PROTOCOL_INFO) * 1) +
                        ( (wcslen( ATM_NAME ) + 1) * sizeof(WCHAR)));

    if ( bytesRequired > *lpdwBufferLength ) {
        *lpdwBufferLength = bytesRequired;
        return -1;
    }

    //
    // Fill in ATM info, if requested.
    //

    if ( useAtm ) {

        atmProtocolInfo = lpProtocolBuffer;

        atmProtocolInfo->dwServiceFlags = XP_GUARANTEED_ORDER |
                                              XP_MESSAGE_ORIENTED |
                                              XP_SUPPORTS_MULTICAST |
                                              XP_BANDWIDTH_ALLOCATION ;
        atmProtocolInfo->iAddressFamily = AF_ATM;
        atmProtocolInfo->iMaxSockAddr = sizeof(SOCKADDR_ATM);
        atmProtocolInfo->iMinSockAddr = sizeof(SOCKADDR_ATM);
        atmProtocolInfo->iSocketType = ATM_AAL5_SOCK_TYPE;
        atmProtocolInfo->iProtocol = ATMPROTO_AAL5;
        atmProtocolInfo->dwMessageSize = 1;
        atmProtocolInfo->lpProtocol = (LPWSTR)
            ( (PBYTE)lpProtocolBuffer + *lpdwBufferLength -
                ( (wcslen( ATM_NAME ) + 1) * sizeof(WCHAR) ) );
        wcscpy( atmProtocolInfo->lpProtocol, ATM_NAME );

        DBGPRINT(("EnumProtocols: lpProtocolBuffer %x, lpProtocol %x, BufLen %d\n",
                                lpProtocolBuffer,
                                atmProtocolInfo->lpProtocol,
                                *lpdwBufferLength));

        }

    *lpdwBufferLength = bytesRequired;

    return (1);

} // WSHEnumProtocols



BOOLEAN
IsTripleInList (
    IN PMAPPING_TRIPLE List,
    IN ULONG ListLength,
    IN INT AddressFamily,
    IN INT SocketType,
    IN INT Protocol
    )

/*++

Routine Description:

    Determines whether the specified triple has an exact match in the
    list of triples.

Arguments:

    List - a list of triples (address family/socket type/protocol) to
        search.

    ListLength - the number of triples in the list.

    AddressFamily - the address family to look for in the list.

    SocketType - the socket type to look for in the list.

    Protocol - the protocol to look for in the list.

Return Value:

    BOOLEAN - TRUE if the triple was found in the list, false if not.

--*/

{
    ULONG i;

    //
    // Walk through the list searching for an exact match.
    //

    for ( i = 0; i < ListLength; i++ ) {

        //
        // If all three elements of the triple match, return indicating
        // that the triple did exist in the list.
        //

        if ( AddressFamily == List[i].AddressFamily &&
             SocketType == List[i].SocketType &&
             Protocol == List[i].Protocol
           ) {
            return TRUE;
        }
    }

    //
    // The triple was not found in the list.
    //

    return FALSE;

} // IsTripleInList



INT
WINAPI
WSHJoinLeaf (
    IN PVOID HelperDllSocketContext,
    IN SOCKET SocketHandle,
    IN HANDLE TdiAddressObjectHandle,
    IN HANDLE TdiConnectionObjectHandle,
    IN PVOID LeafHelperDllSocketContext,
    IN SOCKET LeafSocketHandle,
    IN PSOCKADDR Sockaddr,
    IN DWORD SockaddrLength,
    IN LPWSABUF CallerData,
    IN LPWSABUF CalleeData,
    IN LPQOS SocketQOS,
    IN LPQOS GroupQOS,
    IN DWORD Flags
    )

/*++

Routine Description:

    Performs the protocol-dependent portion of creating a multicast
    socket.

    TBD: Needs a Lot Of Work!

Arguments:

    The following four parameters correspond to the socket passed into
    the WSAJoinLeaf() API:

    HelperDllSocketContext - The context pointer returned from
        WSHOpenSocket().

    SocketHandle - The handle of the socket used to establish the
        multicast "session".

    TdiAddressObjectHandle - The TDI address object of the socket, if
        any.  If the socket is not yet bound to an address, then
        it does not have a TDI address object and this parameter
        will be NULL.

    TdiConnectionObjectHandle - The TDI connection object of the socket,
        if any.  If the socket is not yet connected, then it does not
        have a TDI connection object and this parameter will be NULL.

    The next two parameters correspond to the newly created socket that
    identifies the multicast "session":

    LeafHelperDllSocketContext - The context pointer returned from
        WSHOpenSocket().

    LeafSocketHandle - The handle of the socket that identifies the
        multicast "session".

    Sockaddr - The name of the peer to which the socket is to be joined.

    SockaddrLength - The length of Sockaddr.

    CallerData - Pointer to user data to be transferred to the peer
        during multipoint session establishment.

    CalleeData - Pointer to user data to be transferred back from
        the peer during multipoint session establishment.

    SocketQOS - Pointer to the flowspecs for SocketHandle, one in each
        direction.

    GroupQOS - Pointer to the flowspecs for the socket group, if any.

    Flags - Flags to indicate if the socket is acting as sender,
        receiver, or both.

Return Value:

    INT - 0 if successful, a WinSock error code if not.

--*/

{

        INT err;

        if( Flags != JL_SENDER_ONLY ) {

                return WSAEINVAL;
        
        }

        if (SocketQOS)
        {
                err = WSHAtmSetQoS(
                                        LeafHelperDllSocketContext,
                                        LeafSocketHandle,
                                        SocketQOS,
                                        sizeof(*SocketQOS)
                                        );
        }

        return NO_ERROR;

} // WSHJoinLeaf


INT
WINAPI
WSHGetBroadcastSockaddr (
    IN PVOID HelperDllSocketContext,
    OUT PSOCKADDR Sockaddr,
    OUT PINT SockaddrLength
    )

/*++

Routine Description:

    This routine returns a broadcast socket address.  A broadcast address
    may be used as a destination for the sendto() API to send a datagram
    to all interested clients.

Arguments:

    HelperDllSocketContext - the context pointer returned from
        WSHOpenSocket() for the socket for which we need a broadcast
        address.

    Sockaddr - points to a buffer which will receive the broadcast socket
        address.

    SockaddrLength - receives the length of the broadcast sockaddr.

Return Value:

    INT - a winsock error code indicating the status of the operation, or
        NO_ERROR if the operation succeeded.

--*/

{
#if 1
        return WSAEINVAL;
#else

    LPSOCKADDR_ATM addr;

    if( *SockaddrLength < sizeof(SOCKADDR_ATM) ) {

        return WSAEFAULT;

    }

    *SockaddrLength = sizeof(SOCKADDR_ATM);

    //
    // Build the broadcast address.
    //

    addr = (LPSOCKADDR_ATM)Sockaddr;

    RtlZeroMemory(
        addr,
        sizeof(*addr)
        );

    addr->satm_family = AF_ATM;
    addr->satm_number.s_addr = htonl( INADDR_BROADCAST );

    return NO_ERROR;
#endif // 1

} // WSAGetBroadcastSockaddr


INT
WINAPI
WSHGetWSAProtocolInfo (
    IN LPWSTR ProviderName,
    OUT LPWSAPROTOCOL_INFOW * ProtocolInfo,
    OUT LPDWORD ProtocolInfoEntries
    )

/*++

Routine Description:

    Retrieves a pointer to the WSAPROTOCOL_INFOW structure(s) describing
    the protocol(s) supported by this helper.

Arguments:

    ProviderName - Contains the name of the provider, such as "RawWan".

    ProtocolInfo - Receives a pointer to the WSAPROTOCOL_INFOW array.

    ProtocolInfoEntries - Receives the number of entries in the array.

Return Value:

    INT - 0 if successful, WinSock error code if not.

--*/

{

    if( ProviderName == NULL ||
        ProtocolInfo == NULL ||
        ProtocolInfoEntries == NULL ) {

        return WSAEFAULT;

    }

        DBGPRINT(("WSHGetWSAProtocolInfo: Provider Name: %ws\n", ProviderName));

    if( _wcsicmp( ProviderName, L"RawWan" ) == 0 ) {

        *ProtocolInfo = Winsock2Protocols;
        *ProtocolInfoEntries = NUM_WINSOCK2_PROTOCOLS;

        return NO_ERROR;

    }

    return WSAEINVAL;

} // WSHGetWSAProtocolInfo


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[64];
    PWCHAR pstring;
    INT length; // Number of WCHARs filled into string
    UINT        i;
    LPSOCKADDR_ATM addr;
    UCHAR Val;

    //
    // Quick sanity checks.
    //

    if( Address == NULL ||
        AddressLength < sizeof(SOCKADDR_ATM) ||
        AddressString == NULL ||
        AddressStringLength == NULL ) {

        return WSAEFAULT;

    }

    addr = (LPSOCKADDR_ATM)Address;

    if( addr->satm_family != AF_ATM ) {

        return WSAEINVAL;

    }

    if ( addr->satm_number.NumofDigits > ATM_ADDR_SIZE ) {
        return WSAEINVAL;
    }

    //
    // Do the conversion.
    //
    length = 0;
    pstring = string;

    //
    // If this is an E.164 address, prepend a '+'.
    // Each entry in the array in satm_number consists of one
        // digit coded in IA5 (ANSI).
    //
    if ( addr->satm_number.AddressType == ATM_E164 ) {

        *pstring++ = L'+';
        length++;

        for ( i = 0; i < addr->satm_number.NumofDigits; i++ ) {

                if ( !iswdigit(addr->satm_number.Addr[i]) ) {
                        return WSAEINVAL;
                    }

                *pstring++ = ANSI_TO_WCHAR(addr->satm_number.Addr[i]);
        }

        length += addr->satm_number.NumofDigits;

    } else {

        //
        // This must be NSAP format. Each entry in the array
                // is a full hex byte (two BCD digits). We'll unpack
                // each array entry into two characters.
        //
        for ( i = 0; i < addr->satm_number.NumofDigits; i++ ) {

                Val = (addr->satm_number.Addr[i] >> 4);
                        *pstring++ = DIGIT_TO_WCHAR(Val);

                Val = (addr->satm_number.Addr[i] & 0xf);
                        *pstring++ = DIGIT_TO_WCHAR(Val);
                }

                length += (2 * addr->satm_number.NumofDigits);
        }

        //
        // Terminate the string.
        //
        *pstring = L'\0';

    length++;   // account for terminator

    if( *AddressStringLength < (DWORD)length ) {

        return WSAEFAULT;

    }

    *AddressStringLength = (DWORD)length;

    RtlCopyMemory(
        AddressString,
        string,
        length * sizeof(WCHAR)
        );

    return NO_ERROR;

} // WSHAddressToString


INT
WINAPI
WSHStringToAddress (
    IN LPWSTR AddressString,
    IN DWORD AddressFamily,
    IN LPWSAPROTOCOL_INFOW ProtocolInfo,
    OUT LPSOCKADDR Address,
    IN OUT LPINT AddressLength
    )

/*++

Routine Description:

    Fills in a SOCKADDR structure by parsing a human-readable string.

Arguments:

    AddressString - Points to the zero-terminated human-readable string.

    AddressFamily - The address family to which the string belongs.

    ProtocolInfo - The WSAPROTOCOL_INFOW for a particular provider.

    Address - Receives the SOCKADDR structure.

    AddressLength - On input, contains the length of Address. On output,
        contains the number of bytes actually written to Address.

Return Value:

    INT - 0 if successful, WinSock error code if not.

--*/

{
    WCHAR string[2*ATM_ADDR_SIZE+1];
    UNICODE_STRING unistring;
    CHAR ansistring[2*ATM_ADDR_SIZE+1];
    ANSI_STRING ansstring;
    PCHAR src;
    PCHAR dst;
    LPWSTR terminator;
    LPSOCKADDR_ATM addr;
    PWCHAR s, d;
    INT numDigits, i;
    NTSTATUS status;

    //
    // Quick sanity checks.
    //

    if( AddressString == NULL ||
        Address == NULL ||
        AddressLength == NULL ||
        *AddressLength < sizeof(SOCKADDR_ATM) ) {

        return WSAEFAULT;

    }

    if( AddressFamily != AF_ATM ) {

        DBGPRINT(("StrToAddr: invalid addrfam %d\n", AddressFamily));
        return WSAEINVAL;

    }


    addr = (LPSOCKADDR_ATM)Address;
    RtlZeroMemory(
        Address,
        sizeof(SOCKADDR_ATM)
        );

    //
    // Strip off all punctuation characters (spaces and periods).
    //
    for ( numDigits = 0, s = AddressString, d = string;
          (numDigits <= sizeof(WCHAR)*ATM_ADDR_SIZE) && (*s != L'\0');
          s++ ) {

                if ( *s == ATM_ADDR_BLANK_CHAR ||
                         *s == ATM_ADDR_PUNCTUATION_CHAR ) {
                         continue;
                }

                *d++ = *s;
                numDigits ++;
        }

        if ( numDigits == 0 ) {

        DBGPRINT(("StrToAddr[%ws]: numdigits after stripping is 0!\n",
                        AddressString));
                return WSAEINVAL;
        }

        //
        // Terminate it and convert into Unicode string.
        //
        *d = L'\0';

        RtlInitUnicodeString(&unistring, string);

        //
        // Convert it into an ANSI string.
        //
        ansstring.Buffer = ansistring;
        ansstring.MaximumLength = 2*ATM_ADDR_SIZE + 1;
        ansstring.Length = 0;

        status = RtlUnicodeStringToAnsiString(&ansstring, &unistring, FALSE);

        if ( status != STATUS_SUCCESS ) {
                DBGPRINT(("StrToAddr[%ws]: RtlUnicodeToAnsi failed (%x)\n",
                                string, status));
                return WSAEINVAL;
        }

        addr->satm_family = AF_ATM;

        src = ansistring;

        if ( *src == ATM_ADDR_E164_START_CHAR ) {

                src ++;
                numDigits --;

                if ( numDigits == 0 ) {
                        DBGPRINT(("StrToAddr[%ws]: AnsiString:[%s], numDigits is 0!\n",
                                                string, ansistring));
                        return WSAEINVAL;
                }

                addr->satm_number.AddressType = ATM_E164;
                addr->satm_number.NumofDigits = numDigits;

                RtlCopyMemory(addr->satm_number.Addr, src, numDigits);

        } else {

                UCHAR           hexString[3];
                ULONG           Val;

                hexString[2] = 0;

                if ( numDigits != 2 * ATM_ADDR_SIZE ) {
                        return WSAEINVAL;
                }

                addr->satm_number.AddressType = ATM_NSAP;
                addr->satm_number.NumofDigits = numDigits/2;

                for ( i = 0; i < ATM_ADDR_SIZE; i++ ) {

                        hexString[0] = *src++;
                        hexString[1] = *src++;

                        status = RtlCharToInteger(hexString, 16, &Val);

                        if ( status != STATUS_SUCCESS ) {
                                DBGPRINT(("StrToAtm[%ws]: index %d, hexString: %s, CharToInt %x\n",
                                                string, hexString, status));
                                return WSAEINVAL;
                        }

                        addr->satm_number.Addr[i] = (UCHAR)Val;
                }

        }

        addr->satm_blli.Layer2Protocol = SAP_FIELD_ABSENT;
        addr->satm_blli.Layer3Protocol = SAP_FIELD_ABSENT;
        addr->satm_bhli.HighLayerInfoType = SAP_FIELD_ABSENT;

    *AddressLength = sizeof(SOCKADDR_ATM);

    return NO_ERROR;

} // WSHStringToAddress


INT
WINAPI
WSHGetProviderGuid (
    IN LPWSTR ProviderName,
    OUT LPGUID ProviderGuid
    )

/*++

Routine Description:

    Returns the GUID identifying the protocols supported by this helper.

Arguments:

    ProviderName - Contains the name of the provider, such as "RawWan".

    ProviderGuid - Points to a buffer that receives the provider's GUID.

Return Value:

    INT - 0 if successful, WinSock error code if not.

--*/

{

    if( ProviderName == NULL ||
        ProviderGuid == NULL ) {

        return WSAEFAULT;

    }

    if( _wcsicmp( ProviderName, RWAN_NAME ) == 0 ) {

        RtlCopyMemory(
            ProviderGuid,
            &AtmProviderGuid,
            sizeof(GUID)
            );

        return NO_ERROR;

    }

    return WSAEINVAL;

} // WSHGetProviderGuid


INT
WINAPI
WSHIoctl (
    IN PVOID HelperDllSocketContext,
    IN SOCKET SocketHandle,
    IN HANDLE TdiAddressObjectHandle,
    IN HANDLE TdiConnectionObjectHandle,
    IN DWORD IoControlCode,
    IN LPVOID InputBuffer,
    IN DWORD InputBufferLength,
    IN LPVOID OutputBuffer,
    IN DWORD OutputBufferLength,
    OUT LPDWORD NumberOfBytesReturned,
    IN LPWSAOVERLAPPED Overlapped,
    IN LPWSAOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine,
    OUT LPBOOL NeedsCompletion
    )

/*++

Routine Description:

    Performs queries & controls on the socket. This is basically an
    "escape hatch" for IOCTLs not supported by MSAFD.DLL. Any unknown
    IOCTLs are routed to the socket's helper DLL for protocol-specific
    processing.

Arguments:

    HelperDllSocketContext - the context pointer returned from
        WSHOpenSocket().

    SocketHandle - the handle of the socket for which we're controlling.

    TdiAddressObjectHandle - the TDI address object of the socket, if
        any.  If the socket is not yet bound to an address, then
        it does not have a TDI address object and this parameter
        will be NULL.

    TdiConnectionObjectHandle - the TDI connection object of the socket,
        if any.  If the socket is not yet connected, then it does not
        have a TDI connection object and this parameter will be NULL.

    IoControlCode - Control code of the operation to perform.

    InputBuffer - Address of the input buffer.

    InputBufferLength - The length of InputBuffer.

    OutputBuffer - Address of the output buffer.

    OutputBufferLength - The length of OutputBuffer.

    NumberOfBytesReturned - Receives the number of bytes actually written
        to the output buffer.

    Overlapped - Pointer to a WSAOVERLAPPED structure for overlapped
        operations.

    CompletionRoutine - Pointer to a completion routine to call when
        the operation is completed.

    NeedsCompletion - WSAIoctl() can be overlapped, with all the gory
        details that involves, such as setting events, queuing completion
        routines, and posting to IO completion ports. Since the majority
        of the IOCTL codes can be completed quickly "in-line", MSAFD.DLL
        can optionally perform the overlapped completion of the operation.

        Setting *NeedsCompletion to TRUE (the default) causes MSAFD.DLL
        to handle all of the IO completion details iff this is an
        overlapped operation on an overlapped socket.

        Setting *NeedsCompletion to FALSE tells MSAFD.DLL to take no
        further action because the helper DLL will perform any necessary
        IO completion.

        Note that if a helper performs its own IO completion, the helper
        is responsible for maintaining the "overlapped" mode of the socket
        at socket creation time and NOT performing overlapped IO completion
        on non-overlapped sockets.

Return Value:

    INT - 0 if successful, WinSock error code if not.

--*/

{

    INT err;
    PWSHATM_SOCKET_CONTEXT context;

    //
    // Quick sanity checks.
    //

    if( HelperDllSocketContext == NULL ||
        SocketHandle == INVALID_SOCKET ||
        NumberOfBytesReturned == NULL ||
        NeedsCompletion == NULL ) {

        return WSAEINVAL;

    }

        context = (PWSHATM_SOCKET_CONTEXT)HelperDllSocketContext;
    *NeedsCompletion = TRUE;

        DBGPRINT(("WSHIoctl: IoControlCode x%x, InBuf: x%x/%d, OutBuf: x%x/%d\n",
                                        IoControlCode,
                                        InputBuffer,
                                        InputBufferLength,
                                        OutputBuffer,
                                        OutputBufferLength));

        switch (IoControlCode) {
        
        case SIO_ASSOCIATE_PVC:

                context->SocketHandle = SocketHandle;
                err = AtmAssociatePVC(
                                        SocketHandle,
                                        HelperDllSocketContext,
                                        TdiAddressObjectHandle,
                                        TdiConnectionObjectHandle,
                                        InputBuffer,
                                        InputBufferLength
                                        );

                DBGPRINT(("WSHIoctl: ASSOCIATE_PVC: context %x, LocalFlags %x, err %d\n",
                                        context, context->LocalFlags, err));

                if ( (err == NO_ERROR) && 
                         (( context->LocalFlags & WSHATM_SOCK_IS_BOUND ) == 0 )) { 

                        SOCKADDR_ATM    addr;
                        INT                             len = sizeof(addr);

                        (VOID) WSHGetWildcardSockaddr (
                                        HelperDllSocketContext,
                                        (struct sockaddr *)&addr,
                                        &len);

                        DBGPRINT(("WSHIoctl: ASSOCIATE_PVC: will bind\n"));
                        err = bind(SocketHandle, (struct sockaddr *)&addr, len);

#if DBG
                        if ( err != NO_ERROR ) {

                                DbgPrint("WSHATM: bind err %d, context %x, LocalFlags %x\n",
                                                        err, context, context->LocalFlags);
                        
                        }
#endif
                
                }

                if ( err == NO_ERROR ) {

                        SOCKADDR_ATM    addr;
                        INT                             len = sizeof(addr);

                        (VOID) WSHGetWildcardSockaddr (
                                        HelperDllSocketContext,
                                        (struct sockaddr *)&addr,
                                        &len);

                        addr.satm_family = AF_ATM;
                        addr.satm_number.AddressType = ATM_NSAP;
                        addr.satm_number.NumofDigits = ATM_ADDR_SIZE;

                        err = WSAConnect(
                                        SocketHandle,
                                        (struct sockaddr *)&addr,
                                        len,
                                        NULL,
                                        NULL,
                                        NULL,
                                        NULL);


                        if ( err != NO_ERROR ) {
                                DBGPRINT(("WSHIoctl: connect (%d) returned %d\n",
                                                SocketHandle, err));

                                if ( err == SOCKET_ERROR ) {
                                        err = WSAGetLastError();
                                }
                        }
                }

                break;

        case SIO_SET_QOS:
                err = WSHAtmSetQoS(
                                        HelperDllSocketContext,
                                        SocketHandle,
                                        InputBuffer,
                                        InputBufferLength
                                        );
                break;

        case SIO_GET_QOS:
                err = WSHAtmGetQoS(
                                        HelperDllSocketContext,
                                        SocketHandle,
                                        OutputBuffer,
                                        OutputBufferLength,
                                        NumberOfBytesReturned
                                        );
                break;

        case SIO_GET_GROUP_QOS:
                *NumberOfBytesReturned = 0;
                err = NO_ERROR;
                break;

        case SIO_SET_GROUP_QOS:
                *NumberOfBytesReturned = 0;
                err = WSAEOPNOTSUPP;
                break;

        case SIO_GET_NUMBER_OF_ATM_DEVICES:
                err = AtmQueryAtmGlobalInformation(
                                ATMSP_OID_NUMBER_OF_DEVICES,
                                NULL,
                                0,
                                OutputBuffer,
                                OutputBufferLength,
                                NumberOfBytesReturned
                                );
                break;
        
        case SIO_GET_ATM_ADDRESS:
                err = AtmQueryAtmGlobalInformation(
                                ATMSP_OID_ATM_ADDRESS,
                                InputBuffer,
                                InputBufferLength,
                                OutputBuffer,
                                OutputBufferLength,
                                NumberOfBytesReturned
                                );
                break;
        
        case SIO_GET_ATM_CONNECTION_ID:

                if ( TdiConnectionObjectHandle == NULL ) {

                        //
                        //  Check if this is a PVC. If so, the Connection ID is
                        //  available locally.
                        //
                        if ( context && ( context->LocalFlags & WSHATM_SOCK_IS_PVC )) {

                                if ( ( OutputBuffer != NULL ) &&
                                         ( OutputBufferLength >= sizeof(ATM_CONNECTION_ID) ) ) {
                                
                                        ATM_CONNECTION_ID * pConnId = OutputBuffer;

                                        *pConnId = context->ConnectionId;

                                        err = NO_ERROR;

                                } else {

                                        err = WSAEFAULT;
                                
                                }

                        } else {

                                err = WSAENOTCONN;
                        
                        }
                
                } else {

                        err = AtmGetAtmObjectInformation(
                                        TdiConnectionObjectHandle,
                                        IOCTL_RWAN_MEDIA_SPECIFIC_CONN_HANDLE_QUERY,
                                        ATMSP_OID_CONNECTION_ID,
                                        InputBuffer,
                                        InputBufferLength,
                                        OutputBuffer,
                                        OutputBufferLength,
                                        NumberOfBytesReturned
                                        );
                }
                break;
                                        
        case SIO_ENABLE_CIRCULAR_QUEUEING:
                err = NO_ERROR;
                break;

        default:
                err = WSAEINVAL;
                break;
        }

    DBGPRINT(("WSHIoctl: IoControlCode x%x, returning %d\n",
                        IoControlCode, err));

    return err;

}   // WSHIoctl



INT
WSHAtmSetQoS(
    IN PVOID HelperDllSocketContext,
    IN SOCKET SocketHandle,
    IN LPVOID InputBuffer,
    IN DWORD InputBufferLength
    )
/*++

Routine Description:

        This routine is called to process a SIO_SET_QOS Ioctl. The QoS is represented
        by a basic QOS structure, and an optional provider-specific part. We first
        copy this two-part structure into a single flat buffer, and then call
        setsockopt(SO_CONNOPT) to get MSAFD to copy this down to AFD. Later, if/when
        a WSAConnect() is made, AFD will pass these "connection options" in the TDI
        connect to the transport.

Arguments:

    HelperDllSocketContext - the context pointer returned from
        WSHOpenSocket().

    SocketHandle - the handle of the socket for which we're controlling.

    InputBuffer - Address of the input buffer.

    InputBufferLength - The length of InputBuffer.

Return Value:

    INT - The completion status.

--*/
{
        INT                     err;
        LPQOS           lpQOS;

        PUCHAR          pQoSBuffer;                     // Flat buffer for use in SO_CONNOPT
        INT                     QoSBufferLength;
        LPQOS           lpOutputQOS;

        err = NO_ERROR;
        lpQOS = (LPQOS)InputBuffer;

        DBGPRINT(("SetQoS: Context x%x, Handle x%x, InBuf x%x, InBufLen %d\n",
                                HelperDllSocketContext,
                                SocketHandle,
                                InputBuffer,
                                InputBufferLength));
        do
        {
                if (lpQOS == NULL)
                {
                        err = WSAEINVAL;
                        break;
                }

                //
                //  Expect atleast the base QOS structure to be present.
                //
                if (InputBufferLength < sizeof(*lpQOS))
                {
                        err = WSAENOBUFS;
                        break;
                }

                //
                //  Sanity check the provider-specific part.
                //
                if (((lpQOS->ProviderSpecific.buf != NULL) &&
                         (lpQOS->ProviderSpecific.len == 0))
                                ||
                        ((lpQOS->ProviderSpecific.buf == NULL) &&
                         (lpQOS->ProviderSpecific.len != 0)))
                {
                        DBGPRINT(("lpQOS %x, buf %x, len %x, not consistent\n",
                                        lpQOS, lpQOS->ProviderSpecific.buf,
                                        lpQOS->ProviderSpecific.len));

                        err = WSAEINVAL;
                        break;
                }

                //
                //  Compute the total length we need.
                //
                QoSBufferLength = sizeof(QOS) + lpQOS->ProviderSpecific.len;

                pQoSBuffer = RTL_ALLOCATE_HEAP(RtlProcessHeap(), 0, QoSBufferLength);

                if (pQoSBuffer == NULL)
                {
                        err = WSAENOBUFS;
                        break;
                }

                lpOutputQOS = (LPQOS)pQoSBuffer;

                //
                //  Copy in the generic QOS part.
                //
                RtlCopyMemory(
                        lpOutputQOS,
                        lpQOS,
                        sizeof(QOS)
                        );

                //
                //  Copy in the provider-specific QOS just after the generic part.
                //
                if (lpQOS->ProviderSpecific.len != 0)
                {
                        RtlCopyMemory(
                                (PCHAR)pQoSBuffer+sizeof(QOS),
                                lpQOS->ProviderSpecific.buf,
                                lpQOS->ProviderSpecific.len
                                );
                        
                        //
                        //  Set up the offset to provider-specific part. Note that we
                        //  use the "buf" to mean the offset from the beginning of the
                        //  flat QOS buffer and not a pointer.
                        //
                        lpOutputQOS->ProviderSpecific.buf = (char FAR *)sizeof(QOS);
                }
                else
                {
                        lpOutputQOS->ProviderSpecific.buf = NULL;
                }


                //
                //  Request the Winsock DLL to set the options.
                //
                err = setsockopt(
                                SocketHandle,
                                SOL_SOCKET,
                                SO_CONNOPT,
                                pQoSBuffer,
                                QoSBufferLength
                                );

                RTL_FREE_HEAP(
                                RtlProcessHeap(),
                                0,
                                pQoSBuffer
                                );

                break;
        }
        while (FALSE);

        DBGPRINT(("SetQoS: returning err %d\n", err));
        return (err);
}



INT
WSHAtmGetQoS(
    IN PVOID HelperDllSocketContext,
    IN SOCKET SocketHandle,
    IN LPVOID OutputBuffer,
    IN DWORD OutputBufferLength,
    OUT LPDWORD NumberOfBytesReturned
    )
/*++

Routine Description:

        This routine is called to process a SIO_GET_QOS Ioctl. We translate
        this to a "Get Connect Options" and ask MSAFD to get them for us.
        The connect options for ATM will contain the base QoS structure and
        optionally a provider-specific part that contains additional information
        elements.

        One of the places this might be called is when processing a WSAAccept
        with a condition function specified. MSAFD calls us to get the QoS,
        and we in turn request MSAFD to get SO_CONNOPT.

Arguments:

    HelperDllSocketContext - the context pointer returned from
        WSHOpenSocket().

    SocketHandle - the handle of the socket for which we're controlling.

    OutputBuffer - Address of the Output buffer.

    OutputBufferLength - The length of OutputBuffer.

    NumberOfBytesReturned - where we return the number of bytes we filled into
        OutputBuffer.

Return Value:

    INT - The completion status.

--*/
{
        INT                     err;
        DWORD           BytesReturned;
        LPQOS           lpQOS;

        err = NO_ERROR;

        DBGPRINT(("GetQoS: Context x%x, Handle x%x, OutBuf x%x, OutBufLen %d\n",
                                HelperDllSocketContext,
                                SocketHandle,
                                OutputBuffer,
                                OutputBufferLength));

        do
        {
                //
                //  Expect atleast enough space to fit in the base QoS structure.
                //
                if (OutputBufferLength < sizeof(QOS))
                {
                        *NumberOfBytesReturned = MAX_ATM_OPTIONS_LENGTH;
                        err = WSAEFAULT;
                        break;
                }

                //
                //  Request the Winsock DLL to get the options.
                //
                BytesReturned = OutputBufferLength;
                err = getsockopt(
                                SocketHandle,
                                SOL_SOCKET,
                                SO_CONNOPT,
                                OutputBuffer,
                                &BytesReturned
                                );
        
                if ((err == NO_ERROR) && (BytesReturned != 0))
                {
                        *NumberOfBytesReturned = BytesReturned;

                        lpQOS = (LPQOS)OutputBuffer;

                        //
                        //  Fix up the provider-specific part, if any.
                        //
                        if (BytesReturned > sizeof(QOS))
                        {
                                lpQOS->ProviderSpecific.buf = (PCHAR)((PCHAR)lpQOS + sizeof(QOS));
                        }
                        else
                        {
                                lpQOS->ProviderSpecific.buf = NULL;
                                lpQOS->ProviderSpecific.len = 0;
                        }
                        DBGPRINT(("GetQoS: lpQOS %x, ProvSpec buf %x, len %d\n",
                                                lpQOS, lpQOS->ProviderSpecific.buf, lpQOS->ProviderSpecific.len));
                }
                else
                {
                        if (BytesReturned == 0)
                        {
                                //
                                //  Probably because we aren't connected yet? Let's return
                                //  all "NOT_SPECIFIED" values:
                                //
                                *NumberOfBytesReturned = sizeof(QOS);

                                lpQOS = (LPQOS)OutputBuffer;
                                lpQOS->ProviderSpecific.buf = NULL;
                                lpQOS->ProviderSpecific.len = 0;

                                lpQOS->SendingFlowspec.TokenRate =
                                lpQOS->SendingFlowspec.TokenBucketSize =
                                lpQOS->SendingFlowspec.PeakBandwidth =
                                lpQOS->SendingFlowspec.Latency =
                                lpQOS->SendingFlowspec.DelayVariation =
                                lpQOS->SendingFlowspec.ServiceType =
                                lpQOS->SendingFlowspec.MaxSduSize =
                                lpQOS->SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED;
                                lpQOS->ReceivingFlowspec = lpQOS->SendingFlowspec;

                        } else {

                                err = WSAGetLastError();
                        }
                }

                break;
        }
        while (FALSE);


        return (err);
}


INT
AtmAssociatePVC(
    IN SOCKET SocketHandle,
        IN PVOID HelperDllSocketContext,
        IN HANDLE TdiAddressObjectHandle,
        IN HANDLE TdiConnectionObjectHandle,
        IN LPVOID InputBuffer,
        IN DWORD InputBufferLength
        )
{
        INT err;
    PWSHATM_SOCKET_CONTEXT context;
    ATM_PVC_PARAMS * pInPvcParams;

        context = (PWSHATM_SOCKET_CONTEXT)HelperDllSocketContext;

        DBGPRINT(("AssociatePVC: InputBuffer %x, Length %d, sizeof(ATM_PVC_PARAMS) %d\n",
                        InputBuffer, InputBufferLength, sizeof(ATM_PVC_PARAMS)));

        do {

                if ( InputBuffer == NULL ||
                         InputBufferLength < sizeof(ATM_PVC_PARAMS) ) {
                        
                        err = WSAEFAULT;
                        break;
                }

                if ( context == NULL ) {

                        err = WSAEINVAL;
                        break;
                }

                //
                //  We want to allow the user to change the PVC info any number of times
                //  before connecting, but not after the fact.
                //
                if ( TdiConnectionObjectHandle != NULL ) {

                        err = WSAEISCONN;
                        break;
                }

                if ( context->LocalFlags & WSHATM_SOCK_IS_PVC ) {
                        //
                        //  Already associated. Fail this.
                        //

                        err = WSAEISCONN;
                        break;
                }


                //
                //  Use the standard QoS mechanism to associate the QOS info
                //  with this socket.
                //
                pInPvcParams = InputBuffer;

                err = setsockopt(
                                SocketHandle,
                                SOL_SOCKET,
                                SO_CONNOPT,
                                (PCHAR)&pInPvcParams->PvcQos,
                                InputBufferLength - (DWORD)((PUCHAR)&pInPvcParams->PvcQos - (PUCHAR)pInPvcParams)
                                );


                DBGPRINT(("AssociatePVC: setsockopt, ptr %x, length %d, ret %d\n",
                                &pInPvcParams->PvcQos,
                                InputBufferLength - (DWORD)((PUCHAR)&pInPvcParams->PvcQos - (PUCHAR)pInPvcParams),
                                err));
                                
                if ( err != NO_ERROR ) {

                        break;
                }

                //
                //  Store the Connection Id.
                //
                context->ConnectionId = pInPvcParams->PvcConnectionId;
                context->LocalFlags |= WSHATM_SOCK_IS_PVC;

                if ( TdiAddressObjectHandle == NULL ) {
                
                        //
                        // We've got an ASSOCIATE_PVC before this socket has been
                        // bound. Just remember this, so that we do the rest when
                        // the bind actually happens.
                        //
                        context->LocalFlags |= WSHATM_SOCK_ASSOCIATE_PVC_PENDING;
                        err = NO_ERROR;
                        break;
                }

                //
                //  The socket is bound, so send info about the PVC Connection
                //  ID to the transport.
                //
                err = AtmDoAssociatePVC(
                                context,
                                TdiAddressObjectHandle
                                );
                DBGPRINT(("AssociatePVC: DoAssociatePVC ret %d\n", err));

                break;
        }
        while (FALSE);

        DBGPRINT(("AssociatePVC: context Flags %x, LocalFlags %x, returning %d\n",
                        context? context->Flags: 0,
                        context? context->LocalFlags: 0,
                        err));

        return err;
}


INT
AtmDoAssociatePVC(
        IN PWSHATM_SOCKET_CONTEXT Context,
        IN HANDLE TdiAddressObjectHandle
        )
{
        INT err;

        DBGPRINT(("DoAssociatePVC: Context %x, LocalFlags %x\n",
                                Context, LocalFlags));

        Context->LocalFlags &= ~WSHATM_SOCK_ASSOCIATE_PVC_PENDING;

        err = AtmSetAtmObjectInformation(
                        TdiAddressObjectHandle,
                        IOCTL_RWAN_MEDIA_SPECIFIC_ADDR_HANDLE_SET,
                        ATMSP_OID_PVC_ID,
                        &Context->ConnectionId,
                        sizeof(ATM_CONNECTION_ID)
                        );

        return err;
}


INT
AtmQueryAtmGlobalInformation(
        IN ATM_OBJECT_ID ObjectId,
        IN LPVOID pContext,
        IN DWORD ContextLength,
        IN LPVOID OutputBuffer,
        IN DWORD OutputBufferLength,
        OUT LPDWORD NumberOfBytesReturned
        )
{
        INT err;
    NTSTATUS status;
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING deviceName;
    HANDLE deviceHandle = NULL;
    PIO_STATUS_BLOCK ioStatusBlock;
    PATM_QUERY_INFORMATION_EX pQueryInfo;

        do
        {
                ioStatusBlock = RTL_ALLOCATE_HEAP(
                                                        RtlProcessHeap( ),
                                                        0,
                                                        sizeof(*ioStatusBlock) + sizeof(ATM_QUERY_INFORMATION_EX) + ContextLength
                                                        );
                
                if ( ioStatusBlock == NULL ) {
                        err = WSAENOBUFS;
                        break;
                }


                //
                // Open a handle to the ATM device.
                //

                RtlInitUnicodeString(
                        &deviceName,
                        DD_ATM_DEVICE_NAME
                        );

                InitializeObjectAttributes(
                        &objectAttributes,
                        &deviceName,
                        OBJ_CASE_INSENSITIVE,
                        NULL,
                        NULL
                        );

                status = NtCreateFile(
                                        &deviceHandle,
                                        SYNCHRONIZE | GENERIC_EXECUTE,
                                        &objectAttributes,
                                        ioStatusBlock,
                                        NULL,
                                        FILE_ATTRIBUTE_NORMAL,
                                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                                        FILE_OPEN_IF,
                                        FILE_SYNCHRONOUS_IO_NONALERT,
                                        NULL,
                                        0
                                        );

                if( !NT_SUCCESS(status) ) {

                        if (status == STATUS_INSUFFICIENT_RESOURCES) {
                                err = WSAEFAULT;
                        } else {
                                err = WSAEINVAL;
                        }

                        break;

                }

                pQueryInfo = (PATM_QUERY_INFORMATION_EX)(ioStatusBlock + 1);
                pQueryInfo->ObjectId = ObjectId;
                pQueryInfo->ContextLength = ContextLength;

                if ( pQueryInfo->ContextLength > 0 ) {

                        RtlCopyMemory(
                                pQueryInfo->Context,
                                pContext,
                                pQueryInfo->ContextLength
                                );

                }

                status = NtDeviceIoControlFile(
                                        deviceHandle,
                                        NULL,   // No Event
                                        NULL,   // No completion APC
                                        NULL,   // No completion APC Context
                                        ioStatusBlock,
                                        IOCTL_RWAN_MEDIA_SPECIFIC_GLOBAL_QUERY,
                                        pQueryInfo,
                                        sizeof(ATM_QUERY_INFORMATION_EX) + ContextLength,
                                        OutputBuffer,
                                        OutputBufferLength
                                        );

                DBGPRINT(("DevIoControl (Oid %x) returned x%x, Info %d\n",
                                                pQueryInfo->ObjectId, status, ioStatusBlock->Information));

                if ( NT_SUCCESS(status) ) {
                        err = NO_ERROR;
                        *NumberOfBytesReturned = (ULONG)ioStatusBlock->Information;
                }
                else {
                        if (status == STATUS_INSUFFICIENT_RESOURCES) {
                                err = WSAEFAULT;
                        } else {
                                err = WSAEINVAL;
                        }
                }
        
        }
        while (FALSE);

    if( deviceHandle != NULL ) {

        NtClose( deviceHandle );

    }

        if ( ioStatusBlock != NULL ) {
                RTL_FREE_HEAP( RtlProcessHeap( ), 0, ioStatusBlock );
        }

        return err;
}


INT
AtmSetGenericObjectInformation (
    IN HANDLE TdiObjectHandle,
    IN ULONG IoControlCode,
    IN RWAN_OBJECT_ID ObjectId,
    IN PVOID InputBuffer,
    IN ULONG InputBufferLength
    )

/*++

Routine Description:

        Performs a RWAN Set Information action to the Raw Wan driver. This operation
        is directed to either an Address Object or to a Connection object, according
        to TdiObjectHandle.

Arguments:

    TdiObjectHandle - a TDI handle to either an Address or a Connection object
        on which to perform the Set Info operation.

    IoControlCode - IOCTL_RWAN_GENERIC_XXXSET

    ObjectId - value to put in the ObjectId field of the Set Info structure.

        InputBuffer - Points to buffer containing value for the Object.

        InputBufferLength - Length of the above.

Return Value:

    INT - NO_ERROR, or a Windows Sockets error code.

--*/

{
    NTSTATUS status;
    INT err;
    PRWAN_SET_INFORMATION_EX pSetInfo;
    PIO_STATUS_BLOCK ioStatusBlock;

        do
        {
                ioStatusBlock = RTL_ALLOCATE_HEAP(
                                                        RtlProcessHeap( ),
                                                        0,
                                                        sizeof(*ioStatusBlock) + sizeof(RWAN_SET_INFORMATION_EX) + InputBufferLength
                                                        );
                
                if ( ioStatusBlock == NULL ) {
                        err = WSAENOBUFS;
                        break;
                }

                pSetInfo = (PRWAN_SET_INFORMATION_EX)(ioStatusBlock + 1);
                pSetInfo->ObjectId = ObjectId;
                pSetInfo->BufferSize = InputBufferLength;

                if ( pSetInfo->BufferSize > 0 ) {

                        RtlCopyMemory(
                                pSetInfo->Buffer,
                                InputBuffer,
                                pSetInfo->BufferSize
                                );

                }

                status = NtDeviceIoControlFile(
                                        TdiObjectHandle,
                                        NULL,   // No Event
                                        NULL,   // No completion APC
                                        NULL,   // No completion APC Context
                                        ioStatusBlock,
                                        IoControlCode,
                                        pSetInfo,
                                        sizeof(RWAN_SET_INFORMATION_EX) + InputBufferLength,
                                        NULL,   // No output buffer
                                        0               // output buffer length
                                        );

                DBGPRINT(("AtmSetInfo: IOCTL (Oid %x) returned x%x\n", pSetInfo->ObjectId, status));

                if ( NT_SUCCESS(status) ) {
                        err = NO_ERROR;
                }
                else {
                        if (status == STATUS_INSUFFICIENT_RESOURCES) {
                                err = WSAEFAULT;
                        } else {
                                err = WSAEINVAL;
                        }
                }
        
        }
        while (FALSE);

        if ( ioStatusBlock != NULL ) {
                RTL_FREE_HEAP( RtlProcessHeap( ), 0, ioStatusBlock );
        }

        return err;
}


INT
AtmGetGenericObjectInformation (
    IN HANDLE TdiObjectHandle,
    IN ULONG IoControlCode,
    IN RWAN_OBJECT_ID ObjectId,
    IN PVOID InputBuffer,
    IN ULONG InputBufferLength,
    OUT PVOID OutputBuffer,
    IN ULONG OutputBufferLength,
        OUT LPDWORD NumberOfBytesReturned
    )

/*++

Routine Description:

        Performs a RWAN Get Information action to the Raw Wan driver. This operation
        is directed to either an Address Object or to a Connection object, according
        to TdiObjectHandle.

Arguments:

    TdiObjectHandle - a TDI handle to either an Address or a Connection object
        on which to perform the Set Info operation.

    IoControlCode - IOCTL_RWAN_GENERIC_XXXGET

    ObjectId - value to put in the ObjectId field of the Set Info structure.

        InputBuffer - Points to buffer containing context for the Object.

        InputBufferLength - Length of the above.

        OutputBuffer - place to return value

        OutputBufferLength - bytes available in OutputBuffer

        NumberOfBytesReturned - place to return bytes written

Return Value:

    INT - NO_ERROR, or a Windows Sockets error code.

--*/

{
    NTSTATUS status;
    INT err;
    PRWAN_QUERY_INFORMATION_EX pQueryInfo;
    PIO_STATUS_BLOCK ioStatusBlock;

        do
        {
                ioStatusBlock = RTL_ALLOCATE_HEAP(
                                                        RtlProcessHeap( ),
                                                        0,
                                                        sizeof(*ioStatusBlock) + sizeof(RWAN_QUERY_INFORMATION_EX) + InputBufferLength
                                                        );
                
                if ( ioStatusBlock == NULL ) {
                        err = WSAENOBUFS;
                        break;
                }

                pQueryInfo = (PRWAN_QUERY_INFORMATION_EX)(ioStatusBlock + 1);
                pQueryInfo->ObjectId = ObjectId;
                pQueryInfo->ContextLength = InputBufferLength;

                if ( pQueryInfo->ContextLength > 0 ) {

                        RtlCopyMemory(
                                pQueryInfo->Context,
                                InputBuffer,
                                pQueryInfo->ContextLength
                                );
                }

                status = NtDeviceIoControlFile(
                                        TdiObjectHandle,
                                        NULL,   // No Event
                                        NULL,   // No completion APC
                                        NULL,   // No completion APC Context
                                        ioStatusBlock,
                                        IoControlCode,
                                        pQueryInfo,
                                        sizeof(RWAN_QUERY_INFORMATION_EX) + InputBufferLength,
                                        OutputBuffer,
                                        OutputBufferLength              // output buffer length
                                        );

                DBGPRINT(("AtmGetGenericInfo: IOCTL (Oid %x) returned x%x\n", pQueryInfo->ObjectId, status));
                if ( NT_SUCCESS(status) ) {
                        err = NO_ERROR;
                        *NumberOfBytesReturned = (ULONG)ioStatusBlock->Information;
                }
                else {
                        if (status == STATUS_INSUFFICIENT_RESOURCES) {
                                err = WSAEFAULT;
                        } else {
                                err = WSAEINVAL;
                        }

                }
        
        }
        while (FALSE);

        if ( ioStatusBlock != NULL ) {
                RTL_FREE_HEAP( RtlProcessHeap( ), 0, ioStatusBlock );
        }

        return err;
}



INT
AtmSetAtmObjectInformation (
    IN HANDLE TdiObjectHandle,
    IN ULONG IoControlCode,
    IN ATM_OBJECT_ID ObjectId,
    IN PVOID InputBuffer,
    IN ULONG InputBufferLength
    )

/*++

Routine Description:

        Performs a Media-specific Set Information action to the Raw Wan driver.
        This operation is directed to either an Address Object or to a Connection
        object, according to TdiObjectHandle.

Arguments:

    TdiObjectHandle - a TDI handle to either an Address or a Connection object
        on which to perform the Set Info operation.

    IoControlCode - IOCTL_RWAN_MEDIA_SPECIFIC_XXX

    ObjectId - value to put in the ObjectId field of the Set Info structure.

        InputBuffer - Points to buffer containing value for the Object.

        InputBufferLength - Length of the above.

Return Value:

    INT - NO_ERROR, or a Windows Sockets error code.

--*/

{
    NTSTATUS status;
    INT err;
    PATM_SET_INFORMATION_EX pSetInfo;
    PIO_STATUS_BLOCK ioStatusBlock;

        do
        {
                ioStatusBlock = RTL_ALLOCATE_HEAP(
                                                        RtlProcessHeap( ),
                                                        0,
                                                        sizeof(*ioStatusBlock) + sizeof(ATM_SET_INFORMATION_EX) + InputBufferLength
                                                        );
                
                if ( ioStatusBlock == NULL ) {
                        err = WSAENOBUFS;
                        break;
                }

                pSetInfo = (PATM_SET_INFORMATION_EX)(ioStatusBlock + 1);
                pSetInfo->ObjectId = ObjectId;
                pSetInfo->BufferSize = InputBufferLength;

                if ( pSetInfo->BufferSize > 0 ) {

                        RtlCopyMemory(
                                pSetInfo->Buffer,
                                InputBuffer,
                                pSetInfo->BufferSize
                                );

                }

                status = NtDeviceIoControlFile(
                                        TdiObjectHandle,
                                        NULL,   // No Event
                                        NULL,   // No completion APC
                                        NULL,   // No completion APC Context
                                        ioStatusBlock,
                                        IoControlCode,
                                        pSetInfo,
                                        sizeof(ATM_SET_INFORMATION_EX) + InputBufferLength,
                                        NULL,   // No output buffer
                                        0               // output buffer length
                                        );

                DBGPRINT(("AtmSetInfo: IOCTL (Oid %x) returned x%x\n", pSetInfo->ObjectId, status));

                if ( NT_SUCCESS(status) ) {
                        err = NO_ERROR;
                }
                else {
                        if (status == STATUS_INSUFFICIENT_RESOURCES) {
                                err = WSAEFAULT;
                        } else {
                                err = WSAEINVAL;
                        }
                }
        
        }
        while (FALSE);

        if ( ioStatusBlock != NULL ) {
                RTL_FREE_HEAP( RtlProcessHeap( ), 0, ioStatusBlock );
        }

        return err;
}


INT
AtmGetAtmObjectInformation (
    IN HANDLE TdiObjectHandle,
    IN ULONG IoControlCode,
    IN ATM_OBJECT_ID ObjectId,
    IN PVOID InputBuffer,
    IN ULONG InputBufferLength,
    OUT PVOID OutputBuffer,
    IN ULONG OutputBufferLength,
        OUT LPDWORD NumberOfBytesReturned
    )

/*++

Routine Description:

        Performs a Media-specific Get Information action to the Raw Wan driver.
        This operation is directed to either an Address Object or to a Connection
        object, according to TdiObjectHandle.

Arguments:

    TdiObjectHandle - a TDI handle to either an Address or a Connection object
        on which to perform the Set Info operation.

    IoControlCode - IOCTL_RWAN_MEDIA_SPECIFIC_XXX

    ObjectId - value to put in the ObjectId field of the Set Info structure.

        InputBuffer - Points to buffer containing context for the Object.

        InputBufferLength - Length of the above.

        OutputBuffer - place to return value

        OutputBufferLength - bytes available in OutputBuffer

        NumberOfBytesReturned - place to return bytes written

Return Value:

    INT - NO_ERROR, or a Windows Sockets error code.

--*/

{
    NTSTATUS status;
    INT err;
    PATM_QUERY_INFORMATION_EX pQueryInfo;
    PIO_STATUS_BLOCK ioStatusBlock;

        do
        {

                *NumberOfBytesReturned = 0;

                ioStatusBlock = RTL_ALLOCATE_HEAP(
                                                        RtlProcessHeap( ),
                                                        0,
                                                        sizeof(*ioStatusBlock) + sizeof(ATM_QUERY_INFORMATION_EX) + InputBufferLength
                                                        );
                
                if ( ioStatusBlock == NULL ) {
                        err = WSAENOBUFS;
                        break;
                }

                pQueryInfo = (PATM_QUERY_INFORMATION_EX)(ioStatusBlock + 1);
                pQueryInfo->ObjectId = ObjectId;
                pQueryInfo->ContextLength = InputBufferLength;

                if ( pQueryInfo->ContextLength > 0 ) {

                        RtlCopyMemory(
                                pQueryInfo->Context,
                                InputBuffer,
                                pQueryInfo->ContextLength
                                );
                }

                status = NtDeviceIoControlFile(
                                        TdiObjectHandle,
                                        NULL,   // No Event
                                        NULL,   // No completion APC
                                        NULL,   // No completion APC Context
                                        ioStatusBlock,
                                        IoControlCode,
                                        pQueryInfo,
                                        sizeof(ATM_QUERY_INFORMATION_EX) + InputBufferLength,
                                        OutputBuffer,
                                        OutputBufferLength
                                        );

                DBGPRINT(("AtmGetAtmObjInfo: IOCTL (Oid %x) returned x%x\n",
                                        pQueryInfo->ObjectId, status));

                if ( NT_SUCCESS(status) ) {
                        err = NO_ERROR;
                        *NumberOfBytesReturned = (ULONG)ioStatusBlock->Information;
                }
                else {
                        if (status == STATUS_INSUFFICIENT_RESOURCES) {
                                err = WSAEFAULT;
                        } else {
                                err = WSAEINVAL;
                        }
                }
        
        }
        while (FALSE);

        if ( ioStatusBlock != NULL ) {
                RTL_FREE_HEAP( RtlProcessHeap( ), 0, ioStatusBlock );
        }

        return err;
}

#if DBG
PVOID
MyRtlAllocateHeap(
        IN      PVOID   HeapHandle,
        IN      ULONG   Flags,
        IN      ULONG   Size,
        IN      ULONG   LineNumber
        )
{
        PVOID   pRetValue;

        pRetValue = RtlAllocateHeap(HeapHandle, Flags, Size);

#if DBG2
        DbgPrint("WSHATM: AllocHeap size %d at line %d, ret x%x\n",
                        Size, LineNumber, pRetValue);
#endif
        return (pRetValue);
}


VOID
MyRtlFreeHeap(
        IN PVOID        HeapHandle,
        IN ULONG        Flags,
        IN PVOID        MemPtr,
        IN ULONG        LineNumber
        )
{
#if DBG2
        DbgPrint("WSHATM: FreeHeap x%x, line %d\n", MemPtr, LineNumber);
#endif
        RtlFreeHeap(HeapHandle, Flags, MemPtr);
}

#endif // DBG