diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7f004eb..b704aa1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,6 +5,9 @@ on: schedule: - cron: '0 0 * * 0' +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" + jobs: fetch-ipv6-prefixes: runs-on: ubuntu-latest diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bf3d9df..8e457ba 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -3,6 +3,9 @@ name: CI on: workflow_dispatch: +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" + jobs: test: strategy: @@ -30,7 +33,7 @@ jobs: run: | git config --global user.name 'github-actions' git config --global user.email 'github-actions@github.com' - TAG="v0.0.8-$(date +'%Y%m%d%H%M%S')" + TAG="v0.0.9-$(date +'%Y%m%d%H%M%S')" git tag $TAG git push origin $TAG echo "TAG=$TAG" >> $GITHUB_ENV diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 938b0a8..7b88218 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -9,6 +9,9 @@ on: # types: [published] workflow_dispatch: +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" + jobs: build: name: Build and Test diff --git a/bgptools/pop.go b/bgptools/pop.go index 9e38074..44d84d9 100644 --- a/bgptools/pop.go +++ b/bgptools/pop.go @@ -117,7 +117,10 @@ func getSVGPath(ip string) (string, error) { client := req.C().ImpersonateChrome() url := fmt.Sprintf("https://bgp.tools/prefix/%s#connectivity", ip) resp, err := executeWithRetry(client, url, defaultRetryConfig) - if err == nil { + if err == nil && resp != nil { + if resp.Body != nil { + defer resp.Body.Close() + } body := resp.String() re := regexp.MustCompile(`]+id="pathimg"[^>]+src="([^"]+)"`) matches := re.FindStringSubmatch(body) @@ -142,7 +145,8 @@ func downloadSVG(svgPath string) (string, error) { uuid := uuid.NewString() url := fmt.Sprintf("https://bgp.tools%s?%s&loggedin", svgPath, uuid) resp, err := executeWithRetry(client, url, defaultRetryConfig) - if err == nil { + if err == nil && resp != nil && resp.Body != nil { + defer resp.Body.Close() bodyBytes, err := io.ReadAll(resp.Body) if err == nil { return string(bodyBytes), nil diff --git a/bk/backtrace.go b/bk/backtrace.go index 31e16d8..79543e2 100644 --- a/bk/backtrace.go +++ b/bk/backtrace.go @@ -7,6 +7,14 @@ import ( "github.com/oneclickvirt/backtrace/model" ) +func safeTraceCall(fn func()) { + defer func() { + if r := recover(); r != nil { + } + }() + fn() +} + func BackTrace(enableIpv6 bool) string { if model.CachedIcmpData == "" || model.ParsedIcmpTargets == nil || time.Since(model.CachedIcmpDataFetchTime) > time.Hour { model.CachedIcmpData = getData(model.IcmpTargets) @@ -26,10 +34,16 @@ func BackTrace(enableIpv6 bool) string { t = time.After(time.Second * 10) ) for i := range model.Ipv4s { - go trace(c, i) + idx := i + go safeTraceCall(func() { + trace(c, idx) + }) } for i := range model.Ipv6s { - go traceIPv6(c, i, ipv4Count) + idx := i + go safeTraceCall(func() { + traceIPv6(c, idx, ipv4Count) + }) } loopIPv4v6: for range s { @@ -62,7 +76,10 @@ func BackTrace(enableIpv6 bool) string { t = time.After(time.Second * 10) ) for i := range model.Ipv4s { - go trace(c, i) + idx := i + go safeTraceCall(func() { + trace(c, idx) + }) } loopIPv4: for range s { diff --git a/bk/trace_common.go b/bk/trace_common.go index 32a8299..c12b9f7 100644 --- a/bk/trace_common.go +++ b/bk/trace_common.go @@ -233,7 +233,10 @@ func (t *Tracer) serveData(from net.IP, b []byte) error { return err } if msg.Type == ipv4.ICMPTypeEchoReply { - echo := msg.Body.(*icmp.Echo) + echo, ok := msg.Body.(*icmp.Echo) + if !ok || echo == nil { + return errUnsupportedProtocol + } return t.serveReply(from, &packet{from, uint16(echo.ID), 1, time.Now()}) } b = getReplyData(msg) diff --git a/bk/trace_ipv4.go b/bk/trace_ipv4.go index eef5e87..3a12c4d 100644 --- a/bk/trace_ipv4.go +++ b/bk/trace_ipv4.go @@ -58,6 +58,10 @@ func extractIpv4ASNsFromHops(hops []*Hop, enableLogger bool) []string { // trace IPv4追踪函数 func trace(ch chan Result, i int) { + defer func() { + if r := recover(); r != nil { + } + }() if model.EnableLoger { InitLogger() defer Logger.Sync() @@ -72,6 +76,10 @@ func trace(ch chan Result, i int) { wg.Add(1) go func(attemptNum int) { defer wg.Done() + defer func() { + if r := recover(); r != nil { + } + }() if model.EnableLoger { Logger.Info(fmt.Sprintf("第%d次尝试追踪 %s (%s)", attemptNum, model.Ipv4Names[i], model.Ipv4s[i])) } diff --git a/bk/trace_ipv6.go b/bk/trace_ipv6.go index ae1b938..a35327b 100644 --- a/bk/trace_ipv6.go +++ b/bk/trace_ipv6.go @@ -43,10 +43,21 @@ func (t *Tracer) serveIPv6(conn *ipv6.PacketConn) error { } return err } - if model.EnableLoger { - Logger.Info(fmt.Sprintf("收到IPv6响应: 来源=%v, 跳数=%d", src, cm.HopLimit)) + srcAddr, ok := src.(*net.IPAddr) + if !ok || srcAddr == nil { + continue + } + hopLimit := 0 + if cm != nil { + hopLimit = cm.HopLimit + } + if model.EnableLoger { + Logger.Info(fmt.Sprintf("收到IPv6响应: 来源=%v, 跳数=%d", srcAddr, hopLimit)) + } + fromIP := srcAddr.IP + if fromIP == nil { + continue } - fromIP := src.(*net.IPAddr).IP err = t.serveData(fromIP, buf[:n]) if err != nil && model.EnableLoger { Logger.Warn("处理IPv6数据失败: " + err.Error()) @@ -73,6 +84,10 @@ func extractIpv6ASNsFromHops(hops []*Hop, enableLogger bool) []string { // traceIPv6 IPv6追踪函数 func traceIPv6(ch chan Result, i int, offset int) { + defer func() { + if r := recover(); r != nil { + } + }() if model.EnableLoger { InitLogger() defer Logger.Sync() @@ -87,6 +102,10 @@ func traceIPv6(ch chan Result, i int, offset int) { wg.Add(1) go func(attemptNum int) { defer wg.Done() + defer func() { + if r := recover(); r != nil { + } + }() if model.EnableLoger { Logger.Info(fmt.Sprintf("第%d次尝试追踪 %s (%s)", attemptNum, model.Ipv6Names[i], model.Ipv6s[i])) } diff --git a/bk/utils.go b/bk/utils.go index 7ab1902..3ce97c1 100644 --- a/bk/utils.go +++ b/bk/utils.go @@ -47,7 +47,7 @@ func checkCdn(testUrl string) string { Logger.Info(fmt.Sprintf("Testing CDN: %s", url)) } resp, err := client.R().Get(url) - if err == nil { + if err == nil && resp != nil && resp.Body != nil { b, err := io.ReadAll(resp.Body) resp.Body.Close() if err == nil && strings.Contains(string(b), "success") { @@ -92,7 +92,7 @@ func getData(endpoint string) string { Logger.Info(fmt.Sprintf("Using CDN: %s", url)) } resp, err := client.R().Get(url) - if err == nil { + if err == nil && resp != nil && resp.Body != nil { defer resp.Body.Close() b, err := io.ReadAll(resp.Body) if err == nil && !strings.Contains(string(b), "error") { @@ -112,7 +112,7 @@ func getData(endpoint string) string { Logger.Info(fmt.Sprintf("Trying direct connection: %s", endpoint)) } resp, err := client.R().Get(endpoint) - if err == nil { + if err == nil && resp != nil && resp.Body != nil { defer resp.Body.Close() b, err := io.ReadAll(resp.Body) if err == nil { diff --git a/cmd/main.go b/cmd/main.go index 6a88b28..97bacd7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -32,9 +32,23 @@ type ConcurrentResults struct { // backtraceError error } +func safeGo(wg *sync.WaitGroup, fn func()) { + go func() { + defer wg.Done() + defer func() { + if r := recover(); r != nil { + } + }() + fn() + }() +} + func main() { go func() { - http.Get("https://hits.spiritlhl.net/backtrace.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false") + resp, err := http.Get("https://hits.spiritlhl.net/backtrace.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false") + if err == nil && resp != nil && resp.Body != nil { + resp.Body.Close() + } }() fmt.Println(Green("Repo:"), Yellow("https://github.com/oneclickvirt/backtrace")) var showVersion, showIpInfo, help, ipv6 bool @@ -62,6 +76,7 @@ func main() { if err != nil { fmt.Printf("get ip info err %v \n", err.Error()) } else { + defer rsp.Body.Close() err = json.NewDecoder(rsp.Body).Decode(&info) if err != nil { fmt.Printf("json decode err %v \n", err.Error()) @@ -106,8 +121,7 @@ func main() { } if targetIP != "" { wg.Add(1) - go func() { - defer wg.Done() + safeGo(&wg, func() { for i := 0; i < 2; i++ { result, err := bgptools.GetPoPInfo(targetIP) results.bgpError = err @@ -119,14 +133,13 @@ func main() { time.Sleep(3 * time.Second) } } - }() + }) } wg.Add(1) - go func() { - defer wg.Done() + safeGo(&wg, func() { result := backtrace.BackTrace(useIPv6) results.backtraceResult = result - }() + }) wg.Wait() if results.bgpResult != "" { fmt.Print(results.bgpResult) diff --git a/model/model.go b/model/model.go index ff09144..0b99cf9 100644 --- a/model/model.go +++ b/model/model.go @@ -2,7 +2,7 @@ package model import "time" -const BackTraceVersion = "v0.0.8" +const BackTraceVersion = "v0.0.9" var EnableLoger = false