/*++ Copyright (c) 1992 Microsoft Corporation Module Name: wshatalk.c Abstract: This module contains necessary routines for the Appletalk Windows Sockets Helper DLL. This DLL provides the transport-specific support necessary for the Windows Sockets DLL to use Appletalk as a transport. Author: David Treadwell (davidtr) 19-Jul-1992 - TCP/IP version Nikhil Kamkolkar (nikhilk) 17-Nov- 1992 - Appletalk version Revision History: --*/ #include "nspatalk.h" #include "wshdata.h" // GLOBAL - get the mac code page value from the registry. int WshMacCodePage = 0; #if 0 VOID PrintByteString( PUCHAR pSrcOemString, USHORT SrcStringLen ) { DbgPrint("%x - ", SrcStringLen); while (SrcStringLen-- > 0) { DbgPrint("%x", *pSrcOemString++); } DbgPrint("\n"); } #define DBGPRINT0 DBGPRINT #else #define DBGPRINT0 #endif 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_AT *sockaddr = (PSOCKADDR_AT)Sockaddr; DBGPRINT0(("WSHGetSockAddrType: Entered\n")); // // Make sure that the address family is correct. // if ( sockaddr->sat_family != AF_APPLETALK ) { return WSAEAFNOSUPPORT; } // // Make sure that the length is correct. // if ( SockaddrLength < sizeof(SOCKADDR_AT) ) { return WSAEFAULT; } // // The address passed the tests, looks like a good address. // Determine the type of the address portion of the sockaddr. // if ( sockaddr->sat_socket == ATADDR_ANY ) { SockaddrInfo->AddressInfo = SockaddrAddressInfoWildcard; } else if ( sockaddr->sat_node == ATADDR_BROADCAST ) { SockaddrInfo->AddressInfo = SockaddrAddressInfoBroadcast; } else { SockaddrInfo->AddressInfo = SockaddrAddressInfoNormal; } // // Determine the type of the port (endpoint) in the sockaddr. // if ( sockaddr->sat_socket == 0 ) { SockaddrInfo->EndpointInfo = SockaddrEndpointInfoWildcard; } else { SockaddrInfo->EndpointInfo = SockaddrEndpointInfoNormal; } return NO_ERROR; } // WSHGetSockaddrType // Fix for bug 262107 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 AppleTalk, we just blank out the address with zeros. 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. --*/ { if ( *SockaddrLength < sizeof(SOCKADDR_AT) ) { return WSAEFAULT; } *SockaddrLength = sizeof(SOCKADDR_AT); // // Just zero out the address and set the family to AF_APPLETALK--this is // a wildcard address for AppleTalk. // RtlZeroMemory( Sockaddr, sizeof(SOCKADDR_AT) ); Sockaddr->sa_family = AF_APPLETALK; return NO_ERROR; } // WSAGetWildcardSockaddr 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. The options supported here are SO_LOOKUPNAME/SO_LOOKUPZONES. This routine is called by the winsock DLL when a level/option name combination is passed to getsockopt() that the winsock DLL does not understand. Arguments: 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. --*/ { NTSTATUS status; ULONG tdiActionLength; IO_STATUS_BLOCK ioStatusBlock; HANDLE eventHandle; PTDI_ACTION_HEADER tdiAction; INT error = NO_ERROR; UNREFERENCED_PARAMETER( SocketHandle ); UNREFERENCED_PARAMETER( TdiConnectionObjectHandle ); DBGPRINT0(("WSHGetSocketInformation: Entered, OptionName %ld\n", OptionName)); if ( Level == SOL_INTERNAL && OptionName == SO_CONTEXT ) { PWSHATALK_SOCKET_CONTEXT context = HelperDllSocketContext; // // 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) ) { *OptionLength = sizeof(*context); return WSAEFAULT; } // // Copy in the context information. // RtlCopyMemory( OptionValue, context, sizeof(*context) ); } *OptionLength = sizeof(*context); return NO_ERROR; } // // The only level we support here is SOL_APPLETALK. // if ( Level != SOL_APPLETALK ) { return WSAEINVAL; } // // Fill in the result based on the option name. // switch ( OptionName ) { case SO_LOOKUP_NAME: if (( TdiAddressObjectHandle != NULL) && (*OptionLength > sizeof(WSH_LOOKUP_NAME))) { // Due to the 'greater than' check we are guaranteed atleast // one byte after the parameters. tdiActionLength = sizeof(NBP_LOOKUP_ACTION) + *OptionLength - sizeof(WSH_LOOKUP_NAME); } else { error = WSAEINVAL; } break; case SO_CONFIRM_NAME: if (( TdiAddressObjectHandle != NULL) && (*OptionLength >= sizeof(WSH_NBP_TUPLE))) { tdiActionLength = sizeof(NBP_CONFIRM_ACTION); } else { error = WSAEINVAL; } break; case SO_LOOKUP_MYZONE : if (( TdiAddressObjectHandle != NULL) && (*OptionLength > 0)) { // Due to the 'greater than' check we are guaranteed atleast // one byte after the parameters. tdiActionLength = sizeof(ZIP_GETMYZONE_ACTION) + *OptionLength; } else { error = WSAEINVAL; } break; case SO_LOOKUP_ZONES : if (( TdiAddressObjectHandle != NULL) && (*OptionLength > sizeof(WSH_LOOKUP_ZONES))) { // Due to the 'greater than' check we are guaranteed atleast // one byte after the parameters. tdiActionLength = sizeof(ZIP_GETZONELIST_ACTION) + *OptionLength - sizeof(WSH_LOOKUP_ZONES); } else { error = WSAEINVAL; } break; case SO_LOOKUP_ZONES_ON_ADAPTER: if ((TdiAddressObjectHandle != NULL) && (*OptionLength > sizeof(WSH_LOOKUP_ZONES))) { tdiActionLength = sizeof(ZIP_GETZONELIST_ACTION) + *OptionLength - sizeof(WSH_LOOKUP_ZONES); } else { error = WSAEINVAL; } break; case SO_LOOKUP_NETDEF_ON_ADAPTER: if ((TdiAddressObjectHandle != NULL) && (*OptionLength > sizeof(WSH_LOOKUP_NETDEF_ON_ADAPTER))) { tdiActionLength = sizeof(ZIP_GETPORTDEF_ACTION) + *OptionLength - sizeof(WSH_LOOKUP_NETDEF_ON_ADAPTER); } else { error = WSAEINVAL; } break; case SO_PAP_GET_SERVER_STATUS: if (( TdiAddressObjectHandle != NULL ) && ( *OptionLength >= sizeof(WSH_PAP_GET_SERVER_STATUS))) { tdiActionLength = sizeof(PAP_GETSTATUSSRV_ACTION) + *OptionLength - sizeof(SOCKADDR_AT); } else { error = WSAEINVAL; } break; default: error = WSAENOPROTOOPT; break; } if (error != NO_ERROR) { return(error); } tdiAction = RtlAllocateHeap( RtlProcessHeap( ), 0, tdiActionLength ); if ( tdiAction == NULL ) { return WSAENOBUFS; } tdiAction->TransportId = MATK; status = NtCreateEvent( &eventHandle, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE); if ( !NT_SUCCESS(status) ) { RtlFreeHeap( RtlProcessHeap( ), 0, tdiAction ); return WSAENOBUFS; } switch ( OptionName ) { case SO_LOOKUP_NAME: { PNBP_LOOKUP_ACTION nbpAction; nbpAction = (PNBP_LOOKUP_ACTION)tdiAction; nbpAction->ActionHeader.ActionCode = COMMON_ACTION_NBPLOOKUP; // // Copy the nbp name for lookup in the proper place // RtlCopyMemory( (PCHAR)&nbpAction->Params.LookupTuple, (PCHAR)(&((PWSH_LOOKUP_NAME)OptionValue)->LookupTuple), sizeof(((PWSH_LOOKUP_NAME)OptionValue)->LookupTuple)); if (!WshNbpNameToMacCodePage( &((PWSH_LOOKUP_NAME)OptionValue)->LookupTuple.NbpName)) { error = WSAEINVAL; break; } } break; case SO_CONFIRM_NAME: { PNBP_CONFIRM_ACTION nbpAction; nbpAction = (PNBP_CONFIRM_ACTION)tdiAction; nbpAction->ActionHeader.ActionCode = COMMON_ACTION_NBPCONFIRM; // // Copy the nbp name for confirm in the proper place // RtlCopyMemory( (PCHAR)&nbpAction->Params.ConfirmTuple, (PCHAR)OptionValue, sizeof(WSH_NBP_TUPLE)); if (!WshNbpNameToMacCodePage( &((PWSH_NBP_TUPLE)OptionValue)->NbpName)) { error = WSAEINVAL; break; } } break; case SO_LOOKUP_ZONES : { PZIP_GETZONELIST_ACTION zipAction; zipAction = (PZIP_GETZONELIST_ACTION)tdiAction; zipAction->ActionHeader.ActionCode = COMMON_ACTION_ZIPGETZONELIST; // // No parameters need to be passed // } break; case SO_LOOKUP_NETDEF_ON_ADAPTER: { PZIP_GETPORTDEF_ACTION zipAction; zipAction = (PZIP_GETPORTDEF_ACTION)tdiAction; zipAction->ActionHeader.ActionCode = COMMON_ACTION_ZIPGETADAPTERDEFAULTS; // If the string is not null-terminated, the calling process will *DIE*. wcsncpy( (PWCHAR)((PUCHAR)zipAction + sizeof(ZIP_GETPORTDEF_ACTION)), (PWCHAR)((PUCHAR)OptionValue + sizeof(WSH_LOOKUP_NETDEF_ON_ADAPTER)), ((tdiActionLength - sizeof(ZIP_GETPORTDEF_ACTION))/sizeof(WCHAR))); } break; case SO_LOOKUP_ZONES_ON_ADAPTER: { PZIP_GETZONELIST_ACTION zipAction; zipAction = (PZIP_GETZONELIST_ACTION)tdiAction; zipAction->ActionHeader.ActionCode = COMMON_ACTION_ZIPGETLZONESONADAPTER; // If the string is not null-terminated, the calling process will *DIE*. wcsncpy( (PWCHAR)((PUCHAR)zipAction + sizeof(ZIP_GETZONELIST_ACTION)), (PWCHAR)((PUCHAR)OptionValue + sizeof(WSH_LOOKUP_ZONES)), ((tdiActionLength - sizeof(ZIP_GETZONELIST_ACTION))/sizeof(WCHAR))); } break; case SO_LOOKUP_MYZONE : { PZIP_GETMYZONE_ACTION zipAction; zipAction = (PZIP_GETMYZONE_ACTION)tdiAction; zipAction->ActionHeader.ActionCode = COMMON_ACTION_ZIPGETMYZONE; } break; case SO_PAP_GET_SERVER_STATUS: { PPAP_GETSTATUSSRV_ACTION papAction; papAction = (PPAP_GETSTATUSSRV_ACTION)tdiAction; papAction->ActionHeader.ActionCode = ACTION_PAPGETSTATUSSRV; // Set the server address. SOCK_TO_TDI_ATALKADDR( &papAction->Params.ServerAddr, &((PWSH_PAP_GET_SERVER_STATUS)OptionValue)->ServerAddr); } break; default: // // Should have returned in the first switch statement // error = WSAENOPROTOOPT; break; } if (error != NO_ERROR) { RtlFreeHeap( RtlProcessHeap( ), 0, tdiAction ); NtClose( eventHandle ); return (error); } status = NtDeviceIoControlFile( TdiAddressObjectHandle, eventHandle, NULL, NULL, &ioStatusBlock, IOCTL_TDI_ACTION, NULL, // Input buffer 0, // Length of input buffer tdiAction, tdiActionLength); if ( status == STATUS_PENDING ) { status = NtWaitForSingleObject( eventHandle, FALSE, NULL ); ASSERT( NT_SUCCESS(status) ); status = ioStatusBlock.Status; } error = WSHNtStatusToWinsockErr(status); // Only copy data over if the error code is no-error or buffer too small. // For a confirm, a new socket could be returned for the lookup. if ((error == NO_ERROR) || (error == WSAENOBUFS) || ((error == WSAEADDRNOTAVAIL) && (OptionName == SO_CONFIRM_NAME))) { switch ( OptionName ) { case SO_LOOKUP_NAME: // // We are guaranteed by checks in the beginning atleast one byte // following the buffer // { PNBP_LOOKUP_ACTION nbpAction; PWSH_NBP_TUPLE pNbpTuple; PUCHAR tdiBuffer = (PCHAR)tdiAction+sizeof(NBP_LOOKUP_ACTION); PUCHAR userBuffer = (PCHAR)OptionValue+sizeof(WSH_LOOKUP_NAME); INT copySize = *OptionLength - sizeof(WSH_LOOKUP_NAME); nbpAction = (PNBP_LOOKUP_ACTION)tdiAction; ((PWSH_LOOKUP_NAME)OptionValue)->NoTuples = nbpAction->Params.NoTuplesRead; RtlCopyMemory( userBuffer, tdiBuffer, copySize); // // Convert all tuples from MAC to OEM code page // pNbpTuple = (PWSH_NBP_TUPLE)userBuffer; while (nbpAction->Params.NoTuplesRead-- > 0) { if (!WshNbpNameToOemCodePage( &pNbpTuple->NbpName)) { DBGPRINT(("WSHGetSocketInformation: ToOem failed %d\n!", (USHORT)((PWSH_LOOKUP_ZONES)OptionValue)->NoZones)); error = WSAEINVAL; break; } pNbpTuple++; } } break; case SO_CONFIRM_NAME: { PNBP_CONFIRM_ACTION nbpAction; nbpAction = (PNBP_CONFIRM_ACTION)tdiAction; // // Copy the nbp name for confirm back into the option buffer // RtlCopyMemory( (PCHAR)OptionValue, (PCHAR)&nbpAction->Params.ConfirmTuple, sizeof(WSH_NBP_TUPLE)); // // Convert NbpName from MAC to OEM code page // if (!WshNbpNameToOemCodePage( &((PWSH_NBP_TUPLE)OptionValue)->NbpName)) { DBGPRINT(("WSHGetSocketInformation: ToOem failed %d\n!", (USHORT)((PWSH_LOOKUP_ZONES)OptionValue)->NoZones)); error = WSAEINVAL; break; } } break; case SO_LOOKUP_ZONES: case SO_LOOKUP_ZONES_ON_ADAPTER: // // We are guaranteed by checks in the beginning atleast one byte // following the buffer // { PZIP_GETZONELIST_ACTION zipAction; PUCHAR tdiBuffer = (PCHAR)tdiAction + sizeof(ZIP_GETZONELIST_ACTION); PUCHAR userBuffer = (PCHAR)OptionValue + sizeof(WSH_LOOKUP_ZONES); INT copySize = *OptionLength - sizeof(WSH_LOOKUP_ZONES); zipAction = (PZIP_GETZONELIST_ACTION)tdiAction; ((PWSH_LOOKUP_ZONES)OptionValue)->NoZones= zipAction->Params.ZonesAvailable; RtlCopyMemory( userBuffer, tdiBuffer, copySize); if (!WshZoneListToOemCodePage( userBuffer, (USHORT)((PWSH_LOOKUP_ZONES)OptionValue)->NoZones)) { DBGPRINT(("WSHGetSocketInformation: ToOem failed %d\n!", (USHORT)((PWSH_LOOKUP_ZONES)OptionValue)->NoZones)); error = WSAEINVAL; break; } } break; case SO_LOOKUP_NETDEF_ON_ADAPTER: { PZIP_GETPORTDEF_ACTION zipAction; PUCHAR tdiBuffer = (PCHAR)tdiAction + sizeof(ZIP_GETPORTDEF_ACTION); PUCHAR userBuffer = (PCHAR)OptionValue + sizeof(WSH_LOOKUP_NETDEF_ON_ADAPTER); INT copySize = *OptionLength - sizeof(WSH_LOOKUP_NETDEF_ON_ADAPTER); zipAction = (PZIP_GETPORTDEF_ACTION)tdiAction; ((PWSH_LOOKUP_NETDEF_ON_ADAPTER)OptionValue)->NetworkRangeLowerEnd = zipAction->Params.NwRangeLowEnd; ((PWSH_LOOKUP_NETDEF_ON_ADAPTER)OptionValue)->NetworkRangeUpperEnd = zipAction->Params.NwRangeHighEnd; // Copy the rest of the buffer RtlCopyMemory( userBuffer, tdiBuffer, copySize); if (!WshZoneListToOemCodePage( userBuffer, 1)) { DBGPRINT(("WSHGetSocketInformation: ToOem failed %d\n!", (USHORT)((PWSH_LOOKUP_ZONES)OptionValue)->NoZones)); error = WSAEINVAL; break; } } break; case SO_LOOKUP_MYZONE : { PUCHAR tdiBuffer = (PCHAR)tdiAction+sizeof(ZIP_GETMYZONE_ACTION); PUCHAR userBuffer = (PCHAR)OptionValue; INT copySize = *OptionLength; RtlCopyMemory( userBuffer, tdiBuffer, copySize); if (!WshZoneListToOemCodePage( userBuffer, 1)) { DBGPRINT(("WSHGetSocketInformation: ToOem failed %d\n!", (USHORT)((PWSH_LOOKUP_ZONES)OptionValue)->NoZones)); error = WSAEINVAL; break; } } break; case SO_PAP_GET_SERVER_STATUS: { PUCHAR tdiBuffer = (PCHAR)tdiAction+sizeof(PAP_GETSTATUSSRV_ACTION); PUCHAR userBuffer = (PCHAR)OptionValue+sizeof(SOCKADDR_AT); INT copySize = *OptionLength - sizeof(SOCKADDR_AT); RtlCopyMemory( userBuffer, tdiBuffer, copySize); } break; default: error = WSAENOPROTOOPT; break; } } RtlFreeHeap( RtlProcessHeap( ), 0, tdiAction ); NtClose( eventHandle ); return error; } // WSHGetSocketInformation 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. The options supported here are SO_REGISTERNAME/SO_DEREGISTERNAME. 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. --*/ { NTSTATUS status; ULONG tdiActionLength; HANDLE objectHandle; PIO_STATUS_BLOCK pIoStatusBlock; PTDI_ACTION_HEADER tdiAction; PWSHATALK_SOCKET_CONTEXT context = HelperDllSocketContext; HANDLE eventHandle = NULL; PVOID completionApc = NULL; PVOID apcContext = NULL; BOOLEAN freeTdiAction = FALSE; INT error = NO_ERROR; BOOLEAN waitForCompletion =(OptionName != SO_PAP_PRIME_READ); UNREFERENCED_PARAMETER( SocketHandle ); DBGPRINT0(("WSHSetSocketInformation: Entered, OptionName %ld\n", 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 = RtlAllocateHeap( RtlProcessHeap( ), 0, sizeof(*context) ); if ( context == NULL ) { return WSAENOBUFS; } // // Copy over information into the context block. // RtlCopyMemory( context, OptionValue, sizeof(*context) ); // // Tell the Windows Sockets DLL where our context information is // stored so that it can return the context pointer in future // calls. // *(PWSHATALK_SOCKET_CONTEXT *)OptionValue = context; return NO_ERROR; } else { return NO_ERROR; } } // // The only level we support here is SOL_APPLETALK. // if ( Level != SOL_APPLETALK ) { DBGPRINT0(("WSHSetSocketInformation: Level incorrect %d\n", Level)); return WSAEINVAL; } // // Fill in the result based on the option name. // We support SO_REGISTERNAME/SO_DEREGISTERNAME only // pIoStatusBlock = RtlAllocateHeap( RtlProcessHeap(), 0, sizeof(IO_STATUS_BLOCK)); if (pIoStatusBlock == NULL) { return(WSAENOBUFS); } if (waitForCompletion) { status = NtCreateEvent( &eventHandle, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE ); if ( !NT_SUCCESS(status) ) { RtlFreeHeap( RtlProcessHeap(), 0, pIoStatusBlock); DBGPRINT(("WSHSetSocketInformation: Create event failed\n")); return WSAENOBUFS; } } else { completionApc = CompleteTdiActionApc; apcContext = pIoStatusBlock; } switch (OptionName) { case SO_REGISTER_NAME: { PNBP_REGDEREG_ACTION nbpAction; if (( TdiAddressObjectHandle == NULL) || (OptionLength != sizeof(WSH_REGISTER_NAME))) { error = WSAEINVAL; break; } // Operation is on the address handle objectHandle = TdiAddressObjectHandle; tdiActionLength = sizeof(NBP_REGDEREG_ACTION); tdiAction = RtlAllocateHeap( RtlProcessHeap( ), 0, tdiActionLength ); if ( tdiAction == NULL ) { error = WSAENOBUFS; break; } freeTdiAction = TRUE; tdiAction->TransportId = MATK; tdiAction->ActionCode = COMMON_ACTION_NBPREGISTER; nbpAction = (PNBP_REGDEREG_ACTION)tdiAction; // // Copy the nbp name to the proper place // RtlCopyMemory( (PCHAR)&nbpAction->Params.RegisterTuple.NbpName, OptionValue, OptionLength); // // Convert the tuple to MAC code page // if (!WshNbpNameToMacCodePage( (PWSH_REGISTER_NAME)OptionValue)) { error = WSAEINVAL; break; } } break; case SO_DEREGISTER_NAME: { PNBP_REGDEREG_ACTION nbpAction; if (( TdiAddressObjectHandle == NULL) || (OptionLength != sizeof(WSH_DEREGISTER_NAME))) { error = WSAEINVAL; break; } // Operation is on the address handle objectHandle = TdiAddressObjectHandle; tdiActionLength = sizeof(NBP_REGDEREG_ACTION); tdiAction = RtlAllocateHeap( RtlProcessHeap( ), 0, tdiActionLength ); if ( tdiAction == NULL ) { error = WSAENOBUFS; break; } freeTdiAction = TRUE; tdiAction->TransportId = MATK; tdiAction->ActionCode = COMMON_ACTION_NBPREMOVE; nbpAction = (PNBP_REGDEREG_ACTION)tdiAction; // // Copy the nbp name to the proper place // RtlCopyMemory( (PCHAR)&nbpAction->Params.RegisteredTuple.NbpName, OptionValue, OptionLength); // // Convert the tuple to MAC code page // if (!WshNbpNameToMacCodePage( (PWSH_DEREGISTER_NAME)OptionValue)) { error = WSAEINVAL; break; } } break; case SO_PAP_SET_SERVER_STATUS: { PPAP_SETSTATUS_ACTION papAction; if (( TdiAddressObjectHandle == NULL) || (OptionLength < 0)) { error = WSAEINVAL; break; } // Operation is on the address handle objectHandle = TdiAddressObjectHandle; tdiActionLength = (ULONG)OptionLength + (ULONG)(sizeof(PAP_SETSTATUS_ACTION)); DBGPRINT0(("ActionLen %lx\n", tdiActionLength)); tdiAction = RtlAllocateHeap( RtlProcessHeap( ), 0, tdiActionLength ); if ( tdiAction == NULL ) { error = WSAENOBUFS; break; } freeTdiAction = TRUE; tdiAction->TransportId = MATK; tdiAction->ActionCode = ACTION_PAPSETSTATUS; papAction = (PPAP_SETSTATUS_ACTION)tdiAction; DBGPRINT0(("Setting Status len %lx\n", OptionLength)); // // Copy the passed status into our buffer // if (OptionLength > 0) { RtlCopyMemory( (PCHAR)papAction + sizeof(PAP_SETSTATUS_ACTION), OptionValue, OptionLength); } } break; case SO_PAP_PRIME_READ : { tdiAction = (PTDI_ACTION_HEADER)OptionValue; tdiActionLength = OptionLength; ASSERT(waitForCompletion == FALSE); if ((TdiConnectionObjectHandle == NULL) || (OptionLength < MIN_PAP_READ_BUF_SIZE)) { error = WSAEINVAL; break; } // Operation is on the connection handle objectHandle = TdiConnectionObjectHandle; // These will get overwritten by the incoming data. tdiAction->TransportId = MATK; tdiAction->ActionCode = ACTION_PAPPRIMEREAD; // This is the caller's buffer! Dont free it! Also, we dont wait // for this to complete. freeTdiAction = FALSE; // We potentially have an APC waiting to be delivered from a // previous setsockopt(). Give it a chance NtTestAlert(); } break; default: error = WSAENOPROTOOPT; break; } if (error != NO_ERROR) { if (freeTdiAction) { RtlFreeHeap( RtlProcessHeap( ), 0, tdiAction ); } if (waitForCompletion) { NtClose(eventHandle); } RtlFreeHeap( RtlProcessHeap(), 0, pIoStatusBlock); return(error); } status = NtDeviceIoControlFile( objectHandle, eventHandle, completionApc, apcContext, pIoStatusBlock, IOCTL_TDI_ACTION, NULL, // Input buffer 0, // Length of input buffer tdiAction, tdiActionLength ); if ( status == STATUS_PENDING ) { if (waitForCompletion) { status = NtWaitForSingleObject( eventHandle, FALSE, NULL ); ASSERT( NT_SUCCESS(status) ); status = pIoStatusBlock->Status; } else { status = STATUS_SUCCESS; } } if (freeTdiAction) { RtlFreeHeap( RtlProcessHeap( ), 0, tdiAction ); } // Close the event if (waitForCompletion) { NtClose(eventHandle); RtlFreeHeap( RtlProcessHeap( ), 0, pIoStatusBlock ); } return (WSHNtStatusToWinsockErr(status)); } // WSHSetSocketInformation 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; ULONG offset; DBGPRINT0(("WSHGetWinsockMapping: Entered\n")); mappingLength = sizeof(WINSOCK_MAPPING) - sizeof(MAPPING_TRIPLE) + sizeof(AdspStreamMappingTriples) + sizeof(AdspMsgMappingTriples) + sizeof(PapMsgMappingTriples) + sizeof(DdpMappingTriples); // // 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(AdspStreamMappingTriples) / sizeof(AdspStreamMappingTriples[0]) + sizeof(AdspMsgMappingTriples) / sizeof(AdspMsgMappingTriples[0]) + sizeof(PapMsgMappingTriples) / sizeof(PapMsgMappingTriples[0]) + sizeof(DdpMappingTriples) / sizeof(DdpMappingTriples[0]); Mapping->Columns = sizeof(MAPPING_TRIPLE) / sizeof(DWORD); offset = 0; RtlCopyMemory( Mapping->Mapping, AdspStreamMappingTriples, sizeof(AdspStreamMappingTriples)); offset += sizeof(AdspStreamMappingTriples); RtlCopyMemory( (PCHAR)Mapping->Mapping + offset, AdspMsgMappingTriples, sizeof(AdspMsgMappingTriples)); offset += sizeof(AdspMsgMappingTriples); RtlCopyMemory( (PCHAR)Mapping->Mapping + offset, PapMsgMappingTriples, sizeof(PapMsgMappingTriples)); offset += sizeof(PapMsgMappingTriples); RtlCopyMemory( (PCHAR)Mapping->Mapping + offset, DdpMappingTriples, sizeof(DdpMappingTriples)); // // Return the number of bytes we wrote. // DBGPRINT0(("WSHGetWinsockMapping: Mapping Length = %d\n", mappingLength)); 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 ) /*++ 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. 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. --*/ { PWSHATALK_SOCKET_CONTEXT context; DBGPRINT0(("WSHOpenSocket: Entered\n")); // // Determine whether this is to be a TCP or UDP socket. // if ( IsTripleInList( AdspStreamMappingTriples, sizeof(AdspStreamMappingTriples) / sizeof(AdspStreamMappingTriples[0]), *AddressFamily, *SocketType, *Protocol ) ) { // // Indicate the name of the TDI device that will service // SOCK_STREAM sockets in the internet address family. // RtlInitUnicodeString( TransportDeviceName, WSH_ATALK_ADSPRDM ); } else if ( IsTripleInList( AdspMsgMappingTriples, sizeof(AdspMsgMappingTriples) / sizeof(AdspMsgMappingTriples[0]), *AddressFamily, *SocketType, *Protocol ) ) { // // Indicate the name of the TDI device that will service // SOCK_RDM sockets in the internet address family. // RtlInitUnicodeString( TransportDeviceName, WSH_ATALK_ADSPRDM ); } else if ( IsTripleInList( PapMsgMappingTriples, sizeof(PapMsgMappingTriples) / sizeof(PapMsgMappingTriples[0]), *AddressFamily, *SocketType, *Protocol ) ) { // // Indicate the name of the TDI device that will service // SOCK_RDM sockets in the appletalk address family. // RtlInitUnicodeString( TransportDeviceName, WSH_ATALK_PAPRDM ); } else { BOOLEAN tripleFound = FALSE; // // Check the DDP triples // if ( IsTripleInList( DdpMappingTriples, sizeof(DdpMappingTriples) / sizeof(DdpMappingTriples[0]), *AddressFamily, *SocketType, *Protocol ) ) { tripleFound = TRUE; // // Indicate the name of the TDI device that will service // SOCK_DGRAM sockets in the appletalk address family. // RtlInitUnicodeString( TransportDeviceName, WSH_ATALK_DGRAMDDP[(*Protocol) - ATPROTO_BASE - 1] ); DBGPRINT0(("WSHOpenSocket: Protocol number %d index %d\n", (*Protocol) , (*Protocol) - ATPROTO_BASE - 1)); } // // This should never happen if the registry information about this // helper DLL is correct. If somehow this did happen, just return // an error. // if (!tripleFound) { 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 = RtlAllocateHeap( RtlProcessHeap( ), 0, sizeof(*context) ); if ( context == NULL ) { return WSAENOBUFS; } // // Initialize the context for the socket. // context->AddressFamily = *AddressFamily; context->SocketType = *SocketType; context->Protocol = *Protocol; // // Tell the Windows Sockets DLL which state transitions we're // interested in being notified of. // *NotificationEvents = WSH_NOTIFY_CONNECT | WSH_NOTIFY_CLOSE; // // Everything worked, return success. // *HelperDllSocketContext = context; 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. 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. --*/ { PWSHATALK_SOCKET_CONTEXT context = HelperDllSocketContext; // // We should only be called after a connect() completes or when the // socket is being closed. // if ( NotifyEvent == WSH_NOTIFY_CONNECT ) { // // Just for debugging right now // DBGPRINT0(("WSHNotify: Connect completed, notify called!\n")); } else if ( NotifyEvent == WSH_NOTIFY_CLOSE ) { // // Just free the socket context. // DBGPRINT0(("WSHNotify: Close notify called!\n")); RtlFreeHeap( RtlProcessHeap( ), 0, HelperDllSocketContext ); } else { return WSAEINVAL; } return NO_ERROR; } // WSHNotify INT WSHNtStatusToWinsockErr( IN NTSTATUS Status ) /*++ Routine Description: Arguments: Return Value: --*/ { INT error; switch (Status) { case STATUS_SUCCESS: error = NO_ERROR; break; case STATUS_BUFFER_OVERFLOW: case STATUS_BUFFER_TOO_SMALL: error = WSAENOBUFS; break; case STATUS_INVALID_ADDRESS: error = WSAEADDRNOTAVAIL; break; case STATUS_SHARING_VIOLATION: error = WSAEADDRINUSE; break; default: error = WSAEINVAL; break; } DBGPRINT0(("WSHNtStatusToWinsockErr: Converting %lx to %lx\n", Status, error)); return(error); } 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 VOID CompleteTdiActionApc ( IN PVOID ApcContext, IN PIO_STATUS_BLOCK IoStatusBlock ) /*++ Routine Description: Arguments: Return Value: --*/ { // // Just free the heap we allovcated to hold the IO status block and // the TDI action buffer. There is nothing we can do if the call // failed. // RtlFreeHeap( RtlProcessHeap( ), 0, ApcContext ); } // CompleteTdiActionApc BOOLEAN WshNbpNameToMacCodePage( IN OUT PWSH_NBP_NAME pNbpName ) /*++ Routine Description: Arguments: Return Value: --*/ { USHORT destLen; BOOLEAN retVal = FALSE; do { destLen = MAX_ENTITY; if (!WshConvertStringOemToMac( pNbpName->ObjectName, pNbpName->ObjectNameLen, pNbpName->ObjectName, &destLen)) { break; } pNbpName->ObjectNameLen = (CHAR)destLen; destLen = MAX_ENTITY; if (!WshConvertStringOemToMac( pNbpName->TypeName, pNbpName->TypeNameLen, pNbpName->TypeName, &destLen)) { break; } pNbpName->TypeNameLen = (CHAR)destLen; destLen = MAX_ENTITY; if (!WshConvertStringOemToMac( pNbpName->ZoneName, pNbpName->ZoneNameLen, pNbpName->ZoneName, &destLen)) { break; } pNbpName->ZoneNameLen = (CHAR)destLen; retVal = TRUE; } while (FALSE); return(retVal); } BOOLEAN WshNbpNameToOemCodePage( IN OUT PWSH_NBP_NAME pNbpName ) /*++ Routine Description: Arguments: Return Value: --*/ { USHORT destLen; BOOLEAN retVal = FALSE; do { destLen = MAX_ENTITY; if (!WshConvertStringMacToOem( pNbpName->ObjectName, pNbpName->ObjectNameLen, pNbpName->ObjectName, &destLen)) { break; } pNbpName->ObjectNameLen = (CHAR)destLen; destLen = MAX_ENTITY; if (!WshConvertStringMacToOem( pNbpName->TypeName, pNbpName->TypeNameLen, pNbpName->TypeName, &destLen)) { break; } pNbpName->TypeNameLen = (CHAR)destLen; destLen = MAX_ENTITY; if (!WshConvertStringMacToOem( pNbpName->ZoneName, pNbpName->ZoneNameLen, pNbpName->ZoneName, &destLen)) { break; } pNbpName->ZoneNameLen = (CHAR)destLen; retVal = TRUE; } while (FALSE); return(retVal); } BOOLEAN WshZoneListToOemCodePage( IN OUT PUCHAR pZoneList, IN USHORT NumZones ) /*++ Routine Description: Arguments: Return Value: --*/ { USHORT zoneLen; BOOLEAN retVal = TRUE; PUCHAR pCurZone = pZoneList, pNextZone = NULL, pCopyZone = pZoneList; while (NumZones-- > 0) { zoneLen = strlen(pCurZone) + 1; pNextZone = pCurZone + zoneLen; // Modify current zone. This could decrease its length if (!WshConvertStringMacToOem( pCurZone, zoneLen, pCopyZone, &zoneLen)) { DBGPRINT(("WshZoneListToOemCodePage: FAILED %s-%d\n", pCurZone, zoneLen)); retVal = FALSE; break; } pCopyZone += zoneLen; pCurZone = pNextZone; } return(retVal); } BOOLEAN WshConvertStringOemToMac( IN PUCHAR pSrcOemString, IN USHORT SrcStringLen, OUT PUCHAR pDestMacString, IN PUSHORT pDestStringLen ) /*++ Routine Description: Arguments: Return Value: --*/ { WCHAR wcharBuf[MAX_ENTITY + 1]; INT wcharLen, destLen; BOOLEAN retCode = TRUE; do { if ((SrcStringLen > (MAX_ENTITY+1)) || (*pDestStringLen < SrcStringLen)) { DBGPRINT(("WshConvertStringOemToMac: Invalid len %d.%d\n", SrcStringLen, *pDestStringLen)); retCode = FALSE; break; } // Convert the src string using the OEM codepage. if ((wcharLen = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pSrcOemString, SrcStringLen, wcharBuf, MAX_ENTITY + 1)) == FALSE) { DBGPRINT(("WshConvertStringOemToMac: FAILED mbtowcs %s-%d\n", pSrcOemString, SrcStringLen)); retCode = FALSE; break; } DBGPRINT0(("WshConvertStringOemToMac: Converting mbtowcs %s-%d\n", pSrcOemString, SrcStringLen)); // Convert the wide char string to mac ansi string. if ((destLen = WideCharToMultiByte( WshMacCodePage, 0, wcharBuf, wcharLen, pDestMacString, *pDestStringLen, NULL, NULL)) == FALSE) { DBGPRINT(("WshConvertStringOemToMac: FAILED wctomb %s-%d\n", pDestMacString, *pDestStringLen)); retCode = FALSE; break; } *pDestStringLen = (USHORT)destLen; DBGPRINT0(("WshConvertStringOemToMac: Converted mbtowcs %s-%d\n", pDestMacString, *pDestStringLen)); } while (FALSE); return(retCode); } BOOLEAN WshConvertStringMacToOem( IN PUCHAR pSrcMacString, IN USHORT SrcStringLen, OUT PUCHAR pDestOemString, IN PUSHORT pDestStringLen ) /*++ Routine Description: Arguments: Return Value: --*/ { WCHAR wcharBuf[MAX_ENTITY + 1]; INT wcharLen, destLen; BOOLEAN retCode = TRUE; do { if ((SrcStringLen > (MAX_ENTITY+1)) || (*pDestStringLen < SrcStringLen)) { retCode = FALSE; break; } // Convert the src string using the MAC codepage. if ((wcharLen = MultiByteToWideChar( WshMacCodePage, MB_PRECOMPOSED, pSrcMacString, SrcStringLen, wcharBuf, MAX_ENTITY + 1)) == FALSE) { DBGPRINT(("WshConvertStringMacToOem: FAILED mbtowcs %s-%d\n", pSrcMacString, SrcStringLen)); retCode = FALSE; break; } DBGPRINT0(("WshConvertStringMacToOem: Converting mbtowcs %s-%d\n", pSrcMacString, SrcStringLen)); // Convert the wide char string to mac ansi string. if ((destLen = WideCharToMultiByte( CP_ACP, 0, wcharBuf, wcharLen, pDestOemString, *pDestStringLen, NULL, NULL)) == FALSE) { DBGPRINT(("WshConvertStringMacToOem: FAILED wctomb %s-%d\n", pDestOemString, *pDestStringLen)); retCode = FALSE; break; } *pDestStringLen = (USHORT)destLen; DBGPRINT0(("WshConvertStringMacToOem: Converted mbtowcs %s-%d\n", pDestOemString, *pDestStringLen)); } while (FALSE); return(retCode); } BOOLEAN WshRegGetCodePage( VOID ) /*++ Routine Description: Arguments: Return Value: --*/ { DWORD dwRetCode; HKEY hkeyCodepagePath; DWORD dwType; DWORD dwBufSize; WCHAR wchCodepageNum[60]; UNICODE_STRING wchUnicodeCodePage; NTSTATUS status; // Open the key if (dwRetCode = RegOpenKeyEx( HKEY_LOCAL_MACHINE, WSH_KEYPATH_CODEPAGE, 0, KEY_QUERY_VALUE, &hkeyCodepagePath)) return(FALSE); // Get the Code page number value for the Mac dwBufSize = sizeof(wchCodepageNum); if (dwRetCode = RegQueryValueEx( hkeyCodepagePath, WSHREG_VALNAME_CODEPAGE, NULL, &dwType, (LPBYTE)wchCodepageNum, &dwBufSize)) return(FALSE); // Close the key RegCloseKey(hkeyCodepagePath); // Convert the code page to a numerical value RtlInitUnicodeString(&wchUnicodeCodePage, wchCodepageNum); status = RtlUnicodeStringToInteger( &wchUnicodeCodePage, DECIMAL_BASE, &WshMacCodePage); DBGPRINT0(("WSHGetCodePage %lx.%d\n", WshMacCodePage, WshMacCodePage)); return(NT_SUCCESS(status)); } INT WSHEnumProtocols ( IN LPINT lpiProtocols, IN LPTSTR lpTransportKeyName, // unused IN OUT LPVOID lpProtocolBuffer, IN OUT LPDWORD lpdwBufferLength ) /*++ Routine Description: This routine returns information about the protocols active on the local host. Arguments: lpiProtocols - a NULL terminated array of protocol ids. This parameter is optional; if NULL, information on all available protocols is returned. lpTransportKeyName - unused lpProtocolBuffer - a buffer which is filled with PROTOCOL_INFO structures. lpdwBufferLength - on input, the count of bytes in the lpProtocolBuffer passed to EnumProtocols. On output, the minimum buffersize that can be passed to EnumProtocols to retrieve all the requested information. This routine has no ability to enumerate over multiple calls; the passed in buffer must be large enough to hold all entries in order for the routine to succeed. Return Value: If no error occurs, it returns the number of PROTOCOL_INFO structures written to the lpProtocolBuffer buffer. If there is an error, returns SOCKET_ERROR (-1) and a specific error code is retrieved with the GetLastError() API. --*/ { DWORD bytesRequired; PPROTOCOL_INFO NextProtocolInfo; LPWSTR NextName; BOOL usePap = FALSE; BOOL useAdsp = FALSE; BOOL useRtmp = FALSE; BOOL useZip = FALSE; DWORD i, numRequested = 0; lpTransportKeyName; // Avoid compiler warnings for unused parm // // Make sure that the caller cares about PAP and/or ADSP // if ( ARGUMENT_PRESENT( lpiProtocols ) ) { for ( i = 0; lpiProtocols[i] != 0; i++ ) { if ( lpiProtocols[i] == ATPROTO_ADSP ) { useAdsp = TRUE; numRequested += 1; } if ( lpiProtocols[i] == ATPROTO_PAP ) { usePap = TRUE; numRequested += 1; } if ( lpiProtocols[i] == DDPPROTO_RTMP ) { useRtmp = TRUE; numRequested += 1; } if ( lpiProtocols[i] == DDPPROTO_ZIP ) { useZip = TRUE; numRequested += 1; } } } else { usePap = TRUE; useAdsp = TRUE; useRtmp = TRUE; useZip = TRUE; numRequested = 4; } if ( !usePap && !useAdsp && !useRtmp && !useZip) { *lpdwBufferLength = 0; return 0; } // // Make sure that the caller has specified a sufficiently large // buffer. // bytesRequired = (sizeof(PROTOCOL_INFO) * numRequested); if (useAdsp) { bytesRequired += sizeof( ADSP_NAME ); } if (usePap) { bytesRequired += sizeof( PAP_NAME ); } if (useRtmp) { bytesRequired += sizeof( RTMP_NAME ); } if (useZip) { bytesRequired += sizeof( ZIP_NAME ); } if ( bytesRequired > *lpdwBufferLength ) { *lpdwBufferLength = bytesRequired; return -1; } NextProtocolInfo = lpProtocolBuffer; NextName = (LPWSTR)( (LPBYTE)lpProtocolBuffer + *lpdwBufferLength ); // // Fill in ADSP info, if requested. // if ( useAdsp ) { // Adsp - note that even though we return iSocketType of SOCK_RDM, the // fact that the XP_PSUEDO_STREAM service flag is set tells the caller // they can actually open a adsp socket in SOCK_STREAM mode as well. NextName -= sizeof( ADSP_NAME )/sizeof(WCHAR); NextProtocolInfo->dwServiceFlags = XP_EXPEDITED_DATA | XP_GUARANTEED_ORDER | XP_GUARANTEED_DELIVERY | XP_MESSAGE_ORIENTED | XP_PSEUDO_STREAM | XP_GRACEFUL_CLOSE; NextProtocolInfo->iAddressFamily = AF_APPLETALK; NextProtocolInfo->iMaxSockAddr = sizeof(SOCKADDR_AT); NextProtocolInfo->iMinSockAddr = sizeof(SOCKADDR_AT); NextProtocolInfo->iSocketType = SOCK_RDM; NextProtocolInfo->iProtocol = ATPROTO_ADSP; NextProtocolInfo->dwMessageSize = 65535; NextProtocolInfo->lpProtocol = NextName; lstrcpyW( NextProtocolInfo->lpProtocol, ADSP_NAME ); NextProtocolInfo++; } // // Fill in PAP info, if requested. // if ( usePap ) { NextName -= sizeof( PAP_NAME )/sizeof(WCHAR); NextProtocolInfo->dwServiceFlags = XP_MESSAGE_ORIENTED | XP_GUARANTEED_DELIVERY | XP_GUARANTEED_ORDER | XP_GRACEFUL_CLOSE; NextProtocolInfo->iAddressFamily = AF_APPLETALK; NextProtocolInfo->iMaxSockAddr = sizeof(SOCKADDR_AT); NextProtocolInfo->iMinSockAddr = sizeof(SOCKADDR_AT); NextProtocolInfo->iSocketType = SOCK_RDM; NextProtocolInfo->iProtocol = ATPROTO_PAP; NextProtocolInfo->dwMessageSize = 4096; NextProtocolInfo->lpProtocol = NextName; lstrcpyW( NextProtocolInfo->lpProtocol, PAP_NAME ); NextProtocolInfo++; } if ( useRtmp ) { NextName -= sizeof( RTMP_NAME )/sizeof(WCHAR); NextProtocolInfo->dwServiceFlags = XP_CONNECTIONLESS; NextProtocolInfo->iAddressFamily = AF_APPLETALK; NextProtocolInfo->iMaxSockAddr = sizeof(SOCKADDR_AT); NextProtocolInfo->iMinSockAddr = sizeof(SOCKADDR_AT); NextProtocolInfo->iSocketType = SOCK_DGRAM; NextProtocolInfo->iProtocol = DDPPROTO_RTMP; NextProtocolInfo->dwMessageSize = 0; NextProtocolInfo->lpProtocol = NextName; lstrcpyW( NextProtocolInfo->lpProtocol, RTMP_NAME ); NextProtocolInfo++; } if ( useZip ) { NextName -= sizeof( ZIP_NAME )/sizeof(WCHAR); NextProtocolInfo->dwServiceFlags = XP_CONNECTIONLESS; NextProtocolInfo->iAddressFamily = AF_APPLETALK; NextProtocolInfo->iMaxSockAddr = sizeof(SOCKADDR_AT); NextProtocolInfo->iMinSockAddr = sizeof(SOCKADDR_AT); NextProtocolInfo->iSocketType = SOCK_DGRAM; NextProtocolInfo->iProtocol = DDPPROTO_ZIP; NextProtocolInfo->dwMessageSize = 0; NextProtocolInfo->lpProtocol = NextName; lstrcpyW( NextProtocolInfo->lpProtocol, ZIP_NAME ); NextProtocolInfo++; } *lpdwBufferLength = bytesRequired; return numRequested; } // WSHEnumProtocols BOOL FAR PASCAL WshDllInitialize( HINSTANCE hInstance, DWORD nReason, LPVOID pReserved ) /*++ Routine Description: SYNOPSIS: This DLL entry point is called when processes & threads are initialized and terminated, or upon calls to LoadLibrary() and FreeLibrary(). Arguments: ENTRY: hInstance - A handle to the DLL. nReason - Indicates why the DLL entry point is being called. pReserved - Reserved. Return Value: RETURNS: BOOL - TRUE = DLL init was successful. FALSE = DLL init failed. NOTES: The return value is only relevant during processing of DLL_PROCESS_ATTACH notifications. --*/ { BOOL fResult = TRUE; UNREFERENCED_PARAMETER( pReserved ); switch( nReason ) { case DLL_PROCESS_ATTACH: // // This notification indicates that the DLL is attaching to // the address space of the current process. This is either // the result of the process starting up, or after a call to // LoadLibrary(). The DLL should us this as a hook to // initialize any instance data or to allocate a TLS index. // // This call is made in the context of the thread that // caused the process address space to change. // fResult = WshRegGetCodePage(); break; case DLL_PROCESS_DETACH: // // This notification indicates that the calling process is // detaching the DLL from its address space. This is either // due to a clean process exit or from a FreeLibrary() call. // The DLL should use this opportunity to return any TLS // indexes allocated and to free any thread local data. // // Note that this notification is posted only once per // process. Individual threads do not invoke the // DLL_THREAD_DETACH notification. // break; case DLL_THREAD_ATTACH: // // This notfication indicates that a new thread is being // created in the current process. All DLLs attached to // the process at the time the thread starts will be // notified. The DLL should use this opportunity to // initialize a TLS slot for the thread. // // Note that the thread that posts the DLL_PROCESS_ATTACH // notification will not post a DLL_THREAD_ATTACH. // // Note also that after a DLL is loaded with LoadLibrary, // only threads created after the DLL is loaded will // post this notification. // break; case DLL_THREAD_DETACH: // // This notification indicates that a thread is exiting // cleanly. The DLL should use this opportunity to // free any data stored in TLS indices. // break; default: // // Who knows? Just ignore it. // break; } return fResult; }