예제 #1
0
        /// <summary>
        /// Processes a raw command packet that has been received and identified as being
        /// sent to this connection.
        /// </summary>
        /// <param name="command_packet">Command packet data</param>
        public void ProcessCommandPacket(string command_packet)
        {
            try
            {
                int pcmd = 0;

                Command new_cmd = new Command();
                new_cmd.OPCode = command_packet.Substring(pcmd, 2);
                pcmd += 2;
                new_cmd.SequenceNum = Util.BytesToUint(command_packet.Substring(pcmd, 4));
                pcmd += 4;
                new_cmd.Flags = (byte)command_packet[pcmd];
                pcmd++;
                new_cmd.NumFields = Util.BytesToShort(command_packet.Substring(pcmd, 2));
                pcmd += 2;
                new_cmd.FieldSizes = new short[new_cmd.NumFields];
                for (short i = 0; i < new_cmd.NumFields; i++)
                {
                    new_cmd.FieldSizes[i] = Util.BytesToShort(command_packet.Substring(pcmd, 2));
                    pcmd += 2;
                }

                //Is a reliable packet? Send an acknowledgement back
                if ((new_cmd.Flags & UdpConsts.FLAGS_RELIABLE) > 0)
                {
                    m_Parent.DebugDump("Got a reliable packet (" + new_cmd.SequenceNum.ToString() + ") - sent acknowledgement to sender.");
                    SendUnreliableCommand(0, UdpConsts.OPCODE_RELIABLEACK, new string[] { new_cmd.SequenceNum.ToString() });

                    //Repeat reliable packet - sender must not have received our acknowledgement
                    if (LastReceivedPacketR == new_cmd.SequenceNum)
                    {
                        m_Parent.DebugDump("Repeated reliable packet - not processed.");
                        return;
                    }

                    //Update last received reliable command number
                    LastReceivedPacketR = new_cmd.SequenceNum;
                }
                else
                {
                    LastReceivedPacket = new_cmd.SequenceNum; //Update unreliable command number

                    if ((new_cmd.Flags & UdpConsts.FLAGS_SEQUENCED) > 0)
                    {
                        //If this sequenced packet has arrived too late (a newer one got there first) then don't process it
                        if (LastReceivedPacketSeq > new_cmd.SequenceNum)
                        {
                            m_Parent.DebugDump("Unreliable sequenced packet arrived out of order - ignored.");
                            return;
                        }

                        //Update the last sequenced packet number if we should
                        LastReceivedPacketSeq = new_cmd.SequenceNum;
                    }
                }

                //Encryption check
                if ((new_cmd.Flags & UdpConsts.FLAGS_ENCRYPTED) > 0)
                {
                    if (EncryptionKey != "")
                        if (Util.XORCrypt(command_packet.Substring(pcmd, 2), EncryptionKey) != UdpConsts.ENCRYPT_CHECK_STRING)
                        {
                            //!!BAD ENCRYPTION KEY
                            m_Parent.DebugDump("Received an encrypted packet but the stored encryption key failed to decrypt it!");
                            return;
                        }
                }
                pcmd += 2;

                //Populate the AllFields property of the command
                new_cmd.AllFields = command_packet.Substring(pcmd);

                if ((new_cmd.Flags & UdpConsts.FLAGS_RELIABLE) > 0)
                {
                    //Is this reliable packet a compound piece?
                    if ((new_cmd.Flags & UdpConsts.FLAGS_COMPOUNDPIECE) > 0)
                    {
                        //Assume this is the first piece of the split command
                        if (CompoundCommand == null)
                        {
                            m_Parent.DebugDump("Got first part of a compound command, stored.");
                            CompoundCommand = new Command();
                            CompoundCommand.AllFields = new_cmd.AllFields;
                            CompoundCommand.FieldSizes = new_cmd.FieldSizes;
                            CompoundCommand.Flags = new_cmd.Flags;

                            //Remove the compound piece flag (will be treated as a single packet)
                            CompoundCommand.Flags &= (byte)(~UdpConsts.FLAGS_COMPOUNDPIECE);

                            CompoundCommand.NumFields = new_cmd.NumFields;
                            CompoundCommand.SequenceNum = new_cmd.SequenceNum;
                            CompoundCommand.OPCode = new_cmd.OPCode;
                            return;
                        }
                        else
                        {
                            //Additional pieces have FLAGS_COMPOUNDPIECE set also....

                            m_Parent.DebugDump("Got an additional compound piece.");
                            //Is not the first nor last part so just addon the fields
                            CompoundCommand.AllFields += new_cmd.AllFields;
                            return;
                        }
                    }

                    //The last compound piece has FLAGS_COMPOUNDEND set.
                    if ((new_cmd.Flags & UdpConsts.FLAGS_COMPOUNDEND) > 0)
                    {
                        m_Parent.DebugDump("Got last compound piece - sending to command processing.");

                        //Add the fields on
                        CompoundCommand.AllFields += new_cmd.AllFields;

                        //Swap the incomplete part with the whole command
                        new_cmd = CompoundCommand;
                        CompoundCommand = null;
                    }
                }

                new_cmd.Initialize();

                //If the packet is encrypted decrypt the fields now...
                if ((new_cmd.Flags & UdpConsts.FLAGS_ENCRYPTED) > 0)
                {
                    new_cmd.AllFields = "";
                    //Decrypt the fields
                    for (int i = 0; i < new_cmd.NumFields; i++)
                    {
                        new_cmd.Fields[i] = Util.XORCrypt(new_cmd.Fields[i], EncryptionKey);

                        //Rebuild the allfields property with the decrypted field data
                        new_cmd.AllFields += new_cmd.Fields[i];
                    }
                }

                //Send to command processing
                ProcessCompletedCommand(new_cmd);
            }
			catch
            {
                m_Parent.DebugDump("Exception whilst parsing input from " + RemoteEP.ToString() + " as command, probably not a command. Ignoring.");
            }
        }
