text: /*
   * Copyright (c) 2009 Secure Endpoints Inc.
   *
   * Permission is hereby granted, free of charge, to any person
   * obtaining a copy of this software and associated documentation
   * files (the "Software"), to deal in the Software without
   * restriction, including without limitation the rights to use, copy,
   * modify, merge, publish, distribute, sublicense, and/or sell copies
   * of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be
   * included in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
   * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
   * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   */
  
  #include <afsconfig.h>
  #include <afs/param.h>
  #include <roken.h>
  
  #include <malloc.h>
  #include "ms-wkssvc.h"
  #include "ms-srvsvc.h"
  #include "afsd.h"
  #include "cm_btree.h"
  #include "cm_nls.h"
  #include "msrpc.h"
  
  static void
  MSRPC_AllocBuffer(msrpc_buffer * buf, unsigned int size);
  
  static void
  MSRPC_ReallocBuffer(msrpc_buffer * buf, unsigned int size);
  
  static void
  MSRPC_FreeBuffer(msrpc_buffer * buf);
  
  static void
  MSRPC_SetCmUser(cm_user_t * userp);
  
  /*! \brief Interface list
  
     This is the list of interfaces that are exported through this RPC
     module.  The *_ifspec variable is defined the <interface>_s.c file
     that is generated by MIDL.  We have to use an extra level of
     indirection because the actual RPC_SERVER_INTERFACE structure is
     not exported in the generated code.
   */
  RPC_SERVER_INTERFACE **_Interfaces[] = {
      (RPC_SERVER_INTERFACE **) &wkssvc_v1_0_s_ifspec,
      (RPC_SERVER_INTERFACE **) &srvsvc_v3_0_s_ifspec,
  };
  
  const int n_Interfaces = sizeof(_Interfaces)/sizeof(_Interfaces[0]);
  
  /* Serialization  */
  
  #define RPC_ALIGNMENT 8
  #define ALIGNTO8(x) (((((x) - 1)/8) + 1) * 8)
  
  typedef struct fcursor {
      BYTE * pos;
      BYTE * end;
  } fcursor;
  
  #define INITC(pfc, buf, len) (pfc)->pos = (BYTE *)(buf); (pfc)->end = (pfc)->pos + (len)
  
  #define SPC_LEFT(pfc) ((unsigned int)((pfc)->end - (pfc)->pos))
  #define IS_SPC(pfc, n) (SPC_LEFT(pfc) >= (n))
  
  __inline BYTE READB(fcursor * pfc) {
      BYTE b = *pfc->pos;
      pfc->pos++;
      return b;
  }
  
  #define READW(pfc) (((WORD)READB(pfc)) | (((WORD)READB(pfc)) << 8))
  #define READDW(pfc) (((DWORD)READW(pfc)) | (((DWORD)READW(pfc)) << 16))
  #define O_READS(pfc, s) do { memcpy(s, (pfc)->pos, sizeof(*s)); (pfc)->pos += sizeof(*s); } while (0)
  #define O_READA(pfc, a) do { memcpy(&a, (pfc)->pos, sizeof(a)); (pfc)->pos += sizeof(a); } while (0)
  #define O_READSZ(pfc, s, sz) do { memcpy(s, (pfc)->pos, sz); (pfc)->pos += sz; } while (0)
  #define O_RALIGN4(pfc) do { while(((size_t) (pfc)->pos) & 0x3) READB(pfc); } while (0)
  #define O_RALIGN8(pfc) do { while(((size_t) (pfc)->pos) & 0x7) READB(pfc); } while (0)
  
  __inline void WRITEB(fcursor * pfc, BYTE b) {
      *pfc->pos = b;
      pfc->pos++;
  }
  
  #define WRITEW(pfc, w) (WRITEB(pfc, ((w) & 0xff)), WRITEB(pfc, (((w) >> 8) & 0xff)))
  #define WRITEDW(pfc, dw) (WRITEW(pfc, ((dw) & 0xffff)), WRITEW(pfc, (((dw) >> 16) & 0xffff)))
  #define O_WRITES(pfc, s) do { memcpy((pfc)->pos, s, sizeof(*s)); (pfc)->pos += sizeof(*s); } while (0)
  #define O_WRITEA(pfc, a) do { memcpy((pfc)->pos, &a, sizeof(a)); (pfc)->pos += sizeof(a); } while (0)
  #define O_WRITESZ(pfc, s, sz) do { memcpy((pfc)->pos, s, sz); (pfc)->pos += sz; } while (0)
  #define O_WALIGN4(pfc) do { while(((size_t) (pfc)->pos) & 0x3) WRITEB(pfc, 0); } while (0)
  #define O_WALIGN8(pfc) do { while(((size_t) (pfc)->pos) & 0x7) WRITEB(pfc, 0); } while (0)
  
  #define iread_u_int8(pfc)  READB(pfc)
  #define iread_byte(pfc) READB(pfc)
  #define iread_u_int16(pfc) READW(pfc)
  #define iread_u_int32(pfc) READDW(pfc)
  
  #define write_u_int8(pfc, i) WRITEB(pfc, i)
  #define write_byte(pfc, i) WRITEB(pfc, i)
  #define write_u_int16(pfc, i) WRITEW(pfc, i)
  #define write_u_int32(pfc, i) WRITEDW(pfc, i)
  
  /*! \brief Supported RPC protocol version numbers */
  RPC_VERSION msrpc_supported_protocol_versions[] = {
      { 5, 0 },			/* 5.0 */
      { 5, 1 }			/* 5.1 */
  };
  
  const int n_msrpc_supported_protocol_versions = sizeof(msrpc_supported_protocol_versions)/sizeof(msrpc_supported_protocol_versions[0]);
  
  #define IS_PROTOCOL_VER_SUPPORTED(_maj, _min) ((_maj) == 5 && ((_min) == 0 || (_min) == 1))
  
  /*! \brief Number of bytes of envelope overhead for an RPC response PDU */
  #define RPC_RESPONSE_ENVELOPE_OVERHEAD 24
  
  static int
  read_E_CommonHeader(fcursor *pc, E_CommonHeader * pr)
  {
      if (IS_SPC(pc, 16)) {
  
  	pr->rpc_vers       = iread_u_int8(pc);
  	pr->rpc_vers_minor = iread_u_int8(pc);
  	pr->PTYPE	   = iread_u_int8(pc);
  	pr->pfc_flags      = iread_u_int8(pc);
  	O_READA(pc, pr->packed_drep);
  	pr->frag_length    = iread_u_int16(pc);
  	pr->auth_length    = iread_u_int16(pc);
  	pr->call_id	   = iread_u_int32(pc);
  
  	return 0;
  
      } else {
  	return CM_ERROR_BADFORMAT;
      }
  }
  
  static int
  write_E_CommonHeader(fcursor * pc, msrpc_conn * conn,
  		     msrpc_call * call, PDU_TYPE pdu_type)
  {
      INITC(pc, call->out.buf_data, call->out.buf_alloc);
  
      call->out.pdu_type = pdu_type;
  
      if (IS_SPC(pc, 16)) {
  	write_u_int8(pc, conn->rpc_vers);	/* protocol version */
  	write_u_int8(pc, conn->rpc_vers_minor);	/* minor version */
  	write_u_int8(pc, pdu_type);
  	write_u_int8(pc, PFC_FIRST_FRAG|PFC_LAST_FRAG);
  
  	write_byte(pc, 0x10);	/* Data representation */
  	write_byte(pc, 0);
  	write_byte(pc, 0);
  	write_byte(pc, 0);
  
  	write_u_int16(pc, 0);	/* Fragment ength, to be filled in
  				   later */
  	write_u_int16(pc, 0);	/* Length of auth data. Always 0 since
  				   we don't do authentication for RPC
  				   calls.*/
  	write_u_int32(pc, call->call_id);
  
  	return 0;
      } else {
  	return CM_ERROR_BUFFERTOOSMALL;
      }
  }
  
  #define USE_CURRENT_BUFFER_LENGTH (-1)
  static int
  closeMessage(fcursor * pc, msrpc_call * call, ptrdiff_t len)
  {
  
      unsigned short slen;
  
      if (len == USE_CURRENT_BUFFER_LENGTH)
  	len = pc->pos - call->out.buf_data;
  
      if (len >= USHRT_MAX)
          return CM_ERROR_TOOBIG;
      slen = (unsigned short)len;
  
      pc->pos = &call->out.buf_data[8];
      write_u_int16(pc, slen);
      call->out.buf_length = slen;
  
      return 0;
  }
  
  static int
  read_UUID(fcursor * pc,
  	  UUID * uuid)
  {
      if (IS_SPC(pc, sizeof(*uuid))) {
  	O_READS(pc, uuid);
  	return 0;
      } else {
  	return CM_ERROR_BADFORMAT;
      }
  }
  
  static int
  write_UUID(fcursor * pc,
  	   const UUID * uuid)
  {
      if (IS_SPC(pc, sizeof(*uuid))) {
  	O_WRITES(pc, uuid);
  	return 0;
      } else {
  	return CM_ERROR_BUFFERTOOSMALL;
      }
  }
  
  static int
  read_RPC_VERSION(fcursor * pc,
  		 RPC_VERSION * ver)
  {
      if (IS_SPC(pc, 4)) {
  	ver->MajorVersion = iread_u_int16(pc);
  	ver->MinorVersion = iread_u_int16(pc);
  	return 0;
      } else {
  	return CM_ERROR_BADFORMAT;
      }
  }
  
  static int
  write_RPC_VERSION(fcursor * pc,
  		  const RPC_VERSION * ver)
  {
      if (IS_SPC(pc, 4)) {
  	write_u_int16(pc, ver->MajorVersion);
  	write_u_int16(pc, ver->MinorVersion);
  	return 0;
      } else {
  	return CM_ERROR_BUFFERTOOSMALL;
      }
  }
  
  static int
  read_RPC_SYNTAX_IDENTIFIER(fcursor * pc,
  			   RPC_SYNTAX_IDENTIFIER * psi)
  {
      int code;
  
      code = read_UUID(pc, &psi->SyntaxGUID);
      if (code)
  	return code;
      code = read_RPC_VERSION(pc, &psi->SyntaxVersion);
      return code;
  }
  
  static int
  write_RPC_SYNTAX_IDENTIFIER(fcursor * pc,
  			    const RPC_SYNTAX_IDENTIFIER * psi)
  {
      int code;
  
      code = write_UUID(pc, &psi->SyntaxGUID);
      if (code)
  	return code;
      code = write_RPC_VERSION(pc, &psi->SyntaxVersion);
      return code;
  }
  
  static int
  is_equal_RPC_SYNTAX_IDENTIFIER(const RPC_SYNTAX_IDENTIFIER * s1,
  			       const RPC_SYNTAX_IDENTIFIER * s2)
  {
      return
  	IsEqualGUID(&s1->SyntaxGUID, &s2->SyntaxGUID) &&
  	s1->SyntaxVersion.MajorVersion == s2->SyntaxVersion.MajorVersion &&
  	s2->SyntaxVersion.MinorVersion == s2->SyntaxVersion.MinorVersion;
  }
  
  #define FAULT_PDU_PACKET_SIZE 32
  
  /*! \brief Write a fault PDU as the output of this call */
  static int
  write_fault_PDU(msrpc_conn * conn, msrpc_call * call,
  		u_int32 status, u_int8 flags)
  {
      fcursor o;
  
      MSRPC_AllocBuffer(&call->out, FAULT_PDU_PACKET_SIZE);
  
      write_E_CommonHeader(&o, conn, call, PDU_TYPE_FAULT);
  
      write_u_int32(&o, 16);	/* allocation hint */
      write_u_int16(&o, call->context_id); /* presentation context ID */
  
      write_u_int8(&o, 0);	/* cancel count */
      write_u_int8(&o, 0);	/* reserved */
  
      write_u_int32(&o, status);	/* status code */
  
      O_WALIGN8(&o);		/* alignment */
  
      closeMessage(&o, call, USE_CURRENT_BUFFER_LENGTH);
  
      if (flags) {
  	call->out.buf_data[3] |= flags;
      }
  
      call->status = MSRPC_CALL_DISPATCHED;
  
      return 0;
  }
  
  #define BIND_NAK_PDU_PACKET_SIZE 18
  
  /*! \brief Write a bind_nak PDU as the output of this call */
  static int
  write_bind_nak_PDU(msrpc_conn * conn, msrpc_call * call,
  		   BIND_REJECT_REASON reject_reason)
  {
      fcursor o;
      unsigned buf_size = BIND_NAK_PDU_PACKET_SIZE;
  
      if (reject_reason == BIND_REJ_PROTOCOL_VERSION_NOT_SUPPORTED)
  	buf_size += n_msrpc_supported_protocol_versions * 2 + 1;
  
      MSRPC_AllocBuffer(&call->out, buf_size);
  
      write_E_CommonHeader(&o, conn, call, PDU_TYPE_BIND_NAK);
  
      write_u_int16(&o, reject_reason);
  
      if (reject_reason == BIND_REJ_PROTOCOL_VERSION_NOT_SUPPORTED) {
  	int i;
  
  	write_u_int8(&o, n_msrpc_supported_protocol_versions);
  
  	for (i=0; i < n_msrpc_supported_protocol_versions; i++) {
  	    write_u_int8(&o, (u_int8) msrpc_supported_protocol_versions[i].MajorVersion);
  	    write_u_int8(&o, (u_int8) msrpc_supported_protocol_versions[i].MinorVersion);
  	}
      }
  
      closeMessage(&o, call, USE_CURRENT_BUFFER_LENGTH);
  
      call->status = MSRPC_CALL_DISPATCHED;
  
      return 0;
  }
  
  static int
  handle_ConnBind(msrpc_conn * conn, msrpc_call * call, fcursor * in, fcursor * out)
  {
      int code = 0;
  
      if (!IS_SPC(in, 12))
  	return CM_ERROR_BADFORMAT;
  
      conn->max_xmit_frag = iread_u_int16(in);
      conn->max_recv_frag = iread_u_int16(in);
      conn->assoc_group_id = iread_u_int32(in);
  
      conn->rpc_vers = call->in_header->rpc_vers;
      conn->rpc_vers_minor = call->in_header->rpc_vers_minor;
  
      if (conn->assoc_group_id == 0)
  	conn->assoc_group_id = 1;
  
      write_E_CommonHeader(out, conn, call, PDU_TYPE_BIND_ACK);
  
      write_u_int16(out, conn->max_xmit_frag);
      write_u_int16(out, conn->max_recv_frag);
      write_u_int32(out, conn->assoc_group_id);
  
      if (conn->secondary_address) {
  	int len = (int)strlen(conn->secondary_address) + 1;
  
  	write_u_int16(out, len);
  	O_WRITESZ(out, conn->secondary_address, len);
      } else {
  	write_u_int16(out, 0);
      }
  
      O_WALIGN4(out);
  
      /* presentation context negotiation */
      {
  	int n_context_elem;
  	int i;
  
  	n_context_elem = iread_u_int8(in);
  	iread_u_int8(in);	/* skip over reserved */
  	iread_u_int16(in);	/* skip over reserved */
  
  	write_u_int8(out, n_context_elem);
  	write_u_int8(out, 0);	/* pad */
  	write_u_int16(out, 0);	/* pad */
  
  	for (i=0; i < n_context_elem; i++) {
  	    int n_transfer_syn;
  	    RPC_SYNTAX_IDENTIFIER abstract_syntax;
  	    RPC_SYNTAX_IDENTIFIER transfer_syntax;
  	    RPC_SERVER_INTERFACE *interf = NULL;
  	    int j, k;
  
  	    if (!IS_SPC(in, 4 + 16))
  		return CM_ERROR_BADFORMAT;
  
  	    call->context_id = iread_u_int16(in);
  	    n_transfer_syn = iread_u_int8(in);
  	    iread_u_int8(in);	/* skip */
  
  	    code = read_RPC_SYNTAX_IDENTIFIER(in, &abstract_syntax);
  	    if (code)
  		return code;
  
  	    for (j=0; j < n_Interfaces; j++) {
  		interf = *_Interfaces[j];
  
  		if (is_equal_RPC_SYNTAX_IDENTIFIER(&abstract_syntax, &interf->InterfaceId))
  		    break;
  	    }
  
  	    if (j < n_Interfaces) {
  		for (k=0; k < n_transfer_syn; k++) {
  		    code = read_RPC_SYNTAX_IDENTIFIER(in, &transfer_syntax);
  		    if (code)
  			return code;
  
  		    if (is_equal_RPC_SYNTAX_IDENTIFIER(&transfer_syntax, &interf->TransferSyntax))
  			break;
  		}
  	    }
  
  	    if (j < n_Interfaces && k < n_transfer_syn) {
  		/* accepted */
  		write_u_int16(out, 0);
  		write_u_int16(out, 0);
  		write_RPC_SYNTAX_IDENTIFIER(out, &transfer_syntax);
  
  		conn->interface = interf;
  	    } else {
  		int reason;
  
  		if (j >= n_Interfaces)
  		    reason = 1;	/* abstract syntax not supported */
  		else
  		    reason = 2;	       /* transfer syntax not supported */
  		write_u_int16(out, 2); /* provider rejection */
  		write_u_int16(out, reason);
  		ZeroMemory(&transfer_syntax, sizeof(transfer_syntax));
  		write_RPC_SYNTAX_IDENTIFIER(out, &transfer_syntax);
  	    }
  	}
      }
  
      return closeMessage(out, call, USE_CURRENT_BUFFER_LENGTH);
  }
  
  static int
  handle_ConnRequest(msrpc_conn * conn, msrpc_call * call, fcursor * in, fcursor * out)
  {
      int code = 0;
  
      int alloc_hint;
      unsigned int op_num;
      RPC_STATUS _Status = RPC_S_OK;
  
      int len;
  
      if (!IS_SPC(in, 8))
  	return CM_ERROR_BADFORMAT;
  
      alloc_hint = iread_u_int32(in);
      call->context_id = iread_u_int16(in);
      op_num = iread_u_int16(in);
  
      if (call->in_header->pfc_flags & PFC_OBJECT_UUID) {
  	UUID uuid;
  
  	code = read_UUID(in, &uuid);
  	if (code)
  	    return code;
      }
  
      O_RALIGN8(in);
  
      if (conn->interface == NULL) {
  	/* if there was no interface selected, we can't go ahead with
  	   this request. */
  	return write_fault_PDU(conn, call, RPC_S_NO_BINDINGS, PFC_DID_NOT_EXECUTE);
      }
  
      len = SPC_LEFT(in);
  
      if (op_num < 0 || op_num >= conn->interface->DispatchTable->DispatchTableCount ||
  	conn->interface->DispatchTable->DispatchTable[op_num] == NULL) {
  
  	return write_fault_PDU(conn, call, RPC_S_PROCNUM_OUT_OF_RANGE, PFC_DID_NOT_EXECUTE);
      }
  
      call->msg.BufferLength = len;
      call->msg.ProcNum = op_num;
      call->msg.TransferSyntax = &conn->interface->TransferSyntax;
      call->msg.RpcInterfaceInformation = conn->interface;
  
      _Status = I_RpcGetBuffer(&call->msg);
      if ( _Status || call->msg.Buffer == NULL) {
  	return write_fault_PDU(conn, call, RPC_S_SERVER_OUT_OF_MEMORY, PFC_DID_NOT_EXECUTE);
      }
  
      memcpy(call->msg.Buffer, in->pos, len);
  
      MSRPC_SetCmUser(call->cm_userp);
  
      RpcTryExcept
  	{
  	    (*conn->interface->DispatchTable->DispatchTable[op_num])(&call->msg);
  	}
      RpcExcept ( 1 )
  	{
  	    code = CM_ERROR_UNKNOWN;
  	}
      RpcEndExcept;
  
      MSRPC_SetCmUser(NULL);
  
      if (code == 0) {
  	call->out.pdu_type = PDU_TYPE_RESPONSE;
  
  	MSRPC_AllocBuffer(&call->out, call->msg.BufferLength);
  	call->out.buf_length = call->msg.BufferLength;
  	memcpy(call->out.buf_data, call->msg.Buffer, call->out.buf_length);
      } else {
  	code = write_fault_PDU(conn, call, RPC_S_CALL_FAILED, 0);
      }
  
      return code;
  }
  
  static int
  merge_fragment_Bind(msrpc_call * call, E_CommonHeader * ph,
  			msrpc_call * ncall, E_CommonHeader *pnh)
  {
      if (ph->auth_length + (unsigned int) pnh->auth_length > _UI16_MAX ||
  	ph->frag_length + (unsigned int) pnh->auth_length > _UI16_MAX ||
  	pnh->auth_length + 16 > pnh->frag_length)
  	return CM_ERROR_TOOBIG;
  
      MSRPC_ReallocBuffer(&call->in, call->in.buf_length + pnh->auth_length);
      if (call->in.buf_data == NULL)
  	return CM_ERROR_TOOBIG;
  
      memcpy(call->in.buf_data + ph->frag_length,
  	   ncall->in.buf_data + (pnh->frag_length - pnh->auth_length),
  	   pnh->auth_length);
  
      call->in.buf_length += pnh->auth_length;
      ph->frag_length += pnh->auth_length;
      ph->auth_length += pnh->auth_length;
  
      if ((pnh->pfc_flags & PFC_LAST_FRAG) == PFC_LAST_FRAG) {
  	ph->pfc_flags |= PFC_LAST_FRAG;
      }
  
      return 0;
  }
  
  static int
  merge_fragment_Request(msrpc_call * call, E_CommonHeader * ph,
  			   msrpc_call * ncall, E_CommonHeader * pnh)
  {
      int new_auth_length = pnh->auth_length;
      int new_stub_length = (int)pnh->frag_length - (pnh->auth_length + 24);
  
      if (pnh->pfc_flags & PFC_OBJECT_UUID)
  	new_stub_length -= 16;
  
      if (ph->auth_length + new_auth_length > _UI16_MAX ||
  	ph->frag_length + new_stub_length + new_auth_length > _UI16_MAX ||
  	pnh->auth_length + 24 > pnh->frag_length)
  	return CM_ERROR_TOOBIG;
  
      MSRPC_ReallocBuffer(&call->in, call->in.buf_length + new_auth_length + new_stub_length);
      if (call->in.buf_data == NULL)
  	return CM_ERROR_TOOBIG;
  
      if(ph->auth_length)
  	memmove(call->in.buf_data + (ph->frag_length - ph->auth_length),
  		call->in.buf_data + (ph->frag_length - ph->auth_length) + new_stub_length,
  		ph->auth_length);
  
      memcpy(call->in.buf_data + (ph->frag_length - ph->auth_length),
  	   ncall->in.buf_data + (pnh->frag_length - (new_auth_length + new_stub_length)),
  	   new_stub_length);
  
      memcpy(call->in.buf_data + ph->frag_length + new_stub_length,
  	   ncall->in.buf_data + (pnh->frag_length - pnh->auth_length),
  	   pnh->auth_length);
  
      call->in.buf_length += new_auth_length + new_stub_length;
      ph->frag_length += new_auth_length + new_stub_length;
      ph->auth_length += new_auth_length;
  
      if ((pnh->pfc_flags & PFC_LAST_FRAG) == PFC_LAST_FRAG) {
  	ph->pfc_flags |= PFC_LAST_FRAG;
      }
  
      return 0;
  }
  
  static int
  assemble_message(msrpc_conn * conn)
  {
      int code = 0;
      msrpc_call * call;
      msrpc_call * ncall;
      E_CommonHeader h;
      fcursor c;
  
      call = conn->head;
  
      if (call == NULL)
  	return CM_ERROR_INVAL;
  
      INITC(&c, call->in.buf_data, call->in.buf_length);
  
      code = read_E_CommonHeader(&c, &h);
      if (code)
  	return code;
  
      if ((h.pfc_flags & (PFC_FIRST_FRAG | PFC_LAST_FRAG)) ==
  	(PFC_FIRST_FRAG | PFC_LAST_FRAG))
  	return 0;
  
      if ((h.pfc_flags & PFC_FIRST_FRAG) != PFC_FIRST_FRAG)
  	return CM_ERROR_INVAL;
  
      for (ncall = call->next; ncall; ncall = ncall->next) {
  	fcursor nc;
  	E_CommonHeader nh;
  
  	INITC(&nc, ncall->in.buf_data, ncall->in.buf_length);
  
  	code = read_E_CommonHeader(&nc, &nh);
  
  	if (nh.call_id != h.call_id)
  	    continue;
  
  	if ((nh.pfc_flags & PFC_FIRST_FRAG) != 0)
  	    continue;
  
  	switch(h.PTYPE) {
  	case PDU_TYPE_BIND:
  	case PDU_TYPE_ALTER_CONTEXT:
  	    code = merge_fragment_Bind(call, &h, ncall, &nh);
  	    break;
  
  	case PDU_TYPE_REQUEST:
  	    code = merge_fragment_Request(call, &h, ncall, &nh);
  	    break;
  
  	default:
  	    code = CM_ERROR_INVAL;
  	}
  
  	ncall->status = MSRPC_CALL_SKIP;
  
  	if (nh.pfc_flags & PFC_LAST_FRAG) {
  	    break;
  	}
      }
  
      c.pos = &call->in.buf_data[3];
      write_u_int8(&c, h.pfc_flags);
      write_u_int16(&c, h.frag_length);
      write_u_int16(&c, h.auth_length);
  
      return code;
  }
  
  static int
  dispatch_call(msrpc_conn * conn)
  {
      int code = 0;
      fcursor c;
      fcursor o;
      E_CommonHeader h;
      msrpc_call * call;
  
      call = conn->head;
  
      if (call == NULL ||
  	call->status != MSRPC_CALL_MSGRECEIVED)
  	return CM_ERROR_INVAL;
  
      MSRPC_AllocBuffer(&call->out, DEF_RPC_MSG_SIZE);
  
   retry_buffer:
  
      INITC(&c, call->in.buf_data, call->in.buf_length);
  
      code = read_E_CommonHeader(&c, &h);
      if (code)
  	return code;
  
      if (!IS_PROTOCOL_VER_SUPPORTED(h.rpc_vers, h.rpc_vers_minor)) {
  	if (h.PTYPE == PDU_TYPE_BIND) {
  	    return write_bind_nak_PDU(conn, call, BIND_REJ_PROTOCOL_VERSION_NOT_SUPPORTED);
  	} else {
  	    /* we shouldn't get here, but just in case */
  	    return write_fault_PDU(conn, call, RPC_S_INVALID_ARG, PFC_DID_NOT_EXECUTE);
  	}
      }
  
      if (h.packed_drep[0] != 0x10 ||
  	h.packed_drep[1] != 0 ||
  	h.packed_drep[2] != 0 ||
  	h.packed_drep[3] != 0) {
  
  	if (h.PTYPE == PDU_TYPE_BIND) {
  	    return write_bind_nak_PDU(conn, call, BIND_REJ_REASON_NOT_SPECIFIED);
  	} else {
  	    return write_fault_PDU(conn, call, RPC_S_INVALID_ARG, PFC_DID_NOT_EXECUTE);
  	}
      }
  
      if (h.frag_length > call->in.buf_length) {
  	return CM_ERROR_INVAL;
      }
  
      if ((h.pfc_flags & (PFC_FIRST_FRAG | PFC_LAST_FRAG)) !=
  	(PFC_FIRST_FRAG | PFC_LAST_FRAG)) {
  	code = assemble_message(conn);
  	if (code == 0)
  	    goto retry_buffer;
  	else
  	    return write_fault_PDU(conn, call, RPC_S_INVALID_ARG, PFC_DID_NOT_EXECUTE);
      }
  
      if (h.auth_length != 0) {
  	return write_fault_PDU(conn, call, RPC_S_BINDING_HAS_NO_AUTH, PFC_DID_NOT_EXECUTE);
      }
  
      call->call_id = h.call_id;
  
      call->in.pdu_type = h.PTYPE;
  
      INITC(&o, call->out.buf_data, call->out.buf_alloc);
  
      call->in_header = &h;
  
      code = CM_ERROR_INVAL;
  
      switch (h.PTYPE) {
      case PDU_TYPE_REQUEST:
  	code = handle_ConnRequest(conn, call, &c, &o);
  	break;
  
      case PDU_TYPE_BIND:
  	code = handle_ConnBind(conn, call, &c, &o);
  	break;
  
      case PDU_TYPE_ALTER_CONTEXT:
  	code = write_fault_PDU(conn, call, RPC_S_CALL_FAILED, PFC_DID_NOT_EXECUTE);
  	break;
  
      case PDU_TYPE_SHUTDOWN:
  	code = write_fault_PDU(conn, call, RPC_S_CALL_FAILED, PFC_DID_NOT_EXECUTE);
  	break;
  
      case PDU_TYPE_CO_CANCEL:
  	code = write_fault_PDU(conn, call, RPC_S_CALL_FAILED, PFC_DID_NOT_EXECUTE);
  	break;
  
      case PDU_TYPE_ORPHANED:
  	code = write_fault_PDU(conn, call, RPC_S_CALL_FAILED, PFC_DID_NOT_EXECUTE);
  	break;
  
      default:
  	code = write_fault_PDU(conn, call, RPC_S_CALL_FAILED, PFC_DID_NOT_EXECUTE);
      }
  
      call->in_header = NULL;
  
      call->status = MSRPC_CALL_DISPATCHED;
  
      return code;
  }
  
  static void
  MSRPC_AllocBuffer(msrpc_buffer * buf, unsigned int size)
  {
      if (buf->buf_alloc < size) {
  	if (buf->buf_data)
  	    _aligned_free(buf->buf_data);
  
  	buf->buf_alloc = ALIGNTO8(size);
  	buf->buf_data = _aligned_malloc(buf->buf_alloc, RPC_ALIGNMENT);
  	buf->buf_length = 0;
  	buf->buf_pos = 0;
      }
  }
  
  static void
  MSRPC_FreeBuffer(msrpc_buffer * buf)
  {
      if (buf->buf_data) {
  	_aligned_free(buf->buf_data);
  	buf->buf_data = NULL;
  	buf->buf_alloc = 0;
  	buf->buf_length = 0;
  	buf->buf_pos = 0;
      }
  }
  
  static void
  MSRPC_ReallocBuffer(msrpc_buffer * buf, unsigned int size)
  {
      if (buf->buf_alloc < size) {
  	BYTE * new_buf;
  
  	buf->buf_alloc = ALIGNTO8(size);
  	new_buf = _aligned_realloc(buf->buf_data, buf->buf_alloc, RPC_ALIGNMENT);
  	if (new_buf != NULL) {
  	    buf->buf_data = new_buf;
  	} else {
  	    MSRPC_FreeBuffer(buf);
  	}
      }
  }
  
  static msrpc_call *
  MSRPC_NewCall(void)
  {
      msrpc_call * c;
  
      c = calloc(sizeof(*c), 1);
  
      c->msg.Handle = NULL;
      c->msg.DataRepresentation = 0x00000010;
      c->msg.Buffer = NULL;
      c->msg.BufferLength = 0;
      c->msg.TransferSyntax = NULL;
  
      return c;
  }
  
  static int
  MSRPC_PutCall(msrpc_conn * conn, msrpc_call * pcall)
  {
      if (conn->tail) {
  	conn->tail->next = pcall;
  	pcall->prev = conn->tail;
  	conn->tail = pcall;
  	pcall->next = NULL;
      } else {
  	conn->head = conn->tail = pcall;
  	pcall->next = pcall->prev = NULL;
      }
      return 0;
  }
  
  static int
  MSRPC_DequeueCall(msrpc_conn * conn, msrpc_call * call)
  {
      if (call->prev)
  	call->prev->next = call->next;
      if (call->next)
  	call->next->prev = call->prev;
  
      if (conn->head  && conn->head == call)
  	conn->head = call->next;
  
      if (conn->tail && conn->tail == call)
  	conn->tail = call->prev;
  
      call->next = call->prev = NULL;
  
      return 0;
  }
  
  static int
  MSRPC_GetCall(msrpc_conn * conn, msrpc_call ** ppcall)
  {
      if (conn->head) {
  	*ppcall = conn->head;
  	conn->head = conn->head->next;
  	if (conn->head)
  	    conn->head->prev = NULL;
  	(*ppcall)->next = (*ppcall)->prev = NULL;
  	if (conn->tail == *ppcall)
  	    conn->tail = NULL;
  	return 0;
      } else {
  	*ppcall = NULL;
  	return 1;
      }
  }
  
  static int
  MSRPC_FreeCall(msrpc_call * call)
  {
      I_RpcFreeBuffer(&call->msg);
      MSRPC_FreeBuffer(&call->in);
      MSRPC_FreeBuffer(&call->out);
  
      if (call->cm_userp) {
  	cm_ReleaseUser(call->cm_userp);
  	call->cm_userp = NULL;
      }
  
      ZeroMemory(call, sizeof(*call));
      free (call);
      return 0;
  }
  
  /*! \brief Initialize a msrpc_conn
  
     \param [out] conn Connection to initialize
  
     \param [in] secondary_address An optional secondary address for the
         query.  Typically for RPCs to wellknown endpoints such as
         SRVSVC or WKSSVC, this is a named pipe address (e.g.:
         \PIPE\srvsvc ).
  
     \return Always returns 0.
   */
  int
  MSRPC_InitConn(msrpc_conn * conn, const char * secondary_address)
  {
      ZeroMemory(conn, sizeof(*conn));
      if (secondary_address) {
  	conn->secondary_address = strdup(secondary_address);
      }
      return 0;
  }
  
  /*! \brief Free a msrcp_conn structure contents
  
     This doesn't free the msrpc_conn object, but frees the contents.
   */
  int
  MSRPC_FreeConn(msrpc_conn * conn)
  {
      msrpc_call * call = NULL;
  
      while(!MSRPC_GetCall(conn, &call)) {
  	MSRPC_FreeCall(call);
      }
  
      if (conn->secondary_address)
  	free(conn->secondary_address);
  
      ZeroMemory(conn, sizeof(*conn));
  
      return 0;
  }
  
  /*! \brief Write an RPC message into the RPC module
  
     This function should be used by message mode RPC transports to
     submit a single message.  if successful, the message will be queued
     for dispatch.  The RPC will not be executed until
     MSRPC_PrepareRead() or MSRPC_ReadMessageLength() or
     MSRPC_ReadMessage() is called.
   */
  int
  MSRPC_WriteMessage(msrpc_conn * conn, BYTE * buffer, unsigned int len,
                     cm_user_t * userp)
  {
      msrpc_call * call;
  
      if (len == 0)
  	return CM_ERROR_INVAL;
  
      if (len > MAX_RPC_MSG_SIZE)
  	return CM_ERROR_BUFFERTOOSMALL;
  
      call = MSRPC_NewCall();
      if (!call)
          return ENOMEM;
  
      MSRPC_AllocBuffer(&call->in, len);
      memcpy(call->in.buf_data, buffer, len);
      call->in.buf_pos = len;
      call->in.buf_length = call->in.buf_pos;
      call->status = MSRPC_CALL_MSGRECEIVED;
  
      if (userp) {
  	call->cm_userp = userp;
  	cm_HoldUser(userp);
      }
  
      MSRPC_PutCall(conn, call);
  
      return 0;
  }
  
  /*! \brief Prepare to read the next message from the RPC output queue
  
     Each RPC message received is queued until the response is required.
     MSRPC_PrepareRead() will, if necessary, dispatch the RPC request
     and prepare the output buffer to be sent to the caller.
   */
  int
  MSRPC_PrepareRead(msrpc_conn * conn)
  {
      msrpc_call * call;
      int code = 0;
  
      while (conn->head) {
  	call = conn->head;
  
  	if (call->status == MSRPC_CALL_SKIP ||
  	    call->status == MSRPC_CALL_MSGSENT ||
  	    (call->status == MSRPC_CALL_DISPATCHED &&
  	     call->out.buf_length == 0)) {
  
  	    MSRPC_DequeueCall(conn, call);
  	    MSRPC_FreeCall(call);
  	    continue;
  	}
  
  	if (call->status == MSRPC_CALL_MSGRECEIVED) {
  	    code = dispatch_call(conn);
  
  	    /* If the dispatch failed, then we discard the message.
  	       If the error was handled and a response packet was
  	       created, dispatch_call() would return 0. */
  
  	    if (code) {
  		MSRPC_DequeueCall(conn, call);
  		MSRPC_FreeCall(call);
  	    }
  	    continue;
  	}
  
  	break;			/* the status wasn't SKIP or
  				   MSGRECEIVED, or we successfully
  				   dispatched the message. */
      }
  
      if (conn->head == NULL)
  	return CM_ERROR_BADFD;
  
      if (conn->head->status == MSRPC_CALL_DISPATCHED ||
  	conn->head->status == MSRPC_CALL_MSGSENDING)
  	return 0;
      else
  	return CM_ERROR_BADFD;
  }
  
  /*! \brief Return the size of the buffer for the next RPC message
  
     Given the maximum size of the buffer, returns the actual size of
     the data that will be copied.  If the return value is 0, there is
     no data to be sent to the caller either because there was no RPC
     message queued or the queued message could not be dispatched.
   */
  int
  MSRPC_ReadMessageLength(msrpc_conn * conn, unsigned int max_len)
  {
      int code;
      msrpc_call *call;
  
      max_len = min(max_len, conn->max_recv_frag);
  
      code = MSRPC_PrepareRead(conn);
      if (code)
  	return 0;
  
      call = conn->head;
  
      if (call &&
  	(call->status == MSRPC_CALL_DISPATCHED ||
  	 call->status == MSRPC_CALL_MSGSENDING)) {
  
  	switch(call->out.pdu_type) {
  	case PDU_TYPE_RESPONSE:
  	    {
  		/* This calculation should mirror what
  		   MSRPC_ReadMessage() does below. */
  
  		int fragment_size = conn->max_recv_frag - RPC_RESPONSE_ENVELOPE_OVERHEAD;
  		unsigned int len;
  
  		len = min(max_len, conn->max_recv_frag);
  
  		if (call->out.buf_pos % fragment_size == 0) {
  		    if (len < RPC_RESPONSE_ENVELOPE_OVERHEAD)
  			return 0;
  
  		    len = min(len, RPC_RESPONSE_ENVELOPE_OVERHEAD + call->out.buf_length - call->out.buf_pos);
  		} else {
  		    unsigned int to_copy;
  
  		    to_copy = min(fragment_size - (call->out.buf_pos % fragment_size),
  				  call->out.buf_length - call->out.buf_pos);
  		    len = min(to_copy, len);
  		}
  
  		return len;
  	    }
  
  	case PDU_TYPE_BIND_ACK:
  	case PDU_TYPE_FAULT:
  	case PDU_TYPE_BIND_NAK:
  	    {
  		int len = min(call->out.buf_length - call->out.buf_pos, max_len);
  
  		return len;
  	    }
  	}
  
  	return 0;
      } else {
  	return 0;
      }
  }
  
  /*! \brief Read the next message from the RPC queue
  
     Copies the next chunk of data from the response buffer to the
     buffer indicated by \a buffer and \a len.
  
     \retval 0 The data was copied to the buffer.  There is either no
         more data to send or the existence of more data is implicit in
         the data already sent. I.e. fragment flags were set
         appropriately to indicate more data and we are at a fragment
         boundary.
  
     \retval CM_ERROR_RPC_MOREDATA The data was copied to the buffer and
         there is more data to be sent.
  
     \retval Else something went wrong.  The data was not copied to the
         buffer successfully.
   */
  int
  MSRPC_ReadMessage(msrpc_conn * conn, BYTE *buffer, unsigned int len)
  {
      int code;
      msrpc_call * call;
  
      code = MSRPC_PrepareRead(conn);
      if (code)
  	return code;
  
      if (conn->head &&
  	(conn->head->status == MSRPC_CALL_DISPATCHED ||
  	 conn->head->status == MSRPC_CALL_MSGSENDING)) {
  
  	call = conn->head;
  
  	switch(call->out.pdu_type) {
  	case PDU_TYPE_RESPONSE:
  	    {
  		BYTE * o_buffer = call->out.buf_data;
  		unsigned int o_alloc = call->out.buf_alloc;
  		unsigned int o_pos = call->out.buf_pos;
  		unsigned int o_len = call->out.buf_length;
  		unsigned int start_pos = o_pos;
  		unsigned int fragment_size;
  		fcursor o;
  
  		/* If call->out.buf_pos falls on a fragment boundary:
  
  		     Assume that we are starting a new fragment and deal accordingly.
  
  		   Else
  
  		     Assume we are continuing a fragment.
  		 */
  
  		fragment_size = conn->max_recv_frag - RPC_RESPONSE_ENVELOPE_OVERHEAD;
  
  		/* Don't send more than the max_recv_frag */
  		len = min(len, conn->max_recv_frag);
  
  		/* Switch the supplied user buffer with the output
  		   buffer */
  		call->out.buf_data = buffer;
  		call->out.buf_length = 0;
  		call->out.buf_alloc = len;
  		call->out.buf_pos = 0;
  
  		if (o_pos % fragment_size == 0) {
  
  		    /* New fragment:
  
  		       We need to write the packet headers and
  		       fragment bits here.  The length of the buffer
  		       should be at least big enough to hold the
  		       entire envelope.  We don't handle the case were
  		       the envelope itself is too big.
  
  		     */
  
  		    if (len < RPC_RESPONSE_ENVELOPE_OVERHEAD) {
  			return CM_ERROR_INVAL;
  		    }
  
  		    code = write_E_CommonHeader(&o, conn, call, PDU_TYPE_RESPONSE);
  		    if (code)
  			return code;
  
  		    write_u_int32(&o, o_len); /* allocation hint */
  		    write_u_int16(&o, call->context_id); /* presentation context ID */
  		    write_u_int8(&o, 0);		 /* cancel count */
  		    write_u_int8(&o, 0);		 /* reserved */
  
  		    {
  			int to_copy = min(SPC_LEFT(&o), o_len - o_pos);
  			int rc;
  
  			O_WRITESZ(&o, (o_buffer + o_pos), to_copy);
  
  			/* the fragment size of the message is the minimum of:
  
  			   - max_recv_frag for the connection
  			   - envelope size + (full message size - start offset)
  
  			 */
  			rc = closeMessage(&o, call,
  					  min(conn->max_recv_frag,
  					      RPC_RESPONSE_ENVELOPE_OVERHEAD + o_len - start_pos));
  			if (rc)
  			    return rc;
  
  			o_pos += to_copy;
  
  			/* If there is more data to be sent and we are
  			   not at a fragment boundary after the last
  			   send, we return CM_ERROR_RPC_MOREDATA.
  			   Having more fragments to send is not a case
  			   we indicate via a return value. */
  
  			if (o_pos < o_len &&
  			    o_pos < start_pos + fragment_size)
  			    code = CM_ERROR_RPC_MOREDATA;
  		    }
  
  		    if (start_pos + fragment_size < o_len)
  			buffer[3] &= ~PFC_LAST_FRAG;
  
  		    if (start_pos != 0)
  			buffer[3] &= ~PFC_FIRST_FRAG;
  
  		} else {
  
  		    /* Continuation:
  
  		       The packet header should have already been sent
  		       in a previous block.
  
  		     */
  		    unsigned int to_copy;
  
  		    INITC(&o, call->out.buf_data, call->out.buf_alloc);
  
  		    to_copy = min(fragment_size - (o_pos % fragment_size),
  				  o_len - o_pos);
  		    to_copy = min(to_copy, SPC_LEFT(&o));
  
  		    O_WRITESZ(&o, (o_buffer + o_pos), to_copy);
  
  		    o_pos += to_copy;
  
  		    if (o_pos < o_len && (o_pos % fragment_size) != 0)
  			code = CM_ERROR_RPC_MOREDATA;
  		}
  
  		call->out.buf_data = o_buffer;
  		call->out.buf_alloc = o_alloc;
  		call->out.buf_pos = o_pos;
  		call->out.buf_length = o_len;
  
  		if (call->out.buf_pos < call->out.buf_length) {
  		    call->status = MSRPC_CALL_MSGSENDING;
  		} else {
  		    call->status = MSRPC_CALL_MSGSENT;
  		    MSRPC_DequeueCall(conn, call);
  		    MSRPC_FreeCall(call);
  		}
  	    }
  	    return code;
  
  	case PDU_TYPE_BIND_ACK:
  	case PDU_TYPE_FAULT:
  	case PDU_TYPE_BIND_NAK:
  	    {
  		unsigned int to_send;
  
  		/* These types of packets are already fully formed.
  		   We don't need to wrap them in an envelope.  They
  		   also don't fragment. */
  
  		to_send = call->out.buf_length - call->out.buf_pos;
  
  		if (len < to_send)
  		    code = CM_ERROR_RPC_MOREDATA;
  		else
  		    len = to_send;
  
  		if (len > 0) {
  		    memcpy(buffer, call->out.buf_data + call->out.buf_pos, len);
  		}
  
  		call->out.buf_pos += len;
  
  		if (call->out.buf_pos < call->out.buf_length) {
  		    call->status = MSRPC_CALL_MSGSENDING;
  		} else {
  		    call->status = MSRPC_CALL_MSGSENT;
  		    MSRPC_DequeueCall(conn, call);
  		    MSRPC_FreeCall(call);
  		}
  	    }
  	    return code;
  	}
  
  	return CM_ERROR_INVAL;
      } else {
  	return 0;
      }
  }
  
  pthread_once_t msrpc_user_slot_init = PTHREAD_ONCE_INIT;
  
  DWORD msrpc_tls_user_slot = 0;
  
  static void
  msrpc_user_slot_init_func(void)
  {
      msrpc_tls_user_slot = TlsAlloc();
  }
  
  cm_user_t *
  MSRPC_GetCmUser(void)
  {
      pthread_once(&msrpc_user_slot_init, msrpc_user_slot_init_func);
  
      return (cm_user_t *) TlsGetValue(msrpc_tls_user_slot);
  }
  
  static void
  MSRPC_SetCmUser(cm_user_t * userp)
  {
      pthread_once(&msrpc_user_slot_init, msrpc_user_slot_init_func);
  
      TlsSetValue(msrpc_tls_user_slot, userp);
  }
  
  typedef struct I_RpcAllocatedBuffer {
      DWORD magic;		/* == _RPCALLOCATEDBUFFER_MAGIC */
  #define _RPCALLOCATEDBUFFER_MAGIC 0xa110ca73
      struct I_RpcAllocatedBuffer * next;
  
      /* Data goes here */
  } I_RpcAllocatedBuffer;
  
  #define I_RPCALLOCATEDBUFFER_FROM_PTR(v) (&(((I_RpcAllocatedBuffer *) (v))[-1]))
  
  RPC_STATUS
  RPC_ENTRY
  I_RpcGetBuffer (IN OUT RPC_MESSAGE __RPC_FAR * Message)
  {
      I_RpcAllocatedBuffer * newBuffer = I_RpcAllocate(Message->BufferLength + sizeof(I_RpcAllocatedBuffer));
      I_RpcAllocatedBuffer * oldBuffer = NULL;
  
      if (Message->Buffer) {
  	oldBuffer = I_RPCALLOCATEDBUFFER_FROM_PTR(Message->Buffer);
      }
  
      if (newBuffer) {
  	newBuffer->magic = _RPCALLOCATEDBUFFER_MAGIC;
  	newBuffer->next = oldBuffer;
  
  	Message->Buffer = &newBuffer[1];
      } else {
  	return ERROR_OUTOFMEMORY;
      }
  
      return RPC_S_OK;
  }
  
  RPC_STATUS
  RPC_ENTRY
  I_RpcFreeBuffer (IN OUT RPC_MESSAGE __RPC_FAR * Message)
  {
      if (Message->Buffer) {
  	I_RpcAllocatedBuffer * buffer;
  
  	buffer = I_RPCALLOCATEDBUFFER_FROM_PTR(Message->Buffer);
  
  	while (buffer) {
  	    I_RpcAllocatedBuffer * bnext;
  
  	    bnext = buffer->next;
  
  	    I_RpcFree(buffer);
  
  	    buffer = bnext;
  	}
  
  	Message->Buffer = NULL;
      }
  
      return RPC_S_OK;
  }
  
  unsigned char  *
  RPC_ENTRY
  NdrServerInitializeNew(
      PRPC_MESSAGE            pRpcMsg,
      PMIDL_STUB_MESSAGE      pStubMsg,
      PMIDL_STUB_DESC         pStubDesc
      )
  {
      ZeroMemory(pStubMsg, sizeof(*pStubMsg));
  
      pStubMsg->RpcMsg = pRpcMsg;
      pStubMsg->Buffer = pRpcMsg->Buffer;
      pStubMsg->BufferStart = pRpcMsg->Buffer;
      pStubMsg->BufferEnd = pStubMsg->Buffer + pRpcMsg->BufferLength;
      pStubMsg->BufferLength = pRpcMsg->BufferLength;
      pStubMsg->MemorySize = pRpcMsg->BufferLength;
      pStubMsg->pfnAllocate = pStubDesc->pfnAllocate;
      pStubMsg->pfnFree = pStubDesc->pfnFree;
      pStubMsg->StubDesc = pStubDesc;
      pStubMsg->dwDestContext = MSHCTX_LOCAL;
  
      return NULL;
  }
  
  void
  MSRPC_Init(void) {
      RPC_SRVSVC_Init();
  }
  
  void
  MSRPC_Shutdown(void) {
      RPC_SRVSVC_Shutdown();
  }
  
  int
  MSRPC_IsWellKnownService(const clientchar_t * lastNamep)
  {
      return
  	cm_ClientStrCmpIA(lastNamep,  _C("\\srvsvc")) == 0 ||
  	cm_ClientStrCmpIA(lastNamep,  _C("\\wkssvc")) == 0 ||
  	cm_ClientStrCmpIA(lastNamep,  _C("\\spoolss")) == 0 ||
  	cm_ClientStrCmpIA(lastNamep,  _C("\\winreg")) == 0 ||
  	cm_ClientStrCmpIA(lastNamep,  _C("\\lsass")) == 0 ||
  	cm_ClientStrCmpIA(lastNamep,  _C("\\lsarpc")) == 0 ||
  	cm_ClientStrCmpIA(lastNamep,  _C("\\samr")) == 0 ||
  	cm_ClientStrCmpIA(lastNamep,  _C("\\netlogon")) == 0 ||
  	cm_ClientStrCmpIA(lastNamep,  _C("\\ntsvcs")) == 0 ||
  	cm_ClientStrCmpIA(lastNamep,  _C("\\eventlog")) == 0 ||
  	cm_ClientStrCmpIA(lastNamep,  _C("\\svcctl")) == 0 ||
  	cm_ClientStrCmpIA(lastNamep,  _C("\\browse")) == 0 ||
  	cm_ClientStrCmpIA(lastNamep,  _C("\\msgsvc")) == 0 ||
  	cm_ClientStrCmpIA(lastNamep,  _C("\\w32time")) == 0 ||
  	cm_ClientStrCmpIA(lastNamep,  _C("ipc$")) == 0;
  }
  