예제 #1
0
        /// <summary>
        /// Take care of a packet of UDP data
        /// </summary>
        /// <param name="payload"></param>
        internal static void HandlePacket(byte[] payload)
        {
            ushort ipHeaderLength = (ushort)((payload[14] & 0x0f) * 4);
            
            byte[] SourceIP = Utility.ExtractRangeFromArray(payload, 26, 4);
            byte[] DestinationIP = Utility.ExtractRangeFromArray(payload, 30, 4);
            byte[] SourcePort = Utility.ExtractRangeFromArray(payload, 34, 2);
            byte[] DestinationPort = Utility.ExtractRangeFromArray(payload, 36, 2);

            var socket = new Connection() { RemoteIP = SourceIP, RemotePort = SourcePort.ToShort(), LocalPort = DestinationPort.ToShort() };

            ushort udpDataLength = (ushort)((new byte[2] { payload[38], payload[39] }).ToShort() - 8);

            //We got some data!?
            if (udpDataLength > 0)
                Networking.Adapter.FireUdpPacketEvent(Utility.ExtractRangeFromArray(payload, 42, udpDataLength), socket);
        }
예제 #2
0
        internal static void HandlePacket(byte[] payload)
        {
            bool SYN = (payload[47] & (1<<1)) != 0;  // Synchronize
            bool ACK = (payload[47] & (1<<4)) != 0;  // Acknoledge
            bool FIN = (payload[47] & (1<<0)) != 0;  // Finish
            bool PSH = (payload[47] & (1<<3)) != 0;  // Push
            bool RST = (payload[47] & (1<<2)) != 0;  // Reset

            byte[] SourceIP = Utility.ExtractRangeFromArray(payload, 26, 4);
            byte[] SourcePort = Utility.ExtractRangeFromArray(payload, 34, 2);
            byte[] LocalPort = Utility.ExtractRangeFromArray(payload, 36, 2);
            ulong connectionID = GenerateConnectionID(payload);
            Connection con = null;

            //TODO: Validate TCP checksum and reject packet if invalid!

            uint packetSeqNumber = Utility.ExtractRangeFromArray(payload, 38, 4).ToInt();

            // Handle a new request
            // if this is a new connection SYN, add to the Connections collection
            if (SYN && !ACK)   // new connection
            {
                //Debug.WriteLine("SYN");

                if (Connections.Contains(connectionID) && (Connections[connectionID] as Connection).IsOpen) Connections.Remove(connectionID);  // remove old connection?

                // Prune old Connections

                var keys = new ulong[Connections.Count];
                Connections.Keys.CopyTo(keys, 0);

                foreach (var key in keys)
                {
                    con = Connections[key] as Connection;

                    if (Microsoft.SPOT.Hardware.Utility.GetMachineTime().Subtract((Connections[key] as Connection).LastActivity).Seconds > ConnectionIdleLimit)
                    {
                        con.isClosing = true;
                        //Adapter.StopListeningToPort(con.LocalPort);
                        //Debug.WriteLine("FIN -- Closing idle connection");
                        con.SeqNumber++;
                        con.SendAck(false, true);  // SendAck Fin/Ack
                        Connections.Remove(key);
                    }
                }

                if (!Connections.Contains(connectionID)) Connections.Add(connectionID, new Connection());

                con = Connections[connectionID] as Connection;

                con.RemoteIP = SourceIP;
                con.RemotePort = SourcePort.ToShort();
                con.RemoteMac = Utility.ExtractRangeFromArray(payload, 6, 6);
                con.LocalPort = LocalPort.ToShort();
                con.SeqNumber = packetSeqNumber + 1;
                con.StartSeqNumber = packetSeqNumber;
                con.AckNumber = 2380; //Utility.ExtractRangeFromArray(payload, 42, 4).ToInt(), //TODO: this should be a random number initially?
                con.WindowSize = 1024; //Utility.ExtractRangeFromArray(payload, 48, 2).ToShort()

                con.ReadyForRequest = true;
                con.SendAck(true); // Syn/Ack

                con.AckNumber++;
                con.IsOpen = true;  // This needs to be last because a call to the Connection.Open() may be blocked until this property gets set!
            }
            else if (Connections.Contains(connectionID) && (ACK || FIN || PSH || RST))
            {
                con = Connections[connectionID] as Connection;

                ushort totalLength = Utility.ExtractRangeFromArray(payload, 16, 2).ToShort();
                ushort ipHeaderLength = (ushort)((payload[14] & 0x0f) * 4);
                ushort tcpHeaderLength = (ushort)((payload[26 + ipHeaderLength] >> 4) * 4);

                if (totalLength + 14 > payload.Length)
                {
                    // No Good -- Does not account for 0 padding?
                    Debug.WriteLine("Bad packet size detected?  " + totalLength.ToString() + "/" + payload.Length.ToString());
                    return;
                }

                //Debug.WriteLine("1 - con.seqnum = " + con.SeqNumber);

                con.SeqNumber += (uint)(totalLength - (tcpHeaderLength + ipHeaderLength));
                con.WindowSize -= (ushort)(totalLength - (tcpHeaderLength + ipHeaderLength));

                //Debug.WriteLine("2 - con.seqnum = " + con.SeqNumber);

                if (PSH)
                {
                    //Debug.WriteLine("PSH");
                    con.SendAck();  // PSH indicates we want an ACK after this packet?
                }
                else if (SYN && ACK)
                {
                    con.SeqNumber = packetSeqNumber + 1;
                    con.StartSeqNumber = packetSeqNumber;
                    con.AckNumber++;
                    con.SendAck();
                    con.IsOpen = true;
                    return;
                }
                else if ((FIN || RST) && ACK)
                {
                    // Debug.WriteLine("FIN/RST + ACK");
                    con.isClosing = true;
                    //Adapter.StopListeningToPort(con.LocalPort);
                    con.SeqNumber++;
                    con.SendAck();
                    // This is an ACKnowledgement that the connection is now closed, so delete it.
                    Connections.Remove(connectionID);
                    return;
                }
                else if (FIN)
                {
                    con.isClosing = true;
                    //Adapter.StopListeningToPort(con.LocalPort);
                    // Debug.WriteLine("FIN");

                    con.SeqNumber++;
                    con.SendAck(false, true);
                    return;
                }
                else if (RST)
                {
                    con.isClosing = true;
                    //Adapter.StopListeningToPort(con.LocalPort);
                    // Debug.WriteLine("FIN");
                    con.SeqNumber++;
                    //con.SendAck(false, true);
                    return;
                }
                else if (ACK && con.isClosing)
                {
                    // Debug.WriteLine("ACK + Closing");
                    // This is an ACKnowledgement that the connection is now closed, so delete it.
                    //Adapter.StopListeningToPort(con.LocalPort);
                    Connections.Remove(connectionID);
                    return;
                }

                if (Adapter.VerboseDebugging) Debug.WriteLine("Check for data");

                //We got some data!?
                if ((totalLength - (tcpHeaderLength + ipHeaderLength)) > 0)
                {
                    byte[] segment = Utility.ExtractRangeFromArray(payload, (14 + ipHeaderLength + tcpHeaderLength), (totalLength - (tcpHeaderLength + ipHeaderLength)));

                    if (Adapter.VerboseDebugging) Debug.WriteLine("got some data, psn: " + packetSeqNumber.ToString() + ", ssn: " + con.StartSeqNumber.ToString() + ", header delim: " + segment.Locate(HttpRequest.HeaderDelimiter).ToString());

                    Networking.Adapter.FireTcpPacketEvent(segment, packetSeqNumber - con.StartSeqNumber, con);  // TCP events always fire

                    con.FireOnConnectionPacketReceived(new Packet(PacketType.TCP) { SequenceNumber = packetSeqNumber - con.StartSeqNumber, Content = segment, Socket = con });

                    // Filters out anything that is not a GET or POST Http VERB (I did a byte comparison to avoid utf decoding exceptions, since we don't know that we actually have text yet)
                    //if (packetSeqNumber - con.StartSeqNumber == 1)
                    if (segment.Length < 10 || !(segment[0] == 0x47 && segment[1] == 0x45 && segment[2] == 0x54) && !(segment[0] == 0x50 && segment[1] == 0x4F && segment[2] == 0x53 && segment[3] == 0x54)) return;  // if it is not a get, then we won't handle it through the HTTP Request Handler

                    //if (packetSeqNumber - con.StartSeqNumber == 1) // && segment.Locate(HttpRequest.HeaderDelimiter) > -1)
                    if (con.ReadyForRequest)
                    {
                        // get the TCP checksum from the current packet and make sure it does not match the last request for we start...
                        byte[] lrc = Utility.ExtractRangeFromArray(payload, (30 + ipHeaderLength), 2);

                        if (con.LastRequestChecksum.BytesEqual(lrc))
                        {
                            if (Adapter.VerboseDebugging) Debug.WriteLine("Retransmission of Request Ignored!");
                        }
                        else
                        {
                            con.LastRequestChecksum = lrc;
                            con.ReadyForRequest = false;
                            Networking.Adapter.FireHttpPacketEvent(segment, con);
                        }
                    }
                }
            }
            else if ((FIN || RST) && ACK)
            {
                //Debug.WriteLine("Handling RST for a connection that no longer exists!!!!!!!!!");

                con = new Connection();
                con.RemoteIP = SourceIP;
                con.RemotePort = SourcePort.ToShort();
                con.RemoteMac = Utility.ExtractRangeFromArray(payload, 6, 6);
                con.LocalPort = LocalPort.ToShort();
                con.SeqNumber = Utility.ExtractRangeFromArray(payload, 38, 4).ToInt();
                con.AckNumber = Utility.ExtractRangeFromArray(payload, 42, 4).ToInt();

                con.isClosing = true;
                //Adapter.StopListeningToPort(con.LocalPort);
                con.SendAck();
                return;
            }
        }
