예제 #1
0
        private async Task <Message> WireDecodeMessage(UInt32 magic)
        {
            var command         = DataDecoder.DecodeFixedString(await ReceiveExactly(12));
            var payloadSize     = DataDecoder.DecodeUInt32(await ReceiveExactly(4));
            var payloadChecksum = DataDecoder.DecodeUInt32(await ReceiveExactly(4));
            var payload         = await ReceiveExactly(payloadSize.ToIntChecked());

            if (!Messaging.VerifyPayloadChecksum(payloadChecksum, payload))
            {
                throw new Exception($"Checksum failed for {command}");
            }

            var message = new Message
                          (
                Magic: magic,
                Command: command,
                PayloadSize: payloadSize,
                PayloadChecksum: payloadChecksum,
                Payload: payload.ToImmutableArray()
                          );

            switch (message.Command)
            {
            case "addr":
            {
                var addressPayload = NetworkEncoder.DecodeAddressPayload(payload);

                OnReceivedAddresses?.Invoke(owner, addressPayload.NetworkAddresses);
            }
            break;

            case "alert":
            {
                var alertPayload = NetworkEncoder.DecodeAlertPayload(payload);
            }
            break;

            case "block":
            {
                var block = DataDecoder.DecodeBlock(null, payload);

                OnBlock?.Invoke(owner, block);
            }
            break;

            case "getblocks":
            {
                var getBlocksPayload = NetworkEncoder.DecodeGetBlocksPayload(payload);

                OnGetBlocks?.Invoke(owner, getBlocksPayload);
            }
            break;

            case "getheaders":
            {
                var getHeadersPayload = NetworkEncoder.DecodeGetBlocksPayload(payload);

                OnGetHeaders?.Invoke(owner, getHeadersPayload);
            }
            break;

            case "getdata":
            {
                var invPayload = NetworkEncoder.DecodeInventoryPayload(payload);

                OnGetData?.Invoke(owner, invPayload);
            }
            break;

            case "headers":
            {
                var blockHeaders = ImmutableList.CreateBuilder <BlockHeader>();

                var offset      = 0;
                var headerCount = payload.ReadVarInt(ref offset).ToIntChecked();

                for (var i = 0; i < headerCount; i++)
                {
                    var blockHeader = DataDecoder.DecodeBlockHeader(null, payload, ref offset);
                    // ignore tx count var int
                    payload.ReadVarInt(ref offset);

                    blockHeaders.Add(blockHeader);
                }

                OnBlockHeaders?.Invoke(owner, blockHeaders.ToImmutable());
            }
            break;

            case "inv":
            {
                var invPayload = NetworkEncoder.DecodeInventoryPayload(payload);

                OnInventoryVectors?.Invoke(owner, invPayload.InventoryVectors);
            }
            break;

            case "notfound":
            {
                var invPayload = NetworkEncoder.DecodeInventoryPayload(payload);

                OnNotFound?.Invoke(owner, invPayload.InventoryVectors);
            }
            break;

            case "ping":
            {
                OnPing?.Invoke(owner, payload.ToImmutableArray());
            }
            break;

            case "tx":
            {
                var tx = DataDecoder.DecodeEncodedTx(null, payload);

                OnTransaction?.Invoke(owner, tx);
            }
            break;

            case "version":
            {
                var versionPayload = NetworkEncoder.DecodeVersionPayload(payload, payload.Length);

                OnVersion?.Invoke(owner, versionPayload);
            }
            break;

            case "verack":
            {
                OnVersionAcknowledged?.Invoke(owner);
            }
            break;

            default:
            {
                logger.Warn($"Unhandled incoming message: {message.Command}");
            }
            break;
            }

            //TODO
            //if (payloadStream.Position != payloadStream.Length)
            //{
            //    var exMessage = $"Wrong number of bytes read for {message.Command}, parser error: read {payloadStream.Position} bytes from a {payloadStream.Length} byte payload";
            //    Debug.WriteLine(exMessage);
            //    throw new Exception(exMessage);
            //}

            return(message);
        }
