75 lines
1.4 KiB
Go
75 lines
1.4 KiB
Go
//go:build darwin || linux
|
|
|
|
package diagnostic
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type NetworkCollectorImpl struct{}
|
|
|
|
func (tracer *NetworkCollectorImpl) Collect(ctx context.Context, options TraceOptions) ([]*Hop, string, error) {
|
|
args := []string{
|
|
"-I",
|
|
"-w",
|
|
strconv.FormatInt(int64(options.timeout.Seconds()), 10),
|
|
"-m",
|
|
strconv.FormatUint(options.ttl, 10),
|
|
options.address,
|
|
}
|
|
|
|
var command string
|
|
|
|
switch options.useV4 {
|
|
case false:
|
|
command = "traceroute6"
|
|
default:
|
|
command = "traceroute"
|
|
}
|
|
|
|
process := exec.CommandContext(ctx, command, args...)
|
|
|
|
return decodeNetworkOutputToFile(process, DecodeLine)
|
|
}
|
|
|
|
func DecodeLine(text string) (*Hop, error) {
|
|
fields := strings.Fields(text)
|
|
parts := []string{}
|
|
filter := func(s string) bool { return s != "*" && s != "ms" }
|
|
|
|
for _, field := range fields {
|
|
if filter(field) {
|
|
parts = append(parts, field)
|
|
}
|
|
}
|
|
|
|
index, err := strconv.ParseUint(parts[0], 10, 8)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't parse index from timeout hop: %w", err)
|
|
}
|
|
|
|
if len(parts) == 1 {
|
|
return NewTimeoutHop(uint8(index)), nil
|
|
}
|
|
|
|
domain := ""
|
|
rtts := []time.Duration{}
|
|
|
|
for _, part := range parts[1:] {
|
|
rtt, err := strconv.ParseFloat(part, 64)
|
|
if err != nil {
|
|
domain += part + " "
|
|
} else {
|
|
rtts = append(rtts, time.Duration(rtt*MicrosecondsFactor))
|
|
}
|
|
}
|
|
domain, _ = strings.CutSuffix(domain, " ")
|
|
|
|
return NewHop(uint8(index), domain, rtts), nil
|
|
}
|