/*++ Copyright (c) 1994 Microsoft Corporation Module Name: TCPclnt.c Abstract: (TCP) connection-oriented full duplex transport interface for RPC. Author: Mazhar Mohammed 01/02/95 Revision History: --*/ #include "sysinc.h" #include "rpc.h" #include "rpcdcep.h" #include "rpctran.h" #include "rpcerrp.h" #if _MSC_VER < 1000 #include #endif #include #include #include typedef struct { unsigned long MyA5; // My application's A5 StreamPtr pStream; // Ptr to connections' connection control block Ptr pBuffers; // Send, receive and attn buffers } CONNECTION, *PCONNECTION; #define WDS(bufCount) struct { \ wdsEntry block[bufCount]; \ unsigned short zero; \ } #define RDS(bufCount) struct { \ rdsEntry block[bufCount]; \ unsigned short zero; \ } typedef struct { unsigned char rpc_vers; unsigned char rpc_vers_minor; unsigned char PTYPE; unsigned char pfc_flags; unsigned char drep[4]; unsigned short frag_length; unsigned short auth_length; unsigned long call_id; } message_header; #define MAXIMUM_FRAGMENT 0xFFFF #define QUEUE_SIZE (MAXIMUM_FRAGMENT + 256) #define NETADDR_LEN (MAX_COMPUTERNAME_LENGTH + 1) #define HOSTNAME_LEN (MAX_COMPUTERNAME_LENGTH) #define SOCKET_TYPE SOCKET_STREAM #define MAX_HOSTNAME_LEN 32 #define OBJECTTYPE_PREFIX "DceDspRpc " #define OBJECT_PREFIX_LEN (sizeof(OBJECTTYPE_PREFIX) - 1) #define DEFAULTZONE "*" #define ENDPOINT_MAPPER_EP "135" // BUGBUG #define ENDIAN_MASK 0x10 #define ENDPOINT_LEN 5 #define ByteSwapLong(Value) \ Value = ( (((Value) & 0xFF000000) >> 24) \ | (((Value) & 0x00FF0000) >> 8) \ | (((Value) & 0x0000FF00) << 8) \ | (((Value) & 0x000000FF) << 24)) #define ByteSwapShort(Value) \ Value = ( (((Value) & 0x00FF) << 8) \ | (((Value) & 0xFF00) >> 8)) extern MACYIELDCALLBACK g_pfnCallback ; /* Global variables. These are really initialized in ClientOpen(). BUGBUG MAC DLL: Need to do something different is multiple processes can call ClientOpen and open driver. */ int TCP_driverInitialized= 0; // 1 - dspDriverReference valid. short tcpDriverReference; // DSP driver handle (ref num) /* Following Macros and structs are needed for Tower Stuff */ #pragma pack(1) #define TRANSPORTID 0x07 // BUGBUG: Need real TransID #define TRANSPORTHOSTID 0x09 // BUGBUG: Need real TransHostID #define TOWERFLOORS 5 // BUGBUG: /* Endpoint = 2 bytes, HostId = 4 bytes*/ #define TOWEREPSIZE 4 #define TOWERSIZE (TOWEREPSIZE+2) #define PROTSEQ "ncacn_ip_tcp" Boolean TCP_RecvPending= 0; typedef struct _FLOOR_234 { unsigned short ProtocolIdByteCount; unsigned char FloorId; unsigned short AddressByteCount; unsigned char Data[2]; } FLOOR_234, PAPI * PFLOOR_234; #define NEXTFLOOR(t,x) (t)((unsigned char PAPI *)x +((t)x)->ProtocolIdByteCount\ + ((t)x)->AddressByteCount\ + sizeof(((t)x)->ProtocolIdByteCount)\ + sizeof(((t)x)->AddressByteCount)) /* End of Tower Stuff! */ #if _MSC_VER >= 1000 ResultUPP ResultProc ; #endif pascal void StrToAddrResultProc( struct hostInfo *aHostInfo, char *userdata ) /* utility routine for StrToAddr */ { /* simply watch the aHostInfo.rtnCode! */ } /* Transport interfaces */ RPC_TRANS_STATUS RPC_ENTRY TCP_ClientOpen ( IN PCONNECTION pConn, IN RPC_CHAR * NetworkAddress, IN RPC_CHAR * Endpoint, IN RPC_CHAR * NetworkOptions, IN RPC_CHAR * TransportAddress, IN RPC_CHAR * RpcProtocolSequence, IN unsigned int Timeout ) // Open a client connection { #if _MSC_VER >= 1000 long status; #else OSErr status; #endif TCPiopb pb; // Parameter block for TCP functions ParamBlockRec pbo ; volatile struct hostInfo hostInfoStruct ; char *pRcvBuff ; if (NetworkAddress == 0 || *NetworkAddress == '0') { // Local servers not supported on Mac return(RPC_S_SERVER_UNAVAILABLE); } ASSERT(Endpoint && *Endpoint); if (strlen(Endpoint) > 20) { return(RPC_S_INVALID_ENDPOINT_FORMAT); } if (TCP_driverInitialized<= 0) { // BUGBUG MAC DLL work: If multiple processes can call this at // once we need a better solution for this. pbo.ioParam.ioCompletion = 0; pbo.ioParam.ioNamePtr = "\p.ipp"; pbo.ioParam.ioPermssn = fsCurPerm; status = PBOpen((ParmBlkPtr)&pbo, FALSE); if (status != noErr) { #ifdef DEBUGRPC PrintToDebugger("PBOpen() failed: %d\n", status); #endif return(RPC_S_OUT_OF_RESOURCES); } TCP_driverInitialized= 1; tcpDriverReference = pbo.ioParam.ioRefNum; ; } // lookup name status = OpenResolver(NULL) ; if(status != noErr) { #ifdef DEBUGRPC PrintToDebugger("OpenResolver failed: %d\n", status) ; #endif return (RPC_S_OUT_OF_RESOURCES) ; } #if _MSC_VER >= 1000 status = StrToAddr(NetworkAddress, &hostInfoStruct, ResultProc, (Ptr) NULL); #else status = StrToAddr(NetworkAddress, &hostInfoStruct, StrToAddrResultProc, (Ptr) NULL); #endif // wait for address information or some error other than cacheFault to occur if (status == cacheFault) { while (hostInfoStruct.rtnCode == cacheFault) ; status = hostInfoStruct.rtnCode; } CloseResolver() ; if(status != noErr) { #ifdef DEBUGRPC PrintToDebugger("StrToAddr failed: %d.\n", status); #endif return (RPC_S_SERVER_UNAVAILABLE) ; } pRcvBuff = NewPtr(QUEUE_SIZE) ; if(pRcvBuff == NULL) { ASSERT(0) ; return (RPC_S_OUT_OF_MEMORY) ; } pb.ioCRefNum = tcpDriverReference; // Drivers ref used to dispatch trap pb.ioCompletion = 0; // ClientOpen completely sync. pb.csCode = TCPCreate; pb.csParam.create.rcvBuff = pRcvBuff; pb.csParam.create.rcvBuffLen = QUEUE_SIZE; pb.csParam.create.notifyProc = NULL ; pb.csParam.create.userDataPtr = NULL ; status = PBControl((ParmBlkPtr)&pb, FALSE); if (status != noErr) { #ifdef DEBUGRPC PrintToDebugger("TCPCreate failed %d\n", status); #endif DisposePtr((Ptr)pRcvBuff); return(RPC_S_OUT_OF_MEMORY); } pConn->pStream = pb.tcpStream ; memset(&pb, 0, sizeof(TCPiopb)) ; pb.csCode = TCPActiveOpen; pb.ioCRefNum = tcpDriverReference ; pb.tcpStream = pConn->pStream ; pb.csParam.open.validityFlags = typeOfService ; //only the type of service param is valid pb.csParam.open.remoteHost = hostInfoStruct.addr[0] ; pb.csParam.open.remotePort = atoi(Endpoint) ; // BUGBUG: do some validation here pb.csParam.open.tosFlags = throughPut ; status = PBControl((ParmBlkPtr)&pb, FALSE); if (status != noErr) { #ifdef DEBUGRPC PrintToDebugger("Open failed %d\n", status); #endif pb.csCode = TCPRelease; pb.ioCRefNum = tcpDriverReference ; pb.tcpStream = pConn->pStream ; status = PBControl((ParmBlkPtr)&pb, FALSE); ASSERT(status == noErr); DisposePtr((Ptr)pRcvBuff); return(RPC_S_SERVER_UNAVAILABLE); } // Save new connection the runtime's Connection object. pConn->MyA5 = SetCurrentA5(); pConn->pBuffers = pRcvBuff ; return (RPC_S_OK); } RPC_TRANS_STATUS RPC_ENTRY TCP_ClientClose ( IN PCONNECTION pConn ) // Close a client connection { OSErr status; TCPiopb pb; // Parameter block for DSP functions ASSERT(TCP_driverInitialized== 1); pb.ioCRefNum = tcpDriverReference; pb.csCode = TCPRelease ; pb.tcpStream = pConn->pStream; status = PBControl((ParmBlkPtr)&pb, FALSE); if (status != noErr) { #ifdef DEBUGRPC PrintToDebugger("Closed bad connection\n"); ASSERT(0); #endif // Nobody really cares if they close a bad connection except // when debugging a problem, right? } DisposePtr(pConn->pBuffers); return(RPC_S_OK); } RPC_TRANS_STATUS RPC_ENTRY TCP_ClientSend ( IN PCONNECTION pConn, IN void PAPI * Buffer, IN unsigned int BufferLength ) // Send a message to a connection. { OSErr status; TCPiopb pb; // Parameter block for DSP functions WDS(1) wds ; ASSERT(TCP_driverInitialized== 1); memset(&pb, 0, sizeof(TCPiopb)) ; wds.block[0].ptr = Buffer ; wds.block[0].length = BufferLength ; wds.zero = nil; pb.ioCRefNum = tcpDriverReference; pb.ioCompletion = 0; // sync sends because runtime may change // contents of Buffer after we return. pb.csCode = TCPSend; pb.tcpStream = pConn->pStream ; pb.csParam.send.wdsPtr = (char *) &wds ; // BUGBUG: need to change this if send becomes async pb.csParam.send.pushFlag = 1; // // Note: If we add shutdowns to the NT server we'll need to // check for incoming data here if it has been sufficiently // long since the last send. See the NT TCP/IP ClientSend() fn. // status = PBControl((ParmBlkPtr)&pb, FALSE); if (status != noErr) { TCP_ClientClose(pConn) ; return(RPC_P_SEND_FAILED); } return(RPC_S_OK); } RPC_TRANS_STATUS RecvAlertable( IN PCONNECTION pConn, IN void PAPI *Buf, IN unsigned int BufLen, OUT unsigned long PAPI *retlen, IN int async ) { OSErr status; volatile TCPiopb pb; // Parameter block for DSP functions memset(&pb, 0, sizeof(TCPiopb)) ; pb.ioCRefNum = tcpDriverReference; pb.ioCompletion = 0; pb.csCode = TCPRcv ; pb.tcpStream = pConn->pStream ; pb.csParam.receive.rcvBuff = Buf ; pb.csParam.receive.rcvBuffLen = BufLen; pb.ioResult = async ? 1:0 ; TCP_RecvPending= 0 ; status = PBControl((ParmBlkPtr)&pb, async); if ( status != noErr ) { // If both eom and actCount are 0 then the connection is closed. TCP_RecvPending= 0 ; TCP_ClientClose(pConn) ; return(RPC_P_RECEIVE_FAILED); } if(async) { // the yeild function must return only when pb.ioResult != 1 while(pb.ioResult == 1) { if(g_pfnCallback) (*g_pfnCallback)(&(pb.ioResult)) ; } if (pb.ioResult != noErr) { TCP_RecvPending = 0 ; TCP_ClientClose(pConn) ; return (RPC_P_RECEIVE_FAILED) ; } } *retlen = pb.csParam.receive.rcvBuffLen ; return (RPC_S_OK) ; } RPC_TRANS_STATUS RPC_ENTRY TCP_ClientRecv_Helper ( IN PCONNECTION pConn, IN OUT void PAPI * PAPI * Buffer, IN OUT unsigned int PAPI * BufferLength, IN int async ) { RPC_STATUS RpcStatus; unsigned long bytes; int total_bytes = 0; message_header *header = (message_header *) *Buffer; int native_length = 0; unsigned int maximum_receive; maximum_receive = I_RpcTransClientMaxFrag( pConn ); if (*BufferLength < maximum_receive) maximum_receive = *BufferLength; // // Read protocol header to see how big // the record is... // while (total_bytes < sizeof(message_header)) { RpcStatus = RecvAlertable (pConn, (char *)*Buffer+total_bytes, (maximum_receive - total_bytes), &bytes, async); if (RpcStatus != RPC_S_OK) { return (RpcStatus); } total_bytes += bytes; } native_length = header->frag_length; if ( (header->drep[0] & ENDIAN_MASK) != 0) { ByteSwapShort(native_length) ; } ASSERT( total_bytes <= native_length ); // // Make sure buffer is big enough. If it isn't, then go back // to the runtime to reallocate it. // if (native_length > (unsigned short) *BufferLength) { RpcStatus = I_RpcTransClientReallocBuffer (pConn, Buffer, total_bytes, native_length); if (RpcStatus != RPC_S_OK) { return(RPC_S_OUT_OF_MEMORY); } } *BufferLength = native_length; while (total_bytes < native_length) { RpcStatus = RecvAlertable(pConn, (unsigned char *) *Buffer + total_bytes, (int) (native_length - total_bytes), &bytes, FALSE); if (RpcStatus != RPC_S_OK) { return (RpcStatus); } else { total_bytes += bytes; } } return(RPC_S_OK); } RPC_TRANS_STATUS RPC_ENTRY TCP_ClientRecv ( IN PCONNECTION pConn, IN OUT void PAPI * PAPI * Buffer, IN OUT unsigned int PAPI * BufferLength ) { return TCP_ClientRecv_Helper ( pConn, Buffer, BufferLength, FALSE ) ; } #pragma pack(1) RPC_STATUS RPC_ENTRY TCP_ClientTowerConstruct( IN char PAPI * Endpoint, IN char PAPI * NetworkAddress, OUT UNALIGNED short PAPI * Floors, OUT UNALIGNED unsigned long PAPI * ByteCount, OUT unsigned char PAPI * UNALIGNED PAPI * Tower, IN char PAPI * Protseq ) { unsigned long TowerSize; unsigned short portnum; UNALIGNED PFLOOR_234 Floor; volatile struct hostInfo hostInfoStruct ; #if _MSC_VER >= 1000 long status; #else OSErr status; #endif UNUSED(Protseq); /* Compute the memory size of the tower. */ *Floors = TOWERFLOORS; TowerSize = TOWERSIZE; TowerSize += 2*sizeof(FLOOR_234) - 4; /* Allocate memory for the tower. */ *ByteCount = TowerSize; if ((*Tower = (unsigned char PAPI*)I_RpcAllocate(TowerSize)) == NULL) { return (RPC_S_OUT_OF_MEMORY); } /* Put the endpoint address and transport protocol id in the first floor. */ Floor = (PFLOOR_234) *Tower; Floor->ProtocolIdByteCount = 1; Floor->FloorId = (unsigned char)(TRANSPORTID & 0xFF); Floor->AddressByteCount = 2; if (Endpoint == NULL || *Endpoint == '\0') { Endpoint = ENDPOINT_MAPPER_EP; } portnum = ( (unsigned short) atoi (Endpoint)); // BUGBUG: got rid of htons memcpy((char PAPI *)&Floor->Data[0], &portnum, sizeof(portnum)); /* Put the network address and the transport host protocol id in the second floor. */ Floor = NEXTFLOOR(PFLOOR_234, Floor); Floor->ProtocolIdByteCount = 1; Floor->FloorId = (unsigned char)(TRANSPORTHOSTID & 0xFF); Floor->AddressByteCount = TOWEREPSIZE; Floor->Data[0] = '\0'; Floor->Data[1] = '\0'; if ((NetworkAddress) && (*NetworkAddress)) { status = OpenResolver(NULL) ; if(status != noErr) { #ifdef DEBUGRPC PrintToDebugger("OpenResolver failed: %d\n", status) ; #endif return (RPC_S_OUT_OF_RESOURCES) ; } #if _MSC_VER >= 1000 status = StrToAddr(NetworkAddress, &hostInfoStruct, ResultProc, (Ptr) NULL); #else status = StrToAddr(NetworkAddress, &hostInfoStruct, StrToAddrResultProc, (Ptr) NULL); #endif // wait for address information or some error other than cacheFault to occur if (status == cacheFault) { while(hostInfoStruct.rtnCode == cacheFault) ; status = hostInfoStruct.rtnCode; } CloseResolver() ; if (status != noErr) { #ifdef DEBUGRPC PrintToDebugger("StrToAddr failed: %d.\n", status); #endif return (RPC_S_SERVER_UNAVAILABLE) ; } memcpy((char PAPI *)&Floor->Data[0], &(hostInfoStruct.addr[0]), sizeof(unsigned long)); } return(RPC_S_OK); } RPC_STATUS RPC_ENTRY TCP_ClientTowerExplode( IN unsigned char PAPI * Tower, OUT char PAPI * UNALIGNED PAPI * Protseq, OUT char PAPI * UNALIGNED PAPI * Endpoint, OUT char PAPI * UNALIGNED PAPI * NetworkAddress ) { UNALIGNED PFLOOR_234 Floor = (PFLOOR_234) Tower; RPC_STATUS Status = RPC_S_OK; unsigned short portnum; UNALIGNED unsigned short *Port; if (Protseq != NULL) { *Protseq = I_RpcAllocate(strlen(PROTSEQ) + 1); if (*Protseq == NULL) Status = RPC_S_OUT_OF_MEMORY; else memcpy(*Protseq, PROTSEQ, strlen(PROTSEQ) + 1); } if ((Endpoint == NULL) || (Status != RPC_S_OK)) { return (Status); } *Endpoint = I_RpcAllocate(ENDPOINT_LEN+1); //Ports are all <64K [5 decimal dig +1] if (*Endpoint == NULL) { Status = RPC_S_OUT_OF_MEMORY; if (Protseq != NULL) { I_RpcFree(*Protseq); } } else { Port = (unsigned short *)&Floor->Data[0]; portnum = *Port; RpcItoa(ByteSwapShort(portnum), *Endpoint, 10); } return(Status); } RPC_TRANS_STATUS RPC_ENTRY TCP_ClientSendReceive ( IN PCONNECTION CConnection, IN void PAPI * SendBuffer, IN unsigned int SendBufferLength, IN OUT void PAPI * PAPI * ReceiveBuffer, IN OUT unsigned int PAPI * ReceiveBufferLength ) { RPC_STATUS status ; if((status = TCP_ClientSend(CConnection, SendBuffer, SendBufferLength)) != RPC_S_OK) return status ; return TCP_ClientRecv_Helper (CConnection, ReceiveBuffer, ReceiveBufferLength, TRUE) ; } #pragma pack() RPC_CLIENT_TRANSPORT_INFO ClientTCPTransInfo = { RPC_TRANSPORT_INTERFACE_VERSION, TRANSPORTID, TCP_ClientTowerConstruct, TCP_ClientTowerExplode, MAXIMUM_FRAGMENT, sizeof (CONNECTION), TCP_ClientOpen, TCP_ClientClose, TCP_ClientSend, TCP_ClientRecv, TCP_ClientSendReceive, 0, 0, 0 }; RPC_CLIENT_TRANSPORT_INFO PAPI * RPC_ENTRY ClientTCPTransportLoad ( IN RPC_CHAR PAPI * RpcProtocolSequence ) // Loadable transport initialization function { #if _MSC_VER >= 1000 ResultProc = NewResultProc(StrToAddrResultProc) ; #endif return(&ClientTCPTransInfo); }