/// <summary> /// Expect to receive a call. /// </summary> /// <param name="timeout">Timeout of expecting a call.</param> /// <param name="sessionContext">Session Context of the received call.</param> /// <param name="opnum">Operation number of the method invoked.</param> /// <returns>Received a byte array of the request stub from a client.</returns> /// <exception cref="InvalidOperationException"> /// Thrown when receive error from server. /// </exception> public virtual byte[] ExpectCall( TimeSpan timeout, out RpceServerSessionContext sessionContext, out ushort opnum) { sessionContext = null; RpcePdu receivedPdu = ReceiveAndReassemblePdu(timeout, ref sessionContext); RpceCoRequestPdu requestPdu = receivedPdu as RpceCoRequestPdu; if (requestPdu == null) { throw new InvalidOperationException("Expect request_pdu, but received others."); } opnum = requestPdu.opnum; byte[] stub = requestPdu.stub; if (stub == null) { stub = new byte[0]; } return(stub); }
/// <summary> /// Send request to RPCE server. /// </summary> /// <param name="opnum"> /// The opnum of a method. /// </param> /// <param name="requestStub"> /// A byte array of the request stub of a method.<para/> /// RpceStubEncoder can be used to NDR marshal parameters to a byte array. /// </param> /// <param name="callId"> /// The identifier of this call. /// </param> /// <exception cref="ArgumentNullException"> /// Thrown when requestStub is null. /// </exception> /// <exception cref="InvalidOperationException"> /// Thrown when receive error from server or RPC connection has not been established. /// </exception> /// <exception cref="InvalidOperationException"> /// Thrown when this transport has been used as an synchronous transport. /// </exception> public virtual void SendRequest( ushort opnum, byte[] requestStub, out uint callId) { if (requestStub == null) { throw new ArgumentNullException("requestStub"); } if (rpceClient.IsDisposed) { throw new InvalidOperationException("RPC connection has not been established."); } RpceCoRequestPdu requestPdu = rpceClient.CreateCoRequestPdu( opnum, requestStub); if (rpceClient.Context.AuthenticationType == RpceAuthenticationType.RPC_C_AUTHN_WINNT && rpceClient.Context.AuthenticationLevel == RpceAuthenticationLevel.RPC_C_AUTHN_LEVEL_PKT_INTEGRITY) { verification_trailer_t verificationTrailer = requestPdu.CreateVerificationTrailer( SEC_VT_COMMAND.SEC_VT_COMMAND_BITMASK_1, SEC_VT_COMMAND.SEC_VT_COMMAND_HEADER2, SEC_VT_COMMAND.SEC_VT_COMMAND_PCONTEXT | SEC_VT_COMMAND.SEC_VT_COMMAND_END); requestPdu.AppendVerificationTrailerToStub(verificationTrailer); } FragmentAndSendPdu(requestPdu); lock (responseLock) { callId = requestPdu.call_id; outstandingCallIds.Add(callId); } }
/// <summary> /// update context on sending PDU. /// </summary> /// <param name="pdu">PDU to send.</param> internal void UpdateContextOnSendingPdu(RpcePdu pdu) { RpceCoPdu coPdu = pdu as RpceCoPdu; if (coPdu == null) { return; } this.RpcVersionMajor = coPdu.rpc_vers; this.RpcVersionMinor = coPdu.rpc_vers_minor; if (coPdu.PTYPE == RpcePacketType.Bind || coPdu.PTYPE == RpcePacketType.BindAck || coPdu.PTYPE == RpcePacketType.AlterContext || coPdu.PTYPE == RpcePacketType.AlterContextResp) { this.SupportsHeaderSign = (coPdu.pfc_flags & RpceCoPfcFlags.PFC_SUPPORT_HEADER_SIGN) == RpceCoPfcFlags.PFC_SUPPORT_HEADER_SIGN; } this.SupportsConcurrentMultiplexing = (coPdu.pfc_flags & RpceCoPfcFlags.PFC_CONC_MPX) == RpceCoPfcFlags.PFC_CONC_MPX; this.PackedDataRepresentationFormat = coPdu.packed_drep.dataRepFormat; if (coPdu.PTYPE != RpcePacketType.Request) { this.outstandingCalls.Remove(coPdu.call_id); } switch (coPdu.PTYPE) { case RpcePacketType.BindAck: RpceCoBindAckPdu bindAckPdu = coPdu as RpceCoBindAckPdu; if (bindAckPdu != null) { UpdateContextOnSendingBindAckPdu(bindAckPdu); } break; case RpcePacketType.Request: RpceCoRequestPdu requestPdu = coPdu as RpceCoRequestPdu; if (requestPdu != null) { this.ContextIdentifier = requestPdu.p_cont_id; } break; case RpcePacketType.Response: RpceCoResponsePdu responsePdu = coPdu as RpceCoResponsePdu; if (responsePdu != null) { this.ContextIdentifier = responsePdu.p_cont_id; } break; case RpcePacketType.BindNak: case RpcePacketType.AlterContextResp: case RpcePacketType.Fault: case RpcePacketType.Shutdown: default: //default situation should do nothing. //This is just update the context, if we cannot recognize the PDU, ignore it. break; } }
/// <summary> /// update context on receiving PDU. /// </summary> /// <param name="pdu">PDU to receive.</param> /// <exception cref="ArgumentNullException">Thrown when pdu is null.</exception> internal void UpdateContextOnReceivingPdu(RpcePdu pdu) { RpceCoPdu coPdu = pdu as RpceCoPdu; if (coPdu == null) { return; } this.RpcVersionMajor = coPdu.rpc_vers; this.RpcVersionMinor = coPdu.rpc_vers_minor; if (coPdu.PTYPE == RpcePacketType.Bind || coPdu.PTYPE == RpcePacketType.BindAck) { //TDI: //TD said PFC_SUPPORT_HEADER_SIGN is meanful in Bind and AltContext. //But when using Kerberos, AltContext doesn't have this flag, //if server or client read this flag according to AltContext, Sign/Encrypt will fail. this.SupportsHeaderSign = (coPdu.pfc_flags & RpceCoPfcFlags.PFC_SUPPORT_HEADER_SIGN) == RpceCoPfcFlags.PFC_SUPPORT_HEADER_SIGN; this.SupportsConcurrentMultiplexing = (coPdu.pfc_flags & RpceCoPfcFlags.PFC_CONC_MPX) == RpceCoPfcFlags.PFC_CONC_MPX; } this.PackedDataRepresentationFormat = coPdu.packed_drep.dataRepFormat; this.CurrentCallId = coPdu.call_id; if (coPdu.PTYPE != RpcePacketType.Response && coPdu.PTYPE != RpcePacketType.Auth3) { this.outstandingCalls.Add(coPdu.call_id); } switch (coPdu.PTYPE) { case RpcePacketType.Bind: RpceCoBindPdu bindPdu = coPdu as RpceCoBindPdu; if (bindPdu != null) { UpdateContextOnReceivingBindPdu(bindPdu); } break; case RpcePacketType.Request: RpceCoRequestPdu requestPdu = coPdu as RpceCoRequestPdu; if (requestPdu != null) { this.ContextIdentifier = requestPdu.p_cont_id; } break; case RpcePacketType.Response: RpceCoResponsePdu responsePdu = coPdu as RpceCoResponsePdu; if (responsePdu != null) { this.ContextIdentifier = responsePdu.p_cont_id; } break; case RpcePacketType.Auth3: case RpcePacketType.AlterContext: case RpcePacketType.CoCancel: case RpcePacketType.Orphaned: default: //default situation should do nothing. //This is just update the context, if we cannot recognize the PDU, ignore it. break; } this.SecurityContextNeedContinueProcessing = coPdu.securityContextNeedContinueProcessing; }
public static RpceCoPdu DecodeCoPdu( RpceContext context, byte[] pduBytes) { if (context == null) { throw new ArgumentNullException("context"); } if (pduBytes == null) { throw new ArgumentNullException("pduBytes"); } RpceCoPdu pdu; //#2 byte is PTYPE RpcePacketType packageType = (RpcePacketType)pduBytes[2]; //#3 byte is PFC_*** flags RpceCoPfcFlags pfcFlags = (RpceCoPfcFlags)pduBytes[3]; RpceCoPfcFlags pfcFlagsNoFragment = RpceCoPfcFlags.PFC_FIRST_FRAG | RpceCoPfcFlags.PFC_LAST_FRAG; if (((pfcFlags & pfcFlagsNoFragment) != pfcFlagsNoFragment) && (packageType == RpcePacketType.Bind || packageType == RpcePacketType.BindAck || packageType == RpcePacketType.AlterContext || packageType == RpcePacketType.AlterContextResp || packageType == RpcePacketType.Auth3)) { //If it's a fragment and PTYPE is bind/bind_ack/alter_context/alter_context_resp //Windows RPC support version 5.0 only. //Bind fragment requires RPC ver 5.1. //We don't support it. throw new NotSupportedException("bind/bind_ack/alt_context/alt_context_resp/auth3 PDU fragment are not supported."); } else { switch (packageType) { case RpcePacketType.Bind: pdu = new RpceCoBindPdu(context, pduBytes); break; case RpcePacketType.BindAck: pdu = new RpceCoBindAckPdu(context, pduBytes); break; case RpcePacketType.BindNak: pdu = new RpceCoBindNakPdu(context, pduBytes); break; case RpcePacketType.AlterContext: pdu = new RpceCoAlterContextPdu(context, pduBytes); break; case RpcePacketType.AlterContextResp: pdu = new RpceCoAlterContextRespPdu(context, pduBytes); break; case RpcePacketType.Auth3: pdu = new RpceCoAuth3Pdu(context, pduBytes); break; case RpcePacketType.Request: pdu = new RpceCoRequestPdu(context, pduBytes); break; case RpcePacketType.Response: pdu = new RpceCoResponsePdu(context, pduBytes); break; case RpcePacketType.Fault: pdu = new RpceCoFaultPdu(context, pduBytes); break; case RpcePacketType.CoCancel: pdu = new RpceCoCancelPdu(context, pduBytes); break; case RpcePacketType.Orphaned: pdu = new RpceCoOrphanedPdu(context, pduBytes); break; case RpcePacketType.Shutdown: pdu = new RpceCoShutdownPdu(context, pduBytes); break; default: throw new InvalidOperationException( string.Format("Receive invalid packet - {0}.", packageType)); } } return(pdu); }
/// <summary> /// Reassemble several fragment PDUs to one PDU.<para/> /// Must call after decrypt/verify. /// </summary> /// <param name="context">RpceContext to Reassemble PDU.</param> /// <param name="pdus">Fragment PDUs to be reassembled.</param> /// <returns>A ressembled PDU.</returns> /// <exception cref="ArgumentNullException"> /// Thrown when pdus is null. /// </exception> /// <exception cref="ArgumentException"> /// Thrown when pdus is invalid. /// </exception> /// <exception cref="InvalidOperationException">Thrown when Speicified PDU doesn't /// support fragment and reassemble.</exception> public static RpceCoPdu ReassemblePdu(RpceContext context, params RpceCoPdu[] pdus) { if (context == null) { throw new ArgumentNullException("context"); } if (pdus == null) { throw new ArgumentNullException("pdus"); } if (pdus.Length == 0) { throw new ArgumentException("There's no PDU to reassemble.", "pdus"); } //verify if the fisrt pdu has the valid PFC_FIRST_FRAG. if ((pdus[0].pfc_flags & RpceCoPfcFlags.PFC_FIRST_FRAG) == 0) { throw new ArgumentException("First PDU doesn't have PFC_FIRST_FRAG flag.", "pdus"); } //verify if fragments end expected. for (int i = 0; i < pdus.Length - 1; i++) { if ((pdus[i].pfc_flags & RpceCoPfcFlags.PFC_LAST_FRAG) != 0) { throw new ArgumentException("Fragments ended unexpected.", "pdus"); } } if ((pdus[pdus.Length - 1].pfc_flags & RpceCoPfcFlags.PFC_LAST_FRAG) == 0) { throw new ArgumentException("Fragments is not ended.", "pdus"); } //All PTYPEs should be the same. for (int i = 1; i < pdus.Length; i++) { if (pdus[i].PTYPE != pdus[0].PTYPE) { throw new ArgumentException("PDUs' PTYPE are different.", "pdus"); } } if (pdus.Length == 1) { return(pdus[0]); } RpceCoPdu reassembledPdu; switch (pdus[0].PTYPE) { case RpcePacketType.Request: RpceCoRequestPdu requestPdu = pdus[0] as RpceCoRequestPdu; if (requestPdu == null) { throw new ArgumentException("The PDU is not a valid RpceCoRequestPdu"); } for (int i = 1; i < pdus.Length; i++) { requestPdu.stub = ArrayUtility.ConcatenateArrays( requestPdu.stub, ((RpceCoRequestPdu)pdus[i]).stub); } requestPdu.pfc_flags |= RpceCoPfcFlags.PFC_LAST_FRAG; requestPdu.frag_length = (ushort)(requestPdu.GetSize() + requestPdu.stub.Length); requestPdu.auth_length = 0; requestPdu.auth_verifier = null; reassembledPdu = requestPdu; break; case RpcePacketType.Response: RpceCoResponsePdu responsePdu = pdus[0] as RpceCoResponsePdu; if (responsePdu == null) { throw new ArgumentException("The PDU is not a valid RpceCoResponsePdu"); } for (int i = 1; i < pdus.Length; i++) { responsePdu.stub = ArrayUtility.ConcatenateArrays( responsePdu.stub, ((RpceCoResponsePdu)pdus[i]).stub); } responsePdu.pfc_flags |= RpceCoPfcFlags.PFC_LAST_FRAG; responsePdu.frag_length = (ushort)(responsePdu.GetSize() + responsePdu.stub.Length); responsePdu.auth_length = 0; responsePdu.auth_verifier = null; reassembledPdu = responsePdu; break; case RpcePacketType.Bind: case RpcePacketType.BindAck: case RpcePacketType.AlterContext: case RpcePacketType.AlterContextResp: case RpcePacketType.Auth3: //Windows RPC support version 5.0 only. //Bind fragment requires RPC ver 5.1. //We don't support it. throw new NotSupportedException("bind/bind_ack/alt_context/alt_context_resp/auth3 PDU fragment are not supported."); default: throw new InvalidOperationException("Speicified PDU doesn't support fragment and reassemble."); } return(reassembledPdu); }
/// <summary> /// Fragment a PDU into several PDUs by max_xmit_frag field.<para/> /// Only bind, bind_ack, alter_context, alter_context_response, /// request and response PDUs will be fragmented.<para/> /// Must call before sign/encrypt. /// </summary> /// <param name="context">RpceContext to fragment PDU</param> /// <param name="pdu"> /// A PDU to be fragmented. /// Only bind, bind_ack, alter_context, alter_context_response, /// request and response PDUs will be fragmented. /// </param> /// <returns>Fragmented PDUs.</returns> /// <exception cref="ArgumentNullException"> /// Thrown when pdu or context is null. /// </exception> /// <exception cref="NotSupportedException">Thrown when PDU PacketType isn't supported.</exception> public static RpceCoPdu[] FragmentPdu(RpceContext context, RpceCoPdu pdu) { if (context == null) { throw new ArgumentNullException("context"); } if (pdu == null) { throw new ArgumentNullException("pdu"); } if (pdu.frag_length > context.MaxTransmitFragmentSize) { int headerAndTrailerSize; byte[] stub; switch (pdu.PTYPE) { case RpcePacketType.Request: //Get stub of request PDU RpceCoRequestPdu requestPdu = pdu as RpceCoRequestPdu; if (requestPdu == null) { throw new ArgumentException("The pdu is not a valid RpceCoRequestPdu."); } headerAndTrailerSize = requestPdu.GetSize(); if (requestPdu.auth_verifier != null) { //length of auth_verifier headerAndTrailerSize += RpceUtility.AUTH_VERIFIER_SIZE; headerAndTrailerSize += requestPdu.auth_verifier.Value.auth_value.Length; //To keep stub always be padded to 16 bytes, and pdu doesnot exceed max transmit frag size. int stubLength = context.MaxTransmitFragmentSize - headerAndTrailerSize; headerAndTrailerSize += RpceUtility.Align(stubLength, RpceUtility.AUTH_PAD_LENGTH) - stubLength; //The beginning of the verification_trailer header MUST be 4-byte aligned //with respect to the beginning of the PDU. headerAndTrailerSize = RpceUtility.Align(headerAndTrailerSize, RpceUtility.STUB_PAD_LENGTH); } stub = requestPdu.stub ?? new byte[0]; //Fragment RpceCoRequestPdu[] requestFragmentPduList = FragmentPdu <RpceCoRequestPdu>( context, pdu, stub.Length, headerAndTrailerSize); for (int i = 0; i < requestFragmentPduList.Length; i++) { //SHOULD set the alloc_hint field in every PDU to //be the combined stub data length of all remaining fragment PDUs. requestFragmentPduList[i].alloc_hint = (uint)stub.Length; requestFragmentPduList[i].p_cont_id = requestPdu.p_cont_id; requestFragmentPduList[i].opnum = requestPdu.opnum; requestFragmentPduList[i].@object = requestPdu.@object; requestFragmentPduList[i].stub = ArrayUtility.SubArray( stub, 0, Math.Min(stub.Length, context.MaxTransmitFragmentSize - headerAndTrailerSize)); //For request and response PDUs, where the request and response PDUs are //part of a fragmented request or response and authentication is requested, //the sec_trailer structure MUST be present in every fragment of the request //or response. requestFragmentPduList[i].AppendAuthenticationVerifier(); requestFragmentPduList[i].SetLength(); stub = ArrayUtility.SubArray(stub, requestFragmentPduList[i].stub.Length); } return(requestFragmentPduList); case RpcePacketType.Response: //Get stub of response PDU RpceCoResponsePdu responsePdu = pdu as RpceCoResponsePdu; if (responsePdu == null) { throw new ArgumentException("The PDU is not a valid RpceCoResponsePdu"); } headerAndTrailerSize = responsePdu.GetSize(); if (responsePdu.auth_verifier != null) { //length of auth_verifier headerAndTrailerSize += RpceUtility.AUTH_VERIFIER_SIZE; headerAndTrailerSize += responsePdu.auth_verifier.Value.auth_value.Length; //To keep stub always be padded to 16 bytes, and pdu doesnot exceed max transmit frag size. int stubLength = context.MaxTransmitFragmentSize - headerAndTrailerSize; headerAndTrailerSize += RpceUtility.Align(stubLength, RpceUtility.AUTH_PAD_LENGTH) - stubLength; //The beginning of the verification_trailer header MUST be 4-byte aligned //with respect to the beginning of the PDU. headerAndTrailerSize = RpceUtility.Align(headerAndTrailerSize, RpceUtility.STUB_PAD_LENGTH); } stub = responsePdu.stub ?? new byte[0]; //Fragment RpceCoResponsePdu[] responseFragmentPduList = FragmentPdu <RpceCoResponsePdu>( context, pdu, stub.Length, headerAndTrailerSize); for (int i = 0; i < responseFragmentPduList.Length; i++) { //SHOULD set the alloc_hint field in every PDU to //be the combined stub data length of all remaining fragment PDUs. responseFragmentPduList[i].alloc_hint = (uint)stub.Length; responseFragmentPduList[i].p_cont_id = responsePdu.p_cont_id; responseFragmentPduList[i].cancel_count = responsePdu.cancel_count; responseFragmentPduList[i].reserved = responsePdu.reserved; responseFragmentPduList[i].stub = ArrayUtility.SubArray( stub, 0, Math.Min(stub.Length, context.MaxTransmitFragmentSize - headerAndTrailerSize)); //For request and response PDUs, where the request and response PDUs are //part of a fragmented request or response and authentication is requested, //the sec_trailer structure MUST be present in every fragment of the request //or response. responseFragmentPduList[i].AppendAuthenticationVerifier(); responseFragmentPduList[i].SetLength(); stub = ArrayUtility.SubArray(stub, responseFragmentPduList[i].stub.Length); } return(responseFragmentPduList); case RpcePacketType.Bind: case RpcePacketType.BindAck: case RpcePacketType.AlterContext: case RpcePacketType.AlterContextResp: case RpcePacketType.Auth3: //Windows RPC support version 5.0 only. //Bind fragment requires RPC ver 5.1. //We don't support it. throw new NotSupportedException("bind/bind_ack/alt_context/alt_context_resp/auth3 PDU fragment are not supported."); default: throw new InvalidOperationException("PDU PacketType isn't supported."); } } //If we cannot fragment the PDU return(new RpceCoPdu[] { pdu }); }
public RpceCoRequestPdu CreateCoRequestPdu( RpceServerSessionContext sessionContext, ushort opnum, uint callId, byte[] stub) { if (sessionContext == null) { throw new ArgumentNullException("sessionContext"); } if (stub == null) { stub = new byte[0]; } RpceCoRequestPdu requestPdu = new RpceCoRequestPdu(sessionContext); requestPdu.rpc_vers = sessionContext.RpcVersionMajor; requestPdu.rpc_vers_minor = sessionContext.RpcVersionMinor; requestPdu.PTYPE = RpcePacketType.Request; requestPdu.pfc_flags = RpceUtility.GeneratePfcFlags(sessionContext, RpcePacketType.Request); requestPdu.packed_drep.dataRepFormat = sessionContext.PackedDataRepresentationFormat; requestPdu.packed_drep.reserved = 0; requestPdu.call_id = callId; requestPdu.alloc_hint = (uint)stub.Length; requestPdu.p_cont_id = sessionContext.ContextIdentifier; requestPdu.opnum = opnum; requestPdu.stub = stub; requestPdu.AppendAuthenticationVerifier(); requestPdu.SetLength(); return requestPdu; }
/// <summary> /// Create a RpceCoRequestPdu. /// </summary> /// <param name="opnum"> /// Opnum of a method. /// </param> /// <param name="stub"> /// Request stub. /// </param> /// <returns> /// Created RpceCoRequestPdu, it's ok to be sent out if there's /// no modification to any field of the PDU. /// </returns> public RpceCoRequestPdu CreateCoRequestPdu( ushort opnum, byte[] stub) { if (stub == null) { stub = new byte[0]; } RpceCoRequestPdu requestPdu = new RpceCoRequestPdu(context); requestPdu.rpc_vers = context.RpcVersionMajor; requestPdu.rpc_vers_minor = context.RpcVersionMinor; requestPdu.PTYPE = RpcePacketType.Request; requestPdu.pfc_flags = RpceUtility.GeneratePfcFlags(context, RpcePacketType.Request); requestPdu.packed_drep.dataRepFormat = context.PackedDataRepresentationFormat; requestPdu.packed_drep.reserved = 0; requestPdu.call_id = ComputeNextCallId(); requestPdu.alloc_hint = (uint)stub.Length; requestPdu.p_cont_id = context.ContextIdentifier; requestPdu.opnum = opnum; requestPdu.stub = stub; requestPdu.AppendAuthenticationVerifier(); requestPdu.SetLength(); return requestPdu; }