dreamscached / MineQuery

📡 Minecraft Server List Ping library written in Go
https://pkg.go.dev/github.com/dreamscached/minequery/v2
MIT License
27 stars 4 forks source link

panic: runtime error: slice bounds out of range [-1:] #40

Closed terrifictable closed 1 year ago

terrifictable commented 1 year ago

When running pings in coroutines after a few "threads" finish i get this error/panic:

panic: runtime error: slice bounds out of range [-3:]

goroutine 183 [running]:
github.com/dreamscached/minequery/v2.(*Pinger).ping17ReadStatusResponsePacketPayload(0xc0000564a0?, {0x2673ea66f98, 0xc0065da1c0})
        C:/Users/Sandbox/go/pkg/mod/github.com/dreamscached/minequery/v2@v2.3.0/ping_17.go:1841 +0x525

This is my code:

var wg sync.WaitGroup
var pingout []minequery.Status17
for i, data := range payload {
  wg.Add(1)

  go func(data Data, i int) {
    defer wg.Done()

    res, err := minequery.Ping17(data.IP, data.Ports[0].Port)
    if err != nil { fmt.Printf("[%d -> %s] (Failed) %s\n", i, data.IP, err); return }
    pingout = append(pingout, *res)
  }(data, i)
}
wg.Wait()
dreamscached commented 1 year ago

What server are you trying to ping? Does the issue persist if pinged synchronously?

terrifictable commented 1 year ago

What server are you trying to ping? Does the issue persist if pinged synchronously?

it also happens when running synchronously

terrifictable commented 1 year ago

It happens with quite a few servers (...in context to scanning a list of 5k servers)

i would say its about 2/1000 servers that cause this error but all the servers that cause this problem seem to be offline (or i may be/am getting ip banned)

oh and its always a diffrent server, sometimes ip xxx.xxx.x.xxx causes that panic to happen and if i run again, xxx.xxx.x.xxx works fine without any problems but another server causes the panic

dreamscached commented 1 year ago

I'll add a failsafe there but generally I struggle to imagine what exactly is the cause there without a way to reproduce, so far I couldn't reproduce it anyhow. Can you share a list of your servers? If it's sensitive not to post it here you can direct them to my e-mail or Discord if you prefer.

terrifictable commented 1 year ago

I'll add a failsafe there but generally I struggle to imagine what exactly is the cause there without a way to reproduce, so far I couldn't reproduce it anyhow. Can you share a list of your servers? If it's sensitive not to post it here you can direct them to my e-mail or Discord if you prefer.

i was using a server list from a discord server, im now using mat-1's server ip list because im searching for a server

the new list im using doesnt seem to cause the panic as much as the old one, but it still does, i found a probably really bad workarround by using a function that calls the scan and catches a panic

terrifictable commented 1 year ago

This is my code:

package main

import (
    "fmt"
    "time"
    "io/ioutil"
    "log"
    "encoding/json"

    "os"
    "encoding/binary"
    "image/png"
    "bytes"
    "encoding/base64"
    "github.com/dreamscached/minequery/v2"
)

type Data struct {
    IP        string `json:"ip"`
    Timestamp string `json:"timestamp"`
    Ports     []struct {
        Port    int `json:"port"`
        Proto   string `json:"proto"`
        Status  string `json:"status"`
        Reason  string `json:"reason"`
        TTL     int `json:"ttl"`
    } `json:"ports"`
}

type ResponseData struct {
    VersionName     string
    ProtocolVersion int

    OnlinePlayers int
    MaxPlayers    int
    SamplePlayers []minequery.PlayerEntry17

    Description string
    Icon        string

    PreviewsChat       bool
    EnforcesSecureChat bool
}

type Response struct {
    Data ResponseData
    IP string
    Port int
}

var panics int

func wrapAndCountPanics(f func()) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("(Error) go func panicked")
            panics++
        }
    }()

    f()
}

