Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add specific port support #535

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ dist
Releases
*.exe
*.csv
.vscode/
*.bat
*.sh
6 changes: 5 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ https://github.com/XIU2/CloudflareSpeedTest
-dt 10
下载测速时间;单个 IP 下载测速最长时间,不能太短;(默认 10 秒)
-tp 443
指定测速端口;延迟测速/下载测速时使用的端口;(默认 443 端口)
指定所有测速端口;即延迟测速/下载测速时使用的端口;(默认 443 端口)
(会被-ip参数或IP段文件中指定的 ip/mask:port 覆盖)
-url https://cf.xiu2.xyz/url
指定测速地址;延迟测速(HTTPing)/下载测速时使用的地址,默认地址不保证可用性,建议自建;

Expand Down Expand Up @@ -67,6 +68,8 @@ https://github.com/XIU2/CloudflareSpeedTest
禁用下载测速;禁用后测速结果会按延迟排序 (默认按下载速度排序);(默认 启用)
-allip
测速全部的IP;对 IP 段中的每个 IP (仅支持 IPv4) 进行测速;(默认 每个 /24 段随机测速一个 IP)
-onlyip
命令行输出和结果文件仅输出 IP,不附带端口信息

-v
打印程序版本 + 检查版本更新
Expand Down Expand Up @@ -98,6 +101,7 @@ https://github.com/XIU2/CloudflareSpeedTest

flag.BoolVar(&task.Disable, "dd", false, "禁用下载测速")
flag.BoolVar(&task.TestAll, "allip", false, "测速全部 IP")
flag.BoolVar(&utils.OnlyIP, "onlyip", false, "结果仅输出 IP")