예제 #2
0
        /// <summary>
        /// This function is called internally when a command has been parsed and is ready to be processed
        /// by the UDP engine and possibly the host application.
        /// </summary>
        /// <param name="cmd">Object containing data about the command to be processed.</param>
        public void ProcessCompletedCommand(Command cmd)
        {
            //Client sent login details
            if (!Authed)
            {
                if (cmd.OPCode == UdpConsts.OPCODE_LOGINDETAILS)
                {
                    m_Parent.ConnectionAuthing(this, cmd);
                    return;
                }
            }

            //Remote host sent us a ping
            if (cmd.OPCode == UdpConsts.OPCODE_PING)
            {
                if(Authed)
                {
                    m_Parent.DebugDump("Received ping from " + this.RemoteEP.ToString());

                    UpdateTimeout();

                    if (!Server) //If this is not a connection to a server
                        SendUnreliableCommand(0, UdpConsts.OPCODE_PING, null);
                }
                return;
            }

            //Server sent acknowledgement of our connection
            if (cmd.OPCode == UdpConsts.OPCODE_LOGINACK)
            {
                if (cmd.Fields[0] == "OK")
                {
                    Authed = true;
                    m_Parent.DebugDump("Authenticated with " + this.RemoteEP.ToString() + " OK. Connected!");

                    //Send the authenticated event to the third party application
                    m_Parent.AuthenticatedWithConnection(this, true, "");
                }
                else
                {
                    m_Parent.DebugDump("Authentication to " + this.RemoteEP.ToString() + " Failed!");

                    //Send the not authed event
                    m_Parent.AuthenticatedWithConnection(this, false, cmd.Fields[1]);

                    //Disconnect the connection (but don't send a disconnection packet back)
                    m_Parent.RemoveConnection(this, false, cmd.Fields[1]);
                }
                return;
            }

            //Remote host disconnected from us
            if (cmd.OPCode == UdpConsts.OPCODE_DISCONNECT)
            {
                m_Parent.RemoveConnection(this, false, cmd.Fields[0]);
                return;
            }

            //Reliable packet acknowledgement
            if (cmd.OPCode == UdpConsts.OPCODE_RELIABLEACK)
            {
                m_Parent.DebugDump("Received reliable ACK for packet " + cmd.Fields[0] + ".");

                //Get the current reliable command
                ReliableEntry rcmd = null;
                RQueue.GetCurrentReliableCommand(out rcmd);

                //If the sequence number from the one stored in the first field
                //of the ACK is the same as the one in the queue, remove it.
                if (rcmd != null)
                {
                    try
                    {
                        if (rcmd.SequenceNum == Convert.ToUInt32(cmd.Fields[0]))
                        {
                            RQueue.NextReliableCommand();

                            ReliableEntry next_rel = null;
                            RQueue.GetCurrentReliableCommand(out next_rel);

                            if (next_rel != null)
                            {
                                m_Parent.DebugDump("Moving reliable packet queue - next packet is " + next_rel.SequenceNum.ToString());

                                //Send the next reliable packet
                                m_Parent.SendData(RemoteEP.Address.ToString(), RemoteEP.Port, next_rel.CommandPacket);
                            }
                            else
                                m_Parent.DebugDump("Moving reliable packet queue - no more packets on reliable queue.");
                        }
                    }
                    catch (Exception e)
                    {
                        m_Parent.DebugDump("Exception: " + e.Message);
                    }
                }
            }

            //Give unrecognised commands to the application
            if(Authed)
                m_Parent.CommandReceived(this, cmd);
        }
