TracerouteHops 启动一次 traceroute,将每跳与终态(错误/完成)通过 channel 投递。 通道在追踪结束(reach 或超时)或 ctx 取消后关闭。
(ctx context.Context, req TracerouteRequest)
| 45 | // TracerouteHops 启动一次 traceroute,将每跳与终态(错误/完成)通过 channel 投递。 |
| 46 | // 通道在追踪结束(reach 或超时)或 ctx 取消后关闭。 |
| 47 | func (a *API) TracerouteHops(ctx context.Context, req TracerouteRequest) <-chan TracerouteEvent { |
| 48 | out := make(chan TracerouteEvent, tracerouteMaxHops+2) |
| 49 | go func() { |
| 50 | defer close(out) |
| 51 | host := strings.TrimSpace(req.Host) |
| 52 | if host == "" { |
| 53 | out <- TracerouteEvent{Err: "host 参数不能为空"} |
| 54 | return |
| 55 | } |
| 56 | if strings.ContainsAny(host, " ;|&`$(){}[]<>\\'\"") { |
| 57 | out <- TracerouteEvent{Err: "host 包含非法字符"} |
| 58 | return |
| 59 | } |
| 60 | method := req.Method |
| 61 | if method != "tcp" { |
| 62 | method = "icmp" |
| 63 | } |
| 64 | port := req.Port |
| 65 | if port <= 0 || port > 65535 { |
| 66 | port = 80 |
| 67 | } |
| 68 | |
| 69 | addrs, err := net.DefaultResolver.LookupHost(ctx, host) |
| 70 | if err != nil || len(addrs) == 0 { |
| 71 | out <- TracerouteEvent{Err: fmt.Sprintf("DNS 解析失败: %v", err)} |
| 72 | return |
| 73 | } |
| 74 | destIP := net.ParseIP(addrs[0]) |
| 75 | isIPv6 := destIP.To4() == nil |
| 76 | |
| 77 | runCtx, cancel := context.WithTimeout(ctx, tracerouteTotalLimit) |
| 78 | defer cancel() |
| 79 | |
| 80 | hopCh := make(chan TracerouteHop, tracerouteMaxHops) |
| 81 | doneCh := make(chan error, 1) |
| 82 | go func() { |
| 83 | var e error |
| 84 | if method == "tcp" { |
| 85 | if isIPv6 { |
| 86 | e = traceTCPv6(runCtx, destIP, port, hopCh) |
| 87 | } else { |
| 88 | e = traceTCPv4(runCtx, destIP.To4(), port, hopCh) |
| 89 | } |
| 90 | } else { |
| 91 | if isIPv6 { |
| 92 | e = traceICMPv6(runCtx, destIP, hopCh) |
| 93 | } else { |
| 94 | e = traceICMPv4(runCtx, destIP.To4(), hopCh) |
| 95 | } |
| 96 | } |
| 97 | doneCh <- e |
| 98 | }() |
| 99 | |
| 100 | for { |
| 101 | select { |
| 102 | case hop, ok := <-hopCh: |
| 103 | if !ok { |
| 104 | hopCh = nil |
no test coverage detected