/// <summary> /// The method EcDoRpcExt2 passes generic remote operation (ROP) commands to the server for processing within a Session Context. Each call can contain multiple ROP commands. /// </summary> /// <param name="pcxh">The unique value to be used as a CXH</param> /// <param name="pulFlags">Flags that tell the server how to build the rgbOut parameter.</param> /// <param name="rgbIn">This buffer contains the ROP request payload. </param> /// <param name="rgbOut">On Output, this parameter contains the response payload.</param> /// <param name="pcbOut">On Output, this parameter contains the size of response payload.</param> /// <param name="rgbAuxIn">This parameter contains an auxiliary payload buffer. </param> /// <param name="pcbAuxOut">On input, this parameter contains the maximum length of the rgbAuxOut buffer. On output, this parameter contains the size of the data to be returned in the rgbAuxOut buffer.</param> /// <param name="response">The ROP response returned in this RPC method and parsed by adapter.</param> /// <param name="responseSOHTable">The Response SOH Table returned by ROP call, provides information like object handle.</param> /// <param name="payloadCount">The count of payload that ROP response contains.</param> /// <param name="rgbAuxOut">The data of the output buffer rgbAuxOut. </param> /// <returns>If success, it return 0, else return the error code.</returns> public uint EcDoRpcExt2( ref IntPtr pcxh, PulFlags pulFlags, byte[] rgbIn, ref byte[] rgbOut, ref uint pcbOut, byte[] rgbAuxIn, ref uint pcbAuxOut, out IDeserializable response, ref List<List<uint>> responseSOHTable, out uint payloadCount, ref byte[] rgbAuxOut) { #region Variables uint inputPcbOutValue = pcbOut; uint inputPcbAuxOutValue = pcbAuxOut; IntPtr pcxhInput = pcxh; int pcxhValue = pcxh.ToInt32(); uint flags = (uint)pulFlags; payloadCount = 0; uint returnValue = 0; uint pulTransTime; uint inputROPSize = 0; if (rgbIn != null) { inputROPSize = (uint)rgbIn.Length; } else { rgbIn = new byte[0]; } uint inputAuxSize = 0; if (rgbAuxIn != null) { inputAuxSize = (uint)rgbAuxIn.Length; } else { rgbAuxIn = new byte[0]; } rgbOut = new byte[pcbOut]; response = null; #endregion IntPtr rgbInPtr = Marshal.AllocHGlobal(rgbIn.Length); IntPtr rgbAuxInPtr = Marshal.AllocHGlobal(rgbAuxIn.Length); try { Marshal.Copy(rgbIn, 0, rgbInPtr, rgbIn.Length); Marshal.Copy(rgbAuxIn, 0, rgbAuxInPtr, rgbAuxIn.Length); // Record whether the server is busy and needs to retry later. bool needRetry = false; int retryCount = 0; int maxRetryCount = int.Parse(Common.GetConfigurationPropertyValue("RetryCount", this.Site)); do { IntPtr rgbOutPtr = Marshal.AllocHGlobal((int)pcbOut); IntPtr rgbAuxOutPtr = Marshal.AllocHGlobal((int)pcbAuxOut); needRetry = false; returnValue = NativeMethods.EcDoRpcExt2( ref pcxhInput, ref flags, rgbInPtr, inputROPSize, rgbOutPtr, ref pcbOut, rgbAuxInPtr, inputAuxSize, rgbAuxOutPtr, ref pcbAuxOut, out pulTransTime); rgbOut = new byte[(int)pcbOut]; Marshal.Copy(rgbOutPtr, rgbOut, 0, (int)pcbOut); Marshal.FreeHGlobal(rgbOutPtr); rgbAuxOut = new byte[(int)pcbAuxOut]; Marshal.Copy(rgbAuxOutPtr, rgbAuxOut, 0, (int)pcbAuxOut); Marshal.FreeHGlobal(rgbAuxOutPtr); #region Verify requirements while EcDoRpcExt2 success if (returnValue == 0 && pcbOut > 0) { RPC_HEADER_EXT[] rpcHeaderExts; byte[][] rops; uint[][] serverHandleObjectsTables; byte[] rawData = new byte[pcbOut]; Array.Copy(rgbOut, rawData, pcbOut); int size; int actualSize; int payLoadSizeCount = 0; int payLoadActualSizeCount = 0; short flag; bool isCompressed = false; List<byte> actualData = new List<byte>(); List<short> flagValuesBeforeDecompressing = new List<short>(); #region Decompress or revert rgbOut if it is compressed or obfuscated do { flag = BitConverter.ToInt16(rawData, payLoadSizeCount + ConstValues.RpcHeaderExtVersionByteSize); flagValuesBeforeDecompressing.Add(flag); size = BitConverter.ToInt16(rawData, payLoadSizeCount + ConstValues.RpcHeaderExtVersionByteSize + ConstValues.RpcHeaderExtFlagsByteSize); actualSize = BitConverter.ToInt16(rawData, payLoadSizeCount + ConstValues.RpcHeaderExtVersionByteSize + ConstValues.RpcHeaderExtFlagsByteSize + ConstValues.RpcHeaderExtSizeByteSize); byte[] resultByte = new byte[actualSize + AdapterHelper.RPCHeaderExtlength]; Array.Copy(rawData, payLoadSizeCount, resultByte, 0, size + AdapterHelper.RPCHeaderExtlength); // Compressed (0x00000001) means the data that follows the RPC_HEADER_EXT is compressed. if ((flag & (short)RpcHeaderExtFlags.Compressed) == (short)RpcHeaderExtFlags.Compressed) { isCompressed = true; byte[] rgbOriOut = new byte[size + AdapterHelper.RPCHeaderExtlength]; Array.Copy(rawData, payLoadSizeCount, rgbOriOut, 0, size + AdapterHelper.RPCHeaderExtlength); bool decompressResult = false; try { resultByte = Common.DecompressStream(rgbOriOut); decompressResult = true; } catch (ArgumentException) { decompressResult = false; } catch (InvalidOperationException) { decompressResult = false; } this.VerifyCompressionAlgorithm(decompressResult, false, false, flag); this.VerifyDIRECT2EncodingAlgorithm(decompressResult); } // XorMagic(0x00000002) means that the data follows the RPC_HEADER_EXT has been obfuscated. if ((flag & (short)RpcHeaderExtFlags.XorMagic) == (short)RpcHeaderExtFlags.XorMagic) { byte[] rgbXorOut = null; byte[] rgbOriOut = new byte[size + AdapterHelper.RPCHeaderExtlength]; Array.Copy(rawData, payLoadSizeCount, rgbOriOut, 0, size + AdapterHelper.RPCHeaderExtlength); // According to the Open Specification, every byte of the data to be obfuscated has XOR applied with the value 0xA5. bool obfuscationResult = false; rgbXorOut = Common.XOR(rgbOriOut); if (rgbXorOut != null) { obfuscationResult = true; } Array.Copy(rawData, payLoadSizeCount, resultByte, 0, AdapterHelper.RPCHeaderExtlength); Array.Copy(rgbXorOut, 8, resultByte, AdapterHelper.RPCHeaderExtlength, actualSize); // The first false means current method is EcDoRpcExt2 // The second false means current field is rgbOut this.VerifyObfuscationAlgorithm(obfuscationResult, false, false, flag); } actualData.AddRange(resultByte); payLoadSizeCount += size + AdapterHelper.RPCHeaderExtlength; payLoadActualSizeCount += actualSize + AdapterHelper.RPCHeaderExtlength; } while ((flag & (short)RpcHeaderExtFlags.Last) != (short)RpcHeaderExtFlags.Last); rgbOut = actualData.ToArray(); #endregion #region Resize rgbOut if (rgbOut != null) { if (isCompressed) { Array.Resize<byte>(ref rgbOut, payLoadActualSizeCount + 1); } else { Array.Resize<byte>(ref rgbOut, (int)pcbOut); } } #endregion this.ParseResponseBuffer(rgbOut, out rpcHeaderExts, out rops, out serverHandleObjectsTables); #region Verify RPC_HEADER_EXTs in rgbOut buffer for (int i = 0; i < rpcHeaderExts.Length; i++) { this.VerifyRPCHeaderExt(flagValuesBeforeDecompressing, rpcHeaderExts[i], false, false, i == rpcHeaderExts.Length - 1); } this.VerifyMultiRPCHeader(rpcHeaderExts.Length); #endregion #region Deserialize Rops List<IDeserializable> responseRops = new List<IDeserializable>(); int ropID = 0; if (rops != null) { // Verify the length of payload in the rgbOut this.VerifyPayloadLengthResponse(rops); // Only contains one ROP response if (rops.Length == 1) { ropID = rops[0][0]; if (ropID == (int)RopId.RopBackoff) { needRetry = true; // Revert the value of variables whose type is a reference type and used by method EcDoRpcExt2. pcxh = pcxhInput; flags = (uint)pulFlags; pcbOut = inputPcbOutValue; pcbAuxOut = inputPcbAuxOutValue; // Wait a period of time before retrying since the server is busy now. System.Threading.Thread.Sleep(int.Parse(Common.GetConfigurationPropertyValue("WaitTime", this.Site))); retryCount++; continue; } IDeserializable ropRes = null; RopDeserializer.Deserialize(rops[0], ref ropRes); payloadCount = 1; // True means RopDeserializer.Deserialize method is successful(Because there is no exception thrown and code can arrive here). // 1 means one ROP response this.VerifyIsRopResponse(true, 1); responseRops.Add(ropRes); } else { // Contains more than one ROP response ropID = rops[0][0]; foreach (byte[] rop in rops) { if (rop[0] == (byte)RopId.RopBackoff) { needRetry = true; // Revert the value of variables whose type is a reference type and used by method EcDoRpcExt2. pcxh = pcxhInput; flags = (uint)pulFlags; pcbOut = inputPcbOutValue; pcbAuxOut = inputPcbAuxOutValue; // Wait a period of time before retrying since the server is busy now. System.Threading.Thread.Sleep(int.Parse(Common.GetConfigurationPropertyValue("WaitTime", this.Site))); retryCount++; break; } IDeserializable ropRes = null; RopDeserializer.Deserialize(rop, ref ropRes); payloadCount = (uint)rops.Length; // True means RopDeserializer.Deserialize method is successful(Because there is no exception thrown and code can arrive here). this.VerifyIsRopResponse(true, rops.Length); switch (rop[0]) { // RopReadStream Response Buffer. case (int)RopId.RopReadStream: RopReadStreamResponse readStreamResponse = (RopReadStreamResponse)ropRes; responseRops.Add(readStreamResponse); break; // RopQueryRows Response Buffer. case (int)RopId.RopQueryRows: RopQueryRowsResponse queryRowsResponse = (RopQueryRowsResponse)ropRes; responseRops.Add(queryRowsResponse); break; // RopFastTransferSourceGetBuffer Response Buffer. case (int)RopId.RopFastTransferSourceGetBuffer: RopFastTransferSourceGetBufferResponse bufferResponse = (RopFastTransferSourceGetBufferResponse)ropRes; responseRops.Add(bufferResponse); break; default: throw new InvalidCastException("The returned ROP ID is {0}.", ropID); } } switch (ropID) { // RopReadStream Response Buffer. case (int)RopId.RopReadStream: for (int i = 0; i < responseRops.Count; i++) { for (int j = i + 1; j < responseRops.Count; j++) { RopReadStreamResponse readStreamResponse0 = (RopReadStreamResponse)responseRops[i]; RopReadStreamResponse readStreamResponse1 = (RopReadStreamResponse)responseRops[j]; this.VerifyRopReadStreamResponse(readStreamResponse0.DataSize, readStreamResponse1.DataSize, responseRops.Count); } } break; // RopQueryRows Response Buffer. case (int)RopId.RopQueryRows: for (int i = 0; i < responseRops.Count; i++) { for (int j = i + 1; j < responseRops.Count; j++) { RopQueryRowsResponse queryRowsResponse0 = (RopQueryRowsResponse)responseRops[i]; RopQueryRowsResponse queryRowsResponse1 = (RopQueryRowsResponse)responseRops[j]; this.VerifyRopQueryRowsResponse(queryRowsResponse0.RowCount, queryRowsResponse1.RowCount, responseRops.Count); } } break; // RopFastTransferSourceGetBuffer Response Buffer. case (int)RopId.RopFastTransferSourceGetBuffer: this.VerifyRopFastTransferSourceGetBufferResponse(responseRops.Count); break; default: throw new InvalidCastException("The returned ROP ID is {0}.", ropID); } } if (responseRops != null) { if (responseRops.Count > 0) { response = responseRops[0]; } } else { response = null; } } #endregion #region Deserialize SOH if (needRetry) { continue; } responseSOHTable = new List<List<uint>>(); // Deserialize serverHandleObjectsTables if (serverHandleObjectsTables != null) { foreach (uint[] serverHandleObjectsTable in serverHandleObjectsTables) { List<uint> serverHandleObjectList = new List<uint>(); foreach (uint serverHandleObject in serverHandleObjectsTable) { serverHandleObjectList.Add(serverHandleObject); } responseSOHTable.Add(serverHandleObjectList); } } #endregion #region Parse rgbAuxOut #region Capture code #region Verify rgbAuxOut this.VerifyIsRgbSupportedOnEcDoRpcExt2(pcbAuxOut); List<short> flagValues = new List<short>(); if (pcbAuxOut != 0) { isCompressed = false; // Decompress or revert if the rgbAuxOut is compressed or obfuscated flag = BitConverter.ToInt16(rgbAuxOut, ConstValues.RpcHeaderExtVersionByteSize); size = BitConverter.ToInt16(rgbAuxOut, ConstValues.RpcHeaderExtFlagsByteSize + ConstValues.RpcHeaderExtSizeByteSize) + AdapterHelper.RPCHeaderExtlength; actualSize = BitConverter.ToInt16(rgbAuxOut, ConstValues.RpcHeaderExtSizeByteSize + ConstValues.RpcHeaderExtFlagsByteSize + ConstValues.RpcHeaderExtVersionByteSize) + AdapterHelper.RPCHeaderExtlength; flagValues.Add(flag); // Decompress or revert rgbAuxOut if it is compressed or obfuscated // Compressed (0x00000001) means the data that follows the RPC_HEADER_EXT is compressed. if ((flag & (short)RpcHeaderExtFlags.Compressed) == (short)RpcHeaderExtFlags.Compressed) { isCompressed = true; byte[] rgbOriAuxOut = new byte[size]; Array.Copy(rgbAuxOut, 0, rgbOriAuxOut, 0, size); bool decompressResult = false; try { rgbAuxOut = Common.DecompressStream(rgbOriAuxOut); decompressResult = true; } catch (ArgumentException) { decompressResult = false; } catch (InvalidOperationException) { decompressResult = false; } // False means current method is EcDoRpcExt2 // True means current field is rgbAuxOut this.VerifyCompressionAlgorithm(decompressResult, false, true, flag); this.VerifyDIRECT2EncodingAlgorithm(decompressResult); } // XorMagic(0x00000002) means that the data follows the RPC_HEADER_EXT has been obfuscated. if ((flag & (short)RpcHeaderExtFlags.XorMagic) == (short)RpcHeaderExtFlags.XorMagic) { byte[] rgbXorAuxOut = null; byte[] rgbOriAuxOut = new byte[size]; Array.Copy(rgbAuxOut, AdapterHelper.RPCHeaderExtlength, rgbOriAuxOut, 0, size); // Every byte of the data to be obfuscated has XOR applied with the value 0xA5. bool obfuscationResult = false; rgbXorAuxOut = Common.XOR(rgbOriAuxOut); if (rgbXorAuxOut != null) { obfuscationResult = true; } Array.Copy(rgbXorAuxOut, 0, rgbAuxOut, AdapterHelper.RPCHeaderExtlength, size - AdapterHelper.RPCHeaderExtlength); this.VerifyObfuscationAlgorithm(obfuscationResult, false, true, flag); } if (rgbAuxOut != null) { if (isCompressed) { Array.Resize<byte>(ref rgbAuxOut, actualSize); } else { Array.Resize<byte>(ref rgbAuxOut, (int)pcbAuxOut); } } #region Verify RPC header ExtendedBuffer[] buffer = ExtractExtendedBuffer(rgbAuxOut); this.VerifyRPCHeaderExt(flagValues, buffer[0].Header, false, true, true); #endregion List<AUX_SERVER_TOPOLOGY_STRUCTURE> rgbAuxOutValue = this.ParseRgbAuxOut(rgbAuxOut); this.VerifyRgbAuxOutOnEcDoRpcExt2(rgbAuxOutValue); } #endregion #endregion Capture code #endregion } // Verify method EcDoRpcExt2. this.VerifyEcDoRpcExt2(pcxhValue, pcxhInput.ToInt32(), pcbOut, pcbAuxOut, inputPcbOutValue, rgbOut, inputPcbAuxOutValue, rgbAuxOut, returnValue, (uint)pulFlags, flags); #endregion } while (needRetry && retryCount < maxRetryCount); if (needRetry && retryCount == maxRetryCount) { Site.Assert.Fail("Server is still busy after retrying {0} times which is defined in the common configuration file and the returned ROP is RopBackOff.", retryCount); } } catch (SEHException e) { // Uses try...catch... code structure to get the error code of RPC call returnValue = NativeMethods.RpcExceptionCode(e); Site.Log.Add(LogEntryKind.Comment, "EcDoRpcExt2 throws exception, system error code is {0}, the error message is: {1}", returnValue, (new Win32Exception((int)returnValue)).ToString()); } finally { Marshal.FreeHGlobal(rgbInPtr); Marshal.FreeHGlobal(rgbAuxInPtr); } return returnValue; }
/// <summary> /// The method EcDoRpcExt2 passes generic remote operation (ROP) commands to the server for processing within a Session Context. Each call can contain multiple ROP commands. /// </summary> /// <param name="pcxh">The unique value points to a CXH.</param> /// <param name="pulFlags">Flags that tell the server how to build the rgbOut parameter.</param> /// <param name="rgbIn">This buffer contains the ROP request payload. </param> /// <param name="pcbOut">On input, this parameter contains the maximum size of the rgbOut buffer. On output, this parameter contains the size of the ROP response payload.</param> /// <param name="rgbAuxIn">This parameter contains an auxiliary payload buffer. </param> /// <param name="pcbAuxOut">On input, this parameter contains the maximum length of the rgbAuxOut buffer. On output, this parameter contains the size of the data to be returned in the rgbAuxOut buffer.</param> /// <param name="response">The ROP response returned in this RPC method and parsed by adapter.</param> /// <param name="responseSOHTable">The Response SOH Table returned by ROP call, provides information like object handle.</param> /// <returns>If success, it returns 0, else returns the error code specified in the Open Specification.</returns> public uint EcDoRpcExt2( ref IntPtr pcxh, PulFlags pulFlags, byte[] rgbIn, ref uint pcbOut, byte[] rgbAuxIn, ref uint pcbAuxOut, out IDeserializable response, ref List<List<uint>> responseSOHTable) { byte[] rgbOut = new byte[pcbOut]; byte[] rgbAuxOut = new byte[pcbAuxOut]; uint payloadCount = 0; return this.EcDoRpcExt2(ref pcxh, pulFlags, rgbIn, ref rgbOut, ref pcbOut, rgbAuxIn, ref pcbAuxOut, out response, ref responseSOHTable, out payloadCount, ref rgbAuxOut); }