예제 #3
0
		/// <summary>
		/// Initialises the connection object, setting variables to defaults.
		/// </summary>
		/// <param name="udp">Reference to the main core engine that created the connection.</param>
        public Connection(CommonUdp udp)
        {
            EncryptionKey = "";
            RemoteEP = null;
            LastSentPacket = 0;
            LastReceivedPacket = 0;
            LastSentPacketR = 1;
            LastReceivedPacketR = 0;
			LastReceivedPacketSeq = 0;
            Authed = false; //Either (server) client is authed or (client) is authed with server
            Server = false; //Connection is a server connection
            m_Parent = udp;
            TimeoutTime = DateTime.Now.AddSeconds(UdpConsts.CONNECTION_TIMEOUT);
            CompoundCommand = null;
			m_RequestID = "";

            RQueue = new ReliableQueue();
        }
예제 #4
0
        /// <summary>
        /// Called when a connection is attempting to authenticate
        /// </summary>
        /// <param name="cn">Connection</param>
        /// <param name="cmd">Command</param>
        /// <returns>UDP_OK or error code</returns>
        internal int ConnectionAuthing(Connection cn, Command cmd)
        {
			//Is the connection already authed?
			if(cn.Authed)
			{
				DebugDump("Connection " + cn.RemoteEP.ToString() + " sent an auth packet but is already authed? Command ignored.");
				return UdpConsts.UDP_OK;
			}

            ConnectionAuthEventArgs ea = new ConnectionAuthEventArgs(cn, cmd);

            DebugDump("Connection " + cn.RemoteEP.ToString() + " sent login data...");

            //Have the third party client app process the login data
            if (OnConnectionAuth != null)
                OnConnectionAuth(null, ea);

            //Clamp the disallow reason to 200 characters
            if (ea.DisallowReason.Length > 200)
                ea.DisallowReason = ea.DisallowReason.Substring(0, 200);

            if (!ea.AllowConnection)
            {
                DebugDump("Login data is bad, rejecting connection.");
                cn.SendUnreliableCommand(0, UdpConsts.OPCODE_LOGINACK, new string[] { "FAIL", ea.DisallowReason });
                m_Clients.RemoveConnection(cn, true, ea.DisallowReason);
            }
            else
            {
                DebugDump("Login data is ok, connection authed.");
                cn.SendUnreliableCommand(0, UdpConsts.OPCODE_LOGINACK, new string[] { "OK" });
                cn.Authed = true;
            }
            return UdpConsts.UDP_OK;
        }
