/*++ Copyright (c) 1992 Microsoft Corporation Module Name: winsock.c Abstract: Common code for winsock-based datagram transports. Author: Dave Steckler (davidst) 15-Mar-1993 Revision History: Jeff Roberts (jroberts) 02-Dec-1994 Separated the protocol-independent parts from the UDP- and IPX-specific parts. --*/ #include #include #include #include #include #include #include #define MAX_PACKET_SIZE (1024) struct ENDPOINT_INFO { SOCKET Socket; unsigned Timeout; }; // // The protocol-specific file should define these fns. // RPC_STATUS RPC_ENTRY RegisterEndpoint( IN void * pServerAddress, IN RPC_CHAR * pEndpoint, OUT PDG_SERVER_TRANS_ADDRESS * ppTransAddress, OUT RPC_CHAR PAPI * NetworkAddress, IN unsigned int NetworkAddressLength //CLH 9/19/93 ); RPC_STATUS RPC_ENTRY DeregisterEndpoint( IN OUT PDG_SERVER_TRANS_ADDRESS * pServerTransAddress ); int SetTransportSpecificSocketOptions( SOCKET Socket ); RPC_STATUS QueryClientEndpoint ( IN void * pClientEndpoint, OUT RPC_CHAR * pClientAddress ); RPC_STATUS CreateServerEndpoint( IN char * pEndpoint, IN void * pServerAddr ); // // The protocol-specific file can use these fns. // RPC_STATUS MapStatusCode( int SocketError, RPC_STATUS Default ); PDG_SERVER_TRANS_ADDRESS CreateTransAddress( void * pServerAddress, RPC_CHAR * pEndpoint, RPC_STATUS * pStatus ); void DeleteTransAddress( PDG_SERVER_TRANS_ADDRESS * ppTransAddress ); #if defined(IPX) #include #include #include #include "dgipxs.h" #include "dgipxs.c" #elif defined(UDP) #include "dgudps.h" #include "dgudps.c" #else #error "unknown winsock protocol" #endif RPC_STATUS RPC_ENTRY TransportUnload() /*++ Routine Description: Destructor for the server transport. Arguments: Return Value: --*/ { (void)WSACleanup(); return RPC_S_OK; } RPC_STATUS RPC_ENTRY ReceivePacket( IN void *Address, IN PDG_SERVER_TRANS_ADDRESS pTransAddress, IN unsigned long LargestPacketSize, IN char * pNcaPacketHeader, IN unsigned * pDataLength, unsigned long Timeout, void * pClientEndpoint ) /*++ Routine Description: Receives a packet from the transport address the passed packet is associated with. Arguments: pTransAddress - Server's transport address information. LargestPacketSize - Size of largest packet we can accept. pNcaPacketHeader - Pointer to buffer to place incoming pkt into. pDataLength - Number of bytes read in. Timeout - Receive timeout in milliseconds. ppClientEndpoint - Pointer to the client address structure. Return Value: result of recv Revision History: --*/ { unsigned SocketError; int BytesReceived; int FromLen=sizeof(struct sockaddr); struct ENDPOINT_INFO * pInfo = (struct ENDPOINT_INFO *) pTransAddress->pTsap; ADDRESS_TYPE * pSockaddr = (ADDRESS_TYPE *) pClientEndpoint; if (Timeout != pInfo->Timeout) { pInfo->Timeout = Timeout; setsockopt(pInfo->Socket, SOL_SOCKET, SO_RCVTIMEO, (char *) &Timeout, sizeof(Timeout) ); // // If we can't set the timeout, too bad. We will carry on anyway. // } // // Receive something on our socket. // BytesReceived = recvfrom( pInfo->Socket, // socket (char *)pNcaPacketHeader, // buffer (int)LargestPacketSize, // buflen 0, // flags (struct sockaddr *) pSockaddr, // where received from &FromLen // received from length ); // // Did we get something? // if ((BytesReceived == SOCKET_ERROR) || (BytesReceived == 0)) { return MapStatusCode(WSAGetLastError(), RPC_P_RECEIVE_FAILED); } else { *pDataLength = BytesReceived; return RPC_S_OK; } } RPC_STATUS RPC_ENTRY RegisterAnyEndpoint( IN void * pServerAddress, OUT RPC_CHAR * pEndpointName, OUT PDG_SERVER_TRANS_ADDRESS * ppServerTransAddress, OUT RPC_CHAR PAPI * NetworkAddress, IN unsigned int NetworkAddressLength, // CLH 9/19/93 IN unsigned int EndpointLength // CLH 9/19/93 ) /*++ Routine Description: Figures out a unique endpoint and creates it. Arguments: pServerAddress - pointer to the DG_ADDRESS object we are associated with. (see comments in RegisterEndpoint about why this is 'void *') pEndpointName - Memory of at least MAX_ANY_ENDPOINT_NAME RPC_CHARS in length. This will be filled in with the endpoint. ppServerAddress - Where to place the newly created address. NetworkAddress - Network address in string format - ie "11.2.39.56" Return Value: RPC_S_OK Revision History: Connie Hoppe (CLH) (connieh) 8-Aug-93 Return Network Address Connie Hoppe (CLH) (connieh) 17-Sep-93 Return err if addr len too small Added NetworkAddresLength and Endpointlength to i/f 15-Feb-94 Fixed to ask for an assigned endpoint --*/ { RPC_STATUS Status; int i = 0; if ( NetworkAddressLength < (2 * (NETADDR_LEN + 1)) ) return( RPC_P_NETWORK_ADDRESS_TOO_SMALL ); pEndpointName[0] = (RPC_CHAR)(i+'0'); pEndpointName[1] = '\0'; Status = RegisterEndpoint( pServerAddress, pEndpointName, ppServerTransAddress, NetworkAddress, //CLH 8/8/93 NetworkAddressLength //CLH 9/17/93 ); return Status; } RPC_STATUS RPC_ENTRY SendPacketBack( IN PDG_SERVER_TRANS_ADDRESS pTransAddress, IN char * pNcaPacketHeader, IN unsigned DataLength, void * pClientEndpoint ) /*++ Routine Description: Sends a packet back to the client it was received from. Arguments: pTransAddress - Server's transport address information. pNcaPacketHeader - Pointer to buffer to place incoming pkt into. pDataLength - Number of bytes read in. pClientEndpoint - Pointer to the client address structure in sockaddr format. Return Value: result of send --*/ { PDG_SERVER_TRANS_ADDRESS pTransportAddress = (PDG_SERVER_TRANS_ADDRESS) pTransAddress; unsigned BytesSent; struct ENDPOINT_INFO *pInfo = (struct ENDPOINT_INFO *) pTransAddress->pTsap; ADDRESS_TYPE * pSockaddr = (ADDRESS_TYPE *) pClientEndpoint; BytesSent = sendto( pInfo->Socket, // socket pNcaPacketHeader, // buffer DataLength, // buflen 0, // flags (struct sockaddr *) pSockaddr, // address sizeof(ADDRESS_TYPE) // svr addr size ); if (BytesSent == DataLength) { return RPC_S_OK; } else { return MapStatusCode(WSAGetLastError(), RPC_P_SEND_FAILED); } } RPC_STATUS RPC_ENTRY ForwardPacket( IN PDG_SERVER_TRANS_ADDRESS pTransAddress, IN char * pNcaPacketHeader, IN unsigned long DataLength, void * pEndpoint ) /*++ Routine Description: Sends a packet to the server it was originally destined for (that is, the client had a dynamic endpoint it wished the enpoint mapper to resolve and forward the packet to). Arguments: pTransAddress - Server's transport address information. pNcaPacketHeader - Pointer to buffer to place incoming pkt into. pDataLength - Number of bytes read in. pEndpoint - Pointer to the server port num to forward to. This is in string format. Return Value: result of send Revision History: Connie Hoppe (CLH) (connieh) 17-Feb-94 Created. --*/ { // If a transport had specific needs placed into the // transport address, it would cast pTransAddress into // its own trans address datastructure. UDP has // no additional info. PDG_SERVER_TRANS_ADDRESS pTransportAddress = (PDG_SERVER_TRANS_ADDRESS) pTransAddress; unsigned BytesSent; struct ENDPOINT_INFO * pInfo = (struct ENDPOINT_INFO *) pTransAddress->pTsap; ADDRESS_TYPE SockAddr; //Create an endpoint from the enpoint string name. if ((CreateServerEndpoint(((char*) pEndpoint), &SockAddr)) != RPC_S_OK) { return RPC_S_CANT_CREATE_ENDPOINT; } BytesSent = sendto( pInfo->Socket, // socket pNcaPacketHeader, // buffer DataLength, // buflen 0, // flags (struct sockaddr *) &SockAddr, // address sizeof(ADDRESS_TYPE) // svr addr size ); if (BytesSent == DataLength) { return RPC_S_OK; } else { return MapStatusCode(WSAGetLastError(), RPC_P_SEND_FAILED); } } void RPC_ENTRY CloseClientEndpoint( IN OUT ADDRESS_TYPE * pHandle ) /*++ Routine Description: Deletes a "client handle" Arguments: The handle. Return Value: --*/ { // nothing to do here on IPX or UDP. } PDG_SERVER_TRANS_ADDRESS CreateTransAddress( void * pServerAddress, RPC_CHAR * pEndpoint, RPC_STATUS * pStatus ) /*++ Routine Description: Creates a new endpoint on this server. Arguments: pServerAddress - DG_ADDRESS object this endpoint is associated with. This is a 'void *' instead of a PDG_ADDRESS because we don't want to include or link in all the garbage associated with PDG_ADDRESS. pEndpoint - Name of the endpoint to create. pStatus - Where to place the output status. RPC_S_OK RPC_S_INVALID_ENDPOINT_FORMAT Return Value: Revision History: Connie Hoppe (CLH) (connieh) 15-Feb-94 Fixed to return Endpoint. --*/ { long Endpoint; int EndpointLength; int i; int SockStatus; int Socket; int PacketType; PDG_SERVER_TRANS_ADDRESS pTransAddress; int length; SOCKET PortUsed; char PortAscii[10]; UNICODE_STRING UnicodePortNum; ANSI_STRING AsciiPortNum; ADDRESS_TYPE ReceiveAddr; struct ENDPOINT_INFO * pInfo; int NewSize = 0x40000; // // Convert the endpoint to a number. // EndpointLength = RpcpStringLength(pEndpoint); for (i=0, Endpoint=0 ; i< EndpointLength ; i++) { if ( ((char)pEndpoint[i] >= '0') && ((char)pEndpoint[i] <= '9')) { Endpoint *= 10; Endpoint += (char)pEndpoint[i]-'0'; // Watch out for overflow. if (Endpoint > 0x10000) { *pStatus = RPC_S_INVALID_ENDPOINT_FORMAT; return NULL; } } else { *pStatus = RPC_S_INVALID_ENDPOINT_FORMAT; return NULL; } } // // Create a socket. // Socket = socket(ADDRESS_FAMILY, SOCK_DGRAM, PROTOCOL); if (Socket == INVALID_SOCKET) { *pStatus = RPC_S_CANT_CREATE_ENDPOINT; return NULL; } // // Set transport-variable options. // SockStatus = SetTransportSpecificSocketOptions(Socket); if (NO_ERROR != SockStatus) { closesocket(Socket); *pStatus = MapStatusCode(SockStatus, RPC_S_INTERNAL_ERROR); return NULL; } // // set socket recv buffer size.. // SockStatus = setsockopt(Socket, SOL_SOCKET, SO_RCVBUF, (char *) &NewSize, sizeof(NewSize) ); // // Create a binding to that socket. // InitLocalAddress(ReceiveAddr, (unsigned short) Endpoint); //Bind the socket to the port number. SockStatus = bind( Socket, (struct sockaddr *)&ReceiveAddr, sizeof(ReceiveAddr) ); if (SockStatus == SOCKET_ERROR) { SockStatus = WSAGetLastError(); switch (SockStatus) { case WSAEADDRINUSE: { *pStatus= RPC_S_DUPLICATE_ENDPOINT; break; } case WSAENOBUFS: { *pStatus= RPC_S_OUT_OF_MEMORY; break; } default: { #ifdef DEBUGRPC DbgPrint("RPC DG: surprising error 0x%lx (%lu) from bind()\n", SockStatus, SockStatus); #endif *pStatus= RPC_S_CANT_CREATE_ENDPOINT; } } closesocket(Socket); return NULL; } length = sizeof ( ReceiveAddr ); //Puts the string name of the endpoint used into ReceiveAddr.sin_port if (getsockname ( Socket, (struct sockaddr *) &ReceiveAddr, &length )) { *pStatus = RPC_S_CANT_CREATE_ENDPOINT; closesocket(Socket); return(NULL); } // // If we asked for a specific port(endpoint != 0), return it // Otherwise, fetch the assigned port number (asssigned during the bind) // and stuff it into the given endpoint structure in appropriate format. if (Endpoint == 0) { PortUsed = GetSocket(ReceiveAddr); _itoa ( PortUsed, PortAscii, 10 ); RtlInitAnsiString ( &AsciiPortNum, PortAscii); RtlAnsiStringToUnicodeString( &UnicodePortNum, &AsciiPortNum, TRUE ); memcpy ( pEndpoint, UnicodePortNum.Buffer, UnicodePortNum.Length + sizeof(UNICODE_NULL) ); RtlFreeUnicodeString ( &UnicodePortNum ); } // Allocate mem for the TransAddress pTransAddress = I_RpcAllocate(sizeof(DG_SERVER_TRANS_ADDRESS) + sizeof(struct ENDPOINT_INFO)); if (pTransAddress == 0) { *pStatus = RPC_S_OUT_OF_MEMORY; closesocket(Socket); return NULL; } pTransAddress->pServerAddress = pServerAddress; pInfo = (struct ENDPOINT_INFO *) &pTransAddress[1]; pInfo->Socket = Socket; pInfo->Timeout = INFINITE; pTransAddress->pTsap = pInfo; *pStatus = RPC_S_OK; return pTransAddress; } void DeleteTransAddress( PDG_SERVER_TRANS_ADDRESS * ppTransAddress ) /*++ Routine Description: Destroys an endpoint. Arguments: Return Value: RRR --*/ { struct ENDPOINT_INFO * pInfo = (struct ENDPOINT_INFO *) (*ppTransAddress)->pTsap; if (pInfo->Socket != INVALID_SOCKET) { closesocket(pInfo->Socket); pInfo->Socket = INVALID_SOCKET; pInfo->Timeout = INFINITE; } } RPC_STATUS RPC_ENTRY StartListening( IN PDG_SERVER_TRANS_ADDRESS pTransAddress ) { return RPC_S_OK; } RPC_STATUS RPC_ENTRY SetBufferLength( IN void PAPI * Endpoint, IN unsigned Length ) { struct ENDPOINT_INFO * pInfo = (struct ENDPOINT_INFO *) Endpoint; int SockStatus; SockStatus = setsockopt(pInfo->Socket, SOL_SOCKET, SO_RCVBUF, (char *) &Length, sizeof(Length) ); if (SockStatus == SOCKET_ERROR) { return RPC_S_OUT_OF_MEMORY; } return RPC_S_OK; } DG_RPC_SERVER_TRANSPORT_INFO TransportInformation = { RPC_TRANSPORT_INTERFACE_VERSION, MAX_PACKET_SIZE, sizeof(ADDRESS_TYPE), sizeof(SOCKET), ADDRESS_STRING_SIZE, ENDPOINT_STRING_SIZE, TransportUnload, ReceivePacket, RegisterEndpoint, DeregisterEndpoint, RegisterAnyEndpoint, SendPacketBack, ForwardPacket, CloseClientEndpoint, QueryClientAddress, QueryClientEndpoint, StartListening, SetBufferLength }; PDG_RPC_SERVER_TRANSPORT_INFO TransportLoad( RPC_CHAR * pProtocolSequence ) /*++ Routine Description: This routine is the "psuedo constructor" for the server transport object. This is the exported entry point into this dll. Arguments: pProtocolSequence - The protocol sequence we're running on. Return Value: Pointer to a DG_UDP_SERVER_TRANSPORT if successful, otherwise NULL. --*/ { WSADATA Data; RPC_STATUS Status = 0; // // Initialize Winsock. // Status = WSAStartup( 0x0101, // version required &Data ); if (Status != 0) { return 0; } return(&TransportInformation); } RPC_STATUS MapStatusCode( int SocketError, RPC_STATUS Default ) /*++ Routine Description: Maps a winsock return value into a RPC_STATUS. Arguments: ErrorCode - Input error code. Return Value: mapped status code --*/ { RPC_STATUS Status; switch (SocketError) { case 0: { Status = RPC_S_OK; break; } case WSAETIMEDOUT: { Status = RPC_P_TIMEOUT; break; } case WSAENOBUFS: { Status = RPC_S_OUT_OF_MEMORY; break; } case WSAEMSGSIZE: { Status = RPC_P_OVERSIZE_PACKET; break; } default: { #ifdef DEBUGRPC PrintToDebugger("RPC DG: Winsock error %d\n", SocketError); #endif Status = Default; } } return Status; }