/// <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 }); }