예제 #2
0
        private async Task ReadStream()
        {
            while (!Closing)
            {
                try
                {
                    //try to read a header
                    var hdata = new byte[24];
                    await Stream.ReadAsyncExact(hdata, 0, hdata.Length);

                    var h = new MessageHeader();
                    h.ReadFromPayload(hdata, 0);
                    if (h != null)
                    {
                        //read the payload
                        var pl = new byte[h.PayloadSize];
                        await Stream.ReadAsyncExact(pl, 0, pl.Length);

                        bool checksumOk = false;

                        //verify hash
                        using (var sha = SHA256.Create())
                        {
                            var h1 = sha.ComputeHash(pl);
                            var h2 = sha.ComputeHash(h1);

                            checksumOk = h2[0] == h.Checksum[0] && h2[1] == h.Checksum[1] && h2[2] == h.Checksum[2] && h2[3] == h.Checksum[3];
                        }

                        if (checksumOk)
                        {
                            switch (h.Command)
                            {
                            case "addr\0\0\0\0\0\0\0\0":
                            {
                                if (OnAddr != null)
                                {
                                    var a = new Addr();
                                    a.ReadFromPayload(pl, 0);

                                    await OnAddr?.Invoke(this, a);
                                }
                                break;
                            }

                            case "alert\0\0\0\0\0\0\0":
                            {
                                if (OnAlert != null)
                                {
                                    var a = new Alert();
                                    a.ReadFromPayload(pl, 0);

                                    await OnAlert?.Invoke(this, a);
                                }
                                break;
                            }

                            case "feefilter\0\0\0":
                            {
                                if (OnFeeFilter != null)
                                {
                                    var f = new FeeFilter();
                                    f.ReadFromPayload(pl, 0);

                                    await OnFeeFilter?.Invoke(this, f);
                                }
                                break;
                            }

                            case "filteradd\0\0\0":
                            {
                                if (OnFilterAdd != null)
                                {
                                    var f = new FilterAdd();
                                    f.ReadFromPayload(pl, 0);

                                    await OnFilterAdd?.Invoke(this, f);
                                }
                                break;
                            }

                            case "filterclear\0":
                            {
                                if (OnFilterClear != null)
                                {
                                    var f = new FilterClear();
                                    f.ReadFromPayload(pl, 0);

                                    await OnFilterClear?.Invoke(this, f);
                                }
                                break;
                            }

                            case "filterload\0\0":
                            {
                                if (OnFilterLoad != null)
                                {
                                    var f = new FilterLoad();
                                    f.ReadFromPayload(pl, 0);

                                    await OnFilterLoad?.Invoke(this, f);
                                }
                                break;
                            }

                            case "getaddr\0\0\0\0\0":
                            {
                                if (OnGetAddr != null)
                                {
                                    var ga = new GetAddr();
                                    ga.ReadFromPayload(pl, 0);

                                    await OnGetAddr?.Invoke(this, ga);
                                }
                                break;
                            }

                            case "getblocks\0\0\0":
                            {
                                if (OnGetBlocks != null)
                                {
                                    var gb = new GetBlocks();
                                    gb.ReadFromPayload(pl, 0);

                                    await OnGetBlocks?.Invoke(this, gb);
                                }
                                break;
                            }

                            case "getdata\0\0\0\0\0":
                            {
                                if (OnGetData != null)
                                {
                                    var gd = new GetData();
                                    gd.ReadFromPayload(pl, 0);

                                    await OnGetData?.Invoke(this, gd);
                                }
                                break;
                            }

                            case "getheaders\0\0":
                            {
                                if (OnGetHeaders != null)
                                {
                                    var gh = new GetHeaders();
                                    gh.ReadFromPayload(pl, 0);

                                    await OnGetHeaders?.Invoke(this, gh);
                                }
                                break;
                            }

                            case "headers\0\0\0\0\0":
                            {
                                if (OnHeaders != null)
                                {
                                    var hd = new Headers();
                                    hd.ReadFromPayload(pl, 0);

                                    await OnHeaders?.Invoke(this, hd);
                                }
                                break;
                            }

                            case "inv\0\0\0\0\0\0\0\0\0":
                            {
                                if (OnInv != null)
                                {
                                    var iv = new Inv();
                                    iv.ReadFromPayload(pl, 0);

                                    await OnInv?.Invoke(this, iv);
                                }
                                break;
                            }

                            case "mempool\0\0\0\0\0":
                            {
                                if (OnMemPool != null)
                                {
                                    var mp = new MemPool();
                                    mp.ReadFromPayload(pl, 0);

                                    await OnMemPool?.Invoke(this, mp);
                                }
                                break;
                            }

                            case "notfound\0\0\0\0":
                            {
                                if (OnNotFound != null)
                                {
                                    var nf = new NotFound();
                                    nf.ReadFromPayload(pl, 0);

                                    await OnNotFound?.Invoke(this, nf);
                                }
                                break;
                            }

                            case "ping\0\0\0\0\0\0\0\0":
                            {
                                if (OnPing != null)
                                {
                                    var ping = new Ping();
                                    ping.ReadFromPayload(pl, 0);

                                    await OnPing?.Invoke(this, ping);
                                }
                                break;
                            }

                            case "pong\0\0\0\0\0\0\0\0":
                            {
                                if (OnPong != null)
                                {
                                    var pong = new Pong();
                                    pong.ReadFromPayload(pl, 0);

                                    await OnPong?.Invoke(this, pong);
                                }
                                break;
                            }

                            case "reject\0\0\0\0\0\0":
                            {
                                if (OnReject != null)
                                {
                                    var re = new Reject();
                                    re.ReadFromPayload(pl, 0);

                                    await OnReject?.Invoke(this, re);
                                }
                                break;
                            }

                            case "sendheaders\0":
                            {
                                if (OnSendHeaders != null)
                                {
                                    var sh = new SendHeaders();
                                    sh.ReadFromPayload(pl, 0);

                                    await OnSendHeaders?.Invoke(this, sh);
                                }
                                break;
                            }

                            case "verack\0\0\0\0\0\0":
                            {
                                if (OnVerAck != null)
                                {
                                    var va = new VerAck();
                                    va.ReadFromPayload(pl, 0);

                                    await OnVerAck.Invoke(this, va);
                                }
                                break;
                            }

                            case "version\0\0\0\0\0":
                            {
                                if (OnVersion != null)
                                {
                                    var v = new bitcoin_lib.P2P.Version("");
                                    v.ReadFromPayload(pl, 0);

                                    await OnVersion?.Invoke(this, v);
                                }
                                break;
                            }

                            default:
                            {
                                //Console.WriteLine($"Got cmd: {h.Command}");
                                break;
                            }
                            }
                        }
                        else
                        {
                            Closing = true;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Closing = true;
                }
            }
        }
예제 #3
0
        public async Task <TestSession> ParseAsync(Stream stream, bool copyStreamContentToResult = false, CancellationToken token = new CancellationToken())
        {
            var streamContentCopy = new MemoryStream();
            var streamWriter      = new StreamWriter(streamContentCopy, Encoding.UTF8);

            var    streamReader = new StreamReader(stream, Encoding.UTF8);
            string yamlContent  = String.Empty;
            bool   parsingYaml  = false;

            var    testPlan   = new TestPlan();
            var    results    = new List <TestLine>();
            uint   tapVersion = 0;
            var    sessionDiagnosticMessages = new List <string>();
            string bailoutMessage            = String.Empty;
            bool   bailedOut = false;

            token.ThrowIfCancellationRequested();

            var   taskString = streamReader.ReadLineAsync();
            await taskString;
            var   line = taskString.Result;

            while (line != null)
            {
                token.ThrowIfCancellationRequested();

                if (copyStreamContentToResult)
                {
                    streamWriter.WriteLine(line);
                }

                var parseResult = ParseLine(line, parsingYaml);

                switch (parseResult)
                {
                case ParseResult.TestResult:
                    var result = ParseTestResult(line, (uint)results.Count + 1);
                    results.Add(result);
                    try
                    {
                        OnTestResult?.Invoke(result);
                    }
                    catch (Exception e)
                    {
                        SendError(e);
                    }

                    break;

                case ParseResult.Error:
                    if (!string.IsNullOrEmpty(line))
                    {
                        var e = new TAPParserException($"TAP syntax error. Unrecognized line \"{line}\".");
                        SendError(e);
                    }
                    break;

                case ParseResult.Version:
                    var v = ParseTAPVersion(line);
                    try
                    {
                        OnVersion?.Invoke(v);
                    }
                    catch (Exception e)
                    {
                        SendError(e);
                    }

                    tapVersion = v;
                    break;

                case ParseResult.YamlStart:
                    parsingYaml = true;
                    yamlContent = "";
                    break;

                case ParseResult.YamlEnd:
                    parsingYaml = false;
                    using (TextReader tr = new StringReader(yamlContent))
                    {
                        dynamic yaml     = m_deserializer.Deserialize(tr);
                        var     testLine = results.Last();
                        testLine.YAML = yaml;

                        try
                        {
                            OnYaml?.Invoke(testLine, yaml);
                        }
                        catch (Exception e)
                        {
                            SendError(e);
                        }
                    }
                    break;

                case ParseResult.YamlContent:
                    yamlContent += line + "\n";
                    break;

                case ParseResult.TestPlan:
                    var tp = ParseTestPlan(line);

                    try
                    {
                        OnTestPlan?.Invoke(tp);
                    }
                    catch (Exception e)
                    {
                        SendError(e);
                    }
                    testPlan = tp;
                    break;

                case ParseResult.Diagnostic:
                    string diagnostic = s_diagnostics.Match(line).Groups["diagnostic"].Value.Trim();
                    if (results.Any())
                    {
                        var testLine = results.Last();
                        testLine.DiagnosticMessages.Add(diagnostic);

                        try
                        {
                            OnTestResultDiagnostic?.Invoke(testLine, diagnostic);
                        }
                        catch (Exception e)
                        {
                            SendError(e);
                        }
                    }
                    else
                    {
                        sessionDiagnosticMessages.Add(diagnostic);

                        try
                        {
                            OnDiagnostic?.Invoke(diagnostic);
                        }
                        catch (Exception e)
                        {
                            SendError(e);
                        }
                    }
                    break;

                case ParseResult.BailOut:
                    var message = s_bailout.Match(line).Groups["message"].Value.Trim();
                    bailedOut = true;

                    try
                    {
                        OnBailout?.Invoke(message);
                    }
                    catch (Exception e)
                    {
                        SendError(e);
                    }
                    bailoutMessage = message;
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }

                taskString = streamReader.ReadLineAsync();
                await taskString;
                line = taskString.Result;
            }

            int testCount = (int)testPlan.LastTestIndex - (int)testPlan.FirstTestIndex + 1;

            if (results.Count < testCount)
            {
                for (int i = results.Count + 1; i <= testCount; i++)
                {
                    token.ThrowIfCancellationRequested();

                    var testLine = new TestLine
                    {
                        Description = "",
                        Directive   = "",
                        Index       = (uint)i,
                        Status      = TestResult.NotOk,
                    };

                    results.Add(testLine);

                    try
                    {
                        OnTestResult?.Invoke(testLine);
                    }
                    catch (Exception e)
                    {
                        SendError(e);
                    }
                }
            }
            streamWriter.Flush();
            streamContentCopy.Position = 0;
            return(new TestSession
            {
                TAPVersion = tapVersion,
                TestPlan = testPlan,
                Tests = results,
                DiagnosticMessages = sessionDiagnosticMessages,
                BailOutMessage = bailoutMessage,
                BailedOut = bailedOut,
                TAPContent = streamContentCopy
            });
        }
예제 #4
0
        public void ParseReply(string[] tokens)
        {
            ReplyCode code = (ReplyCode)int.Parse(tokens[1], CultureInfo.InvariantCulture);

            tokens[3] = RemoveLeadingColon(tokens[3]);
            switch (code)
            {
            //Messages sent upon successful registration
            case ReplyCode.RPL_WELCOME:
            case ReplyCode.RPL_YOURESERVICE:
                OnRegistered.Fire(this, new EventArgs());
                break;

            case ReplyCode.RPL_MOTDSTART:
            case ReplyCode.RPL_MOTD:
                OnMotd?.Invoke(CondenseStrings(tokens, 3), false);
                break;

            case ReplyCode.RPL_ENDOFMOTD:
                OnMotd?.Invoke(CondenseStrings(tokens, 3), true);
                break;

            case ReplyCode.RPL_ISON:
                OnIson?.Invoke(tokens[3]);
                break;

            case ReplyCode.RPL_NAMREPLY:
                ProcessNamesReply(tokens, false);
                break;

            case ReplyCode.RPL_ENDOFNAMES:
                ProcessNamesReply(tokens, true);
                break;

            case ReplyCode.RPL_LIST:
                tokens[5] = RemoveLeadingColon(tokens[5]);
                OnList?.Invoke(
                    tokens[3],
                    int.Parse(tokens[4], CultureInfo.InvariantCulture),
                    CondenseStrings(tokens, 5),
                    false);
                break;

            case ReplyCode.RPL_LISTEND:
                OnList?.Invoke("", 0, "", true);
                break;

            case ReplyCode.ERR_NICKNAMEINUSE:
            case ReplyCode.ERR_NICKCOLLISION:
                tokens[4] = RemoveLeadingColon(tokens[4]);
                OnNickError.Fire(this, new NickErrorEventArgs(tokens[3], CondenseStrings(tokens, 4)));
                //Trace.WriteLine("Nick collision", "IRC");
                break;

            case ReplyCode.RPL_NOTOPIC:
                OnError.Fire(this, new ErrorMessageEventArgs(code, CondenseStrings(tokens, 3)));
                break;

            case ReplyCode.RPL_TOPIC:
                tokens[4] = RemoveLeadingColon(tokens[4]);
                OnRecieveTopic?.Invoke(tokens[3], CondenseStrings(tokens, 4));
                break;

            case ReplyCode.RPL_INVITING:
                OnInviteSent.Fire(this, new InviteEventArgs(tokens[3], tokens[4]));
                break;

            case ReplyCode.RPL_AWAY:
                OnAway.Fire(this, new AwayEventArgs(tokens[3], RemoveLeadingColon(CondenseStrings(tokens, 4))));
                break;

            case ReplyCode.RPL_WHOREPLY:
                User user = new User(tokens[7], tokens[4], tokens[5]);
                OnWho?.Invoke(
                    user,
                    tokens[3],
                    tokens[6],
                    tokens[8],
                    int.Parse(RemoveLeadingColon(tokens[9]), CultureInfo.InvariantCulture),
                    tokens[10],
                    false);
                break;

            case ReplyCode.RPL_ENDOFWHO:
                OnWho?.Invoke(User.Empty, "", "", "", 0, "", true);
                break;

            case ReplyCode.RPL_WHOISUSER:
                User      whoUser   = new User(tokens[3], tokens[4], tokens[5]);
                WhoisInfo whoisInfo = LookupInfo(whoUser.Nick);
                whoisInfo.user     = whoUser;
                tokens[7]          = RemoveLeadingColon(tokens[7]);
                whoisInfo.realName = CondenseStrings(tokens, 7);
                break;

            case ReplyCode.RPL_WHOISCHANNELS:
                WhoisInfo whoisChannelInfo = LookupInfo(tokens[3]);
                tokens[4] = RemoveLeadingColon(tokens[4]);
                int      numberOfChannels = tokens.Length - 4;
                string[] channels         = new String[numberOfChannels];
                Array.Copy(tokens, 4, channels, 0, numberOfChannels);
                whoisChannelInfo.SetChannels(channels);
                break;

            case ReplyCode.RPL_WHOISSERVER:
                WhoisInfo whoisServerInfo = LookupInfo(tokens[3]);
                whoisServerInfo.ircServer = tokens[4];
                tokens[5] = RemoveLeadingColon(tokens[5]);
                whoisServerInfo.serverDescription = CondenseStrings(tokens, 5);
                break;

            case ReplyCode.RPL_WHOISOPERATOR:
                WhoisInfo whoisOpInfo = LookupInfo(tokens[3]);
                whoisOpInfo.isOperator = true;
                break;

            case ReplyCode.RPL_WHOISIDLE:
                WhoisInfo whoisIdleInfo = LookupInfo(tokens[3]);
                whoisIdleInfo.idleTime = long.Parse(tokens[5], CultureInfo.InvariantCulture);
                break;

            case ReplyCode.RPL_ENDOFWHOIS:
                string    nick         = tokens[3];
                WhoisInfo whoisEndInfo = LookupInfo(nick);
                OnWhois?.Invoke(whoisEndInfo);
                whoisInfos.Remove(nick);
                break;

            case ReplyCode.RPL_WHOWASUSER:
                User whoWasUser = new User(tokens[3], tokens[4], tokens[5]);
                tokens[7] = RemoveLeadingColon(tokens[7]);
                OnWhowas?.Invoke(whoWasUser, CondenseStrings(tokens, 7), false);
                break;

            case ReplyCode.RPL_ENDOFWHOWAS:
                OnWhowas?.Invoke(User.Empty, "", true);
                break;

            case ReplyCode.RPL_UMODEIS:
            {
                //First drop the '+'
                string     chars = tokens[3].Substring(1);
                UserMode[] modes = Rfc2812Util.UserModesToArray(chars);
                OnUserModeRequest?.Invoke(modes);
            }
            break;

            case ReplyCode.RPL_CHANNELMODEIS:
                try
                {
                    ChannelModeInfo[] modes = ChannelModeInfo.ParseModes(tokens, 4);
                    OnChannelModeRequest?.Invoke(tokens[3], modes);
                }
                catch (Exception)
                {
                    OnError.Fire(this, new ErrorMessageEventArgs(ReplyCode.UnparseableMessage, CondenseStrings(tokens, 0)));
                    Debug.WriteLineIf(Rfc2812Util.IrcTrace.TraceWarning, "[" + Thread.CurrentThread.Name + "] Listener::ParseReply() Bad IRC MODE string=" + tokens[0]);
                }
                break;

            case ReplyCode.RPL_BANLIST:
                OnChannelList?.Invoke(tokens[3], ChannelMode.Ban, tokens[4], Rfc2812Util.UserFromString(tokens[5]), Convert.ToInt64(tokens[6], CultureInfo.InvariantCulture), false);
                break;

            case ReplyCode.RPL_ENDOFBANLIST:
                OnChannelList?.Invoke(tokens[3], ChannelMode.Ban, "", User.Empty, 0, true);
                break;

            case ReplyCode.RPL_INVITELIST:
                OnChannelList?.Invoke(tokens[3], ChannelMode.Invitation, tokens[4], Rfc2812Util.UserFromString(tokens[5]), Convert.ToInt64(tokens[6]), false);
                break;

            case ReplyCode.RPL_ENDOFINVITELIST:
                OnChannelList?.Invoke(tokens[3], ChannelMode.Invitation, "", User.Empty, 0, true);
                break;

            case ReplyCode.RPL_EXCEPTLIST:
                OnChannelList?.Invoke(tokens[3], ChannelMode.Exception, tokens[4], Rfc2812Util.UserFromString(tokens[5]), Convert.ToInt64(tokens[6]), false);
                break;

            case ReplyCode.RPL_ENDOFEXCEPTLIST:
                OnChannelList?.Invoke(tokens[3], ChannelMode.Exception, "", User.Empty, 0, true);
                break;

            case ReplyCode.RPL_UNIQOPIS:
                OnChannelList?.Invoke(tokens[3], ChannelMode.ChannelCreator, tokens[4], User.Empty, 0, true);
                break;

            case ReplyCode.RPL_VERSION:
                OnVersion?.Invoke(CondenseStrings(tokens, 3));
                break;

            case ReplyCode.RPL_TIME:
                OnTime?.Invoke(CondenseStrings(tokens, 3));
                break;

            case ReplyCode.RPL_INFO:
                OnInfo?.Invoke(CondenseStrings(tokens, 3), false);
                break;

            case ReplyCode.RPL_ENDOFINFO:
                OnInfo?.Invoke(CondenseStrings(tokens, 3), true);
                break;

            case ReplyCode.RPL_ADMINME:
            case ReplyCode.RPL_ADMINLOC1:
            case ReplyCode.RPL_ADMINLOC2:
            case ReplyCode.RPL_ADMINEMAIL:
                OnAdmin?.Invoke(RemoveLeadingColon(CondenseStrings(tokens, 3)));
                break;

            case ReplyCode.RPL_LUSERCLIENT:
            case ReplyCode.RPL_LUSEROP:
            case ReplyCode.RPL_LUSERUNKNOWN:
            case ReplyCode.RPL_LUSERCHANNELS:
            case ReplyCode.RPL_LUSERME:
                OnLusers?.Invoke(RemoveLeadingColon(CondenseStrings(tokens, 3)));
                break;

            case ReplyCode.RPL_LINKS:
                OnLinks?.Invoke(tokens[3],                                                              //mask
                                tokens[4],                                                              //hostname
                                int.Parse(RemoveLeadingColon(tokens[5]), CultureInfo.InvariantCulture), //hopcount
                                CondenseStrings(tokens, 6), false);
                break;

            case ReplyCode.RPL_ENDOFLINKS:
                OnLinks?.Invoke(String.Empty, String.Empty, -1, String.Empty, true);
                break;

            case ReplyCode.RPL_STATSLINKINFO:
            case ReplyCode.RPL_STATSCOMMANDS:
            case ReplyCode.RPL_STATSUPTIME:
            case ReplyCode.RPL_STATSOLINE:
                OnStats?.Invoke(GetQueryType(code), RemoveLeadingColon(CondenseStrings(tokens, 3)), false);
                break;

            case ReplyCode.RPL_ENDOFSTATS:
                OnStats?.Invoke(Rfc2812Util.CharToStatsQuery(tokens[3][0]), RemoveLeadingColon(CondenseStrings(tokens, 4)), true);
                break;

            default:
                HandleDefaultReply(code, tokens);
                break;
            }
        }