| /* | 
 |  * RPC messages | 
 |  * | 
 |  * Copyright 2001-2002 Ove Kåven, TransGaming Technologies | 
 |  * Copyright 2004 Filip Navara | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  * | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |  * Lesser General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU Lesser General Public | 
 |  * License along with this library; if not, write to the Free Software | 
 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
 |  * | 
 |  * TODO: | 
 |  *  - figure out whether we *really* got this right | 
 |  *  - check for errors and throw exceptions | 
 |  */ | 
 |  | 
 | #include <stdarg.h> | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 |  | 
 | #include "windef.h" | 
 | #include "winbase.h" | 
 | #include "winerror.h" | 
 | #include "winreg.h" | 
 |  | 
 | #include "rpc.h" | 
 | #include "rpcndr.h" | 
 | #include "rpcdcep.h" | 
 |  | 
 | #include "wine/debug.h" | 
 |  | 
 | #include "rpc_binding.h" | 
 | #include "rpc_misc.h" | 
 | #include "rpc_defs.h" | 
 | #include "rpc_message.h" | 
 |  | 
 | WINE_DEFAULT_DEBUG_CHANNEL(rpc); | 
 |  | 
 | static DWORD RPCRT4_GetHeaderSize(RpcPktHdr *Header) | 
 | { | 
 |   static const DWORD header_sizes[] = { | 
 |     sizeof(Header->request), 0, sizeof(Header->response), | 
 |     sizeof(Header->fault), 0, 0, 0, 0, 0, 0, 0, sizeof(Header->bind), | 
 |     sizeof(Header->bind_ack), sizeof(Header->bind_nack), | 
 |     0, 0, 0, 0, 0 | 
 |   }; | 
 |   ULONG ret = 0; | 
 |    | 
 |   if (Header->common.ptype < sizeof(header_sizes) / sizeof(header_sizes[0])) { | 
 |     ret = header_sizes[Header->common.ptype]; | 
 |     if (ret == 0) | 
 |       FIXME("unhandled packet type\n"); | 
 |     if (Header->common.flags & RPC_FLG_OBJECT_UUID) | 
 |       ret += sizeof(UUID); | 
 |   } else { | 
 |     TRACE("invalid packet type\n"); | 
 |   } | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | static VOID RPCRT4_BuildCommonHeader(RpcPktHdr *Header, unsigned char PacketType, | 
 |                               unsigned long DataRepresentation) | 
 | { | 
 |   Header->common.rpc_ver = RPC_VER_MAJOR; | 
 |   Header->common.rpc_ver_minor = RPC_VER_MINOR; | 
 |   Header->common.ptype = PacketType; | 
 |   Header->common.drep[0] = LOBYTE(LOWORD(DataRepresentation)); | 
 |   Header->common.drep[1] = HIBYTE(LOWORD(DataRepresentation)); | 
 |   Header->common.drep[2] = LOBYTE(HIWORD(DataRepresentation)); | 
 |   Header->common.drep[3] = HIBYTE(HIWORD(DataRepresentation)); | 
 |   Header->common.auth_len = 0; | 
 |   Header->common.call_id = 1; | 
 |   Header->common.flags = 0; | 
 |   /* Flags and fragment length are computed in RPCRT4_Send. */ | 
 | }                               | 
 |  | 
 | static RpcPktHdr *RPCRT4_BuildRequestHeader(unsigned long DataRepresentation, | 
 |                                      unsigned long BufferLength, | 
 |                                      unsigned short ProcNum, | 
 |                                      UUID *ObjectUuid) | 
 | { | 
 |   RpcPktHdr *header; | 
 |   BOOL has_object; | 
 |   RPC_STATUS status; | 
 |  | 
 |   has_object = (ObjectUuid != NULL && !UuidIsNil(ObjectUuid, &status)); | 
 |   header = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, | 
 |                      sizeof(header->request) + (has_object ? sizeof(UUID) : 0)); | 
 |   if (header == NULL) { | 
 |     return NULL; | 
 |   } | 
 |  | 
 |   RPCRT4_BuildCommonHeader(header, PKT_REQUEST, DataRepresentation); | 
 |   header->common.frag_len = sizeof(header->request); | 
 |   header->request.alloc_hint = BufferLength; | 
 |   header->request.context_id = 0; | 
 |   header->request.opnum = ProcNum; | 
 |   if (has_object) { | 
 |     header->common.flags |= RPC_FLG_OBJECT_UUID; | 
 |     header->common.frag_len += sizeof(UUID); | 
 |     memcpy(&header->request + 1, ObjectUuid, sizeof(UUID)); | 
 |   } | 
 |  | 
 |   return header; | 
 | } | 
 |  | 
 | static RpcPktHdr *RPCRT4_BuildResponseHeader(unsigned long DataRepresentation, | 
 |                                       unsigned long BufferLength) | 
 | { | 
 |   RpcPktHdr *header; | 
 |  | 
 |   header = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(header->response)); | 
 |   if (header == NULL) { | 
 |     return NULL; | 
 |   } | 
 |  | 
 |   RPCRT4_BuildCommonHeader(header, PKT_RESPONSE, DataRepresentation); | 
 |   header->common.frag_len = sizeof(header->response); | 
 |   header->response.alloc_hint = BufferLength; | 
 |  | 
 |   return header; | 
 | } | 
 |  | 
 | RpcPktHdr *RPCRT4_BuildFaultHeader(unsigned long DataRepresentation, | 
 |                                    RPC_STATUS Status) | 
 | { | 
 |   RpcPktHdr *header; | 
 |  | 
 |   header = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(header->fault)); | 
 |   if (header == NULL) { | 
 |     return NULL; | 
 |   } | 
 |  | 
 |   RPCRT4_BuildCommonHeader(header, PKT_FAULT, DataRepresentation); | 
 |   header->common.frag_len = sizeof(header->fault); | 
 |   header->fault.status = Status; | 
 |  | 
 |   return header; | 
 | } | 
 |  | 
 | RpcPktHdr *RPCRT4_BuildBindHeader(unsigned long DataRepresentation, | 
 |                                   unsigned short MaxTransmissionSize, | 
 |                                   unsigned short MaxReceiveSize, | 
 |                                   RPC_SYNTAX_IDENTIFIER *AbstractId, | 
 |                                   RPC_SYNTAX_IDENTIFIER *TransferId) | 
 | { | 
 |   RpcPktHdr *header; | 
 |  | 
 |   header = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(header->bind)); | 
 |   if (header == NULL) { | 
 |     return NULL; | 
 |   } | 
 |  | 
 |   RPCRT4_BuildCommonHeader(header, PKT_BIND, DataRepresentation); | 
 |   header->common.frag_len = sizeof(header->bind); | 
 |   header->bind.max_tsize = MaxTransmissionSize; | 
 |   header->bind.max_rsize = MaxReceiveSize; | 
 |   header->bind.num_elements = 1; | 
 |   header->bind.num_syntaxes = 1; | 
 |   memcpy(&header->bind.abstract, AbstractId, sizeof(RPC_SYNTAX_IDENTIFIER)); | 
 |   memcpy(&header->bind.transfer, TransferId, sizeof(RPC_SYNTAX_IDENTIFIER)); | 
 |  | 
 |   return header; | 
 | } | 
 |  | 
 | RpcPktHdr *RPCRT4_BuildBindNackHeader(unsigned long DataRepresentation, | 
 |                                       unsigned char RpcVersion, | 
 |                                       unsigned char RpcVersionMinor) | 
 | { | 
 |   RpcPktHdr *header; | 
 |  | 
 |   header = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(header->bind_nack)); | 
 |   if (header == NULL) { | 
 |     return NULL; | 
 |   } | 
 |  | 
 |   RPCRT4_BuildCommonHeader(header, PKT_BIND_NACK, DataRepresentation); | 
 |   header->common.frag_len = sizeof(header->bind_nack); | 
 |   header->bind_nack.protocols_count = 1; | 
 |   header->bind_nack.protocols[0].rpc_ver = RpcVersion; | 
 |   header->bind_nack.protocols[0].rpc_ver_minor = RpcVersionMinor; | 
 |  | 
 |   return header; | 
 | } | 
 |  | 
 | RpcPktHdr *RPCRT4_BuildBindAckHeader(unsigned long DataRepresentation, | 
 |                                      unsigned short MaxTransmissionSize, | 
 |                                      unsigned short MaxReceiveSize, | 
 |                                      LPSTR ServerAddress, | 
 |                                      unsigned long Result, | 
 |                                      unsigned long Reason, | 
 |                                      RPC_SYNTAX_IDENTIFIER *TransferId) | 
 | { | 
 |   RpcPktHdr *header; | 
 |   unsigned long header_size; | 
 |   RpcAddressString *server_address; | 
 |   RpcResults *results; | 
 |   RPC_SYNTAX_IDENTIFIER *transfer_id; | 
 |  | 
 |   header_size = sizeof(header->bind_ack) + sizeof(RpcResults) + | 
 |                 sizeof(RPC_SYNTAX_IDENTIFIER) + sizeof(RpcAddressString) + | 
 |                 strlen(ServerAddress); | 
 |  | 
 |   header = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, header_size); | 
 |   if (header == NULL) { | 
 |     return NULL; | 
 |   } | 
 |  | 
 |   RPCRT4_BuildCommonHeader(header, PKT_BIND_ACK, DataRepresentation); | 
 |   header->common.frag_len = header_size; | 
 |   header->bind_ack.max_tsize = MaxTransmissionSize; | 
 |   header->bind_ack.max_rsize = MaxReceiveSize; | 
 |   server_address = (RpcAddressString*)(&header->bind_ack + 1); | 
 |   server_address->length = strlen(ServerAddress) + 1; | 
 |   strcpy(server_address->string, ServerAddress); | 
 |   results = (RpcResults*)((ULONG_PTR)server_address + sizeof(RpcAddressString) + server_address->length - 1); | 
 |   results->num_results = 1; | 
 |   results->results[0].result = Result; | 
 |   results->results[0].reason = Reason; | 
 |   transfer_id = (RPC_SYNTAX_IDENTIFIER*)(results + 1); | 
 |   memcpy(transfer_id, TransferId, sizeof(RPC_SYNTAX_IDENTIFIER)); | 
 |  | 
 |   return header; | 
 | } | 
 |  | 
 | VOID RPCRT4_FreeHeader(RpcPktHdr *Header) | 
 | { | 
 |   HeapFree(GetProcessHeap(), 0, Header); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           RPCRT4_Send (internal) | 
 |  *  | 
 |  * Transmit a packet over connection in acceptable fragments. | 
 |  */ | 
 | RPC_STATUS RPCRT4_Send(RpcConnection *Connection, RpcPktHdr *Header, | 
 |                        void *Buffer, unsigned int BufferLength) | 
 | { | 
 |   PUCHAR buffer_pos; | 
 |   DWORD hdr_size, count; | 
 |  | 
 |   buffer_pos = Buffer; | 
 |   /* The packet building functions save the packet header size, so we can use it. */ | 
 |   hdr_size = Header->common.frag_len; | 
 |   Header->common.flags |= RPC_FLG_FIRST; | 
 |   Header->common.flags &= ~RPC_FLG_LAST; | 
 |   while (!(Header->common.flags & RPC_FLG_LAST)) { | 
 |     /* decide if we need to split the packet into fragments */ | 
 |     if ((BufferLength + hdr_size) <= Connection->MaxTransmissionSize) { | 
 |       Header->common.flags |= RPC_FLG_LAST; | 
 |       Header->common.frag_len = BufferLength + hdr_size; | 
 |     } else { | 
 |       Header->common.frag_len = Connection->MaxTransmissionSize; | 
 |     } | 
 |  | 
 |     /* transmit packet header */ | 
 |     if (!WriteFile(Connection->conn, Header, hdr_size, &count, NULL)) { | 
 |       WARN("WriteFile failed with error %ld\n", GetLastError()); | 
 |       return GetLastError(); | 
 |     } | 
 |  | 
 |     /* fragment consisted of header only and is the last one */ | 
 |     if (hdr_size == Header->common.frag_len && | 
 |         Header->common.flags & RPC_FLG_LAST) { | 
 |       return RPC_S_OK; | 
 |     } | 
 |  | 
 |     /* send the fragment data */ | 
 |     if (!WriteFile(Connection->conn, buffer_pos, Header->common.frag_len - hdr_size, &count, NULL)) { | 
 |       WARN("WriteFile failed with error %ld\n", GetLastError()); | 
 |       return GetLastError(); | 
 |     } | 
 |  | 
 |     buffer_pos += Header->common.frag_len - hdr_size; | 
 |     BufferLength -= Header->common.frag_len - hdr_size; | 
 |     Header->common.flags &= ~RPC_FLG_FIRST; | 
 |   } | 
 |  | 
 |   return RPC_S_OK; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           RPCRT4_Receive (internal) | 
 |  *  | 
 |  * Receive a packet from connection and merge the fragments. | 
 |  */ | 
 | RPC_STATUS RPCRT4_Receive(RpcConnection *Connection, RpcPktHdr **Header, | 
 |                           PRPC_MESSAGE pMsg) | 
 | { | 
 |   RPC_STATUS status; | 
 |   DWORD dwRead, hdr_length; | 
 |   unsigned short first_flag; | 
 |   unsigned long data_length; | 
 |   unsigned long buffer_length; | 
 |   unsigned char *buffer_ptr; | 
 |   RpcPktCommonHdr common_hdr; | 
 |  | 
 |   *Header = NULL; | 
 |  | 
 |   TRACE("(%p, %p, %p)\n", Connection, Header, pMsg); | 
 |  | 
 |   /* read packet common header */ | 
 |   if (!ReadFile(Connection->conn, &common_hdr, sizeof(common_hdr), &dwRead, NULL)) { | 
 |     if (GetLastError() != ERROR_MORE_DATA) { | 
 |       WARN("ReadFile failed with error %ld\n", GetLastError()); | 
 |       status = RPC_S_PROTOCOL_ERROR; | 
 |       goto fail; | 
 |     } | 
 |   } | 
 |   if (dwRead != sizeof(common_hdr)) { | 
 |     WARN("Short read of header, %ld/%d bytes\n", dwRead, sizeof(common_hdr)); | 
 |     status = RPC_S_PROTOCOL_ERROR; | 
 |     goto fail; | 
 |   } | 
 |  | 
 |   /* verify if the header really makes sense */ | 
 |   if (common_hdr.rpc_ver != RPC_VER_MAJOR || | 
 |       common_hdr.rpc_ver_minor != RPC_VER_MINOR) { | 
 |     WARN("unhandled packet version\n"); | 
 |     status = RPC_S_PROTOCOL_ERROR; | 
 |     goto fail; | 
 |   } | 
 |  | 
 |   hdr_length = RPCRT4_GetHeaderSize((RpcPktHdr*)&common_hdr); | 
 |   if (hdr_length == 0) { | 
 |     WARN("header length == 0\n"); | 
 |     status = RPC_S_PROTOCOL_ERROR; | 
 |     goto fail; | 
 |   } | 
 |  | 
 |   *Header = HeapAlloc(GetProcessHeap(), 0, hdr_length); | 
 |   memcpy(*Header, &common_hdr, sizeof(common_hdr)); | 
 |  | 
 |   /* read the rest of packet header */ | 
 |   if (!ReadFile(Connection->conn, &(*Header)->common + 1, | 
 |                 hdr_length - sizeof(common_hdr), &dwRead, NULL)) { | 
 |     if (GetLastError() != ERROR_MORE_DATA) { | 
 |       WARN("ReadFile failed with error %ld\n", GetLastError()); | 
 |       status = RPC_S_PROTOCOL_ERROR; | 
 |       goto fail; | 
 |     } | 
 |   } | 
 |   if (dwRead != hdr_length - sizeof(common_hdr)) { | 
 |     WARN("bad header length, %ld/%ld bytes\n", dwRead, hdr_length - sizeof(common_hdr)); | 
 |     status = RPC_S_PROTOCOL_ERROR; | 
 |     goto fail; | 
 |   } | 
 |  | 
 |   /* read packet body */ | 
 |   switch (common_hdr.ptype) { | 
 |   case PKT_RESPONSE: | 
 |     pMsg->BufferLength = (*Header)->response.alloc_hint; | 
 |     break; | 
 |   case PKT_REQUEST: | 
 |     pMsg->BufferLength = (*Header)->request.alloc_hint; | 
 |     break; | 
 |   default: | 
 |     pMsg->BufferLength = common_hdr.frag_len - hdr_length; | 
 |   } | 
 |  | 
 |   TRACE("buffer length = %u\n", pMsg->BufferLength); | 
 |  | 
 |   status = I_RpcGetBuffer(pMsg); | 
 |   if (status != RPC_S_OK) goto fail; | 
 |  | 
 |   first_flag = RPC_FLG_FIRST; | 
 |   buffer_length = 0; | 
 |   buffer_ptr = pMsg->Buffer; | 
 |   while (buffer_length < pMsg->BufferLength) | 
 |   { | 
 |     data_length = (*Header)->common.frag_len - hdr_length; | 
 |     if (((*Header)->common.flags & RPC_FLG_FIRST) != first_flag || | 
 |         data_length + buffer_length > pMsg->BufferLength) { | 
 |       TRACE("invalid packet flags or buffer length\n"); | 
 |       status = RPC_S_PROTOCOL_ERROR; | 
 |       goto fail; | 
 |     } | 
 |  | 
 |     if (data_length == 0) dwRead = 0; else | 
 |     if (!ReadFile(Connection->conn, buffer_ptr, data_length, &dwRead, NULL)) { | 
 |       if (GetLastError() != ERROR_MORE_DATA) { | 
 |         WARN("ReadFile failed with error %ld\n", GetLastError()); | 
 |         status = RPC_S_PROTOCOL_ERROR; | 
 |         goto fail; | 
 |       } | 
 |     } | 
 |     if (dwRead != data_length) { | 
 |       WARN("bad data length, %ld/%ld\n", dwRead, data_length); | 
 |       status = RPC_S_PROTOCOL_ERROR; | 
 |       goto fail; | 
 |     } | 
 |  | 
 |     /* when there is no more data left, it should be the last packet */ | 
 |     if (buffer_length == pMsg->BufferLength && | 
 |         ((*Header)->common.flags & RPC_FLG_LAST) == 0) { | 
 |       WARN("no more data left, but not last packet\n"); | 
 |       status = RPC_S_PROTOCOL_ERROR; | 
 |       goto fail; | 
 |     } | 
 |  | 
 |     buffer_length += data_length; | 
 |     if (buffer_length < pMsg->BufferLength) { | 
 |       TRACE("next header\n"); | 
 |  | 
 |       /* read the header of next packet */ | 
 |       if (!ReadFile(Connection->conn, *Header, hdr_length, &dwRead, NULL)) { | 
 |         if (GetLastError() != ERROR_MORE_DATA) { | 
 |           WARN("ReadFile failed with error %ld\n", GetLastError()); | 
 |           status = GetLastError(); | 
 |           goto fail; | 
 |         } | 
 |       } | 
 |       if (dwRead != hdr_length) { | 
 |         WARN("invalid packet header size (%ld)\n", dwRead); | 
 |         status = RPC_S_PROTOCOL_ERROR; | 
 |         goto fail; | 
 |       } | 
 |  | 
 |       buffer_ptr += data_length; | 
 |       first_flag = 0; | 
 |     } | 
 |   } | 
 |  | 
 |   /* success */ | 
 |   status = RPC_S_OK; | 
 |  | 
 | fail: | 
 |   if (status != RPC_S_OK && *Header) { | 
 |     RPCRT4_FreeHeader(*Header); | 
 |     *Header = NULL; | 
 |   } | 
 |   return status; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           I_RpcGetBuffer [RPCRT4.@] | 
 |  */ | 
 | RPC_STATUS WINAPI I_RpcGetBuffer(PRPC_MESSAGE pMsg) | 
 | { | 
 |   TRACE("(%p): BufferLength=%d\n", pMsg, pMsg->BufferLength); | 
 |   /* FIXME: pfnAllocate? */ | 
 |   pMsg->Buffer = HeapAlloc(GetProcessHeap(), 0, pMsg->BufferLength); | 
 |  | 
 |   TRACE("Buffer=%p\n", pMsg->Buffer); | 
 |   /* FIXME: which errors to return? */ | 
 |   return pMsg->Buffer ? S_OK : E_OUTOFMEMORY; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           I_RpcFreeBuffer [RPCRT4.@] | 
 |  */ | 
 | RPC_STATUS WINAPI I_RpcFreeBuffer(PRPC_MESSAGE pMsg) | 
 | { | 
 |   TRACE("(%p) Buffer=%p\n", pMsg, pMsg->Buffer); | 
 |   /* FIXME: pfnFree? */ | 
 |   HeapFree(GetProcessHeap(), 0, pMsg->Buffer); | 
 |   pMsg->Buffer = NULL; | 
 |   return S_OK; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           I_RpcSend [RPCRT4.@] | 
 |  */ | 
 | RPC_STATUS WINAPI I_RpcSend(PRPC_MESSAGE pMsg) | 
 | { | 
 |   RpcBinding* bind = (RpcBinding*)pMsg->Handle; | 
 |   RpcConnection* conn; | 
 |   RPC_CLIENT_INTERFACE* cif = NULL; | 
 |   RPC_SERVER_INTERFACE* sif = NULL; | 
 |   RPC_STATUS status; | 
 |   RpcPktHdr *hdr; | 
 |  | 
 |   TRACE("(%p)\n", pMsg); | 
 |   if (!bind) return RPC_S_INVALID_BINDING; | 
 |  | 
 |   if (bind->server) { | 
 |     sif = pMsg->RpcInterfaceInformation; | 
 |     if (!sif) return RPC_S_INTERFACE_NOT_FOUND; /* ? */ | 
 |     status = RPCRT4_OpenBinding(bind, &conn, &sif->TransferSyntax, | 
 |                                 &sif->InterfaceId); | 
 |   } else { | 
 |     cif = pMsg->RpcInterfaceInformation; | 
 |     if (!cif) return RPC_S_INTERFACE_NOT_FOUND; /* ? */ | 
 |     status = RPCRT4_OpenBinding(bind, &conn, &cif->TransferSyntax, | 
 |                                 &cif->InterfaceId); | 
 |   } | 
 |  | 
 |   if (status != RPC_S_OK) return status; | 
 |  | 
 |   if (bind->server) { | 
 |     if (pMsg->RpcFlags & WINE_RPCFLAG_EXCEPTION) { | 
 |       hdr = RPCRT4_BuildFaultHeader(pMsg->DataRepresentation, | 
 |                                     RPC_S_CALL_FAILED); | 
 |     } else { | 
 |       hdr = RPCRT4_BuildResponseHeader(pMsg->DataRepresentation, | 
 |                                        pMsg->BufferLength); | 
 |     } | 
 |   } else { | 
 |     hdr = RPCRT4_BuildRequestHeader(pMsg->DataRepresentation, | 
 |                                     pMsg->BufferLength, pMsg->ProcNum, | 
 |                                     &bind->ObjectUuid); | 
 |   } | 
 |  | 
 |   status = RPCRT4_Send(conn, hdr, pMsg->Buffer, pMsg->BufferLength); | 
 |  | 
 |   RPCRT4_FreeHeader(hdr); | 
 |  | 
 |   /* success */ | 
 |   if (!bind->server) { | 
 |     /* save the connection, so the response can be read from it */ | 
 |     pMsg->ReservedForRuntime = conn; | 
 |     return RPC_S_OK; | 
 |   } | 
 |   RPCRT4_CloseBinding(bind, conn); | 
 |   status = RPC_S_OK; | 
 |  | 
 |   return status; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           I_RpcReceive [RPCRT4.@] | 
 |  */ | 
 | RPC_STATUS WINAPI I_RpcReceive(PRPC_MESSAGE pMsg) | 
 | { | 
 |   RpcBinding* bind = (RpcBinding*)pMsg->Handle; | 
 |   RpcConnection* conn; | 
 |   RPC_CLIENT_INTERFACE* cif = NULL; | 
 |   RPC_SERVER_INTERFACE* sif = NULL; | 
 |   RPC_STATUS status; | 
 |   RpcPktHdr *hdr = NULL; | 
 |  | 
 |   TRACE("(%p)\n", pMsg); | 
 |   if (!bind) return RPC_S_INVALID_BINDING; | 
 |  | 
 |   if (pMsg->ReservedForRuntime) { | 
 |     conn = pMsg->ReservedForRuntime; | 
 |     pMsg->ReservedForRuntime = NULL; | 
 |   } else { | 
 |     if (bind->server) { | 
 |       sif = pMsg->RpcInterfaceInformation; | 
 |       if (!sif) return RPC_S_INTERFACE_NOT_FOUND; /* ? */ | 
 |       status = RPCRT4_OpenBinding(bind, &conn, &sif->TransferSyntax, | 
 |                                   &sif->InterfaceId); | 
 |     } else { | 
 |       cif = pMsg->RpcInterfaceInformation; | 
 |       if (!cif) return RPC_S_INTERFACE_NOT_FOUND; /* ? */ | 
 |       status = RPCRT4_OpenBinding(bind, &conn, &cif->TransferSyntax, | 
 |                                   &cif->InterfaceId); | 
 |     } | 
 |     if (status != RPC_S_OK) return status; | 
 |   } | 
 |  | 
 |   status = RPCRT4_Receive(conn, &hdr, pMsg); | 
 |   if (status != RPC_S_OK) { | 
 |     WARN("receive failed with error %lx\n", status); | 
 |     goto fail; | 
 |   } | 
 |  | 
 |   status = RPC_S_PROTOCOL_ERROR; | 
 |  | 
 |   switch (hdr->common.ptype) { | 
 |   case PKT_RESPONSE: | 
 |     if (bind->server) goto fail; | 
 |     break; | 
 |   case PKT_REQUEST: | 
 |     if (!bind->server) goto fail; | 
 |     break; | 
 |   case PKT_FAULT: | 
 |     pMsg->RpcFlags |= WINE_RPCFLAG_EXCEPTION; | 
 |     ERR ("we got fault packet with status %lx\n", hdr->fault.status); | 
 |     status = RPC_S_CALL_FAILED; /* ? */ | 
 |     goto fail; | 
 |   default: | 
 |     WARN("bad packet type %d\n", hdr->common.ptype); | 
 |     goto fail; | 
 |   } | 
 |  | 
 |   /* success */ | 
 |   status = RPC_S_OK; | 
 |  | 
 | fail: | 
 |   if (hdr) { | 
 |     RPCRT4_FreeHeader(hdr); | 
 |   } | 
 |   RPCRT4_CloseBinding(bind, conn); | 
 |   return status; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           I_RpcSendReceive [RPCRT4.@] | 
 |  */ | 
 | RPC_STATUS WINAPI I_RpcSendReceive(PRPC_MESSAGE pMsg) | 
 | { | 
 |   RPC_STATUS status; | 
 |  | 
 |   TRACE("(%p)\n", pMsg); | 
 |   status = I_RpcSend(pMsg); | 
 |   if (status == RPC_S_OK) | 
 |     status = I_RpcReceive(pMsg); | 
 |   return status; | 
 | } |