using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;
using System.Xml.Serialization;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using WinnyProtocolAnalyzer.Data;
//using WinnyProtocolAnalyzer.Analyze;

namespace WinnyProtocolAnalyzer
{
    public partial class MainWindow : Form
    {
        AutoResetEvent clientEvent = new AutoResetEvent(false);

        bool enable = true;
        bool analyzing = false;

        Data.Data Data;
        //List<Clusters> Clusters = new List<Clusters>();
        //List<Queries> Queries = new List<Queries>();
        //List<NodeDomains> NodeDomains = new List<NodeDomains>();
        //List<LineSpeeds> LineSpeeds = new List<LineSpeeds>();
        //List<KeyDomains> KeyDomains = new List<KeyDomains>();

        //object ClustersLock = new object();
        //object QueriesLock = new object();
        //object NodeDomainsLock = new object();
        //object LineSpeedsLock = new object();
        //object KeyDomainsLock = new object();

        int iQueryID = 0;
        int iClientNodeCount = 0;

        IPAddress localIPAddress = null;

        List<EndPoint> connections = new List<EndPoint>();
        List<Query> queryQueue = new List<Query>();

        object queryQueueLock = new object();
        ReaderWriterLock connectionsLock = new ReaderWriterLock();

        Settings settings;

        TcpListener listener;

