private bool ProcessHelloMessage(uint messageType, ArraySegment <byte> messageChunk) { // validate the channel state. if (State != TcpChannelState.Connecting) { ForceChannelFault(StatusCodes.BadTcpMessageTypeInvalid, "Client sent an unexpected Hello message."); return(false); } try { MemoryStream istrm = new MemoryStream(messageChunk.Array, messageChunk.Offset, messageChunk.Count, false); BinaryDecoder decoder = new BinaryDecoder(istrm, Quotas.MessageContext); istrm.Seek(TcpMessageLimits.MessageTypeAndSize, SeekOrigin.Current); // read requested buffer sizes. uint protocolVersion = decoder.ReadUInt32(null); uint receiveBufferSize = decoder.ReadUInt32(null); uint sendBufferSize = decoder.ReadUInt32(null); uint maxMessageSize = decoder.ReadUInt32(null); uint maxChunkCount = decoder.ReadUInt32(null); // read the endpoint url. int length = decoder.ReadInt32(null); if (length > 0) { if (length > TcpMessageLimits.MaxEndpointUrlLength) { ForceChannelFault(StatusCodes.BadTcpEndpointUrlInvalid); return(false); } byte[] endpointUrl = new byte[length]; for (int ii = 0; ii < endpointUrl.Length; ii++) { endpointUrl[ii] = decoder.ReadByte(null); } if (!SetEndpointUrl(new UTF8Encoding().GetString(endpointUrl))) { ForceChannelFault(StatusCodes.BadTcpEndpointUrlInvalid); return(false); } } decoder.Close(); // update receive buffer size. if (receiveBufferSize < ReceiveBufferSize) { ReceiveBufferSize = (int)receiveBufferSize; } if (ReceiveBufferSize < TcpMessageLimits.MinBufferSize) { ReceiveBufferSize = TcpMessageLimits.MinBufferSize; } // update send buffer size. if (sendBufferSize < SendBufferSize) { SendBufferSize = (int)sendBufferSize; } if (SendBufferSize < TcpMessageLimits.MinBufferSize) { SendBufferSize = TcpMessageLimits.MinBufferSize; } // update the max message size. if (maxMessageSize > 0 && maxMessageSize < MaxResponseMessageSize) { MaxResponseMessageSize = (int)maxMessageSize; } if (MaxResponseMessageSize < SendBufferSize) { MaxResponseMessageSize = SendBufferSize; } // update the max chunk count. if (maxChunkCount > 0 && maxChunkCount < MaxResponseChunkCount) { MaxResponseChunkCount = (int)maxChunkCount; } // send acknowledge. byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "ProcessHelloMessage"); try { MemoryStream ostrm = new MemoryStream(buffer, 0, SendBufferSize); BinaryEncoder encoder = new BinaryEncoder(ostrm, Quotas.MessageContext); encoder.WriteUInt32(null, TcpMessageType.Acknowledge); encoder.WriteUInt32(null, 0); encoder.WriteUInt32(null, 0); // ProtocolVersion encoder.WriteUInt32(null, (uint)ReceiveBufferSize); encoder.WriteUInt32(null, (uint)SendBufferSize); encoder.WriteUInt32(null, (uint)MaxRequestMessageSize); encoder.WriteUInt32(null, (uint)MaxRequestChunkCount); int size = encoder.Close(); UpdateMessageSize(buffer, 0, size); // now ready for the open or bind request. State = TcpChannelState.Opening; BeginWriteMessage(new ArraySegment <byte>(buffer, 0, size), Int32.MaxValue, null); buffer = null; } finally { if (buffer != null) { BufferManager.ReturnBuffer(buffer, "ProcessHelloMessage"); } } } catch (Exception e) { ForceChannelFault(e, StatusCodes.BadTcpInternalError, "Unexpected error while processing a Hello message."); } return(false); }
/// <summary> /// Handles a read complete event. /// </summary> private ServiceResult DoReadComplete(SocketAsyncEventArgs e) { // complete operation. int bytesRead = e.BytesTransferred; lock (m_socketLock) { BufferManager.UnlockBuffer(m_receiveBuffer); } Utils.TraceDebug("Bytes read: {0}", bytesRead); if (bytesRead == 0) { // Remote end has closed the connection // free the empty receive buffer. if (m_receiveBuffer != null) { m_bufferManager.ReturnBuffer(m_receiveBuffer, "DoReadComplete"); m_receiveBuffer = null; } return(ServiceResult.Create(StatusCodes.BadConnectionClosed, "Remote side closed connection")); } m_bytesReceived += bytesRead; // check if more data left to read. if (m_bytesReceived < m_bytesToReceive) { ReadNextBlock(); return(ServiceResult.Good); } // start reading the message body. if (m_incomingMessageSize < 0) { m_incomingMessageSize = BitConverter.ToInt32(m_receiveBuffer, 4); if (m_incomingMessageSize <= 0 || m_incomingMessageSize > m_receiveBufferSize) { Utils.Trace( "BadTcpMessageTooLarge: BufferSize={0}; MessageSize={1}", m_receiveBufferSize, m_incomingMessageSize); return(ServiceResult.Create( StatusCodes.BadTcpMessageTooLarge, "Messages size {1} bytes is too large for buffer of size {0}.", m_receiveBufferSize, m_incomingMessageSize)); } // set up buffer for reading the message body. m_bytesToReceive = m_incomingMessageSize; ReadNextBlock(); return(ServiceResult.Good); } // notify the sink. if (m_sink != null) { try { // send notification (implementor responsible for freeing buffer) on success. ArraySegment <byte> messageChunk = new ArraySegment <byte>(m_receiveBuffer, 0, m_incomingMessageSize); // must allocate a new buffer for the next message. m_receiveBuffer = null; m_sink.OnMessageReceived(this, messageChunk); } catch (Exception ex) { Utils.Trace(ex, "Unexpected error invoking OnMessageReceived callback."); } } // free the receive buffer. if (m_receiveBuffer != null) { m_bufferManager.ReturnBuffer(m_receiveBuffer, "DoReadComplete"); m_receiveBuffer = null; } // start receiving next message. ReadNextMessage(); return(ServiceResult.Good); }
/// <summary> /// Reads the next block of data from the socket. /// </summary> private void ReadNextBlock() { Socket socket = null; // check if already closed. lock (m_socketLock) { if (m_socket == null) { if (m_receiveBuffer != null) { m_bufferManager.ReturnBuffer(m_receiveBuffer, "ReadNextBlock"); m_receiveBuffer = null; } m_readState = ReadState.NotConnected; return; } socket = m_socket; // avoid stale ServiceException when socket is disconnected if (!socket.Connected) { m_readState = ReadState.NotConnected; return; } } BufferManager.LockBuffer(m_receiveBuffer); var args = new SocketAsyncEventArgs(); try { m_readState = ReadState.Receive; args.SetBuffer(m_receiveBuffer, m_bytesReceived, m_bytesToReceive - m_bytesReceived); args.Completed += m_readComplete; if (!socket.ReceiveAsync(args)) { // I/O completed synchronously if (args.SocketError != SocketError.Success) { throw ServiceResultException.Create(StatusCodes.BadTcpInternalError, args.SocketError.ToString()); } // set state to inner complete m_readState = ReadState.ReadComplete; m_readComplete(null, args); } } catch (ServiceResultException) { args?.Dispose(); BufferManager.UnlockBuffer(m_receiveBuffer); throw; } catch (Exception ex) { args?.Dispose(); BufferManager.UnlockBuffer(m_receiveBuffer); throw ServiceResultException.Create(StatusCodes.BadTcpInternalError, ex, "BeginReceive failed."); } }
/// <summary> /// Sends a OpenSecureChannel response. /// </summary> protected BufferCollection WriteAsymmetricMessage( uint messageType, uint requestId, X509Certificate2 senderCertificate, X509Certificate2 receiverCertificate, ArraySegment <byte> messageBody) { bool success = false; BufferCollection chunksToSend = new BufferCollection(); byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "WriteAsymmetricMessage"); try { int headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate); int signatureSize = GetAsymmetricSignatureSize(senderCertificate); BinaryEncoder encoder = new BinaryEncoder(buffer, 0, SendBufferSize, Quotas.MessageContext); WriteAsymmetricMessageHeader( encoder, messageType | TcpMessageType.Intermediate, ChannelId, SecurityPolicyUri, senderCertificate, receiverCertificate); // 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) { // need to reserve one byte for the padding. plainTextSize++; if (plainTextSize % plainTextBlockSize != 0) { padding = plainTextBlockSize - (plainTextSize % plainTextBlockSize); } for (int ii = 0; ii <= padding; ii++) { encoder.WriteByte(null, (byte)padding); } // 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); } finally { BufferManager.ReturnBuffer(buffer, "WriteAsymmetricMessage"); if (!success) { chunksToSend.Release(BufferManager, "WriteAsymmetricMessage"); } } }
/// <summary> /// Attaches the object to an existing socket. /// </summary> public UaSCUaBinaryChannel( string contextId, BufferManager bufferManager, ChannelQuotas quotas, X509Certificate2 serverCertificate, X509Certificate2Collection serverCertificateChain, EndpointDescriptionCollection endpoints, MessageSecurityMode securityMode, string securityPolicyUri) { if (bufferManager == null) { throw new ArgumentNullException(nameof(bufferManager)); } if (quotas == null) { throw new ArgumentNullException(nameof(quotas)); } // create a unique contex if none provided. m_contextId = contextId; if (String.IsNullOrEmpty(m_contextId)) { m_contextId = Guid.NewGuid().ToString(); } // secuirty turned off if message security mode is set to none. if (securityMode == MessageSecurityMode.None) { securityPolicyUri = SecurityPolicies.None; } if (securityMode != MessageSecurityMode.None) { if (serverCertificate == null) { throw new ArgumentNullException(nameof(serverCertificate)); } if (serverCertificate.RawData.Length > TcpMessageLimits.MaxCertificateSize) { throw new ArgumentException( Utils.Format("The DER encoded certificate may not be more than {0} bytes.", TcpMessageLimits.MaxCertificateSize), nameof(serverCertificate)); } } if (new UTF8Encoding().GetByteCount(securityPolicyUri) > TcpMessageLimits.MaxSecurityPolicyUriSize) { throw new ArgumentException( Utils.Format("UTF-8 form of the security policy URI may not be more than {0} bytes.", TcpMessageLimits.MaxSecurityPolicyUriSize), nameof(securityPolicyUri)); } m_bufferManager = bufferManager; m_quotas = quotas; m_serverCertificate = serverCertificate; m_serverCertificateChain = serverCertificateChain; m_endpoints = endpoints; m_securityMode = securityMode; m_securityPolicyUri = securityPolicyUri; m_discoveryOnly = false; m_uninitialized = true; m_state = TcpChannelState.Closed; m_receiveBufferSize = quotas.MaxBufferSize; m_sendBufferSize = quotas.MaxBufferSize; if (m_receiveBufferSize < TcpMessageLimits.MinBufferSize) { m_receiveBufferSize = TcpMessageLimits.MinBufferSize; } if (m_receiveBufferSize > TcpMessageLimits.MaxBufferSize) { m_receiveBufferSize = TcpMessageLimits.MaxBufferSize; } if (m_sendBufferSize < TcpMessageLimits.MinBufferSize) { m_sendBufferSize = TcpMessageLimits.MinBufferSize; } if (m_sendBufferSize > TcpMessageLimits.MaxBufferSize) { m_sendBufferSize = TcpMessageLimits.MaxBufferSize; } m_maxRequestMessageSize = quotas.MaxMessageSize; m_maxResponseMessageSize = quotas.MaxMessageSize; CalculateSymmetricKeySizes(); }
/// <summary> /// Secures the message using the security token. /// </summary> protected BufferCollection WriteSymmetricMessage( uint messageType, uint requestId, TcpChannelToken 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> /// Handles a read complete event. /// </summary> private ServiceResult DoReadComplete(IAsyncResult result) { // complete operation. int bytesRead = 0; lock (m_socketLock) { try { if (m_socket != null) { bytesRead = m_socket.EndReceive(result); // Utils.Trace("EndReceive {0} bytes", bytesRead); } #if TRACK_MEMORY int cookie = BitConverter.ToInt32(m_receiveBuffer, 0); if (cookie < 0) { Utils.Trace("BufferCookieError (EndReceive): Cookie={0:X8}", cookie); } #endif } finally { BufferManager.UnlockBuffer(m_receiveBuffer); } } if (bytesRead == 0) { // free the empty receive buffer. if (m_receiveBuffer != null) { m_bufferManager.ReturnBuffer(m_receiveBuffer, "DoReadComplete"); m_receiveBuffer = null; } // put a null buffer to ensure that all queued messages are processed before close. lock (m_readQueue) { m_readQueue.AddLast(new ArraySegment <byte>()); if (m_readQueue.Count == 1 && !m_readThreadActive) { ThreadPool.QueueUserWorkItem(ReadQueuedMessages, null); } } return(ServiceResult.Good); } // Utils.Trace("Bytes read: {0}", bytesRead); m_bytesReceived += bytesRead; // check if more data left to read. if (m_bytesReceived < m_bytesToReceive) { ReadNextBlock(); #if TRACK_MEMORY int cookie = BitConverter.ToInt32(m_receiveBuffer, 0); if (cookie < 0) { Utils.Trace("BufferCookieError (ReadNextBlock): Cookie={0:X8}", cookie); } #endif return(ServiceResult.Good); } // start reading the message body. if (m_incomingMessageSize < 0) { m_incomingMessageSize = BitConverter.ToInt32(m_receiveBuffer, 4); if (m_incomingMessageSize <= 0 || m_incomingMessageSize > m_receiveBufferSize) { Utils.Trace( "BadTcpMessageTooLarge: BufferSize={0}; MessageSize={1}", m_receiveBufferSize, m_incomingMessageSize); return(ServiceResult.Create( StatusCodes.BadTcpMessageTooLarge, "Messages size {1} bytes is too large for buffer of size {0}.", m_receiveBufferSize, m_incomingMessageSize)); } // set up buffer for reading the message body. m_bytesToReceive = m_incomingMessageSize; ReadNextBlock(); return(ServiceResult.Good); } // add message to queue. ArraySegment <byte> messageChunk = new ArraySegment <byte>(m_receiveBuffer, 0, m_incomingMessageSize); // must allocate a new buffer for the next message. m_receiveBuffer = null; lock (m_readQueue) { #if TRACK_MEMORY int cookie = BitConverter.ToInt32(messageChunk.Array, 0); if (cookie < 0) { Utils.Trace("BufferCookieError (DoReadComplete): Cookie={0:X8}", cookie); } #endif BufferManager.LockBuffer(messageChunk.Array); m_readQueue.AddLast(messageChunk); if (m_readQueue.Count == 1 && !m_readThreadActive) { ThreadPool.QueueUserWorkItem(ReadQueuedMessages, null); } } // start receiving next message. ReadNextMessage(); return(ServiceResult.Good); }
/// <summary> /// Cancels all outstanding I/O operations. /// </summary> private void CancelOperations() { // cancel any outstanding write operations. WriteOperation operation = null; do { operation = null; lock (m_writeQueue) { if (m_writeQueue.Count > 0) { operation = m_writeQueue.First.Value; m_writeQueue.RemoveFirst(); } } if (operation != null) { operation.Fault(StatusCodes.BadConnectionClosed); } }while (operation != null); // cancel any outstanding read operations. byte[] buffer = null; do { buffer = null; lock (m_readQueue) { if (m_readQueue.Count > 0) { buffer = m_readQueue.First.Value.Array; m_readQueue.RemoveFirst(); // check for graceful shutdown. if (buffer != null) { BufferManager.UnlockBuffer(buffer); #if TRACK_MEMORY int cookie = BitConverter.ToInt32(buffer, 0); if (cookie < 0) { Utils.Trace("BufferCookieError (CancelOperations): Cookie={0:X8}", cookie); } #endif } } } if (buffer != null) { m_bufferManager.ReturnBuffer(buffer, "CancelOperations"); } }while (buffer != null); }
/// <summary> /// Reads the next block of data from the socket. /// </summary> private SocketAsyncEventArgs ReadNextBlock() { Socket socket = null; // check if already closed. lock (m_socketLock) { if (m_socket == null) { if (m_receiveBuffer != null) { m_bufferManager.ReturnBuffer(m_receiveBuffer, "ReadNextBlock"); m_receiveBuffer = null; } return(null); } socket = m_socket; // avoid stale ServiceException when socket is disconnected if (!socket.Connected) { return(null); } } BufferManager.LockBuffer(m_receiveBuffer); ServiceResult error = ServiceResult.Good; SocketAsyncEventArgs args = new SocketAsyncEventArgs(); try { args.SetBuffer(m_receiveBuffer, m_bytesReceived, m_bytesToReceive - m_bytesReceived); args.Completed += m_ReadComplete; if (!socket.ReceiveAsync(args)) { // I/O completed synchronously if (args.SocketError != SocketError.Success) { throw ServiceResultException.Create(StatusCodes.BadTcpInternalError, args.SocketError.ToString()); } else { return(args); //Root of the problem //m_ReadComplete(null, args); } } } catch (ServiceResultException) { args.Dispose(); BufferManager.UnlockBuffer(m_receiveBuffer); throw; } catch (Exception ex) { args.Dispose(); BufferManager.UnlockBuffer(m_receiveBuffer); throw ServiceResultException.Create(StatusCodes.BadTcpInternalError, ex, "BeginReceive failed."); } return(null); }