/// <summary> /// Sends an OpenSecureChannel response. /// </summary> private void SendOpenSecureChannelResponse(uint requestId, ChannelToken token, OpenSecureChannelRequest request) { Utils.Trace("Channel {0}: SendOpenSecureChannelResponse()", ChannelId); OpenSecureChannelResponse response = new OpenSecureChannelResponse(); response.ResponseHeader.RequestHandle = request.RequestHeader.RequestHandle; response.ResponseHeader.Timestamp = DateTime.UtcNow; response.SecurityToken.ChannelId = token.ChannelId; response.SecurityToken.TokenId = token.TokenId; response.SecurityToken.CreatedAt = token.CreatedAt; response.SecurityToken.RevisedLifetime = (uint)token.Lifetime; response.ServerNonce = token.ServerNonce; byte[] buffer = BinaryEncoder.EncodeMessage(response, Quotas.MessageContext); BufferCollection chunksToSend = WriteAsymmetricMessage( TcpMessageType.Open, requestId, ServerCertificate, ClientCertificate, new ArraySegment <byte>(buffer, 0, buffer.Length)); // write the message to the server. try { BeginWriteMessage(chunksToSend, null); chunksToSend = null; } finally { if (chunksToSend != null) { chunksToSend.Release(BufferManager, "SendOpenSecureChannelResponse"); } } }
/// <summary> /// Saves an intermediate chunk for an incoming message. /// </summary> protected void SaveIntermediateChunk(uint requestId, ArraySegment <byte> chunk) { if (m_partialMessageChunks == null) { m_partialMessageChunks = new BufferCollection(); } if (m_partialRequestId != requestId) { if (m_partialMessageChunks.Count > 0) { Utils.Trace("WARNING - Discarding unprocessed message chunks for Request #{0}", m_partialRequestId); } m_partialMessageChunks.Release(BufferManager, "SaveIntermediateChunk"); } if (requestId != 0) { m_partialRequestId = requestId; m_partialMessageChunks.Add(chunk); } }
/// <summary> /// Secures the message using the security token. /// </summary> protected BufferCollection WriteSymmetricMessage( uint messageType, uint requestId, ChannelToken token, object messageBody, bool isRequest, out bool limitsExceeded) { limitsExceeded = false; bool success = false; BufferCollection chunksToProcess = null; try { // calculate chunk sizes. int maxCipherTextSize = SendBufferSize - TcpMessageLimits.SymmetricHeaderSize; int maxCipherBlocks = maxCipherTextSize / EncryptionBlockSize; int maxPlainTextSize = maxCipherBlocks * EncryptionBlockSize; int maxPayloadSize = maxPlainTextSize - SymmetricSignatureSize - 1 - TcpMessageLimits.SequenceHeaderSize; int headerSize = TcpMessageLimits.SymmetricHeaderSize + TcpMessageLimits.SequenceHeaderSize; // write the body to stream. ArraySegmentStream ostrm = new ArraySegmentStream( BufferManager, SendBufferSize, headerSize, maxPayloadSize); // check for encodeable body. IEncodeable encodeable = messageBody as IEncodeable; if (encodeable != null) { // debug code used to verify that message aborts are handled correctly. // int maxMessageSize = Quotas.MessageContext.MaxMessageSize; // Quotas.MessageContext.MaxMessageSize = Int32.MaxValue; BinaryEncoder.EncodeMessage(encodeable, ostrm, Quotas.MessageContext); // Quotas.MessageContext.MaxMessageSize = maxMessageSize; } // check for raw bytes. ArraySegment <byte>?rawBytes = messageBody as ArraySegment <byte>?; if (rawBytes != null) { BinaryEncoder encoder = new BinaryEncoder(ostrm, Quotas.MessageContext); encoder.WriteRawBytes(rawBytes.Value.Array, rawBytes.Value.Offset, rawBytes.Value.Count); encoder.Close(); } chunksToProcess = ostrm.GetBuffers("WriteSymmetricMessage"); // ensure there is at least one chunk. if (chunksToProcess.Count == 0) { byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "WriteSymmetricMessage"); chunksToProcess.Add(new ArraySegment <byte>(buffer, 0, 0)); } BufferCollection chunksToSend = new BufferCollection(chunksToProcess.Capacity); int messageSize = 0; for (int ii = 0; ii < chunksToProcess.Count; ii++) { ArraySegment <byte> chunkToProcess = chunksToProcess[ii]; // nothing more to do if limits exceeded. if (limitsExceeded) { BufferManager.ReturnBuffer(chunkToProcess.Array, "WriteSymmetricMessage"); continue; } MemoryStream strm = new MemoryStream(chunkToProcess.Array, 0, SendBufferSize); BinaryEncoder encoder = new BinaryEncoder(strm, Quotas.MessageContext); // check if the message needs to be aborted. if (MessageLimitsExceeded(isRequest, messageSize + chunkToProcess.Count - headerSize, ii + 1)) { encoder.WriteUInt32(null, messageType | TcpMessageType.Abort); // replace the body in the chunk with an error message. BinaryEncoder errorEncoder = new BinaryEncoder( chunkToProcess.Array, chunkToProcess.Offset, chunkToProcess.Count, Quotas.MessageContext); WriteErrorMessageBody(errorEncoder, (isRequest) ? StatusCodes.BadRequestTooLarge : StatusCodes.BadResponseTooLarge); int size = errorEncoder.Close(); chunkToProcess = new ArraySegment <byte>(chunkToProcess.Array, chunkToProcess.Offset, size); limitsExceeded = true; } // check if the message is complete. else if (ii == chunksToProcess.Count - 1) { encoder.WriteUInt32(null, messageType | TcpMessageType.Final); } // more chunks to follow. else { encoder.WriteUInt32(null, messageType | TcpMessageType.Intermediate); } int count = 0; count += TcpMessageLimits.SequenceHeaderSize; count += chunkToProcess.Count; count += SymmetricSignatureSize; // calculate the padding. int padding = 0; if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { // reserve one byte for the padding size. count++; if (count % EncryptionBlockSize != 0) { padding = EncryptionBlockSize - (count % EncryptionBlockSize); } count += padding; } count += TcpMessageLimits.SymmetricHeaderSize; encoder.WriteUInt32(null, (uint)count); encoder.WriteUInt32(null, ChannelId); encoder.WriteUInt32(null, token.TokenId); uint sequenceNumber = GetNewSequenceNumber(); encoder.WriteUInt32(null, sequenceNumber); encoder.WriteUInt32(null, requestId); // skip body. strm.Seek(chunkToProcess.Count, SeekOrigin.Current); // update message size count. messageSize += chunkToProcess.Count; // write padding. if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { for (int jj = 0; jj <= padding; jj++) { encoder.WriteByte(null, (byte)padding); } } if (SecurityMode != MessageSecurityMode.None) { // calculate and write signature. byte[] signature = Sign(token, new ArraySegment <byte>(chunkToProcess.Array, 0, encoder.Position), isRequest); if (signature != null) { encoder.WriteRawBytes(signature, 0, signature.Length); } } if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { // encrypt the data. ArraySegment <byte> dataToEncrypt = new ArraySegment <byte>(chunkToProcess.Array, TcpMessageLimits.SymmetricHeaderSize, encoder.Position - TcpMessageLimits.SymmetricHeaderSize); Encrypt(token, dataToEncrypt, isRequest); } // add the header into chunk. chunksToSend.Add(new ArraySegment <byte>(chunkToProcess.Array, 0, encoder.Position)); } // ensure the buffers don't get cleaned up on exit. success = true; return(chunksToSend); } finally { if (!success) { if (chunksToProcess != null) { chunksToProcess.Release(BufferManager, "WriteSymmetricMessage"); } } } }
/// <summary> /// Sends a OpenSecureChannel request. /// </summary> protected BufferCollection WriteAsymmetricMessage( uint messageType, uint requestId, X509Certificate2 senderCertificate, X509Certificate2Collection senderCertificateChain, X509Certificate2 receiverCertificate, ArraySegment <byte> messageBody) { bool success = false; BufferCollection chunksToSend = new BufferCollection(); byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "WriteAsymmetricMessage"); try { BinaryEncoder encoder = new BinaryEncoder(buffer, 0, SendBufferSize, Quotas.MessageContext); int headerSize = 0; if (senderCertificateChain != null && senderCertificateChain.Count > 0) { int senderCertificateSize = 0; WriteAsymmetricMessageHeader( encoder, messageType | TcpMessageType.Intermediate, ChannelId, SecurityPolicyUri, senderCertificate, senderCertificateChain, receiverCertificate, out senderCertificateSize); headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate, senderCertificateSize); } else { WriteAsymmetricMessageHeader( encoder, messageType | TcpMessageType.Intermediate, ChannelId, SecurityPolicyUri, senderCertificate, receiverCertificate); headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate); } int signatureSize = GetAsymmetricSignatureSize(senderCertificate); // save the header. ArraySegment <byte> header = new ArraySegment <byte>(buffer, 0, headerSize); // calculate the space available. int plainTextBlockSize = GetPlainTextBlockSize(receiverCertificate); int cipherTextBlockSize = GetCipherTextBlockSize(receiverCertificate); int maxCipherTextSize = SendBufferSize - headerSize; int maxCipherBlocks = maxCipherTextSize / cipherTextBlockSize; int maxPlainTextSize = maxCipherBlocks * plainTextBlockSize; int maxPayloadSize = maxPlainTextSize - signatureSize - 1 - TcpMessageLimits.SequenceHeaderSize; int bytesToWrite = messageBody.Count; int startOfBytes = messageBody.Offset; while (bytesToWrite > 0) { encoder.WriteUInt32(null, GetNewSequenceNumber()); encoder.WriteUInt32(null, requestId); int payloadSize = bytesToWrite; if (payloadSize > maxPayloadSize) { payloadSize = maxPayloadSize; } else { UpdateMessageType(buffer, 0, messageType | TcpMessageType.Final); } // write the message body. encoder.WriteRawBytes(messageBody.Array, messageBody.Offset + startOfBytes, payloadSize); // calculate the amount of plain text to encrypt. int plainTextSize = encoder.Position - headerSize + signatureSize; // calculate the padding. int padding = 0; if (SecurityMode != MessageSecurityMode.None) { if (CertificateFactory.GetRSAPublicKeySize(receiverCertificate) <= TcpMessageLimits.KeySizeExtraPadding) { // need to reserve one byte for the padding. plainTextSize++; if (plainTextSize % plainTextBlockSize != 0) { padding = plainTextBlockSize - (plainTextSize % plainTextBlockSize); } encoder.WriteByte(null, (byte)padding); for (int ii = 0; ii < padding; ii++) { encoder.WriteByte(null, (byte)padding); } } else { // need to reserve one byte for the padding. plainTextSize++; // need to reserve one byte for the extrapadding. plainTextSize++; if (plainTextSize % plainTextBlockSize != 0) { padding = plainTextBlockSize - (plainTextSize % plainTextBlockSize); } byte paddingSize = (byte)(padding & 0xff); byte extraPaddingByte = (byte)((padding >> 8) & 0xff); encoder.WriteByte(null, paddingSize); for (int ii = 0; ii < padding; ii++) { encoder.WriteByte(null, (byte)paddingSize); } encoder.WriteByte(null, extraPaddingByte); } // update the plaintext size with the padding size. plainTextSize += padding; } // calculate the number of block to encrypt. int encryptedBlocks = plainTextSize / plainTextBlockSize; // calculate the size of the encrypted data. int cipherTextSize = encryptedBlocks * cipherTextBlockSize; // put the message size after encryption into the header. UpdateMessageSize(buffer, 0, cipherTextSize + headerSize); // write the signature. byte[] signature = Sign(new ArraySegment <byte>(buffer, 0, encoder.Position), senderCertificate); if (signature != null) { encoder.WriteRawBytes(signature, 0, signature.Length); } int messageSize = encoder.Close(); // encrypt the data. ArraySegment <byte> encryptedBuffer = Encrypt( new ArraySegment <byte>(buffer, headerSize, messageSize - headerSize), header, receiverCertificate); // check for math errors due to code bugs. if (encryptedBuffer.Count != cipherTextSize + headerSize) { throw new InvalidDataException("Actual message size is not the same as the predicted message size."); } // save chunk. chunksToSend.Add(encryptedBuffer); bytesToWrite -= payloadSize; startOfBytes += payloadSize; // reset the encoder to write the plaintext for the next chunk into the same buffer. if (bytesToWrite > 0) { MemoryStream ostrm = new MemoryStream(buffer, 0, SendBufferSize); ostrm.Seek(header.Count, SeekOrigin.Current); encoder = new BinaryEncoder(ostrm, Quotas.MessageContext); } } // ensure the buffers don't get clean up on exit. success = true; return(chunksToSend); } catch (Exception ex) { throw new Exception("Could not write async message", ex); } finally { BufferManager.ReturnBuffer(buffer, "WriteAsymmetricMessage"); if (!success) { chunksToSend.Release(BufferManager, "WriteAsymmetricMessage"); } } }
/// <summary> /// Processes a request message. /// </summary> private bool ProcessRequestMessage(uint messageType, ArraySegment <byte> messageChunk) { // validate the channel state. if (State != TcpChannelState.Open) { ForceChannelFault(StatusCodes.BadTcpMessageTypeInvalid, "Client sent an unexpected request message."); return(false); } // validate security on the message. ChannelToken token = null; uint requestId = 0; uint sequenceNumber = 0; ArraySegment <byte> messageBody; try { messageBody = ReadSymmetricMessage(messageChunk, true, out token, out requestId, out sequenceNumber); // check for replay attacks. if (!VerifySequenceNumber(sequenceNumber, "ProcessRequestMessage")) { throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid); } if (token == CurrentToken && PreviousToken != null && !PreviousToken.Expired) { Utils.Trace("Server Revoked Token. ChannelId={1}, TokenId={0}", PreviousToken.TokenId, PreviousToken.ChannelId, DateTime.UtcNow); PreviousToken.Lifetime = 0; } } catch (Exception e) { Utils.Trace("Could not verify security on incoming request."); ForceChannelFault(e, StatusCodes.BadSecurityChecksFailed, "Could not verify security on incoming request."); return(false); } BufferCollection chunksToProcess = null; try { // check for an abort. if (TcpMessageType.IsAbort(messageType)) { Utils.Trace("Request was aborted."); chunksToProcess = GetSavedChunks(requestId, messageBody); return(true); } // check if it is necessary to wait for more chunks. if (!TcpMessageType.IsFinal(messageType)) { SaveIntermediateChunk(requestId, messageBody); return(true); } Utils.Trace("Channel {0}: ProcessRequestMessage {1}", ChannelId, requestId); // get the chunks to process. chunksToProcess = GetSavedChunks(requestId, messageBody); // decode the request. IServiceRequest request = BinaryDecoder.DecodeMessage(new ArraySegmentStream(chunksToProcess), null, Quotas.MessageContext) as IServiceRequest; if (request == null) { SendServiceFault(token, requestId, ServiceResult.Create(StatusCodes.BadStructureMissing, "Could not parse request body.")); return(true); } // ensure that only discovery requests come through unsecured. if (DiscoveryOnly) { if (!(request is GetEndpointsRequest || request is FindServersRequest)) { SendServiceFault(token, requestId, ServiceResult.Create(StatusCodes.BadSecurityPolicyRejected, "Channel can only be used for discovery.")); return(true); } } // hand the request to the server. RequestReceived?.Invoke(this, requestId, request); return(true); } catch (Exception e) { Utils.Trace(e, "Unexpected error processing request."); SendServiceFault(token, requestId, ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing request.")); return(true); } finally { if (chunksToProcess != null) { chunksToProcess.Release(BufferManager, "ProcessRequestMessage"); } } }
/// <summary> /// Processes an CloseSecureChannel request message. /// </summary> private bool ProcessCloseSecureChannelRequest(uint messageType, ArraySegment <byte> messageChunk) { // validate security on the message. ChannelToken token = null; uint requestId = 0; uint sequenceNumber = 0; ArraySegment <byte> messageBody; try { messageBody = ReadSymmetricMessage(messageChunk, true, out token, out requestId, out sequenceNumber); // check for replay attacks. if (!VerifySequenceNumber(sequenceNumber, "ProcessCloseSecureChannelRequest")) { throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid); } } catch (Exception e) { throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, e, "Could not verify security on CloseSecureChannel request."); } BufferCollection chunksToProcess = null; try { // check if it is necessary to wait for more chunks. if (!TcpMessageType.IsFinal(messageType)) { SaveIntermediateChunk(requestId, messageBody); return(false); } // get the chunks to process. chunksToProcess = GetSavedChunks(requestId, messageBody); CloseSecureChannelRequest request = BinaryDecoder.DecodeMessage( new ArraySegmentStream(chunksToProcess), typeof(CloseSecureChannelRequest), Quotas.MessageContext) as CloseSecureChannelRequest; if (request == null) { throw ServiceResultException.Create(StatusCodes.BadStructureMissing, "Could not parse CloseSecureChannel request body."); } // send the response. // SendCloseSecureChannelResponse(requestId, token, request); } catch (Exception e) { Utils.Trace(e, "Unexpected error processing OpenSecureChannel request."); } finally { if (chunksToProcess != null) { chunksToProcess.Release(BufferManager, "ProcessCloseSecureChannelRequest"); } Utils.Trace( "{0} ProcessCloseSecureChannelRequest Socket={0:X8}, ChannelId={1}, TokenId={2}", ChannelName, (Socket != null) ? Socket.Handle : 0, (CurrentToken != null) ? CurrentToken.ChannelId : 0, (CurrentToken != null) ? CurrentToken.TokenId : 0); // close the channel. ChannelClosed(); } // return false would double free the buffer return(true); }
/// <summary> /// Processes an OpenSecureChannel request message. /// </summary> private bool ProcessOpenSecureChannelRequest(uint messageType, ArraySegment <byte> messageChunk) { // validate the channel state. if (State != TcpChannelState.Opening && State != TcpChannelState.Open) { ForceChannelFault(StatusCodes.BadTcpMessageTypeInvalid, "Client sent an unexpected OpenSecureChannel message."); return(false); } // parse the security header. uint channelId = 0; X509Certificate2 clientCertificate = null; uint requestId = 0; uint sequenceNumber = 0; ArraySegment <byte> messageBody; try { messageBody = ReadAsymmetricMessage( messageChunk, ServerCertificate, out channelId, out clientCertificate, out requestId, out sequenceNumber); // check for replay attacks. if (!VerifySequenceNumber(sequenceNumber, "ProcessOpenSecureChannelRequest")) { throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid); } } catch (Exception e) { ServiceResultException innerException = e.InnerException as ServiceResultException; // If the certificate structre, signature and trust list checks pass, we return the other specific validation errors instead of BadSecurityChecksFailed if (innerException != null) { if (innerException.StatusCode == StatusCodes.BadCertificateUntrusted || innerException.StatusCode == StatusCodes.BadCertificateChainIncomplete || innerException.StatusCode == StatusCodes.BadCertificateRevoked || innerException.StatusCode == StatusCodes.BadCertificateInvalid || (innerException.InnerResult != null && innerException.InnerResult.StatusCode == StatusCodes.BadCertificateUntrusted)) { ForceChannelFault(StatusCodes.BadSecurityChecksFailed, e.Message); return(false); } else if (innerException.StatusCode == StatusCodes.BadCertificateTimeInvalid || innerException.StatusCode == StatusCodes.BadCertificateIssuerTimeInvalid || innerException.StatusCode == StatusCodes.BadCertificateHostNameInvalid || innerException.StatusCode == StatusCodes.BadCertificateUriInvalid || innerException.StatusCode == StatusCodes.BadCertificateUseNotAllowed || innerException.StatusCode == StatusCodes.BadCertificateIssuerUseNotAllowed || innerException.StatusCode == StatusCodes.BadCertificateRevocationUnknown || innerException.StatusCode == StatusCodes.BadCertificateIssuerRevocationUnknown || innerException.StatusCode == StatusCodes.BadCertificateIssuerRevoked) { ForceChannelFault(innerException, innerException.StatusCode, e.Message); return(false); } } ForceChannelFault(e, StatusCodes.BadSecurityChecksFailed, "Could not verify security on OpenSecureChannel request."); return(false); } BufferCollection chunksToProcess = null; try { bool firstCall = ClientCertificate == null; // must ensure the same certificate was used. if (ClientCertificate != null) { CompareCertificates(ClientCertificate, clientCertificate, false); } else { ClientCertificate = clientCertificate; } // check if it is necessary to wait for more chunks. if (!TcpMessageType.IsFinal(messageType)) { SaveIntermediateChunk(requestId, messageBody); return(false); } // create a new token. ChannelToken token = CreateToken(); token.TokenId = GetNewTokenId(); token.ServerNonce = CreateNonce(); // get the chunks to process. chunksToProcess = GetSavedChunks(requestId, messageBody); OpenSecureChannelRequest request = (OpenSecureChannelRequest)BinaryDecoder.DecodeMessage( new ArraySegmentStream(chunksToProcess), typeof(OpenSecureChannelRequest), Quotas.MessageContext); if (request == null) { throw ServiceResultException.Create(StatusCodes.BadStructureMissing, "Could not parse OpenSecureChannel request body."); } // check the security mode. if (request.SecurityMode != SecurityMode) { ReviseSecurityMode(firstCall, request.SecurityMode); } // check the client nonce. token.ClientNonce = request.ClientNonce; if (!ValidateNonce(token.ClientNonce)) { throw ServiceResultException.Create(StatusCodes.BadNonceInvalid, "Client nonce is not the correct length or not random enough."); } // choose the lifetime. int lifetime = (int)request.RequestedLifetime; if (lifetime < TcpMessageLimits.MinSecurityTokenLifeTime) { lifetime = TcpMessageLimits.MinSecurityTokenLifeTime; } if (lifetime > 0 && lifetime < token.Lifetime) { token.Lifetime = lifetime; } // check the request type. SecurityTokenRequestType requestType = request.RequestType; if (requestType == SecurityTokenRequestType.Issue && State != TcpChannelState.Opening) { throw ServiceResultException.Create(StatusCodes.BadRequestTypeInvalid, "Cannot request a new token for an open channel."); } if (requestType == SecurityTokenRequestType.Renew && State != TcpChannelState.Open) { // may be reconnecting to a dropped channel. if (State == TcpChannelState.Opening) { // tell the listener to find the channel that can process the request. Listener.ReconnectToExistingChannel( Socket, requestId, sequenceNumber, channelId, ClientCertificate, token, request); Utils.Trace( "{0} ReconnectToExistingChannel Socket={0:X8}, ChannelId={1}, TokenId={2}", ChannelName, (Socket != null) ? Socket.Handle : 0, (CurrentToken != null) ? CurrentToken.ChannelId : 0, (CurrentToken != null) ? CurrentToken.TokenId : 0); // close the channel. ChannelClosed(); // nothing more to do. return(false); } throw ServiceResultException.Create(StatusCodes.BadRequestTypeInvalid, "Cannot request to renew a token for a channel that has not been opened."); } // check the channel id. if (requestType == SecurityTokenRequestType.Renew && channelId != ChannelId) { throw ServiceResultException.Create(StatusCodes.BadTcpSecureChannelUnknown, "Do not recognize the secure channel id provided."); } // log security information. if (requestType == SecurityTokenRequestType.Issue) { Opc.Ua.Security.Audit.SecureChannelCreated( m_ImplementationString, Listener.EndpointUrl.ToString(), Utils.Format("{0}", ChannelId), EndpointDescription, ClientCertificate, ServerCertificate, BinaryEncodingSupport.Required); } else { Opc.Ua.Security.Audit.SecureChannelRenewed( m_ImplementationString, Utils.Format("{0}", ChannelId)); } if (requestType == SecurityTokenRequestType.Renew) { SetRenewedToken(token); } else { ActivateToken(token); } State = TcpChannelState.Open; // send the response. SendOpenSecureChannelResponse(requestId, token, request); // notify reverse CompleteReverseHello(null); // notify any monitors. NotifyMonitors(ServiceResult.Good, false); return(false); } catch (Exception e) { SendServiceFault(requestId, ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing OpenSecureChannel request.")); CompleteReverseHello(e); return(false); } finally { if (chunksToProcess != null) { chunksToProcess.Release(BufferManager, "ProcessOpenSecureChannelRequest"); } } }
/// <summary> /// Queues a write request. /// </summary> protected void BeginWriteMessage(BufferCollection buffers, int timeout, object state) { m_socket.BeginWriteMessage(buffers, timeout, m_WriteComplete, state); }