public void OnDataReceived(IAsyncResult asyn) { // Logger.Log(0, "Begin OnDataReceived"); IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0); byte[] buf = null; try { buf = udpConn.EndReceive(asyn, ref remoteIpEndPoint); } catch (SocketException e) { if (e.SocketErrorCode == SocketError.Interrupted) // ignore return; log_status.WarnFormat("Caught socket exception: {0}", e); WaitForData(); return; } catch (ObjectDisposedException) { // just ignore this one return; } RdpConnection conn = GetConnection(remoteIpEndPoint); if (conn == null) { log_status.Info("Handling new connection"); conn = HandleNewConnection(remoteIpEndPoint); } if (conn != null) { RdpPacket packet = new RdpPacket(buf); // Logger.Log(0, "Got data in OnDataReceived"); conn.OnSegmentArrival(packet, remoteIpEndPoint); // Logger.Log(0, "Done with OnDataReceived"); } try { WaitForData(); } catch (SocketException e) { if (e.SocketErrorCode != SocketError.Interrupted) throw; } catch (ObjectDisposedException) { // Just ignore this } }
// TODO: Should I pass in remote endpoint? public void OnSegmentArrival(RdpPacket packet) { switch (state) { case ConnectionState.Closed: if (packet.Rst) return; else if (packet.Ack || packet.Nul) { /// Send <SEQ=SEG.ACK + 1><RST> ; } else { /// Send <SEQ=0><RST><ACK=SEG.SEQ><ACK> ; } break; case ConnectionState.CloseWait: break; case ConnectionState.Listen: if (packet.Rst) return; if (packet.Ack || packet.Nul) { /// Send <SEQ=SEG.ACK + 1><RST> return; } if (packet.Syn) { rcvCur = segSeq; rcvIrs = segSeq; sndMax = segMax; sbufMax = segBmax; /// Send <SEQ=SND.ISS><ACK=RCV.CUR><MAX=RCV.MAX><BUFMAX=RBUF.MAX> /// <ACK><SYN> state = ConnectionState.SynRcvd; return; } Trace.TraceWarning("Shouldn't have gotten here"); break; case ConnectionState.SynSent: if (packet.Rst) { if (packet.Ack) { state = ConnectionState.Closed; throw new Exception("Connection Refused"); // TODO: deallocate connection } return; } if (packet.Syn) { rcvCur = segSeq; rcvIrs = segSeq; sndMax = segMax; rbufMax = segBmax; if (packet.Ack) { sndUna = segAck + 1; // per rfc 1151 state = ConnectionState.Open; /// Send <SEQ=SND.NXT><ACK=RCV.CUR><ACK> } else { state = ConnectionState.SynRcvd; /// Send <SEQ=SND.ISS><ACK=RCV.CUR><MAX=RCV.MAX><BUFMAX=RBUF.MAX> // <SYN><ACK> } return; } if (packet.Ack) { if (!packet.Rst && segAck != sndIss) { /// Send <SEQ=SEG.ACK + 1><RST> state = ConnectionState.Closed; throw new Exception("Connection Reset"); // TODO: deallocate connection return; } } Trace.TraceWarning("Shouldn't have gotten here"); break; case ConnectionState.SynRcvd: if (rcvIrs >= segSeq || segSeq > (rcvCur + rcvMax * 2)) /// Send <SEQ=SND.NXT><ACK=RCV.CUR><ACK> return; if (packet.Rst) { if (passiveOpen) state = ConnectionState.Listen; else { state = ConnectionState.Closed; throw new Exception("Connection Refused"); // TODO: deallocate connection } return; } if (packet.Syn) { /// Send <SEQ=SEG.ACK + 1><RST> state = ConnectionState.Closed; throw new Exception("Connection Reset"); // TODO: deallocate connection return; } if (packet.Eak) { /// Send <SEQ=SEG.ACK + 1><RST> return; } if (packet.Ack) { if (segAck == sndIss) state = ConnectionState.Open; else /// Send <SEQ=SEG.ACK + 1><RST> return; } else return; if (packet.HasData || packet.Nul) { bool inSequence = true; // If the received segment is in sequence if (inSequence) { /// TODO: Copy the data (if any) to user buffers rcvCur = segSeq; /// Send <SEQ=SND.NXT><ACK=RCV.CUR><ACK> } else { if (outOfOrderAllowed) // TODO: Copy the data (if any) to user buffers ; /// Send <SEQ=SND.NXT><ACK=RCV.CUR><ACK><EACK><RCVDSEQNO1> /// ...<RCVDSEQNOn> } } break; case ConnectionState.Open: if (rcvCur >= segSeq || segSeq > (rcvCur + rcvMax * 2)) { /// Send <SEQ=SND.NXT><ACK=RCV.CUR><ACK> return; } if (packet.Rst) { state = ConnectionState.CloseWait; throw new Exception("Connection Reset"); return; } if (packet.Nul) { rcvCur = segSeq; /// Send <SEQ=SND.NXT><ACK=RCV.CUR><ACK> return; } if (packet.Syn) { /// Send <SEQ=SEG.ACK + 1><RST> state = ConnectionState.Closed; throw new Exception("Connection Reset"); // TODO: deallocate connection return; } if (packet.Ack) { if (sndUna <= segAck && segAck < sndNxt) { sndUna = segAck + 1; // per rfc 1151 // TODO: Flush acknowledged segments } } if (packet.Eak) { // TODO: Flush acknowledged segments } if (packet.HasData) { bool inSequence = true; // If the received segment is in sequence if (inSequence) { /// TODO: Copy the data (if any) to user buffers rcvCur = segSeq; // This can have EACKS too, if you want /// Send <SEQ=SND.NXT><ACK=RCV.CUR><ACK> } else { if (outOfOrderAllowed) // TODO: Copy the data (if any) to user buffers ; /// Send <SEQ=SND.NXT><ACK=RCV.CUR><ACK><EACK><RCVDSEQNO1> /// ...<RCVDSEQNOn> } } break; } }
public void Send(RdpPacket packet) { switch (state) { case ConnectionState.Open: if (sndNxt >= sndUna + sndMax) throw new Exception("Error - insufficient resources to send data"); /// Send <SEQ=SND.NXT><ACK=RCV.CUR><ACK><Data> sndNxt = sndNxt + 1; break; case ConnectionState.Listen: case ConnectionState.SynRcvd: case ConnectionState.SynSent: case ConnectionState.Closed: case ConnectionState.CloseWait: throw new Exception("Error - connection not open"); break; } }
/// <summary> /// Method to handle segment arrival /// </summary> /// <param name="inPacket"></param> /// <param name="remote"></param> public void OnSegmentArrival(RdpPacket inPacket, IPEndPoint remoteEP) { try { Monitor.Enter(this); InternalOnSegmentArrival(inPacket, remoteEP); } finally { Monitor.Exit(this); } }
/// <summary> /// Internal method to handle segment arrival. For this version, we already hold the lock. /// </summary> /// <param name="inPacket"></param> /// <param name="remoteEP"></param> private void InternalOnSegmentArrival(RdpPacket inPacket, IPEndPoint remoteEP) { DateTime now = DateTime.Now; log.DebugFormat("OnSegmentArrival: {0} - packet {1}", now, inPacket); // count received bytes bytesReceivedCount += inPacket.PacketLength; totalBytesReceivedCount += inPacket.PacketLength; packetsReceivedCount++; switch (state) { case ConnectionState.Closed: case ConnectionState.CloseWait: if (inPacket.Rst) return; else if (inPacket.Ack || inPacket.Nul) { /// Send <SEQ=SEG.ACK + 1><RST> RdpPacket packet = new RdpPacket(0); packet.SeqNumber = inPacket.AckNumber + 1; packet.Rst = true; SendPacket(packet); } else { /// Send <SEQ=0><RST><ACK=SEG.SEQ><ACK> RdpPacket packet = new RdpPacket(0); packet.SeqNumber = 0; packet.AckNumber = inPacket.SeqNumber; packet.Rst = true; packet.Ack = true; SendPacket(packet); } break; case ConnectionState.Listen: if (inPacket.Rst) return; if (inPacket.Ack || inPacket.Nul) { /// Send <SEQ=SEG.ACK + 1><RST> RdpPacket packet = new RdpPacket(0); packet.SeqNumber = inPacket.AckNumber + 1; packet.Rst = true; SendPacket(packet); return; } if (inPacket.Syn) { rcvCur = inPacket.SeqNumber; rcvIrs = inPacket.SeqNumber; sndMax = inPacket.MaxSegments; sbufMax = inPacket.MaxSegmentSize; /// Send <SEQ=SND.ISS><ACK=RCV.CUR><MAX=RCV.MAX><BUFMAX=RBUF.MAX> /// <ACK><SYN> RdpPacket packet = new RdpPacket(0, RdpPacket.OpenLength); packet.SeqNumber = sndIss; packet.AckNumber = rcvCur; packet.Ack = true; packet.Syn = true; packet.MaxSegments = (short)rcvMax; packet.MaxSegmentSize = (short)rbufMax; packet.Sequenced = inPacket.Sequenced; SendPacket(packet); State = ConnectionState.SynRcvd; return; } log.Warn("Shouldn't have gotten here"); break; case ConnectionState.SynSent: if (inPacket.Rst) { if (inPacket.Ack) { State = ConnectionState.Closed; log.Warn("Connection Refused"); // TODO: deallocate connection } return; } if (inPacket.Syn) { rcvCur = inPacket.SeqNumber; rcvIrs = inPacket.SeqNumber; sndMax = inPacket.MaxSegments; sbufMax = inPacket.MaxSegmentSize; if (inPacket.Ack) { sndUna = inPacket.AckNumber + 1; // per rfc 1151 State = ConnectionState.Open; /// Send <SEQ=SND.NXT><ACK=RCV.CUR><ACK> RdpPacket packet = new RdpPacket(0); packet.SeqNumber = sndNxt; packet.AckNumber = rcvCur; packet.Ack = true; SendPacket(packet); } else { State = ConnectionState.SynRcvd; /// Send <SEQ=SND.ISS><ACK=RCV.CUR><MAX=RCV.MAX><BUFMAX=RBUF.MAX> /// <SYN><ACK> RdpPacket packet = new RdpPacket(0, RdpPacket.OpenLength); packet.SeqNumber = sndIss; packet.AckNumber = rcvCur; packet.Ack = true; packet.Syn = true; packet.MaxSegments = (short)rcvMax; packet.MaxSegmentSize = (short)rbufMax; packet.Sequenced = inPacket.Sequenced; SendPacket(packet); } return; } if (inPacket.Ack) { if (!inPacket.Rst && inPacket.AckNumber != sndIss) { /// Send <SEQ=SEG.ACK + 1><RST> RdpPacket packet = new RdpPacket(0); packet.SeqNumber = inPacket.AckNumber + 1; packet.Rst = true; SendPacket(packet); State = ConnectionState.Closed; log.Warn("Connection Reset (by invalid ACK)"); // TODO: deallocate connection return; } } if (inPacket.Nul) { log.Warn("Shouldn't have gotten here"); break; } log.Error("Shouldn't have gotten here"); break; case ConnectionState.SynRcvd: if (rcvIrs >= inPacket.SeqNumber || inPacket.SeqNumber > (rcvCur + rcvMax * 2)) { /// Send <SEQ=SND.NXT><ACK=RCV.CUR><ACK> RdpPacket packet = new RdpPacket(0); packet.SeqNumber = sndNxt; packet.AckNumber = rcvCur; packet.Ack = true; SendPacket(packet); return; } if (inPacket.Rst) { if (passiveOpen) State = ConnectionState.Listen; else { State = ConnectionState.Closed; throw new Exception("Connection Refused"); } return; } if (inPacket.Syn) { /// Send <SEQ=SEG.ACK + 1><RST> RdpPacket packet = new RdpPacket(0); packet.SeqNumber = inPacket.AckNumber + 1; packet.Rst = true; SendPacket(packet); State = ConnectionState.Closed; log.Warn("Connection Reset (by SYN)"); return; } if (inPacket.Eak) { /// Send <SEQ=SEG.ACK + 1><RST> RdpPacket packet = new RdpPacket(0); packet.SeqNumber = inPacket.AckNumber + 1; packet.Rst = true; SendPacket(packet); return; } if (inPacket.Ack) { if (inPacket.AckNumber == sndIss) State = ConnectionState.Open; else { /// Send <SEQ=SEG.ACK + 1><RST> RdpPacket packet = new RdpPacket(0); packet.SeqNumber = inPacket.AckNumber + 1; packet.Rst = true; SendPacket(packet); return; } } else return; if (inPacket.HasData || inPacket.Nul) { HandleDataPacket(inPacket); /// Send <SEQ=SND.NXT><ACK=RCV.CUR><ACK><EACK><RCVDSEQNO1> /// ...<RCVDSEQNOn> int[] eakArray = AbridgedEakArray; RdpPacket packet = new RdpPacket(0, eakArray.Length * 4); packet.SeqNumber = sndNxt; packet.AckNumber = rcvCur; packet.Ack = true; if (eakArray.Length > 0) { packet.Eak = true; packet.EakEntries = eakArray; } SendPacket(packet); } break; case ConnectionState.Open: if (inPacket.Rst) { State = ConnectionState.CloseWait; log.Warn("Connection Reset"); return; } if (rcvCur >= inPacket.SeqNumber || inPacket.SeqNumber > (rcvCur + rcvMax * 2)) { /// Send <SEQ=SND.NXT><ACK=RCV.CUR><ACK> RdpPacket packet = new RdpPacket(0); packet.SeqNumber = sndNxt; packet.AckNumber = rcvCur; packet.Ack = true; SendPacket(packet); log.Debug("Acking packet that was already received"); return; } #if STRICT_SPEC if (inPacket.Nul) { rcvCur = inPacket.SeqNumber; /// Send <SEQ=SND.NXT><ACK=RCV.CUR><ACK> RdpPacket packet = new RdpPacket(0); packet.SeqNumber = sndNxt; packet.AckNumber = rcvCur; packet.Ack = true; SendPacket(packet); Logger.Log(1, "Got Nul packet"); return; } #endif if (inPacket.Syn) { /// Send <SEQ=SEG.ACK + 1><RST> RdpPacket packet = new RdpPacket(0); packet.SeqNumber = inPacket.AckNumber + 1; packet.Rst = true; SendPacket(packet); State = ConnectionState.Closed; log.Warn("Connection Reset (by SYN)"); // TODO: deallocate connection return; } if (inPacket.Ack) { if (sndUna <= inPacket.AckNumber && inPacket.AckNumber < sndNxt) { sndUna = inPacket.AckNumber + 1; // per rfc 1151 List<int> removeList = new List<int>(); int segAck = inPacket.AckNumber; foreach (int segSeq in unacknowledgedPackets.Keys) if (segSeq <= segAck) removeList.Add(segSeq); foreach (int segSeq in removeList) unacknowledgedPackets.Remove(segSeq); } } if (inPacket.Eak) { int[] eakEntries = inPacket.EakEntries; log.DebugFormat("Received eack packet: {0}", inPacket); foreach (int segSeq in eakEntries) unacknowledgedPackets.Remove(segSeq); } #if STRICT_SPEC if (inPacket.HasData) { #else if (inPacket.HasData || inPacket.Nul) { #endif HandleDataPacket(inPacket); /// Send <SEQ=SND.NXT><ACK=RCV.CUR><ACK><EACK><RCVDSEQNO1> /// ...<RCVDSEQNOn> int[] eakArray = AbridgedEakArray; RdpPacket packet = new RdpPacket(0, eakArray.Length * 4); packet.SeqNumber = sndNxt; packet.AckNumber = rcvCur; packet.Ack = true; if (eakArray.Length > 0) { packet.Eak = true; packet.EakEntries = eakArray; } SendPacket(packet); } break; } }
/// <summary> /// Internal version of the send method. For this version, we already hold the lock. /// </summary> /// <param name="packet"></param> private void InternalSend(RdpPacket packet) { switch (state) { case ConnectionState.Open: if (sndNxt >= sndUna + sndMax) throw new Exception("Error - insufficient resources to send data"); /// Send <ACK=RCV.CUR><SEQ=SND.NXT><ACK><Data>; packet.AckNumber = rcvCur; packet.SeqNumber = sndNxt; packet.Ack = true; SendPacket(packet); sndNxt = sndNxt + 1; break; case ConnectionState.Listen: case ConnectionState.SynRcvd: case ConnectionState.SynSent: case ConnectionState.Closed: case ConnectionState.CloseWait: throw new Exception("Error - connection not open"); } }
/// <summary> /// Internal version of close. For this version, we already hold the lock. /// </summary> private void InternalClose() { int ticks = Environment.TickCount - startTick; log.InfoFormat("Sent {0} bytes, Received {1} bytes in {2} seconds", bytesSentCount, bytesReceivedCount, ticks / 1000); switch (state) { case ConnectionState.Open: { /// Send <SEQ=SND.NXT><RST>; RdpPacket packet = new RdpPacket(0); packet.SeqNumber = sndNxt; packet.Rst = true; SendPacket(packet); } State = ConnectionState.CloseWait; // TODO: Start TIMWAIT Timer break; case ConnectionState.Listen: State = ConnectionState.Closed; break; case ConnectionState.SynRcvd: case ConnectionState.SynSent: { /// Send <SEQ=SND.NXT><RST> RdpPacket packet = new RdpPacket(0); packet.SeqNumber = sndNxt; packet.Rst = true; SendPacket(packet); } State = ConnectionState.Closed; break; case ConnectionState.CloseWait: throw new Exception("Error - Connection closing"); case ConnectionState.Closed: throw new Exception("Error - Connection not open"); } }
/// <summary> /// Takes a packet with nothing but the data portion filled, /// and does what is needed to send it out. /// </summary> /// <param name="packet"></param> public void Send(RdpPacket packet) { if (!valid) throw new Exception("Called Send on closed connection"); try { Monitor.Enter(this); InternalSend(packet); } finally { Monitor.Exit(this); } }
public void Send(byte[] data) { if (!valid) throw new Exception("Called Send on closed connection"); RdpPacket packet = new RdpPacket(data.Length); Array.Copy(data, 0, packet.PacketData, packet.DataOffset, data.Length); Send(packet); }
/// <summary> /// Method to send the packet. If this is a client, we will pass in /// null as the remoteEP, since the UdpClient object will have /// called connect. We should already hold the lock on the connection. /// </summary> /// <param name="packet"></param> private void SendPacket(RdpPacket packet) { // count the bytes of outgoing packet bytesSentCount += packet.PacketLength; totalBytesSentCount += packet.PacketLength; packetsSentCount++; if (packet.HasData) { const int IpHeaderLength = 20; const int UdpHeaderLength = 8; // If the packet is too large, throw if (packet.PacketLength + IpHeaderLength + UdpHeaderLength > sbufMax) throw new RdpFragmentationException("packet size {0} is too large for connection with max of {1}", packet.DataLength, sbufMax); // If we have already sent as many packets as we can, throw if (unacknowledgedPackets.Count > rcvMax) throw new Exception("maximum unacknowledged packets exceeds limit"); DateTime now = DateTime.Now; unacknowledgedPackets[packet.SeqNumber] = packet; retransmissionTimer[packet.SeqNumber] = now.AddMilliseconds(RetransmissionTimeout); log.DebugFormat("SendPacket: {0} - packet {1}", now, packet); } if (packet.Eak) log.DebugFormat("sending packet with eak set: {0}", packet); udpConn.Send(packet.PacketData, packet.PacketData.Length, remoteEP); }
/// <summary> /// Version of Open that bypasses the lock (used internally) /// Generally with these methods, we already hold the lock, but /// in this case, we don't need to hold the lock, since we are /// still in the constructor. /// </summary> /// <param name="passiveOpen"></param> /// <param name="rcvMax"></param> /// <param name="rmaxBuf"></param> /// <param name="isSequenced"></param> private void InternalOpen(bool passiveOpen) { if (state != ConnectionState.Closed) throw new Exception("Error - connection already open"); this.passiveOpen = passiveOpen; State = ConnectionState.Listen; // Create a connection record if (!passiveOpen) { /// Send <SEQ=SND.ISS><MAX=SND.MAX><MAXBUF=RMAX.BUF><SYN> RdpPacket packet = new RdpPacket(0, RdpPacket.OpenLength); packet.SeqNumber = sndIss; packet.Syn = true; packet.MaxSegments = (short)rcvMax; packet.MaxSegmentSize = (short)rbufMax; packet.Sequenced = !outOfOrderAllowed; SendPacket(packet); State = ConnectionState.SynSent; } }
/// <summary> /// Process the packet, adding it to either the out of order list /// or the available list as appropriate. We already have the lock. /// This will also update rcvCur to be appropriate. /// </summary> /// <param name="packet"></param> private void HandleDataPacket(RdpPacket packet) { outOfOrderPackets[packet.SeqNumber] = packet; if (outOfOrderAllowed) availableOutOfOrderPackets[packet.SeqNumber] = packet; int[] sortedSequence = new int[outOfOrderPackets.Keys.Count]; outOfOrderPackets.Keys.CopyTo(sortedSequence, 0); foreach (int segSeq in sortedSequence) { if (segSeq == rcvCur + 1) { RdpPacket currentPacket = outOfOrderPackets[segSeq]; log.DebugFormat("Queued packet {0} : {1}", currentPacket.SeqNumber, segSeq); availablePackets.Add(currentPacket); if (outOfOrderAllowed) availableOutOfOrderPackets.Remove(segSeq); rcvCur = segSeq; outOfOrderPackets.Remove(segSeq); } } Monitor.PulseAll(this); }