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