예제 #3
0
        public void SendAsync()
        {
            // if _con is null, create and open a new connection.  with a DNS lookup of host if necessary
            if (_con == null)
            {
                var remoteIp = DNS.Lookup(Host);
                _con = new Connection() { RemoteIP = remoteIp };
            }

            var r = AssembleRequest();

            _con.SendAsync(r, 0, (short)r.Length);
        }
예제 #4
0
        /// <summary>
        /// A synchronous call to send an HTTP Request and Receive a Response --  this call will block (until the timeout) while trying to get the result
        /// </summary>
        /// <param name="timeout">Time out in seconds</param>
        /// <param name="RetrieveHeaderOnly">Set this to true if you don't need the content of the response (which can consume a lot of memory)</param>
        /// <returns>And HttpResponse object OR a null if it timeout happened</returns>
        public HttpResponse Send(ushort timeout = 5, bool ReturnHeaderOnly = false)
        {
            this.omitContent = ReturnHeaderOnly;

            // if _con is null, create and open a new connection.  with a DNS lookup of host if necessary
            if (_con == null)
            {
                var remoteIp = DNS.Lookup(Host);
                _con = new Connection() { RemoteIP = remoteIp };
            }

            var r = AssembleRequest();
            _responseToSend = null;

            _con.OnConnectionPacketReceived += _con_OnConnectionPacketReceived;

            responseWaitHandle.Reset();  // This will release an Open() call waiting for the connection!

            _con.SendAsync(r, 0, (short)r.Length);

            //Wait for response or timeout

            responseWaitHandle.WaitOne(timeout * 1000, true);

            _con.OnConnectionPacketReceived -= _con_OnConnectionPacketReceived;

            return _responseToSend;
        }