        private void Client(object obj)
        {
            while (analyzing)
            {
                Thread.Sleep(100);

                try
                {
                    EndPoint EndPoint = null;

                    if (connections.Count >= settings.Connections)
                    {
                        Thread.Sleep(1000);
                        continue;
                    }

                    EndPoint = Data.NodeList[iClientNodeCount++].EndPoint;

                    if (iClientNodeCount >= Data.NodeList.Count)
                    {
                        iClientNodeCount = 0;
                    }

                    try
                    {
                        connectionsLock.AcquireReaderLock(Timeout.Infinite);
                        if (connections.Exists(delegate(EndPoint End)
                        {
                            if (EndPoint.IPAddress == End.IPAddress)
                            {
                                return true;
                            }
                            return false;
                        }))
                        {
                            continue;
                        }
                    }
                    finally
                    {
                        connectionsLock.ReleaseReaderLock();
                    }

                    try
                    {
                        TcpClient Client = new TcpClient();
                        Client.BeginConnect(EndPoint.Point, EndPoint.Port, ClientCallback, new object[] { EndPoint, Client });

                        clientEvent.WaitOne(Property.ThreadWait);
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine("Exception caught (Client.BeinConnect)");
                        Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                        Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                        Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("Exception caught (Client)");
                    Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                    Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                    Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
                }
            }
        }

        private void ClientCallback(IAsyncResult ar)
        {
            try
            {
                #region Initialize
                EndPoint EndPoint = (EndPoint)((object[])ar.AsyncState)[0];
                TcpClient Client = (TcpClient)((object[])ar.AsyncState)[1];

                Client.EndConnect(ar);

                try
                {
                    connectionsLock.AcquireWriterLock(Timeout.Infinite);
                    connections.Add(EndPoint);
                }
                finally
                {
                    connectionsLock.ReleaseWriterLock();
                }


                clientEvent.Set();
                #endregion

                try
                {
                    using (NetworkStream Stream = Client.GetStream())
                    {
                        RC4 ReadRC4, WriteRC4;
                        Network.KeyExchange(Stream, out ReadRC4, out WriteRC4);

                        Protocol(EndPoint, Stream, ReadRC4, WriteRC4);
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("Exception caught (ClientCallback)");
                    Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                    Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                    Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
                }
                finally
                {
                    #region Finalize
                    try
                    {
                        connectionsLock.AcquireWriterLock(Timeout.Infinite);
                        connections.Remove(EndPoint);
                    }
                    finally
                    {
                        connectionsLock.ReleaseWriterLock();
                    }
                    #endregion
                }
            }
            catch
            {
            }
        }

        private void CloseConnection(NetworkStream stream, InitializeResult result, RC4 writeRC4)
        {
            switch (result)
            {
                case InitializeResult.Reverse:
                    {
                        List<byte> Buffer = new List<byte>();
                        Buffer.Add((byte)Command.ConnectionClose);
                        Network.Write(Buffer.ToArray(), stream, writeRC4);
                    }
                    break;
                case InitializeResult.Transfer:
                    {
                        List<byte> Buffer = new List<byte>();
                        Buffer.Add((byte)Command.ConnectionLimit);
                        Network.Write(Buffer.ToArray(), stream, writeRC4);
                    }
                    break;
            }
        }

        private void Communication(Node remoteNode, NetworkStream stream, RC4 readRC4, RC4 writeRC4)
        {
            int iKeyword = 0;
            int iCluster = 0;

            #region Diffusion Query
            DateTime DiffusionQueryTime = DateTime.Now;
            {
                List<byte> Buffer = new List<byte>();
                Buffer.Add((byte)Command.DiffusionQueryRequest);
                Network.Write(Buffer.ToArray(), stream, writeRC4);
            }
            #endregion
            try
            {

                while (analyzing)
                {

                    Thread.Sleep(500);

                    #region Read
                    while (stream.DataAvailable)
                    {
                        int iIndex = 0;
                        byte[] aobBuffer = Network.Read(stream, readRC4);
                        Command Command = (Command)Buffer.GetByte(aobBuffer, iIndex++);

                        switch (Command)
                        {
                            case Command.NodeReference:
                                {
                                    Node Node = new Node();

                                    Node.EndPoint.IPAddress = new IPAddress(BitConverter.ToUInt32(aobBuffer, iIndex)).ToString();
                                    Node.EndPoint.Point = (settings.ReverseResolution || settings.ReverseResolutionOnlyForNode) ? Domains.GetNodeEndPoint(Node.EndPoint.IPAddress, true) : Node.EndPoint.IPAddress;
                                    iIndex += sizeof(int);

                                    Node.EndPoint.Port = BitConverter.ToInt32(aobBuffer, iIndex);
                                    iIndex += sizeof(int);

                                    // BBS Port
                                    iIndex += sizeof(int);

                                    // BBS Node Flag
                                    iIndex += sizeof(byte);

                                    Node.LineSpeed = BitConverter.ToSingle(aobBuffer, iIndex);
                                    iIndex += sizeof(float);

                                    int[] ClusterWordLengths = new int[3];
                                    for (int i = 0; i < ClusterWordLengths.Length; i++)
                                    {
                                        ClusterWordLengths[i] = Buffer.GetByte(aobBuffer, iIndex++);
                                    }

                                    for (int i = 0; i < ClusterWordLengths.Length; i++)
                                    {
                                        Node.ClusterWords[i] = Encoding.Default.GetString(aobBuffer, iIndex, ClusterWordLengths[i]);
                                        iIndex += ClusterWordLengths[i];
                                    }

                                    Data.AddNode(Node);
                                    AddNodeList(Node);
                                    AddMessageView(remoteNode.EndPoint.Point, Property.Command_04, string.Format("{0}:{1}, {2:N1}", Node.EndPoint.Point, Node.EndPoint.Port, Node.LineSpeed));
                                    AddNodeView(Node.EndPoint);

                                    continue;
                                }
                            case Command.DiffusionQueryRequest:
                                {
                                    AddMessageView(remoteNode.EndPoint.Point, Property.Command_10, "");
                                    byte[] aobWriteBuffer = CreateDiffusionQuery(remoteNode.LineSpeed);
                                    Network.Write(aobWriteBuffer, stream, writeRC4);
                                }
                                break;
                            case Command.QueryRequest:
                                {
                                    string sKeywords = Encoding.Default.GetString(aobBuffer, iIndex, aobBuffer.Length - (iIndex + Property.TripLength + sizeof(int)));
                                    iIndex += aobBuffer.Length - (iIndex + Property.TripLength + sizeof(int));

                                    string sTrip;
                                    if (aobBuffer[iIndex] != 0)
                                    {
                                        sTrip = Encoding.Default.GetString(aobBuffer, iIndex, Property.TripLength - 1);
                                    }
                                    else
                                    {
                                        sTrip = "";
                                    }
                                    iIndex += Property.TripLength;

                                    int iQueryID = BitConverter.ToInt32(aobBuffer, iIndex);
                                    iIndex += sizeof(int);

                                    AddMessageView(remoteNode.EndPoint.Point, Property.Command_12, string.Format("キーワード: \"{0}\"", sKeywords));
                                    AddQueryList(remoteNode.EndPoint.Point, sKeywords);
                                    byte[] aobWriteBuffer = CreateQueryReply(sKeywords, sTrip, iQueryID);
                                    Network.Write(aobWriteBuffer, stream, writeRC4);
                                }
                                break;
                            case Command.QuerySend:
                                {
                                    int iReplyQueryFlag = aobBuffer[iIndex++];
                                    int iDiffusionQueryFlag = aobBuffer[iIndex++];
                                    int iDownQueryFlag = aobBuffer[iIndex++];
                                    int iBBSQueryFlag = aobBuffer[iIndex++];

                                    if (iDiffusionQueryFlag == 0)
                                    {
                                        List<byte> WriteBuffer = new List<byte>();
                                        WriteBuffer.Add((byte)Command.QuerySend);
                                        WriteBuffer.Add((byte)iReplyQueryFlag);
                                        WriteBuffer.Add((byte)iDiffusionQueryFlag);
                                        WriteBuffer.Add((byte)iDownQueryFlag);
                                        WriteBuffer.Add((byte)iBBSQueryFlag);

                                        int iQueryID = BitConverter.ToInt32(aobBuffer, iIndex);
                                        WriteBuffer.AddRange(BitConverter.GetBytes(iQueryID));
                                        iIndex += sizeof(int);

                                        int iKeywordsLength = aobBuffer[iIndex++];
                                        WriteBuffer.Add((byte)iKeywordsLength);
                                        string sKeywords = Encoding.Default.GetString(aobBuffer, iIndex, iKeywordsLength);
                                        WriteBuffer.AddRange(Encoding.Default.GetBytes(sKeywords));
                                        iIndex += iKeywordsLength;
                                        string sTrip;
                                        if (aobBuffer[iIndex] != 0)
                                        {
                                            sTrip = Encoding.Default.GetString(aobBuffer, iIndex, Property.TripLength - 1);
                                            WriteBuffer.AddRange(Encoding.Default.GetBytes(sTrip));
                                            WriteBuffer.Add(0);
                                        }
                                        else
                                        {
                                            sTrip = "";
                                            WriteBuffer.AddRange(new byte[Property.TripLength]);
                                        }

                                        iIndex += Property.TripLength;

                                        AddQueryList(remoteNode.EndPoint.Point, sKeywords);

                                        // NodeList
                                        EndPoint TargetEndPoint = new EndPoint();
                                        if (iReplyQueryFlag == 0)
                                        {
                                            int iNodeCount = aobBuffer[iIndex++];
                                            WriteBuffer.Add((byte)(iNodeCount + 1));
                                            for (int i = 0; i < iNodeCount - 1; i++)
                                            {
                                                IPAddress RemoteIPAddress = new IPAddress(BitConverter.ToUInt32(aobBuffer, iIndex));
                                                WriteBuffer.AddRange(RemoteIPAddress.GetAddressBytes());
                                                iIndex += sizeof(int);
                                                int RemotePort = BitConverter.ToUInt16(aobBuffer, iIndex);
                                                WriteBuffer.AddRange(BitConverter.GetBytes((ushort)RemotePort));
                                                iIndex += sizeof(ushort);

                                                EndPoint NodeEndPoint = new EndPoint(RemoteIPAddress, RemotePort, (settings.ReverseResolution || settings.ReverseResolutionOnlyForNode) ? true : false);

                                                AddNodeView(NodeEndPoint);
                                                Data.AddNode(new Node(NodeEndPoint));
                                            }

                                            IPAddress SourceIPAddress = new IPAddress(BitConverter.ToUInt32(aobBuffer, iIndex));
                                            WriteBuffer.AddRange(IPAddress.Parse(remoteNode.EndPoint.IPAddress).GetAddressBytes());
                                            iIndex += sizeof(int);
                                            int SourcePort = BitConverter.ToUInt16(aobBuffer, iIndex);
                                            WriteBuffer.AddRange(BitConverter.GetBytes((ushort)remoteNode.EndPoint.Port));
                                            iIndex += sizeof(ushort);
                                            EndPoint NeighborEndPoint = new EndPoint(remoteNode.EndPoint.IPAddress, SourcePort, (settings.ReverseResolution || settings.ReverseResolutionOnlyForNode) ? true : false);

                                            AddNodeView(NeighborEndPoint);
                                            Data.AddNode(new Node(NeighborEndPoint));

                                            if (iNodeCount < 7)
                                            {
                                                WriteBuffer.AddRange(localIPAddress.GetAddressBytes());
                                                WriteBuffer.AddRange(BitConverter.GetBytes((ushort)settings.Port));
                                            }
                                            else
                                            {
                                                WriteBuffer[1] = 1;
                                                WriteBuffer.AddRange(localIPAddress.GetAddressBytes());
                                                WriteBuffer.AddRange(BitConverter.GetBytes((ushort)settings.Port));
                                                TargetEndPoint = remoteNode.EndPoint;
                                            }
                                        }
                                        else
                                        {
                                            int iNodeCount = aobBuffer[iIndex++];
                                            WriteBuffer.Add((byte)(iNodeCount + 1));
                                            EndPoint RemoteEndPoint = null;
                                            for (int i = 0; i < iNodeCount; i++)
                                            {
                                                IPAddress RemoteIPAddress = new IPAddress(BitConverter.ToUInt32(aobBuffer, iIndex));
                                                WriteBuffer.AddRange(RemoteIPAddress.GetAddressBytes());
                                                iIndex += sizeof(int);
                                                int RemotePort = BitConverter.ToUInt16(aobBuffer, iIndex);
                                                WriteBuffer.AddRange(BitConverter.GetBytes((ushort)RemotePort));
                                                iIndex += sizeof(ushort);

                                                if (RemotePort == settings.Port)
                                                {
                                                    TargetEndPoint = RemoteEndPoint;
                                                }
                                                else
                                                {
                                                    RemoteEndPoint = new EndPoint(RemoteIPAddress, RemotePort, (settings.ReverseResolution || settings.ReverseResolutionOnlyForNode) ? true : false);
                                                    AddNodeView(TargetEndPoint);
                                                    Data.AddNode(new Node(RemoteEndPoint));
                                                }
                                            }
                                        }

                                        // KeyList
                                        int iKeyCount = BitConverter.ToInt16(aobBuffer, iIndex);
                                        iIndex += sizeof(short);

                                        if (iKeyCount != 0)
                                        {
                                            byte[] aobKeyListBuffer = new byte[aobBuffer.Length - iIndex];
                                            Buffer.BlockCopy(aobBuffer, iIndex, aobKeyListBuffer, 0, aobKeyListBuffer.Length);
                                            ProcessQuery(remoteNode.EndPoint, aobKeyListBuffer, iKeyCount);
                                        }

                                        List<Key> KeyList = Data.GetKey(sKeywords, sTrip);
                                        WriteBuffer.AddRange(BitConverter.GetBytes((short)KeyList.Count));
                                        for (int i = 0; i < KeyList.Count; i++)
                                        {
                                            WriteBuffer.AddRange(IPAddress.Parse(KeyList[i].EndPoint.IPAddress).GetAddressBytes());
                                            WriteBuffer.AddRange(BitConverter.GetBytes((ushort)KeyList[i].EndPoint.Port));

                                            WriteBuffer.AddRange(new byte[4]);   // BBS IP
                                            WriteBuffer.AddRange(new byte[2]);   // BBS Port

                                            WriteBuffer.AddRange(BitConverter.GetBytes(KeyList[i].Length));
                                            WriteBuffer.AddRange(Utility.HexStringToByteArray(KeyList[i].Hash));
                                            WriteBuffer.Add((byte)Encoding.Default.GetByteCount(KeyList[i].Name));

                                            byte[] aobChecksum = BitConverter.GetBytes(Utility.CalculateChecksum(KeyList[i].Name));
                                            WriteBuffer.AddRange(aobChecksum);

                                            byte[] aobFileName = Encoding.Default.GetBytes(KeyList[i].Name);
                                            RC4 Crypter = new RC4(new byte[1] { aobChecksum[0] });
                                            Crypter.Cipher(aobFileName, aobFileName.Length);
                                            WriteBuffer.AddRange(aobFileName);

                                            if (string.IsNullOrEmpty(KeyList[i].Trip))
                                            {
                                                WriteBuffer.AddRange(new byte[Property.TripLength]);
                                            }
                                            else
                                            {
                                                WriteBuffer.AddRange(Encoding.Default.GetBytes(KeyList[i].Trip));
                                                WriteBuffer.Add(0);
                                            }

                                            WriteBuffer.Add(0);  // BBS Trip Auth Info Length
                                            WriteBuffer.AddRange(BitConverter.GetBytes(Property.KeyTimer));
                                            WriteBuffer.AddRange(BitConverter.GetBytes(0));
                                            WriteBuffer.AddRange(BitConverter.GetBytes(Utility.UnixTime(DateTime.Now)));
                                            WriteBuffer.Add(0);
                                            WriteBuffer.Add(5);
                                        }

                                        lock (queryQueueLock)
                                        {
                                            queryQueue.Add(new Query(remoteNode.EndPoint, TargetEndPoint, WriteBuffer.ToArray()));
                                        }

                                        AddMessageView(remoteNode.EndPoint.Point, Property.Command_13, string.Format("キーワード: {0}, トリップ: {1}, キー数: {2}", sKeywords, sTrip, iKeyCount));
                                    }
                                    else
                                    {
                                        Thread ProcessQueryThread = new Thread(ProcessQuery);
                                        ProcessQueryThread.Name = "Process Query Thread";
                                        ProcessQueryThread.Start(new object[] { remoteNode.EndPoint, aobBuffer });
                                    }
                                }
                                break;
                            default:
                                AddMessageView(remoteNode.EndPoint.Point, ((int)Command).ToString(), "(コネクションを切断します)");
                                return;
                        }

                        break;
                    }
                    #endregion

                    #region Write

                    if (queryQueue.Count != 0)
                    {
                        Query Remove = null;
                        lock (queryQueueLock)
                        {
                            foreach (Query query in queryQueue)
                            {
                                if (query.Destination.IPAddress == remoteNode.EndPoint.IPAddress && query.Source.IPAddress != remoteNode.EndPoint.IPAddress ||
                                   (query.Destination.Point == "" && query.Destination.IPAddress == "" && query.Source.IPAddress != remoteNode.EndPoint.IPAddress))
                                {
                                    Remove = query;
                                    Network.Write(query.Data, stream, writeRC4);
                                }
                            }
                            queryQueue.Remove(Remove);
                        }
                    }


                    if ((DateTime.Now - DiffusionQueryTime).TotalSeconds >= (double)Property.DiffusionQueryInterval)
                    {
                        List<byte> Buffer = new List<byte>();
                        Buffer.Add((byte)Command.DiffusionQueryRequest);
                        Network.Write(Buffer.ToArray(), stream, writeRC4);
                        DiffusionQueryTime = DateTime.Now;
                    }

                    if (iCluster < Property.ClusterCount && !string.IsNullOrEmpty(settings.ClusterWords[iCluster]))
                    {
                        Network.Write(CreateQuery(settings.ClusterWords[iCluster++], ""), stream, writeRC4);
                        if (iCluster >= Property.ClusterCount || iCluster >= settings.ClusterWords.Length)
                        {
                            iKeyword = 0;
                        }
                        continue;
                    }

                    if (settings.Keywords.Count != 0 && iKeyword < settings.Keywords.Count)
                    {
                        if (settings.Keywords[iKeyword].Enable)
                        {
                            Network.Write(CreateQuery(settings.Keywords[iKeyword++].Keywords, ""), stream, writeRC4);
                        }
                        if (iKeyword <= settings.Keywords.Count)
                        {
                            iCluster = 0;
                        }
                        continue;
                    }
                    #endregion
                }

            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception caught (Communication)");
                Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
            }
        }

        private byte[] CreateDiffusionQuery(float lineSpeed)
        {
            List<byte> Buffer = new List<byte>();
            Buffer.Add((byte)Command.QuerySend);

            Buffer.Add(0);  // Normal Query
            Buffer.Add(1);  // Diffusion Query
            Buffer.Add(0);  // Up Stream
            Buffer.Add(0);  // File Query
            Buffer.AddRange(BitConverter.GetBytes(iQueryID++)); // Query ID
            Buffer.Add(0);  // Keyword Length
            Buffer.AddRange(new byte[11]);  // Trip

            Buffer.Add(1);  // Relay Node Count
            Buffer.AddRange(localIPAddress.GetAddressBytes());
            Buffer.AddRange(BitConverter.GetBytes((ushort)settings.Port));

            //Buffer.AddRange(BitConverter.GetBytes((short)0));
            int iQueryCount = (int)(lineSpeed / 40 * 120);
            if (iQueryCount < 1200 && lineSpeed > settings.LineSpeed)
            {
                iQueryCount = 1200;
            }
            if (iQueryCount >= 3000)
            {
                iQueryCount = 2001;
            }
            List<Key> KeyList = Data.GetKey(iQueryCount);

            Buffer.AddRange(BitConverter.GetBytes((short)KeyList.Count));
            for (int i = 0; i < KeyList.Count; i++)
            {
                Buffer.AddRange(IPAddress.Parse(KeyList[i].EndPoint.IPAddress).GetAddressBytes());
                Buffer.AddRange(BitConverter.GetBytes((short)KeyList[i].EndPoint.Port));

                Buffer.AddRange(new byte[4]);   // BBS IP
                Buffer.AddRange(new byte[2]);   // BBS Port

                Buffer.AddRange(BitConverter.GetBytes(KeyList[i].Length));
                Buffer.AddRange(Utility.HexStringToByteArray(KeyList[i].Hash));
                Buffer.Add((byte)Encoding.Default.GetByteCount(KeyList[i].Name));

                byte[] aobChecksum = BitConverter.GetBytes(Utility.CalculateChecksum(KeyList[i].Name));
                Buffer.AddRange(aobChecksum);

                byte[] aobFileName = Encoding.Default.GetBytes(KeyList[i].Name);
                RC4 Crypter = new RC4(new byte[1] { aobChecksum[0] });
                Crypter.Cipher(aobFileName, aobFileName.Length);
                Buffer.AddRange(aobFileName);

                if (string.IsNullOrEmpty(KeyList[i].Trip))
                {
                    Buffer.AddRange(new byte[Property.TripLength]);
                }
                else
                {
                    Buffer.AddRange(Encoding.Default.GetBytes(KeyList[i].Trip));
                    Buffer.Add(0);
                }

                Buffer.Add(0);  // BBS Trip Auth Info Length
                Buffer.AddRange(BitConverter.GetBytes(Property.KeyTimer));
                Buffer.AddRange(BitConverter.GetBytes(0));
                Buffer.AddRange(BitConverter.GetBytes(Utility.UnixTime(DateTime.Now)));
                Buffer.Add(0);
                Buffer.Add(5);
            }

            return Buffer.ToArray();
        }

        private byte[] CreateQuery(string sKeywords, string sTrip)
        {
            List<byte> Buffer = new List<byte>();
            Buffer.Add((byte)Command.QuerySend);

            Buffer.Add(0);
            Buffer.Add(0);
            Buffer.Add(0);
            Buffer.Add(0);
            Buffer.AddRange(BitConverter.GetBytes(iQueryID++));

            Buffer.Add((byte)Encoding.Default.GetByteCount(sKeywords));
            Buffer.AddRange(Encoding.Default.GetBytes(sKeywords));
            if (string.IsNullOrEmpty(sTrip))
            {
                Buffer.AddRange(new byte[Property.TripLength]);
            }
            else
            {
                if (Encoding.Default.GetByteCount(sTrip) == Property.TripLength)
                {
                    Buffer.AddRange(Encoding.Default.GetBytes(sTrip));
                }
            }

            Buffer.Add(1);
            Buffer.AddRange(localIPAddress.GetAddressBytes());
            Buffer.AddRange(BitConverter.GetBytes((ushort)settings.Port));

            Buffer.AddRange(BitConverter.GetBytes((ushort)0));

            return Buffer.ToArray();
        }

        private byte[] CreateQueryReply(string sKeywords, string sTrip, int iQueryID)
        {
            List<byte> Buffer = new List<byte>();
            Buffer.Add((byte)Command.QuerySend);

            Buffer.Add(1);  // Reply Query
            Buffer.Add(0);  // Diffusion Query
            Buffer.Add(0);  // Up Stream
            Buffer.Add(0);  // File Query
            Buffer.AddRange(BitConverter.GetBytes(iQueryID)); // Query ID
            Buffer.Add((byte)Encoding.Default.GetByteCount(sKeywords));  // Keyword Length
            Buffer.AddRange(Encoding.Default.GetBytes(sKeywords));
            Buffer.AddRange(Encoding.Default.GetBytes(sTrip));  // Trip

            Buffer.Add(1);  // Relay Node Count
            Buffer.AddRange(localIPAddress.GetAddressBytes());
            Buffer.AddRange(BitConverter.GetBytes((ushort)settings.Port));

            List<Key> KeyList = Data.GetKey(sKeywords, sTrip);
            for (int i = 0; i < KeyList.Count; i++)
            {
                Buffer.AddRange(IPAddress.Parse(KeyList[i].EndPoint.IPAddress).GetAddressBytes());
                Buffer.AddRange(BitConverter.GetBytes((short)KeyList[i].EndPoint.Port));

                Buffer.AddRange(new byte[4]);   // BBS IP
                Buffer.AddRange(new byte[2]);   // BBS Port

                Buffer.AddRange(BitConverter.GetBytes(KeyList[i].Length));
                Buffer.AddRange(Utility.HexStringToByteArray(KeyList[i].Hash));
                Buffer.Add((byte)Encoding.Default.GetByteCount(KeyList[i].Name));

                byte[] aobChecksum = BitConverter.GetBytes(Utility.CalculateChecksum(KeyList[i].Name));
                Buffer.AddRange(aobChecksum);

                byte[] aobFileName = Encoding.Default.GetBytes(KeyList[i].Name);
                RC4 Crypter = new RC4(new byte[1] { aobChecksum[0] });
                Crypter.Cipher(aobFileName, aobFileName.Length);
                Buffer.AddRange(aobFileName);

                if (string.IsNullOrEmpty(KeyList[i].Trip))
                {
                    Buffer.AddRange(new byte[Property.TripLength]);
                }
                else
                {
                    Buffer.AddRange(Encoding.Default.GetBytes(KeyList[i].Trip));
                    Buffer.Add(0);
                }

                Buffer.Add(0);  // BBS Trip Auth Info Length
                Buffer.AddRange(BitConverter.GetBytes(Property.KeyTimer));
                Buffer.AddRange(BitConverter.GetBytes(0));
                Buffer.AddRange(BitConverter.GetBytes(Utility.UnixTime(DateTime.Now)));
                Buffer.Add(0);
                Buffer.Add(5);
            }

            return Buffer.ToArray();
        }

        private InitializeResult Initialize(ref Node node, NetworkStream stream, ref RC4 readRC4, ref RC4 writeRC4)
        {
            InitializeResult Result = InitializeResult.Normal;

            try
            {
                #region Write
                #region KeyExchange
                {
                    List<byte> Buffer = new List<byte>();
                    Buffer.Add(Property.KeyExchangeInstruction);
                    Network.Write(Buffer.ToArray(), stream, writeRC4);
                }
                #endregion

                #region Command00
                {
                    List<byte> Buffer = new List<byte>();
                    Buffer.Add((byte)Command.WinnyProtocolHeader);  // Command

                    // Double Crypt
                    RC4 ProtocolHeaderCrypter = new RC4(Encoding.Default.GetBytes(Property.ProtocolHeaderKey));
                    List<byte> ProtocolHeader = new List<byte>();

                    ProtocolHeader.AddRange(BitConverter.GetBytes(Property.WinnyVersion));
                    ProtocolHeader.AddRange(Encoding.Default.GetBytes(Property.AuthenticationString));
                    byte[] aobProtocolHeader = ProtocolHeader.ToArray();

                    ProtocolHeaderCrypter.Cipher(aobProtocolHeader, aobProtocolHeader.Length);
                    Buffer.AddRange(aobProtocolHeader);

                    Network.Write(Buffer.ToArray(), stream, writeRC4);
                }
                #endregion

                #region KeyChange
                {
                    byte[] aobRC4Key = writeRC4.RC4Key;
                    for (int i = 1; i < aobRC4Key.Length; i++)
                    {
                        Buffer.SetByte(aobRC4Key, i, (byte)(aobRC4Key[i] ^ (byte)'9'));
                    }
                    writeRC4 = new RC4(aobRC4Key);
                }
                #endregion

                #region Command01
                {
                    List<byte> Buffer = new List<byte>();
                    Buffer.Add((byte)Command.LineSpeedAcknowledgment);

                    Buffer.AddRange(BitConverter.GetBytes(settings.LineSpeed));

                    Network.Write(Buffer.ToArray(), stream, writeRC4);
                }
                #endregion

                #region Command02
                {
                    List<byte> Buffer = new List<byte>();
                    Buffer.Add((byte)Command.ConnectionTypeAcknowledgment);

                    Buffer.Add(0);  // Link Type (0: Search, 1: Transfer)
                    Buffer.Add(0);  // Port 0
                    Buffer.Add(0);  // Reverse
                    Buffer.Add(0);  // BBS

                    Network.Write(Buffer.ToArray(), stream, writeRC4);
                }
                #endregion

                #region Command03
                {
                    List<byte> Buffer = new List<byte>();
                    Buffer.Add((byte)Command.NodeInformationAcknowledgment);

                    // IP Address
                    IPHostEntry Host = Dns.GetHostEntry(Dns.GetHostName());
                    foreach (IPAddress IPAddress in Host.AddressList)
                    {
                        if (IPAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
                        {
                            continue;
                        }
                        if (localIPAddress == null)
                        {
                            localIPAddress = IPAddress;
                        }
                        Buffer.AddRange(IPAddress.GetAddressBytes());
                        break;
                    }

                    Buffer.AddRange(BitConverter.GetBytes(settings.Port));  // Port
                    Buffer.Add(0);  // DDNS Length

                    // Cluster Length
                    Buffer.Add((byte)Encoding.Default.GetByteCount(settings.ClusterWords[0]));  // Cluster 1
                    Buffer.Add((byte)Encoding.Default.GetByteCount(settings.ClusterWords[1]));  // Cluster 2
                    Buffer.Add((byte)Encoding.Default.GetByteCount(settings.ClusterWords[2]));  // Cluster 3

                    // Cluster String
                    Buffer.AddRange(Encoding.Default.GetBytes(settings.ClusterWords[0]));
                    Buffer.AddRange(Encoding.Default.GetBytes(settings.ClusterWords[1]));
                    Buffer.AddRange(Encoding.Default.GetBytes(settings.ClusterWords[2]));

                    Network.Write(Buffer.ToArray(), stream, writeRC4);
                }
                #endregion
                #endregion

                #region Read
                #region KeyExchange
                {
                    byte[] aobBuffer = Network.Read(stream, readRC4);
                    if (Buffer.GetByte(aobBuffer, 0) != Property.KeyExchangeInstruction)
                    {
                        Debug.WriteLine("Key Exchange Instruction: " + Buffer.GetByte(aobBuffer, 0));
                    }
                }
                #endregion

                #region Command00
                {
                    int iIndex = 0;
                    byte[] aobBuffer = Network.Read(stream, readRC4);
                    if (Buffer.GetByte(aobBuffer, iIndex++) != (byte)Command.WinnyProtocolHeader)
                    {
                        Debug.WriteLine("Protocol Error (in Initialize - Commmand00)");
                        return InitializeResult.Error;
                    }

                    // Double Crypt
                    RC4 ProtocolHeaderDecrypter = new RC4(Encoding.Default.GetBytes(Property.ProtocolHeaderKey));
                    byte[] aobProtocolHeader = new byte[aobBuffer.Length - 1];
                    Buffer.BlockCopy(aobBuffer, iIndex++, aobProtocolHeader, 0, aobProtocolHeader.Length);
                    ProtocolHeaderDecrypter.Cipher(aobProtocolHeader, aobProtocolHeader.Length);

                    int iWinnyVersion = BitConverter.ToInt32(aobProtocolHeader, 0);
                    if (iWinnyVersion != Property.WinnyVersion)
                    {
                        Debug.WriteLine("Winny Version: " + BitConverter.ToInt32(aobProtocolHeader, 0));
                        //return InitializeResult.Error;
                    }

                    string sAuthenticationString = Encoding.Default.GetString(aobProtocolHeader, 4, aobProtocolHeader.Length - 4);
                    if (sAuthenticationString.Length > Property.AuthenticationStringLength) sAuthenticationString = sAuthenticationString.Substring(0, Property.AuthenticationStringLength);
                    if (sAuthenticationString != Property.AuthenticationString)
                    {
                        Debug.WriteLine("Authentication String: " + sAuthenticationString);
                        //return InitializeResult.Error;
                    }
                }
                #endregion

                #region KeyChange
                {
                    byte[] aobRC4Key = readRC4.RC4Key;
                    for (int i = 1; i < aobRC4Key.Length; i++)
                    {
                        Buffer.SetByte(aobRC4Key, i, (byte)(aobRC4Key[i] ^ (byte)'9'));
                    }
                    readRC4 = new RC4(aobRC4Key);
                }
                #endregion

                #region Command01
                {
                    int iIndex = 0;
                    byte[] aobBuffer = Network.Read(stream, readRC4);
                    if (Buffer.GetByte(aobBuffer, iIndex++) != (byte)Command.LineSpeedAcknowledgment)
                    {
                        Debug.WriteLine("Protocol Error (in Initialize - Commmand01)");
                        return InitializeResult.Error;
                    }

                    float LineSpeed = BitConverter.ToSingle(aobBuffer, iIndex);
                    iIndex += sizeof(float);
                    if (LineSpeed < 0)
                    {
                        Debug.WriteLine("Protocol Error (in Initialize - Commmand01 - Line Speed)");
                        return InitializeResult.Error;
                    }

                    node.LineSpeed = LineSpeed;
                    AddNodeLineSpeedList(LineSpeed);
                }
                #endregion

                #region Command02
                {
                    int iIndex = 0;
                    byte[] aobBuffer = Network.Read(stream, readRC4);
                    if (Buffer.GetByte(aobBuffer, iIndex++) != (byte)Command.ConnectionTypeAcknowledgment)
                    {
                        Debug.WriteLine("Protocol Error (in Initialize - Commmand02)");
                        return InitializeResult.Error;
                    }

                    switch (Buffer.GetByte(aobBuffer, iIndex++))   // Link Type
                    {
                        case 0:
                            Result = InitializeResult.Normal;
                            break;
                        case 1:
                            Result = InitializeResult.Transfer;
                            break;
                        case 2:
                            Result = InitializeResult.Transfer;
                            break;
                        default:
                            return InitializeResult.Error;
                    }

                    node.Port0 = (Buffer.GetByte(aobBuffer, iIndex++) == 0) ? false : true;    // Port0
                    if (Buffer.GetByte(aobBuffer, iIndex++) == 1) Result = InitializeResult.Reverse;   // BadPort0
                    if (Result != InitializeResult.Reverse && Buffer.GetByte(aobBuffer, iIndex++) == 1) Result = InitializeResult.Transfer;    // BBS
                }
                #endregion

                #region Command03
                {
                    int iIndex = 0;
                    byte[] aobBuffer = Network.Read(stream, readRC4);
                    if (Buffer.GetByte(aobBuffer, iIndex++) != (byte)Command.NodeInformationAcknowledgment)
                    {
                        Debug.WriteLine("Protocol Error (in Initialize - Commmand03)");
                        return InitializeResult.Error;
                    }

                    if (aobBuffer[iIndex] == 192 && aobBuffer[iIndex + 1] == 168)
                    {
                        node.NAT = true;
                    }
                    iIndex += sizeof(int);

                    if (node.EndPoint.Port != BitConverter.ToInt32(aobBuffer, iIndex))
                    {
                        node.EndPoint.Port = BitConverter.ToInt32(aobBuffer, iIndex);
                    }
                    iIndex += sizeof(int);

                    int DDNSLength = Buffer.GetByte(aobBuffer, iIndex++);
                    int[] ClusterWordLengths = new int[3];
                    for (int i = 0; i < ClusterWordLengths.Length; i++)
                    {
                        ClusterWordLengths[i] = Buffer.GetByte(aobBuffer, iIndex++);
                    }

                    string DDNS = Encoding.Default.GetString(aobBuffer, iIndex, DDNSLength);
                    for (int i = 0; i < ClusterWordLengths.Length; i++)
                    {
                        node.ClusterWords[i] = Encoding.Default.GetString(aobBuffer, iIndex, ClusterWordLengths[i]);
                        if (ClusterWordLengths[i] != 0) AddClusterList(node.ClusterWords[i]);
                        iIndex += ClusterWordLengths[i];
                    }
                }
                #endregion
                #endregion

                Data.AddNode(node);
                AddNodeList(node);
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception caught (Initialize)");
                Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
                return InitializeResult.Error;
            }

            return Result;
        }

        /// <summary>
        /// シリアライズされたデータを取り込みます。
        /// </summary>
        /// <param name="obj"></param>
        private void LoadData(object obj)
        {
            ActivateProggressBar("データロード中...");
            StartButtonState(false);
            try
            {
                foreach (Node Node in Data.NodeList)
                {
                    AddNodeList(Node);
                }

                foreach (Key Key in Data.KeyList)
                {
                    AddFileList(Key.EndPoint.Point, Key.EndPoint.Port, Key.EndPoint.Domain, Key.Name, Key.Length, Key.Hash);
                }
            }
            finally
            {
                DeactivateProgressBar();
                StartButtonState(true);
            }
        }

        /// <summary>
        /// ノードリストからノードを読み込みます。
        /// </summary>
        /// <param name="obj"></param>
        private void LoadNodes(object obj)
        {
            string sPath = (string)obj;
            ActivateProggressBar("ノードリスト読み込み中...");
            StartButtonState(false);

            try
            {
                using (FileStream Stream = new FileStream(sPath, FileMode.Open))
                using (StreamReader Reader = new StreamReader(Stream))
                {
                    while (!Reader.EndOfStream && enable)
                    {
                        byte[] aobNodeKey = Encoding.Default.GetBytes(Property.NodeKey);

                        string sNode = Reader.ReadLine();
                        if (string.IsNullOrEmpty(sNode) || !sNode.StartsWith("@"))
                        {
                            continue;
                        }

                        byte[] aobAddress = Utility.HexStringToByteArray(sNode.Substring(3));
                        aobNodeKey[0] = Convert.ToByte((sNode.Substring(1, 2)), 16);
                        RC4 Crypter = new RC4(aobNodeKey);
                        Crypter.Cipher(aobAddress, aobAddress.Length);

                        string sAddressLine = Encoding.Default.GetString(aobAddress);
                        Debug.WriteLine(sAddressLine);
                        if (!sAddressLine.Contains(":"))
                        {
                            continue;
                        }
                        string sIPAddress = sAddressLine.Substring(0, sAddressLine.IndexOf(":"));
                        int iPort = int.Parse(sAddressLine.Substring(sAddressLine.IndexOf(":") + 1));

                        EndPoint EndPoint = new EndPoint(sIPAddress, iPort, (settings.ReverseResolution || settings.ReverseResolutionOnlyForNode) ? true : false);

                        AddNodeView(EndPoint);
                        Data.AddNode(new Node(EndPoint));
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception caught (LoadNodes)");
                Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
            }

            if (enable)
            {
                DeactivateProgressBar();
                StartButtonState(true);
            }
        }

        private void ListenerCallback(IAsyncResult ar)
        {
            EndPoint EndPoint = null;

            try
            {
                #region Initialize
                TcpListener Listener = (TcpListener)ar.AsyncState;
                TcpClient Client = Listener.EndAcceptTcpClient(ar);
                Listener.BeginAcceptTcpClient(ListenerCallback, Listener);

                EndPoint = new EndPoint(((System.Net.IPEndPoint)Client.Client.RemoteEndPoint).Address, ((System.Net.IPEndPoint)Client.Client.RemoteEndPoint).Port, (settings.ReverseResolution || settings.ReverseResolutionOnlyForNode) ? true : false);
                AddNodeView(EndPoint);
                if (EndPoint != null)
                {
                    try
                    {
                        connectionsLock.AcquireWriterLock(Timeout.Infinite);
                        connections.Add(EndPoint);
                    }
                    finally
                    {
                        connectionsLock.ReleaseWriterLock();
                    }
                }
                #endregion

                using (NetworkStream Stream = Client.GetStream())
                {
                    RC4 ReadRC4, WriteRC4;
                    Network.KeyExchange(Stream, out ReadRC4, out WriteRC4);

                    Protocol(EndPoint, Stream, ReadRC4, WriteRC4);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception caught (ListenerCallback)");
                Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
            }
            finally
            {
                #region Finalize
                if (EndPoint != null)
                {
                    try
                    {
                        connectionsLock.AcquireWriterLock(Timeout.Infinite);
                        connections.Remove(EndPoint);
                    }
                    finally
                    {
                        connectionsLock.ReleaseWriterLock();
                    }
                }
                #endregion
            }
        }

        private void ProcessQuery(object obj)
        {
            EndPoint EndPoint = (EndPoint)((object[])obj)[0];
            byte[] aobBuffer = (byte[])((object[])obj)[1];

            ProcessQuery(EndPoint, aobBuffer, 0);
        }

        private void ProcessQuery(EndPoint endPoint, byte[] aobBuffer, int iKeyCount)
        {
            int iIndex = 0;

            try
            {
                List<Key> List = new List<Key>();

                if (iKeyCount == 0)
                {
                    iIndex = 9; // Command + Flag[4] + QID[4]

                    int iKeywordsLength = aobBuffer[iIndex++];
                    //string sKeywords = Encoding.Default.GetString(aobBuffer, iIndex, iKeywordsLength);
                    //iIndex += iKeywordsLength;
                    //string sTrip = Encoding.Default.GetString(aobBuffer, iIndex, Property.TripLength);
                    iIndex += Property.TripLength;

                    // NodeList
                    int iNodeCount = aobBuffer[iIndex++];
                    for (int i = 0; i < iNodeCount - 1; i++)
                    {
                        IPAddress RemoteIPAddress = new IPAddress(BitConverter.ToUInt32(aobBuffer, iIndex));
                        iIndex += sizeof(int);
                        int RemotePort = BitConverter.ToUInt16(aobBuffer, iIndex);
                        iIndex += sizeof(ushort);

                        EndPoint NodeEndPoint = new EndPoint(RemoteIPAddress, RemotePort, (settings.ReverseResolution || settings.ReverseResolutionOnlyForNode) ? true : false);

                        AddNodeView(NodeEndPoint);
                        Data.AddNode(new Node(NodeEndPoint));
                    }

                    IPAddress SouceIPAddress = new IPAddress(BitConverter.ToUInt32(aobBuffer, iIndex));
                    iIndex += sizeof(int);
                    int SoucePort = BitConverter.ToUInt16(aobBuffer, iIndex);
                    iIndex += sizeof(ushort);
                    EndPoint NeighborEndPoint = new EndPoint(endPoint.IPAddress, SoucePort, (settings.ReverseResolution || settings.ReverseResolutionOnlyForNode) ? true : false);

                    AddNodeView(NeighborEndPoint);
                    Data.AddNode(new Node(NeighborEndPoint));

                    iKeyCount = BitConverter.ToInt16(aobBuffer, iIndex);
                    iIndex += sizeof(short);

                    AddMessageView(endPoint.Point, Property.Command_13, string.Format("キー数: {0}", iKeyCount));
                }

                for (int i = 0; i < iKeyCount && analyzing; i++)
                {
                    IPAddress RemoteIPAddress = new IPAddress(BitConverter.ToUInt32(aobBuffer, iIndex));
                    iIndex += sizeof(int);

                    byte[] aobIPAddress = RemoteIPAddress.GetAddressBytes();
                    if (aobIPAddress[0] == 192 && aobIPAddress[1] == 168)
                    {
                        RemoteIPAddress = IPAddress.Parse(endPoint.IPAddress);
                    }

                    int RemotePort = BitConverter.ToUInt16(aobBuffer, iIndex);
                    iIndex += sizeof(ushort);

                    iIndex += sizeof(int) + sizeof(ushort);

                    int Length = BitConverter.ToInt32(aobBuffer, iIndex);
                    if (Length < 0)
                    {
                        Debug.WriteLine("Parse Error");
                        return;
                    }
                    iIndex += sizeof(int);

                    byte[] aobHash = new byte[Property.HashLength];
                    Buffer.BlockCopy(aobBuffer, iIndex, aobHash, 0, aobHash.Length);
                    string Hash = Utility.ByteArrayToHexString(aobHash);
                    iIndex += aobHash.Length;

                    int iFileNameLength = aobBuffer[iIndex++];
                    // <Checksum>
                    RC4 Crypter = new RC4(new byte[1] { aobBuffer[iIndex++] });
                    iIndex++;
                    // </Checksum>
                    byte[] aobFileName = new byte[iFileNameLength];
                    Buffer.BlockCopy(aobBuffer, iIndex, aobFileName, 0, aobFileName.Length);
                    Crypter.Cipher(aobFileName, aobFileName.Length);
                    string Name = Utility.ValidFileName(Encoding.Default.GetString(aobFileName));
                    iIndex += aobFileName.Length;

                    string Trip;
                    if (aobBuffer[iIndex] != 0)
                    {
                        Trip = Encoding.Default.GetString(aobBuffer, iIndex, Property.TripLength - 1);
                    }
                    else
                    {
                        Trip = "";
                    }
                    iIndex += Property.TripLength;

                    int iBBSTrip = aobBuffer[iIndex++];
                    iIndex += iBBSTrip;

                    int iKeyTimer = BitConverter.ToInt16(aobBuffer, iIndex);
                    iIndex += sizeof(short);

                    int iReferenceBlock = BitConverter.ToInt32(aobBuffer, iIndex);
                    iIndex += sizeof(int);

                    int iKeyRenewalTime = BitConverter.ToInt32(aobBuffer, iIndex);
                    iIndex += sizeof(int);

                    int iKeyIgnoreFlag = aobBuffer[iIndex++];
                    int iKeyVersion = aobBuffer[iIndex++];

                    Key Key = new Key(RemoteIPAddress, RemotePort, (settings.ReverseResolution || settings.ReverseResolutionOnlyForKey) ? true : false, Length, Hash, Name, Trip, iKeyTimer, iReferenceBlock, iKeyRenewalTime, (iKeyIgnoreFlag == 0) ? true : false, iKeyVersion);
                    List.Add(Key);
                    AddFileList(Key.EndPoint.Point, Key.EndPoint.Port, Key.EndPoint.Domain, Name, Length, Hash);
                }

                Data.AddKey(List);
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception caught (Protocol)");
                Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
            }
        }

        private void Protocol(EndPoint endPoint, NetworkStream stream, RC4 readRC4, RC4 writeRC4)
        {
            SetNode(endPoint);

            try
            {
                Node Node = new Node(endPoint);
                InitializeResult Result = Initialize(ref Node, stream, ref readRC4, ref writeRC4);

                switch (Result)
                {
                    case InitializeResult.Normal:
                        {
                            Communication(Node, stream, readRC4, writeRC4);
                        }
                        break;
                    case InitializeResult.Reverse:
                        {
                            CloseConnection(stream, Result, writeRC4);
                        }
                        break;
                    case InitializeResult.Transfer:
                        {
                            CloseConnection(stream, Result, writeRC4);
                        }
                        break;
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception caught (Protocol)");
                Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
            }
            finally
            {
                UnsetNode(endPoint);
            }
        }

        private void QueryQueueControl(object obj)
        {
            lock (queryQueueLock)
            {
                foreach (Query query in queryQueue)
                {
                    if ((DateTime.Now - query.Time).TotalMinutes > Property.QueryKeepingLimit)
                    {
                        queryQueue.Remove(query);
                    }
                }
            }
        }

        private void Server(object obj)
        {
            try
            {
                do
                {
                    if (listener == null || ((System.Net.IPEndPoint)listener.Server.LocalEndPoint).Port != settings.Port)
                    {
                        listener = new TcpListener(System.Net.IPAddress.Any, settings.Port);

                        if (settings.UPnP)
                        {
                            if (((System.Net.IPEndPoint)listener.Server.LocalEndPoint != null))
                            {
                                UPnP.RemovePortMapping(((System.Net.IPEndPoint)listener.Server.LocalEndPoint).Port);
                            }
                            UPnP.AddPortMapping(settings.Port);
                        }

                        listener.Start();
                        listener.BeginAcceptTcpClient(ListenerCallback, listener);
                    }

                    Thread.Sleep(1000);

                } while (analyzing);
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception caught (Server)");
                Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
            }
        }

        public void Start()
        {
            analyzing = true;

            Thread ServerThread = new Thread(Server);
            ServerThread.Name = "Server Start Thread";
            ServerThread.Start();

            Thread ClientThread = new Thread(Client);
            ClientThread.Name = "Client Thread";
            ClientThread.Start();

            System.Threading.Timer QueryQueueControlTimer = new System.Threading.Timer(new TimerCallback(QueryQueueControl), null, 60000, 60000);
        }

        public void Stop()
        {
            analyzing = false;
            NodeView.DefaultCellStyle.BackColor = Color.White;
        }


        #region Interface
        delegate void ActivateProgressBarDelegate(string sStatus);
        void ActivateProggressBar(string sStatus)
        {
            if (InvokeRequired)
            {
                try
                {
                    Invoke(new ActivateProgressBarDelegate(ActivateProggressBar), sStatus);
                }
                catch { }
                return;
            }
            try
            {
                ProgressBar.Visible = true;
                ProgressBar.Style = ProgressBarStyle.Marquee;
                StatusLabel.Visible = true;
                StatusLabel.Text = sStatus;
            }
            catch { }
        }

        delegate void DeactivateProgressBarDelegate();
        void DeactivateProgressBar()
        {
            if (InvokeRequired)
            {
                try
                {
                    Invoke(new DeactivateProgressBarDelegate(DeactivateProgressBar));
                }
                catch { }
                return;
            }
            try
            {
                ProgressBar.Visible = false;
                StatusLabel.Visible = false;
            }
            catch { }
        }

        delegate void StartButtonStateDelegate(bool Enabled);
        void StartButtonState(bool Enabled)
        {
            if (InvokeRequired)
            {
                try
                {
                    Invoke(new StartButtonStateDelegate(StartButtonState), Enabled);
                }
                catch { }
                return;
            }
            try
            {
                StartButton.Enabled = Enabled;
            }
            catch { }
        }

        delegate void SetNodeDelegate(int iRow);
        void _SetNode(int iRow)
        {
            if (InvokeRequired)
            {
                try
                {
                    Invoke(new SetNodeDelegate(_SetNode), iRow);
                }
                catch { }
                return;
            }
            try
            {
                NodeView.Rows[iRow].DefaultCellStyle.BackColor = Color.LawnGreen;
                NodeView.FirstDisplayedScrollingRowIndex = iRow;
            }
            catch { }
        }

        void SetNode(EndPoint EndPoint)
        {
            for (int i = 0; i < NodeView.Rows.Count; i++)
            {
                if ((string)NodeView.Rows[i].Cells[0].Value == EndPoint.Point || (string)NodeView.Rows[i].Cells[0].Value == EndPoint.IPAddress)
                {
                    _SetNode(i);
                    break;
                }
            }
        }

        delegate void UnsetNodeDelegate(int iRow);
        void _UnsetNode(int iRow)
        {
            if (InvokeRequired)
            {
                try
                {
                    Invoke(new UnsetNodeDelegate(_UnsetNode), iRow);
                }
                catch { }
                return;
            }
            try
            {
                NodeView.Rows[iRow].DefaultCellStyle.BackColor = Color.White;
            }
            catch { }
        }

        void UnsetNode(EndPoint EndPoint)
        {
            for (int i = 0; i < NodeView.Rows.Count; i++)
            {
                if ((string)NodeView.Rows[i].Cells[0].Value == EndPoint.Point || (string)NodeView.Rows[i].Cells[0].Value == EndPoint.IPAddress)
                {
                    _UnsetNode(i);
                    return;
                }
            }
        }

        delegate void AddNodeListDelegate(Node Node);
        void AddNodeList(Node Node)
        {
            if (InvokeRequired)
            {
                try
                {
                    Invoke(new AddNodeListDelegate(AddNodeList), Node);
                }
                catch { }
                return;
            }
            try
            {
                int iRow = NodeList.Rows.Add(Node.EndPoint.Point, Node.EndPoint.Port, Node.Port0, Node.LineSpeed, string.Format("{0}, {1}, {2}", Node.ClusterWords[0], Node.ClusterWords[1], Node.ClusterWords[2]));
                NodeList.FirstDisplayedScrollingRowIndex = iRow;
                AddNodeDomainList(Node.EndPoint.Domain);
                AddClusterList(Node.ClusterWords[0]);
                AddClusterList(Node.ClusterWords[1]);
                AddClusterList(Node.ClusterWords[2]);
            }
            catch { }
        }

        delegate void AddNodeViewDelegate(EndPoint EndPoint);
        void _AddNodeView(EndPoint EndPoint)
        {
            if (InvokeRequired)
            {
                try
                {
                    Invoke(new AddNodeViewDelegate(_AddNodeView), EndPoint);
                }
                catch { }
                return;
            }
            try
            {
                NodeView.Rows.Add(EndPoint.Point, EndPoint.Port);
                AddNodeDomainList(EndPoint.Domain);
            }
            catch { }
        }

        void AddNodeView(EndPoint EndPoint)
        {
            for (int i = 0; i < NodeView.Rows.Count; i++)
            {
                if ((string)NodeView.Rows[i].Cells[0].Value == EndPoint.Point)
                {
                    return;
                }
            }
            try
            {
                _AddNodeView(EndPoint);
            }
            catch { }
        }

        delegate void AddMessageViewDelegate(string Point, string Message, string Value);
        void AddMessageView(string Point, string Message, string Value)
        {
            if (InvokeRequired)
            {
                try
                {
                    Invoke(new AddMessageViewDelegate(AddMessageView), Point, Message, Value);
                }
                catch { }
                return;
            }
            try
            {
                int iRow = MessageView.Rows.Add(DateTime.Now, Point, Message, Value);
                MessageView.FirstDisplayedScrollingRowIndex = iRow;
            }
            catch { }
        }

        delegate void AddQueryListDelegate(string Point, string Keyword);
        void _AddQueryList(string Point, string Keyword)
        {
            if (InvokeRequired)
            {
                try
                {
                    Invoke(new AddQueryListDelegate(_AddQueryList), Point, Keyword);
                }
                catch { }
                return;
            }
            try
            {
                QueryList.Rows.Add(Point, Keyword);
            }
            catch { }
        }

        void AddQueryList(string Point, string Keyword)
        {
            for (int i = 0; i < QueryList.Rows.Count; i++)
            {
                if ((string)QueryList.Rows[i].Cells[0].Value == Point && (string)QueryList.Rows[i].Cells[1].Value == Keyword)
                {
                    return;
                }
            }
            _AddQueryList(Point, Keyword);
        }

        delegate void AddFileListDelegate(string Point, int Port, string Domain, string Name, int Length, string Hash);
        void _AddFileList(string Point, int Port, string Domain, string Name, int Length, string Hash)
        {
            if (InvokeRequired)
            {
                try
                {
                    Invoke(new AddFileListDelegate(_AddFileList), Point, Port, Domain, Name, Length, Hash);
                }
                catch { }
                return;
            }
            try
            {
                int iRow = FileList.Rows.Add(Point, Port, Domain, Name, Length, Hash);
                FileList.FirstDisplayedScrollingRowIndex = iRow;
                AddFileDomainList(Domain);
            }
            catch { }
        }

        void AddFileList(string Point, int Port, string Domain, string Name, int Length, string Hash)
        {
            for (int i = 0; i < FileList.Rows.Count; i++)
            {
                if ((string)FileList.Rows[i].Cells[0].Value == Point && (int)FileList.Rows[i].Cells[1].Value == Port && (string)FileList.Rows[i].Cells[5].Value == Hash)
                {
                    return;
                }
            }
            _AddFileList(Point, Port, Domain, Name, Length, Hash);
        }

        delegate void AddClusterListDelegate(string ClusterWord);
        void AddClusterList(string ClusterWord)
        {
            if (string.IsNullOrEmpty(ClusterWord))
            {
                return;
            }
            if (InvokeRequired)
            {
                try
                {
                    Invoke(new AddClusterListDelegate(AddClusterList), ClusterWord);
                }
                catch { }
                return;
            }
            try
            {
                for (int i = 0; i < ClusterList.Rows.Count; i++)
                {
                    if ((string)ClusterList.Rows[i].Cells[0].Value == ClusterWord)
                    {
                        ClusterList.Rows[i].Cells[1].Value = (int)ClusterList.Rows[i].Cells[1].Value + 1;
                        return;
                    }
                }
                ClusterList.Rows.Add(ClusterWord, 1);
            }
            catch { }
        }

        delegate void AddNodeDomainListDelegate(string Domain);
        void AddNodeDomainList(string Domain)
        {
            if (string.IsNullOrEmpty(Domain) && (settings.ReverseResolution || settings.ReverseResolutionOnlyForNode))
            {
                return;
            }
            if (InvokeRequired)
            {
                try
                {
                    Invoke(new AddNodeDomainListDelegate(AddNodeDomainList), Domain);
                }
                catch { }
                return;
            }
            try
            {
                for (int i = 0; i < NodeDomainList.Rows.Count; i++)
                {
                    if ((string)NodeDomainList.Rows[i].Cells[0].Value == Domain)
                    {
                        NodeDomainList.Rows[i].Cells[1].Value = (int)NodeDomainList.Rows[i].Cells[1].Value + 1;
                        return;
                    }
                }
                NodeDomainList.Rows.Add(Domain, 1);
            }
            catch { }
        }

        delegate void AddNodeLineSpeedListDelegate(float LineSpeed);
        void AddNodeLineSpeedList(float LineSpeed)
        {
            if (InvokeRequired)
            {
                try
                {
                    Invoke(new AddNodeLineSpeedListDelegate(AddNodeLineSpeedList), LineSpeed);
                }
                catch { }
                return;
            }
            try
            {
                for (int i = 0; i < NodeLineSpeedList.Rows.Count; i++)
                {
                    if ((string)NodeLineSpeedList.Rows[i].Cells[0].Value == LineSpeed.ToString("N1"))
                    {
                        NodeLineSpeedList.Rows[i].Cells[1].Value = (int)NodeLineSpeedList.Rows[i].Cells[1].Value + 1;
                        return;
                    }
                }
                NodeLineSpeedList.Rows.Add(LineSpeed.ToString("N1"), 1);
            }
            catch { }
        }

        delegate void AddFileDomainListDelegate(string Domain);
        void AddFileDomainList(string Domain)
        {
            if (InvokeRequired)
            {
                try
                {
                    Invoke(new AddFileDomainListDelegate(AddFileDomainList), Domain);
                }
                catch { }
                return;
            }
            try
            {
                for (int i = 0; i < FileDomainList.Rows.Count; i++)
                {
                    if ((string)FileDomainList.Rows[i].Cells[0].Value == Domain)
                    {
                        FileDomainList.Rows[i].Cells[1].Value = (int)FileDomainList.Rows[i].Cells[1].Value + 1;
                        return;
                    }
                }
                FileDomainList.Rows.Add(Domain, 1);
            }
            catch { }
        }

        delegate void AddLogDelegate(string Log);
        void AddLog(string Log)
        {
            if (InvokeRequired)
            {
                try
                {
                    Invoke(new AddLogDelegate(AddLog), Log);
                }
                catch { }
                return;
            }
            try
            {
                LogList.Rows.Add(DateTime.Now, Log);
            }
            catch { }
        }

        public MainWindow()
        {
            InitializeComponent();
        }

        private void ノードリストの読み込みRToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                Thread LoadNodesThread = new Thread(LoadNodes);
                LoadNodesThread.Name = "Load Nodes Thread";
                LoadNodesThread.Start(openFileDialog.FileName);
            }
        }

        private void 環境設定ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SettingsControl Control = new SettingsControl(ref settings);
            Control.ShowDialog();
        }

        private void 終了XToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void バージョン情報VToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            Version Version = new Version();
            Version.ShowDialog();
        }

        private void MainWindow_Load(object sender, EventArgs e)
        {
            AddLog("Winny Protocol Analyzer 起動");

            AddLog("設定ロード開始");
            if (File.Exists(@"Settings.xml"))
            {
                try
                {
                    using (FileStream Stream = new FileStream("Settings.xml", FileMode.Open))
                    {
                        XmlSerializer Serializer = new XmlSerializer(typeof(Settings));
                        settings = (Settings)Serializer.Deserialize(Stream);
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("Exception caught (MainWindow_Load)");
                    Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                    Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                    Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));

                    settings = new Settings();
                }
            }
            else
            {
                settings = new Settings();
            }

            if (settings.ReverseResolution)
            {
                settings.ReverseResolutionOnlyForNode = false;
                settings.ReverseResolutionOnlyForKey = false;
            }

            if (settings.ReverseResolutionOnlyForKey || settings.ReverseResolutionOnlyForNode)
            {
                settings.ReverseResolution = false;
            }

            if (settings.ReverseResolutionOnlyForKey && settings.ReverseResolutionOnlyForNode)
            {
                settings.ReverseResolution = true;
                settings.ReverseResolutionOnlyForNode = false;
                settings.ReverseResolutionOnlyForKey = false;
            }
            AddLog("設定ロード完了");

            if (File.Exists(settings.DataFolder + Property.NodeListSerializeFileName) && File.Exists(settings.DataFolder + Property.KeyListSerializaFileName))
            {
                if (MessageBox.Show("以前取得したデータを読み込みますか？\r\n（読み込みに時間がかかる可能性があります）", "Winny Protocol Analyzer", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
                {
                    Data = new WinnyProtocolAnalyzer.Data.Data(true, settings.DataFolder);
                    Thread LoadDataThread = new Thread(LoadData);
                    LoadDataThread.Name = "Load Data Thread";
                    LoadDataThread.Start();
                }
                else
                {
                    Data = new Data.Data(false, settings.DataFolder);
                }
            }

            AddLog("ノードリスト読み込み開始");
            if (File.Exists(settings.DataFolder + Property.NodeListSerializeFileName))
            {
                foreach (Node Node in Data.NodeList)
                {
                    AddNodeView(Node.EndPoint);
                }
            }
            else if (File.Exists(@"Noderef.txt") && Data.NodeList.Count == 0)
            {
                Thread LoadNodesThread = new Thread(LoadNodes);
                LoadNodesThread.Name = "Load Nodes Thread";
                LoadNodesThread.Start(@"Noderef.txt");
            }
            else
            {
                MessageBox.Show("ノードリストが読み込まれていません", "Winny Protocol Analyzer", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                AddLog("ノードリストファイルが見つかりませんでした");
            }
            AddLog("ノードリスト読み込み完了");


            #region Interface
            //MessageView.AlternatingRowsDefaultCellStyle.BackColor = Color.Azure;
            //NodeView.AlternatingRowsDefaultCellStyle.BackColor = Color.Azure;
            #endregion
        }

        private void MainWindow_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (MessageBox.Show("Winny Protocol Analyzer を終了しますか？", "Winny Protocol Analyzer", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
            {
                e.Cancel = true;
                return;
            }

            ActivateProggressBar("終了処理中...");

            Stop();
            enable = false;
            UPnP.RemovePortMapping(settings.Port);
            Data.Update(settings.DataFolder);

            try
            {
                using (FileStream Stream = new FileStream("Settings.xml", FileMode.Create))
                {
                    XmlSerializer Serializer = new XmlSerializer(typeof(Settings));
                    Serializer.Serialize(Stream, settings);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception caught (MainWindow_FormClosing)");
                Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
            }

            Environment.Exit(0);
        }

        private void StartButton_Click(object sender, EventArgs e)
        {
            if (Data.NodeList.Count == 0)
            {
                MessageBox.Show("ノードリストが読み込まれていません", "Winny Protocol Analyzer", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            if (!analyzing)
            {
                ActivateProggressBar("解析中");
                StartButton.Text = "停止(&S)";
                ノードリストの読み込みRToolStripMenuItem.Enabled = false;
                AddLog("解析開始");
                Start();
            }
            else
            {
                DeactivateProgressBar();
                StartButton.Text = "開始(&S)";
                ノードリストの読み込みRToolStripMenuItem.Enabled = true;
                AddLog("解析停止");
                Stop();
            }
        }

        private void MainWindow_Shown(object sender, EventArgs e)
        {
            //Thread LoadDataThread = new Thread(LoadData);
            //LoadDataThread.Name = "Load Data Thread";
            //LoadDataThread.Start();
        }
        #endregion
    }
}