예제 #5
0
        /// <summary>
        /// Called by a connection object when a command comes in
        /// </summary>
        /// <param name="cn">Connection that sent the command</param>
        /// <param name="cmd">Command received</param>
        /// <returns>UDP_OK or error code</returns>
        internal int CommandReceived(Connection cn, Command cmd)
        {
            if (OnCommandReceived != null)
                OnCommandReceived(null, new CommandEventArgs(cmd, cn, null));

            return UdpConsts.UDP_OK;
        }
예제 #6
0
        /// <summary>
        /// Processes a connectionless command.
        /// </summary>
        /// <param name="c">Command to process</param>
        /// <returns>UDP_OK or error code.</returns>
        private int ProcessConnectionlessComand(IPEndPoint remote_ep, Command c)
        {
            int retval = UdpConsts.UDP_OK;

            try
            {
                //========================================================================
                //Connection request from a remote host
                if (c.OPCode == UdpConsts.OPCODE_CONNECTIONREQUEST)
                {
                    Connection temp;
                    bool cant_connect = false;

                    m_Servers.ConnectionByIPPort(remote_ep.Address.ToString(), remote_ep.Port, out temp);
                    if (temp != null)
                    {
                        DebugDump("Unable to accept connection, connection exists in server list.");
                        retval = UdpConsts.UDP_ALREADYCONNECTED;
                        cant_connect = true;
                    }

                    m_Clients.ConnectionByIPPort(remote_ep.Address.ToString(), remote_ep.Port, out temp);
                    if (temp != null)
                    {
                        DebugDump("Unable to accept connection, connection exists in client list.");
                        retval = UdpConsts.UDP_ALREADYCONNECTED;
                        cant_connect = true;
                    }

                    //Can't accept this connection as we already have it in the list
                    //Client will just have to wait until it times out.
                    if (cant_connect)
                    {
                        SendConnectionlessCommand(remote_ep.Address.ToString(), remote_ep.Port, UdpConsts.OPCODE_CONNECTIONACK, new string[] { c.Fields[0], "FAIL", "Connection from this client already exists." });
                        return retval;
                    }

                    string encryption_key = "";
					
					//Is encryption on? If so generate an encryption key.
					if(m_bEncrypt)
						encryption_key = Util.GenerateEncryptionKey();

					//Create a new connection object and add it to the clients list...
                    Connection new_conn = new Connection(this);
                    new_conn.EncryptionKey = encryption_key;
                    new_conn.RemoteEP = remote_ep;

                    DebugDump("Remote host at " + remote_ep.ToString() + " requested a connection.");
                    DebugDump("Sending acknowledgement packet.");
                    SendConnectionlessCommand(remote_ep.Address.ToString(), remote_ep.Port, UdpConsts.OPCODE_CONNECTIONACK, new string[] { c.Fields[0], "OK", encryption_key });

                    DebugDump("Adding remote host to clients list.");
                    m_Clients.NewConnection(new_conn);

                    return UdpConsts.UDP_OK;
                }

                //========================================================================
                //Connection acknowledge from a remote host
                if (c.OPCode == UdpConsts.OPCODE_CONNECTIONACK)
                {
                    DebugDump("Got connection acknowledgement from " + remote_ep.ToString() + ".");

					if(!m_ConnTimeouts.EntryExists(c.Fields[0]))
					{
						//Make sure we actually _asked_ for this connection ack.
						DebugDump("Connection accept packet sent by " + remote_ep.ToString() + " but was unrequested - ignored.");
					}
                    else if (c.Fields[1] == "OK")
                    {
						//Server accepted our connection.

                        DebugDump("Connection " + c.Fields[0] + " accepted by " + remote_ep.ToString());

						//Create and store a connection object for the server that accepted us.
                        Connection new_con = new Connection(this);
                        new_con.EncryptionKey = c.Fields[2];
                        new_con.RemoteEP = remote_ep;
                        new_con.Server = true; //Is a connection to a server
						new_con.RequestID = c.Fields[0];
                        m_Servers.NewConnection(new_con);

						//Remove from the timeouts list
						m_ConnTimeouts.RemoveConnectionEntry(c.Fields[0]);

                        //Sending login data is to be handled in the actual application
                        //Basically it needs to send an command packet to the host containing the login data.
                        DebugDump("Server is requesting login information, sending.");
                        if (OnLoginRequested != null)
                            OnLoginRequested(null, new LoginSendEventArgs(new_con, true, ""));
                        else
                            //Application did nothing, so send a blank login packet now.
                            SendCommand(remote_ep.Address.ToString(), remote_ep.Port, new_con.EncryptionKey, 0, 0, UdpConsts.OPCODE_LOGINDETAILS, null);
                    }
                    else
                    {
						//Server did not accept our connection.

                        //Make a temporary connection object
                        Connection new_con = new Connection(this);
                        new_con.EncryptionKey = c.Fields[1];
                        new_con.RemoteEP = remote_ep;
                        new_con.Server = true; //Is a connection to a server

                        DebugDump("Connection not accepted - " + c.Fields[2] + ".");
                        if (OnLoginRequested != null)
                            OnLoginRequested(null, new LoginSendEventArgs(new_con, false, c.Fields[1]));
                    }

                    return UdpConsts.UDP_OK;
                }
            }
            catch (Exception e)
            {
                DebugDump("Exception whilst processing connectionless command (" + e.Message + ")");
                return UdpConsts.UDP_FAIL;
            }

            //========================================================================
            //Call the event (so it can be handled in the application)
            if (OnConnectionlessCommand != null)
                OnConnectionlessCommand(null, new CommandEventArgs(c, null, remote_ep));

            return UdpConsts.UDP_OK;
        }