예제 #5
0
        /// <summary>
        /// A New Http Request
        /// </summary>
        /// <param name="tcpContent">The data content of the TCP packet</param>
        /// <param name="socket"></param>
        internal HttpRequest(byte[] tcpContent, Connection socket)
        {
            // Parse the packet into the various properties...
            _con = socket;
            Headers = new Hashtable();

            // VERY worrisome.  I have seen the last 4 bytes be wrong twice now.  The second time I could see the packet was transmitted correctly in Wireshark, but the last 4 bytes were wrong as produced by the ReceiveFrame() method...
            if (!Utility.ExtractRangeFromArray(tcpContent, tcpContent.Length - 4, 4).BytesEqual(HeaderDelimiter))
                Debug.WriteLine("This should never happen!  ");

            tcpContent.Overwrite(tcpContent.Length - 4, HeaderDelimiter);  // ugly workaround for possible bug...  should not be necessary

            var delimiterLocation = tcpContent.Locate(HeaderDelimiter);
            var firstLineLocation = tcpContent.Locate(CrLf);

            if (firstLineLocation < 12) throw new Exception("Malformed HTTP Request.");

            var firstLine = new string(Encoding.UTF8.GetChars(tcpContent, 0, firstLineLocation));

            RequestType = firstLine.Split(' ')[0].Trim().ToUpper();
            Path = HttpUtility.UrlDecode(firstLine.Split(' ')[1].Trim(), false);

            int colonLocation = -1;
            int start = firstLineLocation;
            bool malformed = false;

            // Parse all the header keys and values
            for (int i = firstLineLocation; i <= delimiterLocation; i++)
            {
                if (tcpContent[i] == 0x3A && colonLocation == -1) colonLocation = i;
                if (tcpContent[i] > 0x7E || tcpContent[i] < 0x09) malformed = true;

                if (tcpContent[i] == 0x0D || tcpContent[i] == 0x0A)
                {
                    if (colonLocation > start && !malformed)
                    {
                        // By handling the exception at each parameter, we can salvage the other headers if one contains an invalid character
                        // Although, we should be avoiding any exceptions with the malformed flag.
                        try
                        {
                            Headers.Add(new string(Encoding.UTF8.GetChars(tcpContent, start, colonLocation - start)).Trim(),
                                        new string(Encoding.UTF8.GetChars(tcpContent, colonLocation + 1, i - colonLocation)).Trim());
                        }
                        catch { }
                    }

                    colonLocation = -1;
                    start = i + 1;
                    malformed = false;
                }
            }

                // Parse all the header keys and values
                //foreach (var aLine in new string(Encoding.UTF8.GetChars(tcpContent, firstLineLocation, (delimiterLocation - firstLineLocation))).Split('\r', '\n'))
                //    if (aLine.IndexOf(':') > 0) Headers.Add(aLine.Substring(0, aLine.IndexOf(':')), aLine.Substring(aLine.IndexOf(':') + 1).TrimStart());
            if (RequestType != "GET")
                Content = new string(Encoding.UTF8.GetChars(tcpContent, delimiterLocation + 4, tcpContent.Length - (delimiterLocation + 4)));
            else
                Content = string.Empty;

            // Assume HTTP 1.1 if cannot parse protocol...
            Protocol = (firstLine.IndexOf("HTTP") > 5) ? firstLine.Split(' ')[2] : "HTTP/1.1";

            if (Headers.Contains("Host")) this.Host = Headers["Host"] as string;
        }
