diff --git a/README.md b/README.md index e315b7a..7b0d62d 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ Flags: eg: --proxy=socks://10.20.0.101:7890 eg: --proxy=http://10.20.0.101:7890 --source=SOURCE Bind a source interface for the speedtest. + --dns-bind-source DNS request binding source.(Experimental) eg: --source=10.20.0.101 -m --multi Enable multi-server mode. -t --thread=THREAD Set the number of concurrent connections. diff --git a/speedtest.go b/speedtest.go index c4caa0e..021336c 100644 --- a/speedtest.go +++ b/speedtest.go @@ -13,23 +13,24 @@ import ( ) var ( - showList = kingpin.Flag("list", "Show available speedtest.net servers.").Short('l').Bool() - serverIds = kingpin.Flag("server", "Select server id to run speedtest.").Short('s').Ints() - customURL = kingpin.Flag("custom-url", "Specify the url of the server instead of getting a list from speedtest.net.").String() - savingMode = kingpin.Flag("saving-mode", "Test with few resources, though low accuracy (especially > 30Mbps).").Bool() - jsonOutput = kingpin.Flag("json", "Output results in json format.").Bool() - location = kingpin.Flag("location", "Change the location with a precise coordinate. Format: lat,lon").String() - city = kingpin.Flag("city", "Change the location with a predefined city label.").String() - showCityList = kingpin.Flag("city-list", "List all predefined city labels.").Bool() - proxy = kingpin.Flag("proxy", "Set a proxy(http[s] or socks) for the speedtest.").String() - source = kingpin.Flag("source", "Bind a source interface for the speedtest.").String() - multi = kingpin.Flag("multi", "Enable multi-server mode.").Short('m').Bool() - thread = kingpin.Flag("thread", "Set the number of concurrent connections.").Short('t').Int() - search = kingpin.Flag("search", "Fuzzy search servers by a keyword.").String() - noDownload = kingpin.Flag("no-download", "Disable download test.").Bool() - noUpload = kingpin.Flag("no-upload", "Disable upload test.").Bool() - pingMode = kingpin.Flag("ping-mode", "Select a method for Ping. (support icmp/tcp/http)").Default("http").String() - debug = kingpin.Flag("debug", "Enable debug mode.").Short('d').Bool() + showList = kingpin.Flag("list", "Show available speedtest.net servers.").Short('l').Bool() + serverIds = kingpin.Flag("server", "Select server id to run speedtest.").Short('s').Ints() + customURL = kingpin.Flag("custom-url", "Specify the url of the server instead of getting a list from speedtest.net.").String() + savingMode = kingpin.Flag("saving-mode", "Test with few resources, though low accuracy (especially > 30Mbps).").Bool() + jsonOutput = kingpin.Flag("json", "Output results in json format.").Bool() + location = kingpin.Flag("location", "Change the location with a precise coordinate. Format: lat,lon").String() + city = kingpin.Flag("city", "Change the location with a predefined city label.").String() + showCityList = kingpin.Flag("city-list", "List all predefined city labels.").Bool() + proxy = kingpin.Flag("proxy", "Set a proxy(http[s] or socks) for the speedtest.").String() + source = kingpin.Flag("source", "Bind a source interface for the speedtest.").String() + dnsBindSource = kingpin.Flag("dns-bind-source", "DNS request binding source.(Experimental)").Bool() + multi = kingpin.Flag("multi", "Enable multi-server mode.").Short('m').Bool() + thread = kingpin.Flag("thread", "Set the number of concurrent connections.").Short('t').Int() + search = kingpin.Flag("search", "Fuzzy search servers by a keyword.").String() + noDownload = kingpin.Flag("no-download", "Disable download test.").Bool() + noUpload = kingpin.Flag("no-upload", "Disable upload test.").Bool() + pingMode = kingpin.Flag("ping-mode", "Select a method for Ping. (support icmp/tcp/http)").Default("http").String() + debug = kingpin.Flag("debug", "Enable debug mode.").Short('d').Bool() ) func main() { @@ -41,17 +42,18 @@ func main() { // 0. speed test setting var speedtestClient = speedtest.New(speedtest.WithUserConfig( &speedtest.UserConfig{ - UserAgent: speedtest.DefaultUserAgent, - Proxy: *proxy, - Source: *source, - Debug: *debug, - PingMode: parseProto(*pingMode), // TCP as default - SavingMode: *savingMode, - CityFlag: *city, - LocationFlag: *location, - Keyword: *search, - NoDownload: *noDownload, - NoUpload: *noUpload, + UserAgent: speedtest.DefaultUserAgent, + Proxy: *proxy, + Source: *source, + DnsBindSource: *dnsBindSource, + Debug: *debug, + PingMode: parseProto(*pingMode), // TCP as default + SavingMode: *savingMode, + CityFlag: *city, + LocationFlag: *location, + Keyword: *search, + NoDownload: *noDownload, + NoUpload: *noUpload, })) speedtestClient.SetNThread(*thread) diff --git a/speedtest/speedtest.go b/speedtest/speedtest.go index c52250a..f8c6f29 100644 --- a/speedtest/speedtest.go +++ b/speedtest/speedtest.go @@ -1,6 +1,7 @@ package speedtest import ( + "context" "fmt" "net" "net/http" @@ -35,12 +36,13 @@ type Speedtest struct { } type UserConfig struct { - T *http.Transport - UserAgent string - Proxy string - Source string - Debug bool - PingMode Proto + T *http.Transport + UserAgent string + Proxy string + Source string + DnsBindSource bool + Debug bool + PingMode Proto SavingMode bool @@ -107,6 +109,24 @@ func (s *Speedtest) NewUserConfig(uc *UserConfig) { } else { dbg.Printf("Warning: skipping parse the source address. err: %s\n", err.Error()) } + if uc.DnsBindSource { + net.DefaultResolver.Dial = func(ctx context.Context, network, dnsServer string) (net.Conn, error) { + dialer := &net.Dialer{ + Timeout: 5 * time.Second, + LocalAddr: func(network string) net.Addr { + switch network { + case "udp", "udp4", "udp6": + return &net.UDPAddr{IP: net.ParseIP(address)} + case "tcp", "tcp4", "tcp6": + return &net.TCPAddr{IP: net.ParseIP(address)} + default: + return nil + } + }(network), + } + return dialer.DialContext(ctx, network, dnsServer) + } + } } if len(uc.Proxy) > 0 {