/// <summary> /// Receive and reassemble PDU. /// </summary> /// <param name="timeout">timeout</param> /// <returns>PDU</returns> private RpcePdu ReceiveAndReassemblePdu(TimeSpan timeout) { RpcePdu receivedPdu = rpceClient.ExpectPdu(timeout); RpceCoPdu receivedCoPdu = receivedPdu as RpceCoPdu; if (receivedCoPdu == null) { return(receivedPdu); } List <RpceCoPdu> pduList = new List <RpceCoPdu>(); pduList.Add(receivedCoPdu); while ((receivedCoPdu.pfc_flags & RpceCoPfcFlags.PFC_LAST_FRAG) == 0) { receivedPdu = rpceClient.ExpectPdu(timeout); receivedCoPdu = receivedPdu as RpceCoPdu; if (receivedCoPdu == null) { throw new InvalidOperationException("CL PDU received inside a connection."); } pduList.Add(receivedCoPdu); } return(RpceUtility.ReassemblePdu(rpceClient.Context, pduList.ToArray())); }
/// <summary> /// Fragment and send PDU. /// </summary> /// <param name="sessionContext">Context of the RPCE session</param> /// <param name="pdu">PDU to Fragment and send.</param> private void FragmentAndSendPdu( RpceServerSessionContext sessionContext, RpceCoPdu pdu) { if (pdu.PTYPE == RpcePacketType.Bind || pdu.PTYPE == RpcePacketType.BindAck || pdu.PTYPE == RpcePacketType.AlterContext || pdu.PTYPE == RpcePacketType.AlterContextResp || pdu.PTYPE == RpcePacketType.Auth3) { pdu.InitializeAuthenticationToken(); pdu.SetLength(); foreach (RpceCoPdu fragPdu in RpceUtility.FragmentPdu(sessionContext, pdu)) { rpceServer.SendPdu(sessionContext, fragPdu); } } else { foreach (RpceCoPdu fragPdu in RpceUtility.FragmentPdu(sessionContext, pdu)) { fragPdu.InitializeAuthenticationToken(); rpceServer.SendPdu(sessionContext, fragPdu); } } }
/// <summary> /// Decode CO PDU. /// </summary> /// <param name="context">The context that received data.</param> /// <param name="messageBytes">bytes received</param> /// <param name="consumedLength">num of bytes consumed in processing</param> /// <param name="expectedLength">num of bytes expected if the bytes is not enough</param> /// <returns>pdus</returns> internal static RpceCoPdu[] DecodeCoPdu( RpceContext context, byte[] messageBytes, out int consumedLength, out int expectedLength) { List <RpceCoPdu> pduList = new List <RpceCoPdu>(); consumedLength = 0; expectedLength = 0; while (consumedLength < messageBytes.Length) { if ((messageBytes.Length - consumedLength) < RpceUtility.CO_PDU_HEADER_SIZE) { expectedLength = RpceUtility.CO_PDU_HEADER_SIZE; break; } //#4 byte is drep uint dataRepresentation = BitConverter.ToUInt32( messageBytes, consumedLength + RpceUtility.DREP_FIELD_OFFSET); //#8 byte is frag_length ushort fragmentLength = BitConverter.ToUInt16( messageBytes, consumedLength + RpceUtility.FRAG_LENGTH_FIELD_OFFSET); if ((dataRepresentation & 0x0000FFFFU) != NativeMethods.NDR_LOCAL_DATA_REPRESENTATION) { fragmentLength = EndianUtility.ReverseByteOrder(fragmentLength); } if ((messageBytes.Length - consumedLength) < fragmentLength) { expectedLength = fragmentLength; break; } byte[] pduBytes = new byte[fragmentLength]; Buffer.BlockCopy(messageBytes, consumedLength, pduBytes, 0, fragmentLength); RpceCoPdu pdu = RpceUtility.DecodeCoPdu(context, pduBytes); pduList.Add(pdu); consumedLength += fragmentLength; } return(pduList.ToArray()); }
/// <summary> /// Decode CO PDU. /// </summary> /// <param name="context">The context that received data.</param> /// <param name="messageBytes">bytes received</param> /// <param name="consumedLength">num of bytes consumed in processing</param> /// <param name="expectedLength">num of bytes expected if the bytes is not enough</param> /// <returns>pdus</returns> internal static RpceCoPdu[] DecodeCoPdu( RpceContext context, byte[] messageBytes, out int consumedLength, out int expectedLength) { List <RpceCoPdu> pduList = new List <RpceCoPdu>(); consumedLength = 0; expectedLength = 0; while (consumedLength < messageBytes.Length) { if ((messageBytes.Length - consumedLength) < RpceUtility.CO_PDU_HEADER_SIZE) { expectedLength = RpceUtility.CO_PDU_HEADER_SIZE; break; } //#8 byte is frag_length ushort fragmentLength = BitConverter.ToUInt16( messageBytes, consumedLength + RpceUtility.FRAG_LENGTH_FIELD_OFFSET); if ((messageBytes.Length - consumedLength) < fragmentLength) { expectedLength = fragmentLength; break; } byte[] pduBytes = new byte[fragmentLength]; Buffer.BlockCopy(messageBytes, consumedLength, pduBytes, 0, fragmentLength); RpceCoPdu pdu = RpceUtility.DecodeCoPdu(context, pduBytes); pduList.Add(pdu); consumedLength += fragmentLength; } return(pduList.ToArray()); }
/// <summary> /// Fragment a PDU /// </summary> /// <param name="context">RpceContext to fragment PDU</param> /// <typeparam name="T">Type of PDU to fragment</typeparam> /// <param name="pdu">PDU to fragment</param> /// <param name="stubLength">length of data to be fragmented</param> /// <param name="extraLength">length of header and trailer</param> /// <returns>Fragmented PDUs</returns> private static T[] FragmentPdu <T>( RpceContext context, RpceCoPdu pdu, int stubLength, int extraLength) where T : RpceCoPdu { List <T> fragmentPduList = new List <T>(); bool isFirstFragment = true; while (stubLength > 0) { T fragmentPdu = (T)Activator.CreateInstance(typeof(T), context); fragmentPdu.rpc_vers = pdu.rpc_vers; fragmentPdu.rpc_vers_minor = pdu.rpc_vers_minor; fragmentPdu.PTYPE = pdu.PTYPE; fragmentPdu.pfc_flags = pdu.pfc_flags & ~(RpceCoPfcFlags.PFC_FIRST_FRAG | RpceCoPfcFlags.PFC_LAST_FRAG); fragmentPdu.packed_drep = pdu.packed_drep; fragmentPdu.frag_length = (ushort)Math.Min(context.MaxTransmitFragmentSize, stubLength + extraLength); fragmentPdu.auth_length = pdu.auth_length; fragmentPdu.call_id = pdu.call_id; if (isFirstFragment) { fragmentPdu.pfc_flags |= RpceCoPfcFlags.PFC_FIRST_FRAG; isFirstFragment = false; } if (stubLength <= (context.MaxTransmitFragmentSize - extraLength)) { fragmentPdu.pfc_flags |= RpceCoPfcFlags.PFC_LAST_FRAG; } stubLength -= (context.MaxTransmitFragmentSize - extraLength); fragmentPduList.Add(fragmentPdu); } return(fragmentPduList.ToArray()); }
/// <summary> /// Receive and reassemble PDU. /// </summary> /// <param name="timeout">Timeout of receiving PDU</param> /// <param name="sessionContext">Context of the RPCE session</param> /// <returns>Received PDU</returns> private RpcePdu ReceiveAndReassemblePdu( TimeSpan timeout, ref RpceServerSessionContext sessionContext) { RpcePdu receivedPdu; bool expectAny = sessionContext == null; WaitForEvent: if (expectAny) { sessionContext = null; } EventType eventType = rpceServer.ExpectEvent(timeout, ref sessionContext, out receivedPdu); if (this.registeredInterfaceList.Count > 0) { // auto accept connect/bind/disconnect if (eventType == EventType.Connected) { RpcIfMatchFunc matchFunc = delegate(RpcIf rpcIf) { for (int i = 0; i < this.registeredInterfaceList.Count; i++) { if (this.registeredInterfaceList[i].Equals(rpcIf)) { return(true); } } return(false); }; InternalExpectBind(matchFunc, timeout, ref sessionContext); goto WaitForEvent; } else if (eventType == EventType.Disconnected) { goto WaitForEvent; } else { // it is a PDU. } } else if (eventType != EventType.ReceivedPacket) { throw new InvalidOperationException( string.Format("Unexpected event ({0}) received.", eventType)); } RpceCoPdu receivedCoPdu = receivedPdu as RpceCoPdu; if (receivedCoPdu == null) { return(receivedPdu); } List <RpceCoPdu> pduList = new List <RpceCoPdu>(); pduList.Add(receivedCoPdu); while ((receivedCoPdu.pfc_flags & RpceCoPfcFlags.PFC_LAST_FRAG) == 0) { receivedPdu = rpceServer.ExpectPdu(timeout, ref sessionContext); receivedCoPdu = receivedPdu as RpceCoPdu; if (receivedCoPdu == null) { throw new InvalidOperationException("CL PDU received inside a connection."); } pduList.Add(receivedCoPdu); } return(RpceUtility.ReassemblePdu(sessionContext, pduList.ToArray())); }
/// <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; }
/// <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> /// 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 }); }