mirror of
https://github.com/oneclickvirt/backtrace.git
synced 2025-07-18 22:38:47 +08:00
Compare commits
No commits in common. "ea5a90a8b1c43d5ee72ff68aeade15f3b7b0c99f" and "bde352cce2673f6cd816b4d78a7f1234e684475c" have entirely different histories.
ea5a90a8b1
...
bde352cce2
7
.github/workflows/main.yaml
vendored
7
.github/workflows/main.yaml
vendored
@ -55,12 +55,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mkdir -p bin
|
mkdir -p bin
|
||||||
cd cmd
|
cd cmd
|
||||||
CGO_ENABLED=0 GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build \
|
CGO_ENABLED=0 GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build -o ../bin/backtrace-${{ matrix.goos }}-${{ matrix.goarch }} -v -ldflags="-extldflags=-static" .
|
||||||
-o ../bin/backtrace-${{ matrix.goos }}-${{ matrix.goarch }} \
|
|
||||||
-v \
|
|
||||||
-ldflags="-extldflags=-static -s -w" \
|
|
||||||
-trimpath \
|
|
||||||
.
|
|
||||||
|
|
||||||
- name: Upload New Assets
|
- name: Upload New Assets
|
||||||
run: |
|
run: |
|
||||||
|
@ -74,7 +74,7 @@ rm -rf /usr/bin/backtrace
|
|||||||
## 在Golang中使用
|
## 在Golang中使用
|
||||||
|
|
||||||
```
|
```
|
||||||
go get github.com/oneclickvirt/backtrace@v0.0.5-20250629024536
|
go get github.com/oneclickvirt/backtrace@v0.0.5-20250517095024
|
||||||
```
|
```
|
||||||
|
|
||||||
## 概览图
|
## 概览图
|
||||||
|
@ -75,7 +75,6 @@
|
|||||||
2409:8013:2905
|
2409:8013:2905
|
||||||
2409:8013:2907
|
2409:8013:2907
|
||||||
2409:8013:2908
|
2409:8013:2908
|
||||||
2409:8013:290a
|
|
||||||
2409:8013:2b01
|
2409:8013:2b01
|
||||||
2409:8013:2b02
|
2409:8013:2b02
|
||||||
2409:8013:2b04
|
2409:8013:2b04
|
||||||
@ -248,7 +247,6 @@
|
|||||||
2409:803c:300b
|
2409:803c:300b
|
||||||
2409:803c:3090
|
2409:803c:3090
|
||||||
2409:803c:3098
|
2409:803c:3098
|
||||||
2409:803c:30a0
|
|
||||||
2409:803c:30b0
|
2409:803c:30b0
|
||||||
2409:803c:30c0
|
2409:803c:30c0
|
||||||
2409:8043:2901
|
2409:8043:2901
|
||||||
|
15
cmd/main.go
15
cmd/main.go
@ -7,11 +7,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
|
||||||
|
|
||||||
backtrace "github.com/oneclickvirt/backtrace/bk"
|
backtrace "github.com/oneclickvirt/backtrace/bk"
|
||||||
"github.com/oneclickvirt/backtrace/model"
|
"github.com/oneclickvirt/backtrace/model"
|
||||||
"github.com/oneclickvirt/backtrace/utils"
|
|
||||||
. "github.com/oneclickvirt/defaultset"
|
. "github.com/oneclickvirt/defaultset"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,7 +25,7 @@ func main() {
|
|||||||
go func() {
|
go func() {
|
||||||
http.Get("https://hits.spiritlhl.net/backtrace.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false")
|
http.Get("https://hits.spiritlhl.net/backtrace.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false")
|
||||||
}()
|
}()
|
||||||
fmt.Println(Green("Repo:"), Yellow("https://github.com/oneclickvirt/backtrace"))
|
fmt.Println(Green("项目地址:"), Yellow("https://github.com/oneclickvirt/backtrace"))
|
||||||
var showVersion, showIpInfo, help, ipv6 bool
|
var showVersion, showIpInfo, help, ipv6 bool
|
||||||
backtraceFlag := flag.NewFlagSet("backtrace", flag.ContinueOnError)
|
backtraceFlag := flag.NewFlagSet("backtrace", flag.ContinueOnError)
|
||||||
backtraceFlag.BoolVar(&help, "h", false, "Show help information")
|
backtraceFlag.BoolVar(&help, "h", false, "Show help information")
|
||||||
@ -60,16 +58,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
preCheck := utils.CheckPublicAccess(3 * time.Second)
|
backtrace.BackTrace(ipv6)
|
||||||
if preCheck.Connected && preCheck.StackType == "DualStack" {
|
|
||||||
backtrace.BackTrace(ipv6)
|
|
||||||
} else if preCheck.Connected && preCheck.StackType == "IPv4" {
|
|
||||||
backtrace.BackTrace(false)
|
|
||||||
} else if preCheck.Connected && preCheck.StackType == "IPv6" {
|
|
||||||
backtrace.BackTrace(true)
|
|
||||||
} else {
|
|
||||||
fmt.Println(Red("PreCheck IP Type Failed"))
|
|
||||||
}
|
|
||||||
fmt.Println(Yellow("准确线路自行查看详细路由,本测试结果仅作参考"))
|
fmt.Println(Yellow("准确线路自行查看详细路由,本测试结果仅作参考"))
|
||||||
fmt.Println(Yellow("同一目标地址多个线路时,可能检测已越过汇聚层,除了第一个线路外,后续信息可能无效"))
|
fmt.Println(Yellow("同一目标地址多个线路时,可能检测已越过汇聚层,除了第一个线路外,后续信息可能无效"))
|
||||||
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
|
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
|
||||||
|
160
utils/utils.go
160
utils/utils.go
@ -1,160 +0,0 @@
|
|||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type NetCheckResult struct {
|
|
||||||
HasIPv4 bool
|
|
||||||
HasIPv6 bool
|
|
||||||
Connected bool
|
|
||||||
StackType string // "IPv4", "IPv6", "DualStack", "None"
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeResolver(proto, dnsAddr string) *net.Resolver {
|
|
||||||
return &net.Resolver{
|
|
||||||
PreferGo: true,
|
|
||||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
|
||||||
d := net.Dialer{
|
|
||||||
Timeout: 5 * time.Second,
|
|
||||||
}
|
|
||||||
return d.DialContext(ctx, proto, dnsAddr)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func CheckPublicAccess(timeout time.Duration) NetCheckResult {
|
|
||||||
if timeout < 2*time.Second {
|
|
||||||
timeout = 2 * time.Second
|
|
||||||
}
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
resultChan := make(chan string, 8)
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
checks := []struct {
|
|
||||||
Tag string
|
|
||||||
Addr string
|
|
||||||
Kind string // udp4, udp6, http4, http6
|
|
||||||
}{
|
|
||||||
// UDP DNS
|
|
||||||
{"IPv4", "223.5.5.5:53", "udp4"}, // 阿里 DNS
|
|
||||||
{"IPv4", "8.8.8.8:53", "udp4"}, // Google DNS
|
|
||||||
{"IPv6", "[2400:3200::1]:53", "udp6"}, // 阿里 IPv6 DNS
|
|
||||||
{"IPv6", "[2001:4860:4860::8888]:53", "udp6"}, // Google IPv6 DNS
|
|
||||||
// HTTP HEAD
|
|
||||||
{"IPv4", "https://www.baidu.com", "http4"}, // 百度
|
|
||||||
{"IPv4", "https://1.1.1.1", "http4"}, // Cloudflare
|
|
||||||
{"IPv6", "https://[2400:3200::1]", "http6"}, // 阿里 IPv6
|
|
||||||
{"IPv6", "https://[2606:4700::1111]", "http6"}, // Cloudflare IPv6
|
|
||||||
}
|
|
||||||
for _, check := range checks {
|
|
||||||
wg.Add(1)
|
|
||||||
go func(tag, addr, kind string) {
|
|
||||||
defer wg.Done()
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
switch kind {
|
|
||||||
case "udp4", "udp6":
|
|
||||||
dialer := &net.Dialer{
|
|
||||||
Timeout: timeout / 4,
|
|
||||||
}
|
|
||||||
conn, err := dialer.DialContext(ctx, kind, addr)
|
|
||||||
if err == nil && conn != nil {
|
|
||||||
conn.Close()
|
|
||||||
select {
|
|
||||||
case resultChan <- tag:
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "http4", "http6":
|
|
||||||
var resolver *net.Resolver
|
|
||||||
if kind == "http4" {
|
|
||||||
resolver = makeResolver("udp4", "223.5.5.5:53")
|
|
||||||
} else {
|
|
||||||
resolver = makeResolver("udp6", "[2400:3200::1]:53")
|
|
||||||
}
|
|
||||||
dialer := &net.Dialer{
|
|
||||||
Timeout: timeout / 4,
|
|
||||||
Resolver: resolver,
|
|
||||||
}
|
|
||||||
transport := &http.Transport{
|
|
||||||
DialContext: dialer.DialContext,
|
|
||||||
MaxIdleConns: 1,
|
|
||||||
MaxIdleConnsPerHost: 1,
|
|
||||||
IdleConnTimeout: time.Second,
|
|
||||||
TLSHandshakeTimeout: timeout / 4,
|
|
||||||
ResponseHeaderTimeout: timeout / 4,
|
|
||||||
DisableKeepAlives: true,
|
|
||||||
}
|
|
||||||
client := &http.Client{
|
|
||||||
Timeout: timeout / 4,
|
|
||||||
Transport: transport,
|
|
||||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
|
||||||
return http.ErrUseLastResponse
|
|
||||||
},
|
|
||||||
}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, "HEAD", addr, nil)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err == nil && resp != nil {
|
|
||||||
if resp.Body != nil {
|
|
||||||
resp.Body.Close()
|
|
||||||
}
|
|
||||||
if resp.StatusCode < 500 {
|
|
||||||
select {
|
|
||||||
case resultChan <- tag:
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}(check.Tag, check.Addr, check.Kind)
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
close(resultChan)
|
|
||||||
}()
|
|
||||||
hasV4 := false
|
|
||||||
hasV6 := false
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case res, ok := <-resultChan:
|
|
||||||
if !ok {
|
|
||||||
goto result
|
|
||||||
}
|
|
||||||
if res == "IPv4" {
|
|
||||||
hasV4 = true
|
|
||||||
}
|
|
||||||
if res == "IPv6" {
|
|
||||||
hasV6 = true
|
|
||||||
}
|
|
||||||
case <-ctx.Done():
|
|
||||||
goto result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result:
|
|
||||||
stack := "None"
|
|
||||||
if hasV4 && hasV6 {
|
|
||||||
stack = "DualStack"
|
|
||||||
} else if hasV4 {
|
|
||||||
stack = "IPv4"
|
|
||||||
} else if hasV6 {
|
|
||||||
stack = "IPv6"
|
|
||||||
}
|
|
||||||
return NetCheckResult{
|
|
||||||
HasIPv4: hasV4,
|
|
||||||
HasIPv6: hasV6,
|
|
||||||
Connected: hasV4 || hasV6,
|
|
||||||
StackType: stack,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCheckPublicAccess(t *testing.T) {
|
|
||||||
timeout := 3 * time.Second
|
|
||||||
result := CheckPublicAccess(timeout)
|
|
||||||
if result.Connected {
|
|
||||||
fmt.Printf("✅ 本机有公网连接,类型: %s\n", result.StackType)
|
|
||||||
} else {
|
|
||||||
fmt.Println("❌ 本机未检测到公网连接")
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user