#include #include #include #include #include #include #include #include #include #include #include #include "callback.h" #define MAX_CONNECTIONS 10 // NULL Pointer macros #define NULL_HGLOBAL (HGLOBAL)NULL #define NULL_ECB 0 #define NULL_SPX 0 #define NULL_CHAR 0 // SPX ConnectionControl BIT masks #define SPX_CC_EOM 0x10 // venerable byte swaping macro #define SwapWord(i) ((( (unsigned short) (i) & 0xff) << 8) | \ (( (unsigned short) (i) & 0xff00) >> 8)) // Macros for critical section protection #define Begin_Critical_Section() _asm cli #define End_Critical_Section() _asm sti // Tower Stuff #define TRANSPORTID 0x0c #define TRANSPORTHOSTID 0x0d #define TOWERFLOORS 5 #pragma pack(1) typedef struct _FLLOR_234 { unsigned short ProtocolIdByteCount; unsigned char FloorId; unsigned short AddressByteCount; unsigned char Data[2]; } FLOOR_234, * PFLOOR_234; #pragma pack() #define NEXTFLOOR(t,x) (t)((unsigned char *)x +((t)x)->ProtocolIdByteCount\ + ((t)x)->AddressByteCount \ + sizeof(((t)x)->ProtocolIdByteCount) \ + sizeof(((t)x)->AddressByteCount)) #define ENDPOINT_MAPPER_EP "34280" // Structure definitions typedef struct { DWORD taskid; WORD ConnID; WORD local_socket; HGLOBAL yield; int yielding; #define MAX_ECBS 5 struct XECB_st * ecbs[MAX_ECBS]; struct XECB_st * q_head; struct XECB_st * q_tail; } CONNECTION, * PCONNECTION; typedef struct XECB_st { void * linkAddress; void (*ESRAddress)(); BYTE inUseFlag; BYTE completionCode; WORD socketNumber; /* high-low */ BYTE IPXWorkspace[4]; /* N/A */ BYTE driverWorkspace[12]; /* N/A */ BYTE immediateAddress[6]; /* high-low */ WORD fragmentCount; /* low-high */ ECBFragment fragmentDescriptor[2]; PCONNECTION pConn; } XECB; typedef struct { unsigned char rpc_vers; unsigned char rpc_vers_minor; unsigned char PTYPE; unsigned char pfc_flags; unsigned char drep[4]; #define BIG_ENDIAN 16 // drep[0] & BIG_ENDIAN indicate byte ordering unsigned short frag_length; unsigned short auth_length; unsigned long call_id; } ncacn_header; // DLL global variables unsigned int spx_max_userdata_size; // max userdata size per SPX packet extern void (_far pascal _far *DllTermination)(void); void __cdecl __export __loadds post_ecb(void); #pragma alloc_text(RPC16C6_FIXED, post_ecb) #ifdef DEBUGRPC void do_popup(char * format, ...) { char errmsg[256]; va_list args; va_start(args, format); wvsprintf(errmsg, format, args); va_end(args); MessageBox((HWND)0, errmsg, "RPC/SPX", MB_SYSTEMMODAL|MB_ICONSTOP); } #else #define do_popup(x) #endif void free_ecb(XECB * ecb) { #ifdef DEBUGRPC if (!ecb) { _asm int 3 } if (ecb->inUseFlag) { _asm int 3 } #endif if (ecb->fragmentCount > 1) { GlobalFreePtr(ecb->fragmentDescriptor[1].address); } GlobalFreePtr(ecb->fragmentDescriptor[0].address); GlobalFreePtr(ecb); } XECB * allocate_send_ecb(PCONNECTION pConn) { XECB * ecb; SPX_HEADER * spx; if ((ecb = (XECB *)GlobalAllocPtr(GPTR, sizeof(XECB))) == NULL_ECB) { return (NULL_ECB); } if ((spx = (SPX_HEADER *)GlobalAllocPtr(GPTR, sizeof(SPX_HEADER))) == NULL_SPX) { GlobalFreePtr(ecb); return (NULL_ECB); } // Initialize SPX Header... spx->ipx.PacketType = 5; // SPX packet // Initialize ECB... ecb->ESRAddress = 0; ecb->fragmentCount = 1; ecb->fragmentDescriptor[0].address = spx; ecb->fragmentDescriptor[0].size = sizeof(SPX_HEADER); ecb->socketNumber = pConn->local_socket; ecb->pConn = pConn; return (ecb); } XECB * allocate_receive_ecb(PCONNECTION pConn) { XECB * ecb; char * buf; if ((ecb = allocate_send_ecb(pConn)) == NULL_ECB) { return (NULL_ECB); } // // Win95 servers are willing to send 0x5d9 bytes on an 802.2 network. // Novell clients report 802.2 max as 0x5d8, wasting one byte of potential, // and if a full packet arrives they copy the whole thing. So we must // allocate space for that extra byte. // // if ((buf = (char *)GlobalAllocPtr(GPTR, 2+spx_max_userdata_size)) == NULL_CHAR) { free_ecb(ecb); return (NULL_ECB); } ecb->fragmentDescriptor[1].address = buf; ecb->fragmentDescriptor[1].size = 2+spx_max_userdata_size; ecb->ESRAddress = post_ecb; ecb->fragmentCount = 2; return (ecb); } void __loadds post_ecb() { XECB * ecb; PCONNECTION pConn; __asm { mov word ptr ecb+2, es mov word ptr ecb, si } #ifdef DEBUGRPC if (!ecb) { _asm int 3 } #endif pConn = ecb->pConn; ecb->linkAddress = NULL_ECB; if (pConn->q_tail == NULL_ECB) { pConn->q_head = ecb; } else { pConn->q_tail->linkAddress = ecb; } pConn->q_tail = ecb; if (pConn->yielding) { I_RpcWinAsyncCallComplete(pConn); } } XECB * wait_for_ecb(PCONNECTION pConn) { XECB * ecb; Begin_Critical_Section(); if (pConn->q_head == NULL_ECB) { pConn->yield = I_RpcWinAsyncCallBegin(pConn); pConn->yielding = 1; End_Critical_Section(); IPXRelinquishControl(); if (I_RpcWinAsyncCallWait(pConn->yield, (HWND)NULL, RPC_WIN_INFINITE_TIMEOUT) == 0) { pConn->yielding = 0; I_RpcWinAsyncCallEnd(pConn->yield); return (NULL_ECB); } Begin_Critical_Section(); I_RpcWinAsyncCallEnd(pConn->yield); pConn->yielding = 0; } ecb = pConn->q_head; if ((pConn->q_head = ecb->linkAddress) == NULL_ECB) { pConn->q_tail = NULL_ECB; } End_Critical_Section(); return (ecb); } void spx_abort(PCONNECTION pConn) { int i; if (pConn->local_socket) { if (pConn->ConnID) { SPXAbortConnection(pConn->ConnID); pConn->ConnID = 0; } IPXCloseSocket(taskid, pConn->local_socket); pConn->local_socket = 0; } for (i = 0; i < MAX_ECBS; i++) { if (pConn->ecbs[i] != NULL) { free_ecb(pConn->ecbs[i]); pConn->ecbs[i] = 0; } } } RPC_STATUS RPC_ENTRY _loadds spx_close(PCONNECTION pConn) { int i; XECB * ecb; if (pConn->ConnID) { if ((ecb = allocate_send_ecb(pConn)) == NULL_ECB) { return (RPC_S_OUT_OF_MEMORY); } SPXTerminateConnection(taskid, pConn->ConnID, (ECB *) ecb); while (ecb->inUseFlag) { IPXRelinquishControl(); } free_ecb(ecb); pConn->ConnID = 0; } if (pConn->local_socket) { IPXCloseSocket(taskid, pConn->local_socket); pConn->local_socket = 0; } for (i = 0; i < MAX_ECBS; i++) { if (pConn->ecbs[i] != NULL) { free_ecb(pConn->ecbs[i]); pConn->ecbs[i] = 0; } } return (RPC_S_OK); } RPC_STATUS RPC_ENTRY _loadds spx_open (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 ) { int i; int ret; int status; XECB * ecb; SPX_HEADER * spx; retry: pConn->q_head = pConn->q_tail = NULL_ECB; pConn->yielding = 0; // Allocate socket ... pConn->ConnID = 0; pConn->local_socket = 0; for (i = 0; i < MAX_ECBS; i++) { pConn->ecbs[i] = NULL; } status = IPXOpenSocket(taskid, &pConn->local_socket, 0xff); if (status != SUCCESSFUL) { do_popup("IPXOpenSocket %x", status); return (RPC_S_OUT_OF_RESOURCES); } for (i = 0; i < MAX_ECBS; i++) { if ((pConn->ecbs[i] = allocate_receive_ecb(pConn)) == NULL_ECB) { spx_close(pConn); return (RPC_S_OUT_OF_RESOURCES); } SPXListenForSequencedPacket(taskid, (ECB *) pConn->ecbs[i]); } if ((ecb = allocate_send_ecb(pConn)) == NULL_ECB) { spx_abort(pConn); return (RPC_S_OUT_OF_MEMORY); } // Map Address into IPX address... spx = ecb->fragmentDescriptor[0].address; status = IpxGetHostByName(NetworkAddress, &spx->ipx.Destination, Endpoint, Timeout, RpcRuntimeInfo ); if (status != RPC_S_OK) { free_ecb(ecb); spx_close(pConn); return(status); } ret = SPXEstablishConnection(taskid, 0, 0xff,(LPWORD)&pConn->ConnID, (ECB *) ecb); if (ret != SUCCESSFUL) { free_ecb(ecb); spx_close(pConn); return RPC_S_OUT_OF_RESOURCES; } while (ecb->inUseFlag) { IPXRelinquishControl(); } if (ecb->completionCode != SUCCESSFUL) { free_ecb(ecb); spx_close(pConn); if (TRUE == CachedServerNotContacted(NetworkAddress)) { goto retry; } return (RPC_S_SERVER_UNAVAILABLE); } free_ecb(ecb); CachedServerContacted(NetworkAddress); return (RPC_S_OK); } int put_packets(PCONNECTION pConn, XECB * ecb, char * buf, unsigned int bufsiz) { SPX_HEADER * spx; unsigned int packet_size; spx = ecb->fragmentDescriptor[0].address; while (bufsiz > 0) { packet_size = min(bufsiz, spx_max_userdata_size); spx->ConnControl = packet_size == bufsiz ? SPX_CC_EOM : 0; spx->DataType = 0; ecb->fragmentDescriptor[1].address = buf; ecb->fragmentDescriptor[1].size = packet_size; SPXSendSequencedPacket(taskid, pConn->ConnID, (ECB *) ecb); while (ecb->inUseFlag) { IPXRelinquishControl(); } if (ecb->completionCode != SUCCESSFUL) { return (-1); } buf += packet_size; bufsiz -= packet_size; } return (0); } RPC_STATUS RPC_ENTRY _loadds spx_send(PCONNECTION pConn, void * Buffer, unsigned int Length ) { XECB * ecb; if ((ecb = allocate_send_ecb(pConn)) == NULL_ECB) { return (RPC_S_OUT_OF_MEMORY); } ecb->fragmentCount = 2; if (put_packets(pConn, ecb, Buffer, Length) == -1) { ecb->fragmentCount = 1; free_ecb(ecb); spx_close(pConn); return (RPC_P_SEND_FAILED); } ecb->fragmentCount = 1; free_ecb(ecb); return (RPC_S_OK); } RPC_STATUS get_remaining_packets(PCONNECTION pConn, char * buf, unsigned int bufsiz) { unsigned int length; XECB * ecb; SPX_HEADER * spx; while (bufsiz > 0) { ecb = wait_for_ecb(pConn); if (ecb == NULL_ECB || ecb->completionCode != SUCCESSFUL) { spx_abort(pConn); return (RPC_P_RECEIVE_FAILED); } spx = ecb->fragmentDescriptor[0].address; length = SwapWord(spx->ipx.Length) - sizeof(SPX_HEADER); memcpy(buf, ecb->fragmentDescriptor[1].address, length); SPXListenForSequencedPacket(taskid, (ECB *) ecb); bufsiz -= length; buf += length; } return (RPC_S_OK); } RPC_STATUS RPC_ENTRY _loadds spx_receive(PCONNECTION pConn, void * * Buffer, unsigned int * BufferLength ) { unsigned int first_packet_length, fragment_length; SPX_HEADER * spx; XECB * ecb; ncacn_header *ncacn; ecb = wait_for_ecb(pConn); if (ecb == NULL_ECB || ecb->completionCode != SUCCESSFUL) { spx_close(pConn); return (RPC_P_RECEIVE_FAILED); } spx = ecb->fragmentDescriptor[0].address; if (spx->DataType == 0xFE) { spx_close(pConn); return (RPC_P_RECEIVE_FAILED); } first_packet_length = SwapWord(spx->ipx.Length) - sizeof(SPX_HEADER); ncacn = (ncacn_header *)ecb->fragmentDescriptor[1].address; if ( (ncacn->drep[0] & BIG_ENDIAN) == 0) { fragment_length = SwapWord(ncacn->frag_length); } else { fragment_length = ncacn->frag_length; // it's little-endian } // // If the runtime supplied buffer isn't big enough to hold all SPX packets // which form the RPC fragment, then go back to the runtime to reallocate it. // if (fragment_length > *BufferLength) { if (I_RpcTransClientReallocBuffer(pConn, Buffer, 0, fragment_length)) { return (RPC_S_OUT_OF_MEMORY); } } *BufferLength = fragment_length; memcpy(*Buffer, ncacn, first_packet_length); SPXListenForSequencedPacket(taskid, (ECB *) ecb); if (fragment_length > first_packet_length) { return get_remaining_packets(pConn, (char *)*Buffer + first_packet_length, fragment_length - first_packet_length); } return (RPC_S_OK); } RPC_STATUS RPC_ENTRY spx_tower_construct( IN char * Endpoint, IN char * NetworkAddress, OUT unsigned short * Floors, OUT unsigned long * ByteCount, OUT unsigned char * * Tower, IN char * Protseq ) { unsigned int TowerSize; PFLOOR_234 Floor; IPX_ADDRESS ipx; RPC_STATUS status; unsigned portnum; /* Compute the memory size of the tower. */ *Floors = TOWERFLOORS; TowerSize = 12; TowerSize += 2*sizeof(FLOOR_234) - 4; /* Allocate memory for the tower. */ *ByteCount = TowerSize; if ((*Tower = (unsigned char *)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 = atoi(Endpoint); Floor->Data[0] = portnum / 0x100; Floor->Data[1] = portnum % 0x100; /* 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 = 4 + 6; memset(Floor->Data, '\0', 10); return(RPC_S_OK); } RPC_STATUS RPC_ENTRY spx_tower_explode( IN unsigned char * Tower, OUT char * * Protseq, OUT char * * Endpoint, OUT char * * NetworkAddress ) { PFLOOR_234 Floor = (PFLOOR_234) Tower; RPC_STATUS Status = RPC_S_OK; unsigned short portnum; char * spx_protseq = "ncacn_spx"; if (Protseq != NULL) { *Protseq = (char *)I_RpcAllocate(strlen(spx_protseq) + 1); if (*Protseq == NULL) Status = RPC_S_OUT_OF_MEMORY; else memcpy(*Protseq, spx_protseq, strlen(spx_protseq) + 1); } if ((Endpoint == NULL) || (Status != RPC_S_OK)) { return (Status); } *Endpoint = (char *)I_RpcAllocate(6); //Ports are all <64K [5 decimal dig +1] if (*Endpoint == NULL) { Status = RPC_S_OUT_OF_MEMORY; if (Protseq != NULL) { I_RpcFree(*Protseq); } } else { portnum = SwapWord(*(short *)Floor->Data); _itoa(portnum, *Endpoint, 10); } return(Status); } void PASCAL __loadds spx_wrapup(void) { if (nwipxspx && 0 != GetModuleHandle("NWIPXSPX")) { IPXSPXDeinit(taskid); FreeLibrary(nwipxspx); } } RPC_CLIENT_TRANSPORT_INFO TransInfo = { RPC_TRANSPORT_INTERFACE_VERSION, TRANSPORTID, (TRANS_CLIENT_TOWERCONSTRUCT) spx_tower_construct, (TRANS_CLIENT_TOWEREXPLODE) spx_tower_explode, 0, sizeof(CONNECTION), (TRANS_CLIENT_OPEN) spx_open, (TRANS_CLIENT_CLOSE) spx_close, (TRANS_CLIENT_SEND) spx_send, (TRANS_CLIENT_RECEIVE) spx_receive, 0, 0, 0, 0 }; RPC_CLIENT_TRANSPORT_INFO * RPC_ENTRY TransPortLoad ( IN RPC_CHAR * RpcProtocolSequence, IN RPC_CLIENT_RUNTIME_INFO PAPI * RpcClientRuntimeInfo ) { int retcode; BYTE major_revision, minor_revision; unsigned int max_connections, available_connections; if (IPXInitialize(&taskid, MAX_CONNECTIONS * MAX_ECBS, 0) != 0) { return 0; } AsyncCallComplete = RpcClientRuntimeInfo->AsyncCallComplete; RpcRuntimeInfo = RpcClientRuntimeInfo; taskid = 0xffffffff; retcode = SPXInitialize(&taskid, MAX_CONNECTIONS * MAX_ECBS, // maxECBs 0, &major_revision, &minor_revision, &max_connections, &available_connections); switch (retcode) { case 0: do_popup("Not installed"); return (NULL); case 0xf0: if (GetWinFlags() & WF_ENHANCED) { do_popup("not supported"); return (NULL); } do_popup("Failed increasing local memory"); return (NULL); case 0xf1: do_popup("not initialized"); return (NULL); case 0xf2: do_popup("no DOS memory"); return(NULL); case 0xf3: do_popup("no free ECB"); return(NULL); case 0xf4: do_popup("Lock failed"); return(NULL); case 0xf5: do_popup("Over the maximum limit"); return(NULL); case 0xf6: do_popup("previously initialized"); //Intentional Fall Through case SPX_INSTALLED: break; default: do_popup("Unknown SPX Failure"); return(NULL); } spx_max_userdata_size = IPXGetMaxPacketSize() - sizeof(SPX_HEADER); TransInfo.MaximumPacketSize = (spx_max_userdata_size * MAX_ECBS) & 0xFFF8; DllTermination = spx_wrapup; return(&TransInfo); }