From 8b72938f3470aa7fabb53a97687a70b63e54dd77 Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Wed, 3 Aug 2022 16:13:01 +0800 Subject: [PATCH] adapt windows tracert --- go.mod | 6 - go.sum | 19 --- hrp/cmd/dial.go | 4 + hrp/internal/dial/traceroute.go | 210 ------------------------ hrp/internal/dial/traceroute_unix.go | 106 ++++++++++++ hrp/internal/dial/traceroute_windows.go | 105 ++++++++++++ 6 files changed, 215 insertions(+), 235 deletions(-) create mode 100644 hrp/internal/dial/traceroute_unix.go create mode 100644 hrp/internal/dial/traceroute_windows.go diff --git a/go.mod b/go.mod index 73df7b9d..51da4a79 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/denisbrodbeck/machineid v1.0.1 github.com/fatih/color v1.13.0 github.com/getsentry/sentry-go v0.13.0 - github.com/gizak/termui v2.3.0+incompatible // indirect github.com/go-errors/errors v1.0.1 github.com/go-openapi/spec v0.20.6 github.com/go-ping/ping v1.1.0 @@ -19,12 +18,8 @@ require ( github.com/jmespath/go-jmespath v0.4.0 github.com/json-iterator/go v1.1.12 github.com/maja42/goval v1.2.1 - github.com/maruel/panicparse v1.6.2 // indirect - github.com/mehrdadrad/mylg v0.2.6 github.com/miekg/dns v1.0.14 - github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.4.1 - github.com/nsf/termbox-go v1.1.1 // indirect github.com/olekukonko/tablewriter v0.0.5 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.11.0 @@ -38,7 +33,6 @@ require ( golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 google.golang.org/grpc v1.45.0 google.golang.org/protobuf v1.28.0 - gopkg.in/h2non/gock.v0 v0.1.6 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) diff --git a/go.sum b/go.sum index 7140eef6..09453f97 100644 --- a/go.sum +++ b/go.sum @@ -68,11 +68,8 @@ github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqO github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -123,8 +120,6 @@ github.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= -github.com/gizak/termui v2.3.0+incompatible h1:S8wJoNumYfc/rR5UezUM4HsPEo3RJh0LKdiuDWQpjqw= -github.com/gizak/termui v2.3.0+incompatible/go.mod h1:PkJoWUt/zacQKysNfQtcw1RW+eK2SxkieVBtl+4ovLA= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -330,13 +325,9 @@ github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/maja42/goval v1.2.1 h1:fyEgzddqPgCZsKcFLk4C6SdCHyEaAHYvtZG4mGzQOHU= github.com/maja42/goval v1.2.1/go.mod h1:42LU+BQXL/veE9jnTTUOSj38GRmOTSThYSXRVodI5J4= -github.com/maruel/panicparse v1.6.2 h1:tZuGQTlbOY5jCprrWMJTikREqKPn+UAKdR4CHSpj834= -github.com/maruel/panicparse v1.6.2/go.mod h1:uoxI4w9gJL6XahaYPMq/z9uadrdr1SyHuQwV2q80Mm0= -github.com/maruel/panicparse/v2 v2.1.1/go.mod h1:AeTWdCE4lcq8OKsLb6cHSj1RWHVSnV9HBCk7sKLF4Jg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= @@ -354,9 +345,6 @@ github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpe github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= -github.com/mehrdadrad/mylg v0.2.6 h1:MsIreb998Yn/T9h2u92fqPMDZrAO+4720swtfjEqbKM= -github.com/mehrdadrad/mylg v0.2.6/go.mod h1:mh70kG4nkk0dgfP+jRnkCX3E9wOi/bvJTmtiBlFmai4= -github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -366,8 +354,6 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= -github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -390,8 +376,6 @@ github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY= -github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= @@ -678,7 +662,6 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -899,8 +882,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/h2non/gock.v0 v0.1.6 h1:vKwe08YIEZEZtspT36nTlo9S+edPFwU6k1tWC7mTw3A= -gopkg.in/h2non/gock.v0 v0.1.6/go.mod h1:KjTyaFK6xOUSpvIeLQDkMc/AyfyRKVKXhFxT4QxkM3Q= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= diff --git a/hrp/cmd/dial.go b/hrp/cmd/dial.go index 1eeb0881..cb602234 100644 --- a/hrp/cmd/dial.go +++ b/hrp/cmd/dial.go @@ -1,6 +1,7 @@ package cmd import ( + "runtime" "time" "github.com/rs/zerolog/log" @@ -53,6 +54,9 @@ var traceRouteCmd = &cobra.Command{ setLogLevel(logLevel) }, RunE: func(cmd *cobra.Command, args []string) error { + if runtime.GOOS == "windows" { + log.Info().Msg("using default probe number (3) on Windows") + } return dial.DoTraceRoute(&traceRouteOptions, args) }, } diff --git a/hrp/internal/dial/traceroute.go b/hrp/internal/dial/traceroute.go index f9d6550c..d20e5f1b 100644 --- a/hrp/internal/dial/traceroute.go +++ b/hrp/internal/dial/traceroute.go @@ -1,33 +1,5 @@ package dial -import ( - "bufio" - "fmt" - "net/url" - "os" - "os/exec" - "path/filepath" - "regexp" - "strconv" - "strings" - "time" - - "github.com/mehrdadrad/mylg/cli" - "github.com/mehrdadrad/mylg/icmp" - "github.com/pkg/errors" - "github.com/rs/zerolog/log" - - "github.com/httprunner/httprunner/v4/hrp/internal/builtin" - "github.com/httprunner/httprunner/v4/hrp/internal/json" -) - -var ( - regexIPAddr = regexp.MustCompile(`([\d.]+)`) - regexElapsedTime = regexp.MustCompile(`(\d+\.\d+)`) - regexTraceroutePass = regexp.MustCompile(fmt.Sprintf(`(\d+)[\s*]+(\S+)\s+\(%s\)\s+%s\s+ms`, regexIPAddr, regexElapsedTime)) - regexTracerouteFailure = regexp.MustCompile(`(\d+)[\s*]+$`) -) - type TraceRouteOptions struct { MaxTTL int Queries int @@ -46,185 +18,3 @@ type TraceRouteResultNode struct { Ip string `json:"ip"` Time string `json:"time"` } - -type HopResp struct { - Num int `json:"Id"` - Hop string `json:"Hop"` - Ip string `json:"Ip"` - Elapsed float64 `json:"Elapsed"` - Holder string `json:"Holder"` - ASN float64 `json:"ASN"` - Last bool `json:"Last"` -} - -func DoTraceRoute(traceRouteOptions *TraceRouteOptions, args []string) (err error) { - if len(args) != 1 { - return errors.New("there should be one argument") - } - var traceRouteResult TraceRouteResult - defer func() { - if traceRouteOptions.SaveTests { - dir, _ := os.Getwd() - traceRouteResultName := fmt.Sprintf("traceroute_result_%v.json", time.Now().Format("20060102150405")) - traceRouteResultPath := filepath.Join(dir, traceRouteResultName) - err = builtin.Dump2JSON(traceRouteResult, traceRouteResultPath) - if err != nil { - log.Error().Err(err).Msg("save traceroute result failed") - } - } - }() - - traceRouteTarget := args[0] - parsedURL, err := url.Parse(traceRouteTarget) - if err == nil && parsedURL.Host != "" { - log.Info().Msgf("parse input url %v and extract host %v", traceRouteTarget, parsedURL.Host) - traceRouteTarget = strings.Split(parsedURL.Host, ":")[0] - } - - cmd := exec.Command("traceroute", "-m", strconv.Itoa(traceRouteOptions.MaxTTL), - "-q", strconv.Itoa(traceRouteOptions.Queries), traceRouteTarget) - stdout, _ := cmd.StdoutPipe() - - startT := time.Now() - defer func() { - log.Info().Msgf("for target %s, traceroute costs %v", traceRouteTarget, time.Since(startT)) - }() - - log.Info().Msgf("start to traceroute %v", traceRouteTarget) - err = cmd.Start() - if err != nil { - traceRouteResult.Suc = false - traceRouteResult.ErrMsg = "execute traceroute failed" - log.Error().Err(err).Msg("start command failed") - return - } - - scanner := bufio.NewScanner(stdout) - for scanner.Scan() { - hopLine := scanner.Text() - fmt.Println(hopLine) - passLine := regexTraceroutePass.FindStringSubmatch(hopLine) - if len(passLine) == 5 { - hopID, _ := strconv.Atoi(passLine[1]) - traceRouteResult.Details = append(traceRouteResult.Details, TraceRouteResultNode{ - Id: hopID, - Ip: passLine[3], - Time: passLine[4], - }) - traceRouteResult.Suc = true - continue - } - failureLine := regexTracerouteFailure.FindStringSubmatch(hopLine) - if len(failureLine) == 2 { - hopID, _ := strconv.Atoi(failureLine[1]) - traceRouteResult.Details = append(traceRouteResult.Details, TraceRouteResultNode{ - Id: hopID, - }) - continue - } - } - hopCount := len(traceRouteResult.Details) - traceRouteResult.IP = traceRouteResult.Details[hopCount-1].Ip - err = cmd.Wait() - if err != nil { - traceRouteResult.Suc = false - traceRouteResult.ErrMsg = "wait traceroute finish failed" - log.Error().Err(err).Msg("wait command failed") - return - } - return -} - -// DoTraceRouteSDK with golang SDK, which needs root privilege -func DoTraceRouteSDK(traceRouteOptions *TraceRouteOptions, args []string) (err error) { - if len(args) != 1 { - return errors.New("there should be one argument") - } - - var traceRouteResult TraceRouteResult - defer func() { - if traceRouteOptions.SaveTests { - dir, _ := os.Getwd() - traceRouteResultName := fmt.Sprintf("traceroute_result_%v.json", time.Now().Format("20060102150405")) - traceRouteResultPath := filepath.Join(dir, traceRouteResultName) - err = builtin.Dump2JSON(traceRouteResult, traceRouteResultPath) - if err != nil { - log.Error().Err(err).Msg("save traceroute result failed") - } - } - }() - - traceRouteTarget := args[0] - parsedURL, err := url.Parse(traceRouteTarget) - if err == nil && parsedURL.Host != "" { - log.Info().Msgf("parse input url %v and extract host %v", traceRouteTarget, parsedURL.Host) - traceRouteTarget = strings.Split(parsedURL.Host, ":")[0] - } - - cfg, err := cli.ReadDefaultConfig() - if err != nil { - log.Error().Err(err).Msgf("fail to read default config") - traceRouteResult.Suc = false - traceRouteResult.ErrMsg = err.Error() - return - } - - traceRouter, err := icmp.NewTrace(traceRouteTarget, cfg) - if err != nil { - log.Error().Err(err).Msgf("fail to new traceRouter for %s", traceRouteTarget) - traceRouteResult.Suc = false - traceRouteResult.ErrMsg = err.Error() - return - } - - startT := time.Now() - defer func() { - log.Info().Msgf("for target %s, traceroute costs %v", traceRouteTarget, time.Since(startT)) - }() - - log.Info().Msgf("start to trace route of %v", traceRouteTarget) - hopRespChan, err := traceRouter.MRun() - if err != nil { - log.Error().Err(err).Msgf("fail to trace route of %v", traceRouteTarget) - traceRouteResult.Suc = false - traceRouteResult.ErrMsg = err.Error() - } - count := 0 - t := time.NewTicker(2 * time.Minute) - for { - select { - case <-t.C: - log.Error().Err(err).Msgf("fail to do traceroute for %s because timeout", traceRouteTarget) - traceRouteResult.Suc = false - traceRouteResult.ErrMsg = "timeout" - return - case resp := <-hopRespChan: - respJSON := resp.Marshal() - fmt.Printf("traceroute hop: %v\n", respJSON) - var hopResp HopResp - err = json.Unmarshal([]byte(respJSON), &hopResp) - if err != nil { - log.Error().Err(err).Msgf("fail to do traceroute for %s because of hop response %v unmarshal error", traceRouteTarget, respJSON) - traceRouteResult.Suc = false - traceRouteResult.ErrMsg = "hop response unmarshal error" - } - traceRouteResult.Details = append(traceRouteResult.Details, TraceRouteResultNode{ - Id: hopResp.Num, - Ip: hopResp.Ip, - Time: fmt.Sprintf("%.2f", hopResp.Elapsed), - }) - traceRouteResult.Suc = true - traceRouteResult.ErrMsg = "" - if hopResp.Last { - traceRouteResult.IP = hopResp.Ip - log.Info().Msgf("for target %s, traceroute completed", traceRouteTarget) - return - } - count += 1 - if count > 30 { - log.Info().Msgf("for target %s, traceroute hop counts reach limit", traceRouteTarget) - return - } - } - } -} diff --git a/hrp/internal/dial/traceroute_unix.go b/hrp/internal/dial/traceroute_unix.go new file mode 100644 index 00000000..b6621592 --- /dev/null +++ b/hrp/internal/dial/traceroute_unix.go @@ -0,0 +1,106 @@ +//go:build darwin || linux +// +build darwin linux + +package dial + +import ( + "bufio" + "fmt" + "net/url" + "os" + "os/exec" + "path/filepath" + "regexp" + "strconv" + "strings" + "time" + + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + + "github.com/httprunner/httprunner/v4/hrp/internal/builtin" +) + +var ( + regexIPAddr = regexp.MustCompile(`([\d.]+)`) + regexElapsedTime = regexp.MustCompile(`(\d+\.\d+)`) + regexTraceroutePass = regexp.MustCompile(fmt.Sprintf(`(\d+)[\s*]+(\S+)\s+\(%s\)\s+%s\s+ms`, regexIPAddr, regexElapsedTime)) + regexTracerouteFailure = regexp.MustCompile(`(\d+)[\s*]+$`) +) + +func DoTraceRoute(traceRouteOptions *TraceRouteOptions, args []string) (err error) { + if len(args) != 1 { + return errors.New("there should be one argument") + } + var traceRouteResult TraceRouteResult + defer func() { + if traceRouteOptions.SaveTests { + dir, _ := os.Getwd() + traceRouteResultName := fmt.Sprintf("traceroute_result_%v.json", time.Now().Format("20060102150405")) + traceRouteResultPath := filepath.Join(dir, traceRouteResultName) + err = builtin.Dump2JSON(traceRouteResult, traceRouteResultPath) + if err != nil { + log.Error().Err(err).Msg("save traceroute result failed") + } + } + }() + + traceRouteTarget := args[0] + parsedURL, err := url.Parse(traceRouteTarget) + if err == nil && parsedURL.Host != "" { + log.Info().Msgf("parse input url %v and extract host %v", traceRouteTarget, parsedURL.Host) + traceRouteTarget = strings.Split(parsedURL.Host, ":")[0] + } + + cmd := exec.Command("traceroute", "-m", strconv.Itoa(traceRouteOptions.MaxTTL), + "-q", strconv.Itoa(traceRouteOptions.Queries), traceRouteTarget) + stdout, _ := cmd.StdoutPipe() + + startT := time.Now() + defer func() { + log.Info().Msgf("for target %s, traceroute costs %v", traceRouteTarget, time.Since(startT)) + }() + + log.Info().Msgf("start to traceroute %v", traceRouteTarget) + err = cmd.Start() + if err != nil { + traceRouteResult.Suc = false + traceRouteResult.ErrMsg = "execute traceroute failed" + log.Error().Err(err).Msg("start command failed") + return + } + + scanner := bufio.NewScanner(stdout) + for scanner.Scan() { + hopLine := scanner.Text() + fmt.Println(hopLine) + failureLine := regexTracerouteFailure.FindStringSubmatch(hopLine) + if len(failureLine) == 2 { + hopID, _ := strconv.Atoi(failureLine[1]) + traceRouteResult.Details = append(traceRouteResult.Details, TraceRouteResultNode{ + Id: hopID, + }) + continue + } + passLine := regexTraceroutePass.FindStringSubmatch(hopLine) + if len(passLine) == 5 { + hopID, _ := strconv.Atoi(passLine[1]) + traceRouteResult.Details = append(traceRouteResult.Details, TraceRouteResultNode{ + Id: hopID, + Ip: passLine[3], + Time: passLine[4], + }) + traceRouteResult.Suc = true + } + } + hopCount := len(traceRouteResult.Details) + traceRouteResult.IP = traceRouteResult.Details[hopCount-1].Ip + err = cmd.Wait() + if err != nil { + traceRouteResult.Suc = false + traceRouteResult.ErrMsg = "wait traceroute finish failed" + log.Error().Err(err).Msg("wait command failed") + return + } + return +} diff --git a/hrp/internal/dial/traceroute_windows.go b/hrp/internal/dial/traceroute_windows.go new file mode 100644 index 00000000..a1b4b37b --- /dev/null +++ b/hrp/internal/dial/traceroute_windows.go @@ -0,0 +1,105 @@ +//go:build windows +// +build windows + +package dial + +import ( + "bufio" + "fmt" + "net/url" + "os" + "os/exec" + "path/filepath" + "regexp" + "strconv" + "strings" + "time" + + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + + "github.com/httprunner/httprunner/v4/hrp/internal/builtin" +) + +var ( + regexTracertPass = regexp.MustCompile(`(\d+)[\s*<]+(\d+)\s+ms`) + regexTracertFailure = regexp.MustCompile(`(\d+)[\s*]+Request timed out`) +) + +func DoTraceRoute(traceRouteOptions *TraceRouteOptions, args []string) (err error) { + if len(args) != 1 { + return errors.New("there should be one argument") + } + var traceRouteResult TraceRouteResult + defer func() { + if traceRouteOptions.SaveTests { + dir, _ := os.Getwd() + traceRouteResultName := fmt.Sprintf("traceroute_result_%v.json", time.Now().Format("20060102150405")) + traceRouteResultPath := filepath.Join(dir, traceRouteResultName) + err = builtin.Dump2JSON(traceRouteResult, traceRouteResultPath) + if err != nil { + log.Error().Err(err).Msg("save traceroute result failed") + } + } + }() + + traceRouteTarget := args[0] + parsedURL, err := url.Parse(traceRouteTarget) + if err == nil && parsedURL.Host != "" { + log.Info().Msgf("parse input url %v and extract host %v", traceRouteTarget, parsedURL.Host) + traceRouteTarget = strings.Split(parsedURL.Host, ":")[0] + } + + cmd := exec.Command("tracert", "-h", strconv.Itoa(traceRouteOptions.MaxTTL), traceRouteTarget) + stdout, _ := cmd.StdoutPipe() + + startT := time.Now() + defer func() { + log.Info().Msgf("for target %s, traceroute costs %v", traceRouteTarget, time.Since(startT)) + }() + + log.Info().Msgf("start to traceroute %v", traceRouteTarget) + err = cmd.Start() + if err != nil { + traceRouteResult.Suc = false + traceRouteResult.ErrMsg = "execute traceroute failed" + log.Error().Err(err).Msg("start command failed") + return + } + + scanner := bufio.NewScanner(stdout) + for scanner.Scan() { + hopLine := scanner.Text() + fmt.Println(hopLine) + failureLine := regexTracertFailure.FindStringSubmatch(hopLine) + if len(failureLine) == 2 { + hopID, _ := strconv.Atoi(failureLine[1]) + traceRouteResult.Details = append(traceRouteResult.Details, TraceRouteResultNode{ + Id: hopID, + }) + continue + } + passLine := regexTracertPass.FindStringSubmatch(hopLine) + if len(passLine) == 3 { + hopID, _ := strconv.Atoi(passLine[1]) + fields := strings.Fields(hopLine) + hopIP := strings.Trim(fields[len(fields)-1], "[]") + traceRouteResult.Details = append(traceRouteResult.Details, TraceRouteResultNode{ + Id: hopID, + Ip: hopIP, + Time: passLine[2], + }) + traceRouteResult.Suc = true + } + } + hopCount := len(traceRouteResult.Details) + traceRouteResult.IP = traceRouteResult.Details[hopCount-1].Ip + err = cmd.Wait() + if err != nil { + traceRouteResult.Suc = false + traceRouteResult.ErrMsg = "wait traceroute finish failed" + log.Error().Err(err).Msg("wait command failed") + return + } + return +}