flag.BoolVar(&printVersion, "v", false, "打印程序版本")
flag.Usage = func() { fmt.Print(help) }
Expand Down
12 changes: 6 additions & 6 deletions task/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func TestDownloadSpeed(ipSet utils.PingDelaySet) (speedSet utils.DownloadSpeedSe
}
bar := utils.NewBar(TestCount, bar_b, "")
for i := 0; i < testNum; i++ {
speed := downloadHandler(ipSet[i].IP)
speed := downloadHandler(ipSet[i].IP, ipSet[i].Port)
ipSet[i].DownloadSpeed = speed
// 在每个 IP 下载测速后,以 [下载速度下限] 条件过滤结果
if speed >= MinSpeed*1024*1024 {
Expand All @@ -94,22 +94,22 @@ func TestDownloadSpeed(ipSet utils.PingDelaySet) (speedSet utils.DownloadSpeedSe
return
}

func getDialContext(ip *net.IPAddr) func(ctx context.Context, network, address string) (net.Conn, error) {
func getDialContext(ip *net.IPAddr, port int) func(ctx context.Context, network, address string) (net.Conn, error) {
var fakeSourceAddr string
if isIPv4(ip.String()) {
fakeSourceAddr = fmt.Sprintf("%s:%d", ip.String(), TCPPort)
fakeSourceAddr = fmt.Sprintf("%s:%d", ip.String(), port)
} else {
fakeSourceAddr = fmt.Sprintf("[%s]:%d", ip.String(), TCPPort)
fakeSourceAddr = fmt.Sprintf("[%s]:%d", ip.String(), port)
}
return func(ctx context.Context, network, address string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, network, fakeSourceAddr)
}
}

// return download Speed
func downloadHandler(ip *net.IPAddr) float64 {
func downloadHandler(ip *net.IPAddr, port int) float64 {
client := &http.Client{
Transport: &http.Transport{DialContext: getDialContext(ip)},
Transport: &http.Transport{DialContext: getDialContext(ip, port)},
Timeout: Timeout,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if len(via) > 10 { // 限制最多重定向 10 次
Expand Down
5 changes: 2 additions & 3 deletions task/httping.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
//"fmt"
"io"
"log"
"net"
"net/http"
"regexp"
"strings"
Expand All @@ -22,11 +21,11 @@ var (
)

// pingReceived pingTotalTime
func (p *Ping) httping(ip *net.IPAddr) (int, time.Duration) {
func (p *Ping) httping(ip *dest) (int, time.Duration) {
hc := http.Client{
Timeout: time.Second * 2,
Transport: &http.Transport{
DialContext: getDialContext(ip),
DialContext: getDialContext(ip.IPAddr, ip.Port),
//TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 跳过证书验证
},
CheckRedirect: func(req *http.Request, via []*http.Request) error {
Expand Down
71 changes: 61 additions & 10 deletions task/ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ var (
TestAll = false
// IPFile is the filename of IP Rangs
IPFile = defaultInputFile
seed *rand.Rand
IPText string
)

func InitRandSeed() {
rand.Seed(time.Now().UnixNano())
seed = rand.New(rand.NewSource(time.Now().UnixNano()))
}

func isIPv4(ip string) bool {
Expand All @@ -33,19 +34,25 @@ func randIPEndWith(num byte) byte {
if num == 0 { // 对于 /32 这种单独的 IP
return byte(0)
}
return byte(rand.Intn(int(num)))
return byte(seed.Intn(int(num)))
}

type IPRanges struct {
ips []*net.IPAddr
ips []*dest
port int
mask string
firstIP net.IP
ipNet *net.IPNet
}

type dest struct {
*net.IPAddr
Port int
}

func newIPRanges() *IPRanges {
return &IPRanges{
ips: make([]*net.IPAddr, 0),
ips: make([]*dest, 0),
}
}

Expand Down Expand Up @@ -73,12 +80,54 @@ func (r *IPRanges) parseCIDR(ip string) {
}
}

// 解析自定义的 IP 段,获得 IP、端口、IP 范围、子网掩码
func (r *IPRanges) parseCIDRwithport(ip string) {
var (
err error
port int64
isIPv4bool bool
sep string
)

if isIPv4(ip) {
isIPv4bool = true
sep = ":"
} else {
isIPv4bool = false
sep = "]:"
}

arr := strings.Split(ip, sep)
if len(arr) > 1 {
if isIPv4bool {
ip = arr[0]
} else {
ip = arr[0][1:]
}
port, err = strconv.ParseInt(arr[1], 10, 0)
if (err == nil) && (port > 0) && (port < 65535) {
r.port = int(port)
}
}

if err != nil || port == 0 {
r.port = TCPPort
}

if r.firstIP, r.ipNet, err = net.ParseCIDR(r.fixIP(ip)); err != nil {
log.Fatalln("ParseCIDRwithport err", err)
}
}

func (r *IPRanges) appendIPv4(d byte) {
r.appendIP(net.IPv4(r.firstIP[12], r.firstIP[13], r.firstIP[14], d))
}

func (r *IPRanges) appendIP(ip net.IP) {
r.ips = append(r.ips, &net.IPAddr{IP: ip})
r.ips = append(r.ips, &dest{
&net.IPAddr{IP: ip},
r.port,
})
}

// 返回第四段 ip 的最小值及可用数目
Expand Down Expand Up @@ -147,7 +196,7 @@ func (r *IPRanges) chooseIPv6() {
}
}

func loadIPRanges() []*net.IPAddr {
func loadIPRanges() []*dest {
ranges := newIPRanges()
if IPText != "" { // 从参数中获取 IP 段数据
IPs := strings.Split(IPText, ",") // 以逗号分隔为数组并循环遍历
Expand All @@ -156,8 +205,9 @@ func loadIPRanges() []*net.IPAddr {
if IP == "" { // 跳过空的(即开头、结尾或连续多个 ,, 的情况)
continue
}
ranges.parseCIDR(IP) // 解析 IP 段,获得 IP、IP 范围、子网掩码
if isIPv4(IP) { // 生成要测速的所有 IPv4 / IPv6 地址(单个/随机/全部)
//ranges.parseCIDR(IP) // 解析 IP 段,获得 IP、IP 范围、子网掩码
ranges.parseCIDRwithport(IP) // 解析 IP 段,获得 IP、端口、IP 范围、子网掩码
if isIPv4(IP) { // 生成要测速的所有 IPv4 / IPv6 地址(单个/随机/全部)
ranges.chooseIPv4()
} else {
ranges.chooseIPv6()
Expand All @@ -178,8 +228,9 @@ func loadIPRanges() []*net.IPAddr {
if line == "" { // 跳过空行
continue
}
ranges.parseCIDR(line) // 解析 IP 段,获得 IP、IP 范围、子网掩码
if isIPv4(line) { // 生成要测速的所有 IPv4 / IPv6 地址(单个/随机/全部)
//ranges.parseCIDR(IP) // 解析 IP 段,获得 IP、IP 范围、子网掩码
ranges.parseCIDRwithport(line) // 解析 IP 段,获得 IP、端口、IP 范围、子网掩码
if isIPv4(line) { // 生成要测速的所有 IPv4 / IPv6 地址(单个/随机/全部)
ranges.chooseIPv4()
} else {
ranges.chooseIPv6()
Expand Down
21 changes: 11 additions & 10 deletions task/tcping.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ var (
type Ping struct {
wg *sync.WaitGroup
m *sync.Mutex
ips []*net.IPAddr
ips []*dest
csv utils.PingDelaySet
control chan bool
bar *utils.Bar
Expand Down Expand Up @@ -64,9 +64,9 @@ func (p *Ping) Run() utils.PingDelaySet {
return p.csv
}
if Httping {
fmt.Printf("开始延迟测速(模式:HTTP, 端口:%d, 范围:%v ~ %v ms, 丢包:%.2f)\n", TCPPort, utils.InputMinDelay.Milliseconds(), utils.InputMaxDelay.Milliseconds(), utils.InputMaxLossRate)
fmt.Printf("开始延迟测速(模式:HTTP, 默认端口:%d, 范围:%v ~ %v ms, 丢包:%.2f)\n", TCPPort, utils.InputMinDelay.Milliseconds(), utils.InputMaxDelay.Milliseconds(), utils.InputMaxLossRate)
} else {
fmt.Printf("开始延迟测速(模式:TCP, 端口:%d, 范围:%v ~ %v ms, 丢包:%.2f)\n", TCPPort, utils.InputMinDelay.Milliseconds(), utils.InputMaxDelay.Milliseconds(), utils.InputMaxLossRate)
fmt.Printf("开始延迟测速(模式:TCP, 默认端口:%d, 范围:%v ~ %v ms, 丢包:%.2f)\n", TCPPort, utils.InputMinDelay.Milliseconds(), utils.InputMaxDelay.Milliseconds(), utils.InputMaxLossRate)
}
for _, ip := range p.ips {
p.wg.Add(1)
Expand All @@ -79,20 +79,20 @@ func (p *Ping) Run() utils.PingDelaySet {
return p.csv
}

func (p *Ping) start(ip *net.IPAddr) {
func (p *Ping) start(ip *dest) {
defer p.wg.Done()
p.tcpingHandler(ip)
<-p.control
}

// bool connectionSucceed float32 time
func (p *Ping) tcping(ip *net.IPAddr) (bool, time.Duration) {
func (p *Ping) tcping(ip *dest) (bool, time.Duration) {
startTime := time.Now()
var fullAddress string
if isIPv4(ip.String()) {
fullAddress = fmt.Sprintf("%s:%d", ip.String(), TCPPort)
fullAddress = fmt.Sprintf("%s:%d", ip.IPAddr.String(), ip.Port)
} else {
fullAddress = fmt.Sprintf("[%s]:%d", ip.String(), TCPPort)
fullAddress = fmt.Sprintf("[%s]:%d", ip.IPAddr.String(), ip.Port)
}
conn, err := net.DialTimeout("tcp", fullAddress, tcpConnectTimeout)
if err != nil {
Expand All @@ -104,7 +104,7 @@ func (p *Ping) tcping(ip *net.IPAddr) (bool, time.Duration) {
}

// pingReceived pingTotalTime
func (p *Ping) checkConnection(ip *net.IPAddr) (recv int, totalDelay time.Duration) {
func (p *Ping) checkConnection(ip *dest) (recv int, totalDelay time.Duration) {
if Httping {
recv, totalDelay = p.httping(ip)
return
Expand All @@ -127,7 +127,7 @@ func (p *Ping) appendIPData(data *utils.PingData) {
}

// handle tcping
func (p *Ping) tcpingHandler(ip *net.IPAddr) {
func (p *Ping) tcpingHandler(ip *dest) {
recv, totalDlay := p.checkConnection(ip)
nowAble := len(p.csv)
if recv != 0 {
Expand All @@ -138,7 +138,8 @@ func (p *Ping) tcpingHandler(ip *net.IPAddr) {
return
}
data := &utils.PingData{
IP: ip,
IP: ip.IPAddr,
Port: ip.Port,
Sended: PingTimes,
Received: recv,
Delay: totalDlay / time.Duration(recv),
Expand Down
8 changes: 7 additions & 1 deletion utils/csv.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var (
InputMaxLossRate = maxLossRate
Output = defaultOutput
PrintNum = 10
OnlyIP = false
)

// 是否打印测试结果
Expand All @@ -37,6 +38,7 @@ func noOutput() bool {

type PingData struct {
IP *net.IPAddr
Port int
Sended int
Received int
Delay time.Duration
Expand All @@ -59,7 +61,11 @@ func (cf *CloudflareIPData) getLossRate() float32 {

func (cf *CloudflareIPData) toString() []string {
result := make([]string, 6)
result[0] = cf.IP.String()
if OnlyIP {
result[0] = cf.IP.String()
} else {
result[0] = fmt.Sprintf("%s:%d", cf.IP.String(), cf.Port)
}
result[1] = strconv.Itoa(cf.Sended)
result[2] = strconv.Itoa(cf.Received)
result[3] = strconv.FormatFloat(float64(cf.getLossRate()), 'f', 2, 32)
Expand Down