func main() {
    start := time.Now().UnixMilli()
    var pingout []Response

    fmt.Println("\n [ Start ] ")

    file, err := os.Open("../ips")
    if err != nil {
        log.Fatal("Error when opening file: ", err)
    }

    defer func() {
        final, _ := json.MarshalIndent(pingout, "", "  ")
        err := ioutil.WriteFile("../ping_out.json", final, os.ModePerm)
        if err != nil {
            fmt.Printf("(Error -> ioutil.WriteFile) %s\n", err)
        }
        file.Close()

        fmt.Printf("\n [ End | Took: %dms ] \n", time.Now().UnixMilli()-start)
    }()

    i := 0
    byteBuff := make([]byte, 6)
    for ;; {

        func() {
            defer func() { i++ }()

            _, err = file.Read(byteBuff)
            if err != nil { return }

            ip := fmt.Sprintf("%d.%d.%d.%d", byteBuff[0], byteBuff[1], byteBuff[2], byteBuff[3])
            port := int(binary.BigEndian.Uint16(byteBuff[4:]))

            res, err := minequery.Ping17(ip, port)
            if err != nil { fmt.Printf("[%d -> %s:%d] (Failed) %s\n", i, ip, port, err); return }

            var iconBuf bytes.Buffer
            var b64Icon string
            if res.Icon != nil {
                png.Encode(&iconBuf, res.Icon)
                b64Icon = base64.StdEncoding.EncodeToString(iconBuf.Bytes())
            }

            pingout = append(pingout, Response {
                Data: ResponseData {
                    res.VersionName,
                    res.ProtocolVersion,
                    res.OnlinePlayers,
                    res.MaxPlayers,
                    res.SamplePlayers,
                    res.DescriptionText(),
                    b64Icon,
                    res.PreviewsChat,
                    res.EnforcesSecureChat,
                },
                IP: ip,
                Port: port,
            })

        }()

        if i >= 500 {
            break
        }
    }

    fmt.Printf("IPs: %d\n", i)

}

(its really bad ik)

the ips file is from mat-1's server ip list/dump

maybe you can reproduce the issue with this image

dreamscached commented 1 year ago

Thank you for reporting and providing data.

terrifictable commented 1 year ago

ok, so it appears that all servers that cause this panic are from: craftserve.pl

dreamscached commented 1 year ago

Could you provide exact IPs that caused this error? I tried running tests on IPs you sent but so far none could help reproduce the issue.

dreamscached commented 1 year ago

Actually, managed to reproduce, it seems like a DDoS measure of some sort is used.

Testuj za darmo przez 24h! <nil> false false}
&{??? 0 0 1 [] Craftserve.pl - wydajny hosting Minecraft!
Testuj za darmo przez 24h! <nil> false false}
&{??? 0 0 1 [] Ochrona DDoS: Przekroczono limit polaczen. (1) <nil> false false}
&{??? 0 0 1 [] Ochrona DDoS: Przekroczono limit polaczen. (1) <nil> false false}
&{??? 0 0 1 [] Ochrona DDoS: Przekroczono limit polaczen. (1) <nil> false false}
panic: runtime error: slice bounds out of range [-1:]

goroutine 1 [running]:
github.com/dreamscached/minequery/v2.(*Pinger).ping17ReadStatusResponsePacketPayload(0xc00006c100?, {0x598320, 0xc000014608})
        /home/dreamscached/Dokumenty/Vývoj/Projekty/dreamscached/minequery/ping_17.go:1841 +0x525
github.com/dreamscached/minequery/v2.(*Pinger).Ping17(0xc00006c100, {0x565026, 0xe}, 0x1?)
        /home/dreamscached/Dokumenty/Vývoj/Projekty/dreamscached/minequery/ping_17.go:1757 +0x27b
github.com/dreamscached/minequery/v2.Ping17(...)
        /home/dreamscached/Dokumenty/Vývoj/Projekty/dreamscached/minequery/ping_17.go:1731
main.main()
        /home/dreamscached/Dokumenty/Vývoj/Projekty/dreamscached/minequery/main/main.go:11 +0x8b

Process finished with the exit code 2

For now I'd suggest setting a limit of retries/pings or somehow employ longer delays for this specific IP/subnet you experience issue with. While I'll implement a failsafe that will prevent panicking, I cannot ensure that MineQuery will not trigger any anti-DDoS and alike measures.

dreamscached commented 1 year ago

The issue has been resolved, use v2.3.1 in your Go module which contains fix patch.

terrifictable commented 1 year ago

The issue has been resolved, use v2.3.1 in your Go module which contains fix patch.

thank you for resolving the problem so quickly :)