예제 #7
0
        /// <summary>
        /// Receive loop continually receives data.
        /// </summary>
        private void ReceiveLoop()
        {
            int iExCount = 0;

			if(m_CurrentIP == "")
				m_LocalEndPoint = new IPEndPoint(IPAddress.Any, m_CurrentPort);
			else
				m_LocalEndPoint = new IPEndPoint(IPAddress.Parse(m_CurrentIP), m_CurrentPort);

            DebugDump("Created the local IP endpoint at " + m_LocalEndPoint.ToString( ));
            try
            {
                m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                DebugDump("Created the socket.");

                m_Socket.Bind(m_LocalEndPoint);
                DebugDump("Bound the socket to the local end point, now entering receive loop...");

                m_iState = UdpConsts.UDP_STATE_LISTENING;

                if (OnListenStateChanged != null)
                    OnListenStateChanged(null, new ListenEventArgs(true));

                while (true)
                {
                    try
                    {
                        //Make space to store the data from the socket
                        Byte[] received = new Byte[UdpConsts.MAX_COMMAND_LEN];
                        
                        //Create an end point, just give it temporary values
                        EndPoint remoteEP = new IPEndPoint(m_LocalEndPoint.Address, m_LocalEndPoint.Port);

                        //Read bytes from the socket
                        int bytesReceived = m_Socket.ReceiveFrom(received, ref remoteEP);
                        IPEndPoint remoteIPEP = (IPEndPoint)remoteEP;

                        //string str_received = Encoding.UTF8.GetString(received);
                        string str_received = Util.BytesToString(received);
                        str_received = str_received.Substring(0, bytesReceived);

                        //Fire the received event if it is being used (allowing raw data to be caught)
                        if(OnDataReceived != null)
                            OnDataReceived(null, new ReceivedEventArgs((IPEndPoint)remoteEP, str_received));

                        //Handle connectionless packets
                        try
                        {
                            int pcmd = 0;

                            //Allocate a temporary command object
                            Command new_cmd = new Command();

                            //Parse the start of the command
                            new_cmd.OPCode = str_received.Substring(pcmd, 2);
                            pcmd += 2;
                            new_cmd.SequenceNum = Util.BytesToUint(str_received.Substring(pcmd, 4));
                            pcmd += 4;
                            new_cmd.Flags = (byte)str_received[pcmd];
                            pcmd++;

                            //If the command packet is connectionless then parse it now.
                            if ((new_cmd.Flags & UdpConsts.FLAGS_CONNECTIONLESS) > 0)
                            {
                                new_cmd.NumFields = Util.BytesToShort(str_received.Substring(pcmd, 2));
                                pcmd += 2;
                                new_cmd.FieldSizes = new short[new_cmd.NumFields];
                                for (short i = 0; i < new_cmd.NumFields; i++)
                                {
                                    new_cmd.FieldSizes[i] = Util.BytesToShort(str_received.Substring(pcmd, 2));
                                    pcmd += 2;
                                }
                                pcmd += 2;
                                new_cmd.AllFields = str_received.Substring(pcmd);
                                new_cmd.Initialize();

                                //Process the connectionless command
                                ProcessConnectionlessComand(remoteIPEP, new_cmd);
                            }
                            else
                            {
                                //Locate connection this command belongs to and process it there.
                                Connection conn;

                                //Check both the client and server lists
                                m_Clients.ConnectionByRemoteEndpoint(remoteIPEP, out conn);
                                if(conn == null)
                                    m_Servers.ConnectionByRemoteEndpoint(remoteIPEP, out conn);

                                if (conn == null)
                                    DebugDump("Connection-related packet with no matching connection arrived - ignored.");
                                else
                                    conn.ProcessCommandPacket(str_received);
                            }
                        }
                        catch
                        {
                            DebugDump("Exception whilst parsing input from " + remoteIPEP.ToString() + " as command, probably not a command. Ignoring.");
                        }

                        //Reset the exception count to 0
                        iExCount = 0;
                    }
                    catch (SocketException se)
                    {
                        if ((se.ErrorCode != 10061) && (se.ErrorCode != 10054))
                        {
                            //Fire the socket error event.
                            if (se.ErrorCode != 10004)
                            {
                                if (OnSocketError != null)
                                    OnSocketError(null, new SocketEventArgs(se.ErrorCode, se.Message));
                            }

                            DebugDump("Socket Exception in receive loop (" + se.ErrorCode.ToString() + "): " + se.Message);
                            if (m_iState != UdpConsts.UDP_STATE_LISTENING)
                                break;

                            iExCount++;
                        }
                    }
                    catch (Exception e)
                    {
                        DebugDump("Exception in receive loop: " + e.Message);
                        if (m_iState != UdpConsts.UDP_STATE_LISTENING)
                            break;

                        iExCount++;
                    }

                    if (iExCount == UdpConsts.MAX_EXCEPTIONS)
                    {
                        DebugDump("Got too many consecutive exceptions in the receive loop, terminating.");
                        break;
                    }
                }
            }
            catch (SocketException se)
            {
                //Fire the socket error event.
                if (OnSocketError != null)
                    OnSocketError(null, new SocketEventArgs(se.ErrorCode, se.Message));

                DebugDump("Socket Exception (" + se.ErrorCode.ToString() + "): " + se.Message);
            }
            catch (Exception e)
            {
                DebugDump("Exception: " + e.Message);
            }

            //We are out of the loop but the server thinks we are not? make sure it does.
            if((m_iState != UdpConsts.UDP_STATE_IDLE) && (m_iState != UdpConsts.UDP_STATE_CLOSING))
            {
				StopListen();
            }

            DebugDump("Set the system state to idle.");

            //Set the state to idle.
            m_iState = UdpConsts.UDP_STATE_IDLE;

            DebugDump("Exited the receive loop.");
        }