/// <summary> /// the filter that gets all disconnected event. /// </summary> /// <param name="obj"> /// a TransportEvent object that contains the event to filt. /// </param> /// <returns> /// return a bool value that indicates whether the event is disconnected event. /// if true, the transport event is disconnected event; otherwise, return false. /// </returns> public bool FilterDisconnected(TransportEvent obj) { if (obj != null && obj.EventType == EventType.Disconnected) { return true; } return false; }
/// <summary> /// filter the disconnected event from specified client. /// </summary> /// <param name="obj"> /// a TransportEvent object that contains the event to filt. /// </param> /// <returns> /// if the disconnected event is from specified endpoint, return true; otherwise, false. /// </returns> public bool FilterEndPointDisconnected(TransportEvent obj) { if (obj == null || obj.EventType != EventType.Disconnected) { return false; } if (obj.EndPoint == null || !obj.EndPoint.Equals(this.endpoint)) { return false; } return true; }
/// <summary> /// add transport event to transport, TSD can invoke ExpectTransportEvent to get it. /// </summary> /// <param name="transportEvent"> /// a TransportEvent object that contains the event to add to the queue /// </param> /// <exception cref="ObjectDisposedException"> /// thrown when this object is disposed. /// </exception> /// <exception cref="InvalidOperationException"> /// thrown when the underlayer transport is null! the config for TransportStack is invalid. /// </exception> public virtual void AddEvent(TransportEvent transportEvent) { if (disposed) { throw new ObjectDisposedException("TransportStack"); } if (this.transport == null) { throw new InvalidOperationException( "the underlayer transport is null! the config for TransportStack is invalid."); } this.transport.AddEvent(transportEvent); }
/// <summary> /// add transport event to transport, TSD can invoke ExpectTransportEvent to get it. /// </summary> /// <param name="transportEvent"> /// a TransportEvent object that contains the event to add to the queue /// </param> /// <exception cref="ObjectDisposedException"> /// thrown when this object is disposed. /// </exception> /// <exception cref="ArgumentNullException"> /// thrown when transportEvent is null /// </exception> /// <exception cref="InvalidOperationException"> /// thrown when the RemoteEndPoint of disconnected event is null /// </exception> /// <exception cref="InvalidOperationException"> /// thrown when the specified endpoint is not an IPEndPoint. /// </exception> /// <exception cref="InvalidOperationException"> /// thrown when the connection specified by endpoint cannot be found! /// </exception> /// <exception cref="InvalidOperationException"> /// thrown when the connection specified by endpoint is null! /// </exception> public void AddEvent(TransportEvent transportEvent) { if (disposed) { throw new ObjectDisposedException("TcpServerTransport"); } if (transportEvent == null) { throw new ArgumentNullException("transportEvent"); } if (transportEvent.EventType == EventType.Disconnected) { if (transportEvent.RemoteEndPoint == null) { throw new InvalidOperationException("the RemoteEndPoint of disconnected event is null"); } IPEndPoint endpoint = transportEvent.RemoteEndPoint as IPEndPoint; if (endpoint == null) { throw new InvalidOperationException("the specified endpoint is not an IPEndPoint."); } if (!this.connections.ContainsKey(endpoint)) { throw new InvalidOperationException("the connection specified by endpoint cannot be found!"); } TcpServerConnection connection = this.connections[endpoint]; if (connection == null) { throw new InvalidOperationException("the connection specified by endpoint is null!"); } // if the disconnected event is triggered by server, skip it. if (connection.IsServerStopped) { return; } } Utility.Enqueue(this.eventQueue, this.sequence, transportEvent); }
/// <summary> /// Check if transportEvent is not null, and is not exception. /// </summary> /// <param name="transportEvent">An instance of TransportEvent to check.</param> /// <param name="expectedEventType">Expected event type, throw exception when not equal.</param> private void ValidateTransportEvent(TransportEvent transportEvent, EventType expectedEventType) { if (transportEvent == null) { throw new InvalidOperationException("Unknown object received from transport."); } if (transportEvent.EventType == EventType.Exception) { throw new InvalidOperationException( "Error occurred, please find details in innerException.", (Exception)transportEvent.EventObject); } if ((transportEvent.EventType & expectedEventType) == 0) { throw new InvalidOperationException( string.Format("{0} (unexpected) event received.", transportEvent.EventType)); } }
/// <summary> /// add transport event to transport, TSD can invoke ExpectTransportEvent to get it. /// </summary> /// <param name="transportEvent"> /// a TransportEvent object that contains the event to add to the queue /// </param> /// <exception cref="ObjectDisposedException"> /// thrown when this object is disposed. /// </exception> /// <exception cref="ArgumentNullException"> /// thrown when transportEvent is null. /// </exception> public void AddEvent(TransportEvent transportEvent) { if (disposed) { throw new ObjectDisposedException("StreamTransport"); } if (transportEvent == null) { throw new ArgumentNullException("transportEvent"); } this.eventQueue.Enqueue(transportEvent); }
/// <summary> /// add transport event to transport, TSD can invoke ExpectTransportEvent to get it. /// </summary> /// <param name="transportEvent"> /// a TransportEvent object that contains the event to add to the queue /// </param> /// <exception cref="ObjectDisposedException"> /// thrown when this object is disposed. /// </exception> /// <exception cref="ArgumentNullException"> /// thrown when transportEvent is null. /// </exception> public void AddEvent(TransportEvent transportEvent) { if (disposed) { throw new ObjectDisposedException("UdpServerTransport"); } if (transportEvent == null) { throw new ArgumentNullException("transportEvent"); } this.eventQueue.Enqueue(transportEvent); this.buffer.Enqueue(new UdpReceivedBytes(null, transportEvent.RemoteEndPoint as IPEndPoint, transportEvent.LocalEndPoint as IPEndPoint)); }
/// <summary> /// enqueue the receivied transport event.<para/> /// add the transport event to the event queue, for user to expect specified event directly.<para/> /// add the transport event to data sequence, for user to expect any event(include received packet).<para/> /// it's thread-safe. /// </summary> /// <param name="eventQueue"> /// a SyncFilterQueue<TransportEvent> that specifies the queue to store event. /// </param> /// <param name="sequence"> /// a DataSequence object that stores the received data or event sequence. /// </param> /// <param name="transportEvent"> /// a TransportEvent object that specifies the received event. /// </param> public static void Enqueue(SyncFilterQueue <TransportEvent> eventQueue, DataSequence sequence, TransportEvent transportEvent) { lock (eventQueue) { sequence.Add(transportEvent, new byte[0], null); eventQueue.Enqueue(transportEvent); } }
/// <summary> /// Creates a new Socket for a newly created connection and a new thread to receive packet in the loop. /// </summary> private void AcceptLoop() { while (!exitLoop) { if (this.receivingStreams.Count >= config.MaxConnections) { // not listen untill the current connections are less than the max value. // the interval to query is 1 seconds: Thread.Sleep(1000); continue; } Socket socket = null; try { socket = this.listenSock.Accept(); } catch (SocketException) { exitLoop = true; continue; } TransportEvent connectEvent; Stream receiveStream = null; Stream baseStream = new NetworkStream(socket); switch (streamType) { case SecurityStreamType.None: receiveStream = baseStream; break; case SecurityStreamType.Ssl: receiveStream = new SslStream( new ETWStream( baseStream), false ); ((SslStream)receiveStream).AuthenticateAsServer(cert); break; case SecurityStreamType.CredSsp: string targetSPN = ConstValue.CREDSSP_SERVER_NAME_PREFIX + config.LocalIpAddress; RdpbcgrServerCredSspStream credSspStream = new RdpbcgrServerCredSspStream(new ETWStream(baseStream), targetSPN); credSspStream.Authenticate(cert); receiveStream = credSspStream; break; default: receiveStream = baseStream; break; } RdpbcgrReceiveThread receiveThread = new RdpbcgrReceiveThread( socket.RemoteEndPoint, this.packetQueue, this.decoder, receiveStream, this.config.BufferSize, this.rdpbcgrServer); connectEvent = new TransportEvent(EventType.Connected, socket.RemoteEndPoint, null); RdpbcgrServerSessionContext session = new RdpbcgrServerSessionContext(); session.Identity = connectEvent.EndPoint; session.Server = this.rdpbcgrServer; session.LocalIdentity = socket.LocalEndPoint; session.IsClientToServerEncrypted = this.rdpbcgrServer.IsClientToServerEncrypted; this.rdpbcgrServer.ServerContext.AddSession(session); this.packetQueue.AddObject(connectEvent); lock (this.receivingStreams) { this.receivingStreams.Add(socket, receiveThread); } receiveThread.Start(); } }
/// <summary> /// Add transport event to transport, TSD can invoke ExpectTransportEvent to get it. /// </summary> /// <param name="transportEvent"> /// a TransportEvent object that contains the event to add to the queue /// </param> public void AddEvent(TransportEvent transportEvent) { this.packetQueue.AddObject(transportEvent); }
/// <summary> /// Receive data, decode Packet and add them to QueueManager in the loop. /// </summary> private void ReceiveLoop() { StackPacket[] packets = null; ReceiveStatus receiveStatus = ReceiveStatus.Success; //object endPoint = null; int bytesRecv = 0; int leftCount = 0; int expectedLength = this.maxBufferSize; byte[] receivedCaches = new byte[0]; while (!exitLoop) { if (expectedLength <= 0) { expectedLength = this.maxBufferSize; } byte[] receivingBuffer = new byte[expectedLength]; try { bytesRecv = this.receiveStream.Read(receivingBuffer, 0, receivingBuffer.Length); if (bytesRecv == 0) { receiveStatus = ReceiveStatus.Disconnected; } else { receiveStatus = ReceiveStatus.Success; } } catch (System.IO.IOException) { // If this is an IOException, treat it as a disconnection. if (!exitLoop) { if (this.packetQueue != null) { TransportEvent exceptionEvent = new TransportEvent(EventType.Disconnected, this.endPointIdentity, null); this.packetQueue.AddObject(exceptionEvent); break; } else { throw; } } } catch (Exception e) { if (!exitLoop) { if (this.packetQueue != null) { TransportEvent exceptionEvent = new TransportEvent(EventType.Exception, this.endPointIdentity, e); this.packetQueue.AddObject(exceptionEvent); break; } else { throw; } } } if (receiveStatus == ReceiveStatus.Success) { byte[] data = new byte[bytesRecv + receivedCaches.Length]; Array.Copy(receivedCaches, data, receivedCaches.Length); Array.Copy(receivingBuffer, 0, data, receivedCaches.Length, bytesRecv); while (true) { int consumedLength; try { packets = this.decoder(this.endPointIdentity, data, out consumedLength, out expectedLength); } catch (Exception e) { TransportEvent exceptionEvent = new TransportEvent(EventType.Exception, this.endPointIdentity, e); this.packetQueue.AddObject(exceptionEvent); // If decoder throw exception, we think decoder will throw exception again when it decode // subsequent received data So here we terminate the receive thread. return; } if (consumedLength > 0) { //add packet to queue if (packets != null && packets.Length > 0) { AddPacketToQueueManager(this.endPointIdentity, packets); bytesRecv = 0; foreach (StackPacket pdu in packets) { if (pdu.GetType() == typeof(Client_X_224_Connection_Request_Pdu)) { // Block the thread if received a Client X224 Connection Request PDU // the main thread will resume the thread after it send X224 Connection confirm PDU and other necessary process, such as TLS Handshake rdpbcgrServer.ReceiveThreadControlEvent.WaitOne(); } } } //check if continue the decoding leftCount = data.Length - consumedLength; if (leftCount <= 0) { receivedCaches = new byte[0]; data = new byte[0]; break; } else { // Update caches contents to the bytes which is not consumed receivedCaches = new byte[leftCount]; Array.Copy(data, consumedLength, receivedCaches, 0, leftCount); data = new byte[receivedCaches.Length]; Array.Copy(receivedCaches, data, data.Length); } } else { //if no data consumed, it means the left data cannot be decoded separately, so cache it and jump out. receivedCaches = new byte[data.Length]; Array.Copy(data, receivedCaches, receivedCaches.Length); break; } } } else if (receiveStatus == ReceiveStatus.Disconnected) { if (this.packetQueue != null) { TransportEvent disconnectEvent = new TransportEvent(EventType.Disconnected, this.endPointIdentity, null); this.packetQueue.AddObject(disconnectEvent); break; } else { throw new InvalidOperationException("The transport is disconnected by remote host."); } } else { throw new InvalidOperationException("Unknown status returned from receiving method."); } } }
/// <summary> /// Add the given stack packets to the QueueManager object if packet type not in filters or Customize filter. /// </summary> /// <param name="endPoint">the endpoint of the packets</param> /// <param name="packets">decoded packets</param> private void AddPacketToQueueManager(object endPoint, StackPacket[] packets) { if (packets == null) { return; } foreach (StackPacket packet in packets) { TransportEvent packetEvent = new TransportEvent(EventType.ReceivedPacket, endPoint, packet); this.packetQueue.AddObject(packetEvent); } }
/// <summary> /// expect packet from transport.<para/> /// the transport must be a TcpServer or NetbiosServer. /// </summary> /// <param name="host"> /// an IVisitorGetAnyPacket interface that specifies the host of visitor. /// </param> /// <param name="eventQueue"> /// a SyncFilterQueue<TransportEvent> that specifies the queue to store event. /// </param> /// <param name="sequence"> /// a DataSequence object that manages the sequence information of multiple clients. /// </param> /// <param name="timeout"> /// a TimeSpan object that indicates the timeout to expect event. /// </param> /// <param name="skipEvent"> /// a bool value that specifies whether skip the event.<para/> /// if true, just wait for packet coming; otherwise, both data and event will return. /// </param> /// <returns> /// a StackPacket object that specifies the received packet.<para/> /// if all buffer is closed in this while, and required to return if all buffer is closed, return null.<para/> /// otherwise never return null, if no packets coming in timespan, throw exception. /// </returns> public static TransportEvent Visit( IVisitorGetAnyData host, SyncFilterQueue <TransportEvent> eventQueue, DataSequence sequence, TimeSpan timeout, bool skipEvent) { // the end time for operation. DateTime endTime = DateTime.Now + timeout; TimeSpan currentTimeout = timeout; while (true) { sequence.Reset(); // try to decode packet from all clients in sequence. while (true) { SequenceItem item = sequence.Next(TimeSpan.MinValue); // all item in the sequences returned if (item == null) { break; } TransportEvent transportEvent = item.Source as TransportEvent; // if event arrived and donot skip the event, return the event directly. if (transportEvent != null) { if (skipEvent) { continue; } sequence.Remove(transportEvent); Utility.Remove(eventQueue, transportEvent); return(transportEvent); } object remoteEndPoint; object localEndPoint; host.VisitorGetEndPoint(item.Source, out remoteEndPoint, out localEndPoint); int consumedLength = 0; StackPacket packet = null; try { // set timeout to zero, must not wait for more data. // if timeout, process next. packet = host.VisitorDecodePackets( item.Source, remoteEndPoint, localEndPoint, out consumedLength); // remove the sequence information in data sequence. sequence.Consume(item.Source, consumedLength); if (packet != null) { TcpServerConnection connection = item.Source as TcpServerConnection; if (connection != null) { return(connection.VisitorCreateTransportEvent(EventType.ReceivedPacket, packet)); } else { return(new TransportEvent(EventType.ReceivedPacket, remoteEndPoint, localEndPoint, packet)); } } } // skip timeout of any host. catch (TimeoutException) { } } // waiting for next data coming. sequence.Next(currentTimeout); currentTimeout = endTime - DateTime.Now; } }