예제 #6
0
        /// <summary>
        /// Sends an HTTP Request to the specified URL.  If no content is specified a GET is sent, otherwise a POST is sent.  
        /// </summary>
        /// <param name="url">URL address with path, such as http://odata.netflix.com/Catalog/Titles%28%27BVIuO%27%29/Synopsis/$value</param>
        /// <param name="content">The body of a POST</param>
        /// <param name="connection">Specify a connection object when you want to get the response event from ConnectionResponseReceived</param>
        public HttpRequest(string url, string content = null, Connection connection = null)
        {
            if (connection != null) _con = connection;

            url = (url.IndexOf("http://") >= 0) ? url.Substring(url.IndexOf("http://") + 7) : url;

            Host = url.Split('/')[0].Trim();
            Path = System.Web.HttpUtility.UrlEncode(url.Substring(Host.Length).Trim(), false);
            if (Path == string.Empty) Path = "/";

            Protocol = "HTTP/1.1";
            Content = content;
            RequestType = "GET";

            Headers = new Hashtable();

            if (content != null && content != string.Empty)
            {
                RequestType = "POST";
                Headers.Add("Content-Length", content.Length.ToString());
            }
        }
예제 #7
0
 internal static void FireUdpPacketEvent(byte[] packet, Connection socket)
 {
     OnUdpReceivedPacketEvent.Invoke(new Packet(PacketType.UDP) { Content = packet, Socket = socket });
 }
예제 #8
0
 internal static void FireHttpPacketEvent(byte[] packet, Connection socket)
 {
     try
     {
         if (OnHttpReceivedPacketEvent != null)
             OnHttpReceivedPacketEvent.Invoke(new HttpRequest(packet, socket));
     }
     catch
     {
         //TODO: throwing an exception is expensive and could waste precious resources.  A simple Validation of the message could be very effective.  
         // if an http message is malformed, or it just isn't http, it will get caught here
         Debug.WriteLine("A bad Request was received and ignored. ");
     }
 }
예제 #9
0
 internal static void FireTcpPacketEvent(byte[] packet, uint seqNumber, Connection socket)
 {
     if (OnTcpReceivedPacketEvent != null)
         OnTcpReceivedPacketEvent.Invoke(new Packet(PacketType.TCP) { SequenceNumber = seqNumber, Content = packet, Socket = socket });
 }