测试代理是否可用
This commit is contained in:
@@ -1,14 +1,34 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
const (
|
import "time"
|
||||||
AppEnv = "dev"
|
|
||||||
EnvConfig = "config.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Debug bool `mapstructure:"debug" json:"debug" yaml:"debug"`
|
Debug bool `mapstructure:"debug" yaml:"debug"`
|
||||||
TargetURL string `mapstructure:"target_url" json:"target_url" yaml:"target_url"`
|
TargetURL string `mapstructure:"target_url" yaml:"target_url"`
|
||||||
Nodes []Node `mapstructure:"nodes" json:"nodes" yaml:"nodes"`
|
ProxyCheck ProxyCheck `mapstructure:"proxy_check" yaml:"proxy_check"`
|
||||||
|
Files Files `mapstructure:"files" yaml:"files"`
|
||||||
|
Output Output `mapstructure:"output" yaml:"output"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProxyCheck struct {
|
||||||
|
Workers int `mapstructure:"workers" yaml:"workers"`
|
||||||
|
MaxRetries int `mapstructure:"max_retries" yaml:"max_retries"`
|
||||||
|
RetryDelay time.Duration `mapstructure:"retry_delay" yaml:"retry_delay"`
|
||||||
|
Timeout time.Duration `mapstructure:"timeout" yaml:"timeout"`
|
||||||
|
SortByLatency bool `mapstructure:"sort_by_latency" yaml:"sort_by_latency"`
|
||||||
|
SaveReport bool `mapstructure:"save_report" yaml:"save_report"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Files struct {
|
||||||
|
Nodes string `mapstructure:"nodes" yaml:"nodes"`
|
||||||
|
AvailableNodes string `mapstructure:"available_nodes" yaml:"available_nodes"`
|
||||||
|
Report string `mapstructure:"report" yaml:"report"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Output struct {
|
||||||
|
ShowProgress bool `mapstructure:"show_progress" yaml:"show_progress"`
|
||||||
|
ShowTitle bool `mapstructure:"show_title" yaml:"show_title"`
|
||||||
|
ShowLatency bool `mapstructure:"show_latency" yaml:"show_latency"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
@@ -17,5 +37,4 @@ type Node struct {
|
|||||||
Password string `mapstructure:"password" yaml:"password" json:"password"`
|
Password string `mapstructure:"password" yaml:"password" json:"password"`
|
||||||
Addr string `mapstructure:"addr" yaml:"addr" json:"addr"`
|
Addr string `mapstructure:"addr" yaml:"addr" json:"addr"`
|
||||||
Port int `mapstructure:"port" yaml:"port" json:"port"`
|
Port int `mapstructure:"port" yaml:"port" json:"port"`
|
||||||
Ports []int `mapstructure:"ports" yaml:"ports" json:"ports"`
|
|
||||||
}
|
}
|
||||||
|
|||||||
108
common/file.go
Normal file
108
common/file.go
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReadNodesFile 读取nodes.txt文件并解析所有节点
|
||||||
|
func ReadNodesFile(filename string) ([]*Node, error) {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var nodes []*Node
|
||||||
|
nodeSet := make(map[string]bool) // 用于去重
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := strings.TrimSpace(scanner.Text())
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否已经存在
|
||||||
|
if _, exists := nodeSet[line]; exists {
|
||||||
|
continue // 跳过重复的节点
|
||||||
|
}
|
||||||
|
|
||||||
|
node, err := ParseNodeURL(line)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("解析节点失败: %v\n", err)
|
||||||
|
continue // 跳过解析失败的行
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes = append(nodes, node)
|
||||||
|
nodeSet[line] = true // 标记为已存在
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseNodeURL 解析代理URL字符串为Node结构
|
||||||
|
func ParseNodeURL(urlStr string) (*Node, error) {
|
||||||
|
u, err := url.Parse(urlStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("解析URL失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
username := ""
|
||||||
|
password := ""
|
||||||
|
if u.User != nil {
|
||||||
|
username = u.User.Username()
|
||||||
|
if pass, ok := u.User.Password(); ok {
|
||||||
|
password = pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析端口
|
||||||
|
port := 443 // 默认端口
|
||||||
|
if u.Port() != "" {
|
||||||
|
port, err = strconv.Atoi(u.Port())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("解析端口失败: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Node{
|
||||||
|
Protocol: u.Scheme,
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
Addr: u.Hostname(),
|
||||||
|
Port: port,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveAvailableNodes 保存可用的节点到文件
|
||||||
|
func SaveAvailableNodes(filename string, nodes []*Node) error {
|
||||||
|
file, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
for _, node := range nodes {
|
||||||
|
// 重新构建URL格式
|
||||||
|
url := fmt.Sprintf("https://%s:%s@%s:%d\n",
|
||||||
|
node.Username,
|
||||||
|
node.Password,
|
||||||
|
node.Addr,
|
||||||
|
node.Port)
|
||||||
|
if _, err := file.WriteString(url); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,54 +1,27 @@
|
|||||||
package viper
|
package viper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fsnotify/fsnotify"
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"httppp/common"
|
"httppp/common"
|
||||||
"httppp/global"
|
"httppp/global"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitViper(path ...string) {
|
func InitViper() {
|
||||||
var confPath string
|
|
||||||
if len(path) == 0 {
|
|
||||||
flag.StringVar(&confPath, "c", "", "choose config file.")
|
|
||||||
flag.Parse()
|
|
||||||
if confPath == "" {
|
|
||||||
if AppEnv := os.Getenv(common.AppEnv); AppEnv == "" {
|
|
||||||
confPath = common.EnvConfig
|
|
||||||
} else {
|
|
||||||
confPath = AppEnv
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
confPath = path[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
v := viper.New()
|
v := viper.New()
|
||||||
v.SetConfigFile(confPath)
|
v.SetConfigFile("config.yaml")
|
||||||
v.SetConfigType("json")
|
v.SetConfigType("yaml")
|
||||||
|
|
||||||
err := v.ReadInConfig()
|
err := v.ReadInConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("[-]读取配置文件错误: %s \n", err)
|
panic(fmt.Errorf("读取配置文件失败: %s", err))
|
||||||
os.Exit(0)
|
|
||||||
}
|
}
|
||||||
v.WatchConfig()
|
|
||||||
v.OnConfigChange(func(e fsnotify.Event) {
|
config := &common.Config{}
|
||||||
if err = v.Unmarshal(&global.Conf); err != nil {
|
err = v.Unmarshal(config)
|
||||||
fmt.Printf("[-]重新解析配置文件失败: %s \n", err)
|
if err != nil {
|
||||||
os.Exit(0)
|
panic(fmt.Errorf("解析配置文件失败: %s", err))
|
||||||
}
|
|
||||||
fmt.Println("[+]重新加载配置文件完成")
|
|
||||||
})
|
|
||||||
if err = v.Unmarshal(&global.Conf); err != nil {
|
|
||||||
fmt.Printf("[-]解析配置文件失败: %s \n", err)
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
}
|
||||||
fmt.Println("[+]加载配置文件完成")
|
|
||||||
|
|
||||||
//dump.P(global.AppConf)
|
|
||||||
|
|
||||||
|
global.Conf = config
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
{
|
{
|
||||||
"debug": false,
|
"debug": false,
|
||||||
"target_url": "https://www.google.com",
|
"target_url": "https://www.youtube.com",
|
||||||
"nodes": [
|
"nodes": [
|
||||||
{
|
{
|
||||||
"protocol": "https",
|
"protocol": "https",
|
||||||
"username": "mrwdfNTD8M79LCukCieldrqZWqs=",
|
"username": "mrwdfNTD8M79LCukCieldrqZWqs=",
|
||||||
"password": "exaxgqkKkd0TAMrCxeonWg==",
|
"password": "exaxgqkKkd0TAMrCxeonWg==",
|
||||||
"addr": "jpgmo101-cdn-route.couldflare-cdn.com",
|
"addr": "jpgmo101-cdn-route.couldflare-cdn.com",
|
||||||
"ports": [
|
"port": "443"
|
||||||
"443",
|
|
||||||
"1443"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
24
config.yaml
Normal file
24
config.yaml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# 基本配置
|
||||||
|
debug: false
|
||||||
|
target_url: "https://www.youtube.com"
|
||||||
|
|
||||||
|
# 代理检测配置
|
||||||
|
proxy_check:
|
||||||
|
workers: 10 # 并发工作协程数
|
||||||
|
max_retries: 3 # 最大重试次数
|
||||||
|
retry_delay: 1s # 重试延迟
|
||||||
|
timeout: 30s # 超时时间
|
||||||
|
sort_by_latency: true # 按延迟排序
|
||||||
|
save_report: true # 保存详细报告
|
||||||
|
|
||||||
|
# 文件配置
|
||||||
|
files:
|
||||||
|
nodes: "nodes.txt" # 节点文件路径
|
||||||
|
available_nodes: "available_nodes" # 可用节点保存文件名前缀
|
||||||
|
report: "proxy_report" # 报告文件名前缀
|
||||||
|
|
||||||
|
# 输出配置
|
||||||
|
output:
|
||||||
|
show_progress: true # 显示进度条
|
||||||
|
show_title: true # 显示网页标题
|
||||||
|
show_latency: true # 显示延迟
|
||||||
148
core/check.go
148
core/check.go
@@ -9,116 +9,92 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
//for _, node := range global.Conf.Nodes {
|
|
||||||
//for _, port := range node.Ports {
|
|
||||||
//node.Port = port
|
|
||||||
//success, message := core.CheckHttpsProxy(&node, global.Conf.TargetURL)
|
|
||||||
//if success {
|
|
||||||
//fmt.Printf("代理 %s 端口 %d 可用.\n", node.Addr, node.Port)
|
|
||||||
//} else {
|
|
||||||
//fmt.Printf("代理 %s 端口 %d 不可用: %s\n", node.Addr, node.Port, message)
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
||||||
// CheckHttpsProxy 检测代理是否可用的函数
|
// CheckHttpsProxy 检测代理是否可用的函数
|
||||||
func CheckHttpsProxy(node *common.Node, targetURL string) (bool, string) {
|
func CheckHttpsProxy(node *common.Node, targetURL string) (bool, string) {
|
||||||
proxyURL, err := Node2ProxyURL(node)
|
proxyURL, err := Node2ProxyURL(node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("节点转换代理URL失败: %v\n", err)
|
return false, fmt.Sprintf("节点转换代理URL失败: %v", err)
|
||||||
return false, err.Error()
|
|
||||||
}
|
}
|
||||||
// 解析代理 URL
|
|
||||||
proxy, err := url.Parse(proxyURL)
|
proxy, err := url.Parse(proxyURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("代理解析失败: %v\n", err)
|
return false, fmt.Sprintf("代理解析失败: %v", err)
|
||||||
return false, err.Error()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建自定义 Transport,用于调试代理信息
|
client := createHTTPClient(proxy)
|
||||||
transport := &http.Transport{
|
|
||||||
Proxy: http.ProxyURL(proxy), // 设置代理
|
|
||||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
||||||
if global.Conf.Debug {
|
|
||||||
fmt.Printf("网络拨号: %s, 地址: %s\n", network, addr)
|
|
||||||
}
|
|
||||||
conn, err := net.Dial(network, addr)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("网络拨号失败: %v\n", err)
|
|
||||||
}
|
|
||||||
return conn, err
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建 HTTP 客户端并使用自定义 Transport
|
|
||||||
client := &http.Client{
|
|
||||||
Transport: transport,
|
|
||||||
Timeout: 30 * time.Second, // 设置超时时间
|
|
||||||
}
|
|
||||||
|
|
||||||
if global.Conf.Debug {
|
if global.Conf.Debug {
|
||||||
fmt.Printf("发送请求到: %s 使用代理: %s\n", targetURL, proxyURL)
|
fmt.Printf("发送请求到: %s 使用代理: %s\n", targetURL, proxyURL)
|
||||||
}
|
}
|
||||||
// 发起 GET 请求
|
|
||||||
|
// 记录请求开始时间
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
resp, err := client.Get(targetURL)
|
resp, err := client.Get(targetURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("通过代理发送请求失败 %s: %v\n", proxyURL, err)
|
return false, fmt.Sprintf("通过代理发送请求失败 %s: %v", proxyURL, err)
|
||||||
return false, err.Error()
|
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if global.Conf.Debug {
|
// 计算延迟(转换为毫秒)
|
||||||
// 打印响应状态
|
latencyMs := time.Since(startTime).Milliseconds()
|
||||||
fmt.Printf("Response 状态码: %s\n", resp.Status)
|
|
||||||
// 打印响应头
|
// 读取响应体
|
||||||
fmt.Printf("Response 响应头: \n")
|
body, err := io.ReadAll(resp.Body)
|
||||||
for key, value := range resp.Header {
|
if err != nil {
|
||||||
fmt.Printf("\t%s: %s\n", key, value)
|
return false, fmt.Sprintf("读取响应失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查网页标题
|
||||||
|
title := extractTitle(string(body))
|
||||||
|
if title == "" {
|
||||||
|
return false, "未找到网页标题"
|
||||||
}
|
}
|
||||||
|
|
||||||
if global.Conf.Debug {
|
if global.Conf.Debug {
|
||||||
// 创建一个缓冲区,用于分块读取响应体
|
printResponseDetails(resp)
|
||||||
buffer := make([]byte, 4096) // 4KB 缓冲区
|
fmt.Printf("网页标题: %s\n", title)
|
||||||
fmt.Printf("分块读取响应正文:\n")
|
|
||||||
// 分块读取响应体
|
|
||||||
for {
|
|
||||||
n, err := resp.Body.Read(buffer)
|
|
||||||
if err == io.EOF {
|
|
||||||
fmt.Println("响应正文读取完毕.")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("响应正文读取错误: %v\n", err)
|
|
||||||
return false, err.Error()
|
|
||||||
}
|
|
||||||
// 打印每次读取的内容
|
|
||||||
fmt.Printf("%s\n", string(buffer[:n])) // 不打印内容,只检测可用性
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否发生了重定向
|
return true, fmt.Sprintf("标题: %s, 延迟: %dms", title, latencyMs)
|
||||||
if resp.StatusCode == 301 || resp.StatusCode == 302 {
|
}
|
||||||
redirectURL := resp.Header.Get("Location")
|
|
||||||
if global.Conf.Debug {
|
// extractTitle 从HTML内容中提取标题
|
||||||
fmt.Printf("重定向到: %s\n", redirectURL)
|
func extractTitle(html string) string {
|
||||||
}
|
// 匹配 <title> 标签内容,支持多行匹配
|
||||||
// 如果需要处理重定向,可以发起新的请求
|
titleRegex := regexp.MustCompile(`(?i)<title.*?>(.*?)</title>`)
|
||||||
newResp, err := client.Get(redirectURL)
|
matches := titleRegex.FindStringSubmatch(html)
|
||||||
if err != nil {
|
if len(matches) > 1 {
|
||||||
fmt.Printf("重定向失败: %v\n", err)
|
return matches[1]
|
||||||
return false, err.Error()
|
}
|
||||||
}
|
return ""
|
||||||
defer newResp.Body.Close()
|
}
|
||||||
|
|
||||||
// 打印重定向后的响应状态
|
// createHTTPClient 创建一个带有代理设置的 HTTP 客户端
|
||||||
if global.Conf.Debug {
|
func createHTTPClient(proxy *url.URL) *http.Client {
|
||||||
fmt.Printf("重定向响应状态: %s\n", newResp.Status)
|
return &http.Client{
|
||||||
}
|
Transport: &http.Transport{
|
||||||
}
|
Proxy: http.ProxyURL(proxy),
|
||||||
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
return true, "代理可用"
|
if global.Conf.Debug {
|
||||||
|
fmt.Printf("网络拨号: %s, 地址: %s\n", network, addr)
|
||||||
|
}
|
||||||
|
return net.Dial(network, addr)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// printResponseDetails 打印响应的详细信息
|
||||||
|
func printResponseDetails(resp *http.Response) {
|
||||||
|
fmt.Printf("Response 状态码: %s\n", resp.Status)
|
||||||
|
fmt.Printf("Response 响应头: \n")
|
||||||
|
for key, value := range resp.Header {
|
||||||
|
fmt.Printf("\t%s: %s\n", key, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
277
core/proxy_checker.go
Normal file
277
core/proxy_checker.go
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"httppp/common"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// simplifyErrorMessage 简化错误消息
|
||||||
|
func simplifyErrorMessage(msg string) string {
|
||||||
|
// 常见错误类型
|
||||||
|
errorPatterns := map[string]string{
|
||||||
|
"no such host": "域名解析失败",
|
||||||
|
"connection refused": "连接被拒绝",
|
||||||
|
"connection reset": "连接被重置",
|
||||||
|
"timeout": "连接超时",
|
||||||
|
"EOF": "连接中断",
|
||||||
|
"i/o timeout": "连接超时",
|
||||||
|
"connection timed out": "连接超时",
|
||||||
|
"network is unreachable": "网络不可达",
|
||||||
|
"certificate": "证书错误",
|
||||||
|
"tls": "TLS错误",
|
||||||
|
"read: connection": "连接断开",
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = strings.ToLower(msg)
|
||||||
|
for pattern, simpleMsg := range errorPatterns {
|
||||||
|
if strings.Contains(msg, pattern) {
|
||||||
|
return simpleMsg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "连接失败"
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProxyResult struct {
|
||||||
|
Node *common.Node
|
||||||
|
Success bool
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProxyChecker struct {
|
||||||
|
workers int
|
||||||
|
nodes []*common.Node
|
||||||
|
targetURL string
|
||||||
|
progress *Progress
|
||||||
|
config common.ProxyCheck
|
||||||
|
}
|
||||||
|
|
||||||
|
type Progress struct {
|
||||||
|
total int
|
||||||
|
current int
|
||||||
|
mu sync.Mutex
|
||||||
|
startTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Progress) increment() {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
p.current++
|
||||||
|
elapsed := time.Since(p.startTime)
|
||||||
|
fmt.Printf("\r进度: %d/%d (%.1f%%) 已用时间: %v",
|
||||||
|
p.current, p.total,
|
||||||
|
float64(p.current)/float64(p.total)*100,
|
||||||
|
elapsed.Round(time.Second))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProxyChecker(config common.ProxyCheck, nodes []*common.Node, targetURL string) *ProxyChecker {
|
||||||
|
return &ProxyChecker{
|
||||||
|
workers: config.Workers,
|
||||||
|
nodes: nodes,
|
||||||
|
targetURL: targetURL,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc *ProxyChecker) Check() ([]*common.Node, []*common.Node) {
|
||||||
|
pc.progress = &Progress{
|
||||||
|
total: len(pc.nodes),
|
||||||
|
startTime: time.Now(),
|
||||||
|
}
|
||||||
|
results := make(chan ProxyResult, len(pc.nodes))
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
// 创建工作协程池
|
||||||
|
nodeChan := make(chan *common.Node, pc.workers)
|
||||||
|
|
||||||
|
// 启动工作协程
|
||||||
|
for i := 0; i < pc.workers; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go pc.worker(&wg, nodeChan, results)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送任务到工作协程
|
||||||
|
go func() {
|
||||||
|
for _, node := range pc.nodes {
|
||||||
|
nodeChan <- node
|
||||||
|
}
|
||||||
|
close(nodeChan)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 等待所有检测完成
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(results)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return pc.processResults(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc *ProxyChecker) worker(wg *sync.WaitGroup, nodes chan *common.Node, results chan<- ProxyResult) {
|
||||||
|
defer wg.Done()
|
||||||
|
for node := range nodes {
|
||||||
|
success, message := pc.checkWithRetry(node, 3)
|
||||||
|
results <- ProxyResult{
|
||||||
|
Node: node,
|
||||||
|
Success: success,
|
||||||
|
Message: message,
|
||||||
|
}
|
||||||
|
pc.progress.increment()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc *ProxyChecker) checkWithRetry(node *common.Node, maxRetries int) (bool, string) {
|
||||||
|
for i := 0; i < maxRetries; i++ {
|
||||||
|
success, message := CheckHttpsProxy(node, pc.targetURL)
|
||||||
|
if success {
|
||||||
|
return true, message
|
||||||
|
}
|
||||||
|
if !strings.Contains(message, "timeout") {
|
||||||
|
return false, message
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
return false, fmt.Sprintf("重试%d次后仍然失败", maxRetries)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加一个新的结构体来保存节点和延迟信息
|
||||||
|
type NodeWithLatency struct {
|
||||||
|
Node *common.Node
|
||||||
|
Latency int64 // 延迟(毫秒)
|
||||||
|
Message string // 保存原始消息
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc *ProxyChecker) processResults(results <-chan ProxyResult) ([]*common.Node, []*common.Node) {
|
||||||
|
var availableNodes []*common.Node
|
||||||
|
var unavailableNodes []*common.Node
|
||||||
|
var nodesWithLatency []NodeWithLatency
|
||||||
|
|
||||||
|
// 收集结果
|
||||||
|
for result := range results {
|
||||||
|
if result.Success {
|
||||||
|
// 从消息中提取延迟时间
|
||||||
|
latency := extractLatency(result.Message)
|
||||||
|
nodesWithLatency = append(nodesWithLatency, NodeWithLatency{
|
||||||
|
Node: result.Node,
|
||||||
|
Latency: latency,
|
||||||
|
Message: result.Message, // 保存原始消息
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if strings.Contains(result.Message, "标题") {
|
||||||
|
fmt.Printf("❌ %s:%d [%s]\n",
|
||||||
|
result.Node.Addr,
|
||||||
|
result.Node.Port,
|
||||||
|
result.Message)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("❌ %s:%d (%s)\n",
|
||||||
|
result.Node.Addr,
|
||||||
|
result.Node.Port,
|
||||||
|
simplifyErrorMessage(result.Message))
|
||||||
|
}
|
||||||
|
unavailableNodes = append(unavailableNodes, result.Node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按延迟排序
|
||||||
|
sort.Slice(nodesWithLatency, func(i, j int) bool {
|
||||||
|
return nodesWithLatency[i].Latency < nodesWithLatency[j].Latency
|
||||||
|
})
|
||||||
|
|
||||||
|
// 输出排序后的结果
|
||||||
|
for _, nwl := range nodesWithLatency {
|
||||||
|
fmt.Printf("✅ %s:%d [%s]\n",
|
||||||
|
nwl.Node.Addr,
|
||||||
|
nwl.Node.Port,
|
||||||
|
extractTitleAndLatency(nwl.Message))
|
||||||
|
availableNodes = append(availableNodes, nwl.Node)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println() // 添加一个空行,使输出更清晰
|
||||||
|
|
||||||
|
return availableNodes, unavailableNodes
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从消息中提取延迟时间
|
||||||
|
func extractLatency(message string) int64 {
|
||||||
|
re := regexp.MustCompile(`延迟: (\d+)ms`)
|
||||||
|
matches := re.FindStringSubmatch(message)
|
||||||
|
if len(matches) > 1 {
|
||||||
|
if latency, err := strconv.ParseInt(matches[1], 10, 64); err == nil {
|
||||||
|
return latency
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从消息中提取标题和延迟信息
|
||||||
|
func extractTitleAndLatency(message string) string {
|
||||||
|
re := regexp.MustCompile(`标题: (.*?), 延迟: (\d+)ms`)
|
||||||
|
matches := re.FindStringSubmatch(message)
|
||||||
|
if len(matches) > 2 {
|
||||||
|
return matches[0] // 返回完整的匹配结果
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveResults(availableNodes []*common.Node, totalNodes int) {
|
||||||
|
fmt.Printf("\n检测完成! 总计 %d 个节点:\n", totalNodes)
|
||||||
|
fmt.Printf("✅ 可用节点: %d\n", len(availableNodes))
|
||||||
|
fmt.Printf("❌ 不可用节点: %d\n", totalNodes-len(availableNodes))
|
||||||
|
|
||||||
|
if len(availableNodes) > 0 {
|
||||||
|
filename := fmt.Sprintf("available_nodes_%s.txt", time.Now().Format("20060102_150405"))
|
||||||
|
if err := common.SaveAvailableNodes(filename, availableNodes); err != nil {
|
||||||
|
log.Printf("保存可用节点失败: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("可用节点已保存到: %s\n", filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveDetailedReport(filename string, availableNodes []*common.Node, unavailableNodes []*common.Node, duration time.Duration) {
|
||||||
|
file, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("创建报告文件失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
w := bufio.NewWriter(file)
|
||||||
|
|
||||||
|
// 写入报告头部
|
||||||
|
fmt.Fprintf(w, "代理测试报告\n")
|
||||||
|
fmt.Fprintf(w, "测试时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
|
||||||
|
fmt.Fprintf(w, "测试用时: %v\n", duration)
|
||||||
|
fmt.Fprintf(w, "总节点数: %d\n", len(availableNodes)+len(unavailableNodes))
|
||||||
|
fmt.Fprintf(w, "可用节点: %d\n", len(availableNodes))
|
||||||
|
fmt.Fprintf(w, "不可用节点: %d\n\n", len(unavailableNodes))
|
||||||
|
|
||||||
|
// 写入可用节点
|
||||||
|
fmt.Fprintf(w, "=== 可用节点列表 ===\n")
|
||||||
|
for _, node := range availableNodes {
|
||||||
|
fmt.Fprintf(w, "https://%s:%s@%s:%d\n",
|
||||||
|
node.Username, node.Password, node.Addr, node.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入不可用节点
|
||||||
|
fmt.Fprintf(w, "\n=== 不可用节点列表 ===\n")
|
||||||
|
for _, node := range unavailableNodes {
|
||||||
|
fmt.Fprintf(w, "https://%s:%s@%s:%d\n",
|
||||||
|
node.Username, node.Password, node.Addr, node.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入报告时间
|
||||||
|
fmt.Fprintf(w, "\n报告生成时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
|
||||||
|
|
||||||
|
w.Flush()
|
||||||
|
//fmt.Printf("详细报告已保存到: %s\n", filename)
|
||||||
|
}
|
||||||
@@ -3,5 +3,6 @@ package global
|
|||||||
import "httppp/common"
|
import "httppp/common"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Conf *common.Config
|
Conf *common.Config
|
||||||
|
Nodes []*common.Node
|
||||||
)
|
)
|
||||||
|
|||||||
26
go.mod
26
go.mod
@@ -1,32 +1,8 @@
|
|||||||
module httppp
|
module httppp
|
||||||
|
|
||||||
go 1.23.3
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fsnotify/fsnotify v1.8.0
|
github.com/fsnotify/fsnotify v1.8.0
|
||||||
github.com/gookit/goutil v0.6.18
|
|
||||||
github.com/spf13/viper v1.19.0
|
github.com/spf13/viper v1.19.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/gookit/color v1.5.4 // indirect
|
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
|
||||||
golang.org/x/sys v0.28.0 // indirect
|
|
||||||
golang.org/x/text v0.21.0 // indirect
|
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
)
|
|
||||||
|
|||||||
60
main.go
60
main.go
@@ -2,27 +2,65 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"httppp/common"
|
||||||
"httppp/common/viper"
|
"httppp/common/viper"
|
||||||
"httppp/core"
|
"httppp/core"
|
||||||
"httppp/global"
|
"httppp/global"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
viper.InitViper()
|
viper.InitViper()
|
||||||
|
|
||||||
|
// 读取节点文件
|
||||||
|
nodes, err := common.ReadNodesFile(global.Conf.Files.Nodes)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("读取节点文件失败: %v", err)
|
||||||
|
}
|
||||||
|
global.Nodes = nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
// 主程序,遍历多个代理地址
|
|
||||||
func main() {
|
func main() {
|
||||||
//dump.P(global.Conf)
|
// 创建代理检测器
|
||||||
for _, node := range global.Conf.Nodes {
|
checker := core.NewProxyChecker(global.Conf.ProxyCheck, global.Nodes, global.Conf.TargetURL)
|
||||||
for _, port := range node.Ports {
|
|
||||||
node.Port = port
|
// 记录开始时间
|
||||||
success, message := core.CheckHttpsProxy(&node, global.Conf.TargetURL)
|
startTime := time.Now()
|
||||||
if success {
|
|
||||||
fmt.Printf("代理 %s 端口 %d 可用.\n", node.Addr, node.Port)
|
// 执行检测
|
||||||
} else {
|
availableNodes, unavailableNodes := checker.Check()
|
||||||
fmt.Printf("代理 %s 端口 %d 不可用: %s\n", node.Addr, node.Port, message)
|
|
||||||
}
|
// 计算测试用时
|
||||||
|
duration := time.Since(startTime)
|
||||||
|
|
||||||
|
// 打印测试报告到命令行
|
||||||
|
fmt.Println("\n========== 代理测试报告 ==========")
|
||||||
|
fmt.Printf("测试时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
|
||||||
|
fmt.Printf("测试用时: %v\n", duration)
|
||||||
|
fmt.Printf("总节点数: %d\n", len(availableNodes)+len(unavailableNodes))
|
||||||
|
fmt.Printf("可用节点: %d\n", len(availableNodes))
|
||||||
|
fmt.Printf("不可用节点: %d\n", len(unavailableNodes))
|
||||||
|
fmt.Println("================================")
|
||||||
|
|
||||||
|
// 保存结果
|
||||||
|
if len(availableNodes) > 0 {
|
||||||
|
filename := fmt.Sprintf("%s_%s.txt",
|
||||||
|
global.Conf.Files.AvailableNodes,
|
||||||
|
time.Now().Format("20060102_150405"))
|
||||||
|
if err := common.SaveAvailableNodes(filename, availableNodes); err != nil {
|
||||||
|
log.Printf("保存可用节点失败: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("可用节点已保存到: %s\n", filename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果配置了保存报告,则保存详细报告
|
||||||
|
if global.Conf.ProxyCheck.SaveReport {
|
||||||
|
filename := fmt.Sprintf("%s_%s.txt",
|
||||||
|
global.Conf.Files.Report,
|
||||||
|
time.Now().Format("20060102_150405"))
|
||||||
|
core.SaveDetailedReport(filename, availableNodes, unavailableNodes, duration)
|
||||||
|
fmt.Printf("详细报告已保存到: %s\n", filename)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
123
main.go---
Normal file
123
main.go---
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
localAddr = "127.0.0.1:1080" // 本地监听地址
|
||||||
|
remoteAddr = "jpgmo101-cdn-route.couldflare-cdn.com:443"
|
||||||
|
username = "mrwdfNTD8M79LCukCieldrqZWqs="
|
||||||
|
password = "exaxgqkKkd0TAMrCxeonWg=="
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
listener, err := net.Listen("tcp", localAddr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("无法启动本地监听: %v", err)
|
||||||
|
}
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
fmt.Printf("SOCKS5 代理服务器正在监听 %s\n", localAddr)
|
||||||
|
|
||||||
|
for {
|
||||||
|
client, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("接受连接错误: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go handleConnection(client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleConnection(client net.Conn) {
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
// 创建到远程服务器的认证信息
|
||||||
|
auth := proxy.Auth{
|
||||||
|
User: username,
|
||||||
|
Password: password,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 连接远程服务器
|
||||||
|
dialer, err := proxy.SOCKS5("tcp", remoteAddr, &auth, proxy.Direct)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("创建代理连接失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 SOCKS5 协议
|
||||||
|
if err := handleSocks5(client, dialer); err != nil {
|
||||||
|
log.Printf("处理 SOCKS5 协议失败: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleSocks5(client net.Conn, dialer proxy.Dialer) error {
|
||||||
|
// SOCKS5 握手
|
||||||
|
buf := make([]byte, 2)
|
||||||
|
if _, err := client.Read(buf); err != nil {
|
||||||
|
return fmt.Errorf("读取版本标识符失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否是 SOCKS5
|
||||||
|
if buf[0] != 0x05 {
|
||||||
|
return fmt.Errorf("不支持的 SOCKS 版本: %d", buf[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不需要认证
|
||||||
|
client.Write([]byte{0x05, 0x00})
|
||||||
|
|
||||||
|
// 读取请求
|
||||||
|
buf = make([]byte, 4)
|
||||||
|
if _, err := client.Read(buf); err != nil {
|
||||||
|
return fmt.Errorf("读取请求失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取目标地址
|
||||||
|
var targetAddr string
|
||||||
|
switch buf[3] {
|
||||||
|
case 0x01: // IPv4
|
||||||
|
addr := make([]byte, 4)
|
||||||
|
client.Read(addr)
|
||||||
|
targetAddr = net.IP(addr).String()
|
||||||
|
case 0x03: // 域名
|
||||||
|
var length byte
|
||||||
|
client.Read([]byte{length})
|
||||||
|
addr := make([]byte, length)
|
||||||
|
client.Read(addr)
|
||||||
|
targetAddr = string(addr)
|
||||||
|
case 0x04: // IPv6
|
||||||
|
addr := make([]byte, 16)
|
||||||
|
client.Read(addr)
|
||||||
|
targetAddr = net.IP(addr).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取端口
|
||||||
|
var port uint16
|
||||||
|
binary.Read(client, binary.BigEndian, &port)
|
||||||
|
targetAddr = fmt.Sprintf("%s:%d", targetAddr, port)
|
||||||
|
|
||||||
|
// 连接目标服务器
|
||||||
|
target, err := dialer.Dial("tcp", targetAddr)
|
||||||
|
if err != nil {
|
||||||
|
client.Write([]byte{0x05, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||||
|
return fmt.Errorf("连接目标服务器失败: %v", err)
|
||||||
|
}
|
||||||
|
defer target.Close()
|
||||||
|
|
||||||
|
// 发送成功响应
|
||||||
|
client.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||||
|
|
||||||
|
// 开始转发数据
|
||||||
|
go func() {
|
||||||
|
io.Copy(target, client)
|
||||||
|
}()
|
||||||
|
io.Copy(client, target)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
25
nodes.txt
Normal file
25
nodes.txt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@infree1-cdn-route.couldflare-cdn.com:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@fre151.windmilltest.xyz:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@tw1-cdn-route.couldflare-cdn.com:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@tw4-cdn-route.couldflare-cdn.com:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@twmoon1-cdn-route.couldflare-cdn.com:1443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@ustx004-cdn-route.couldflare-cdn.com:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@au1-cdn-route.couldflare-cdn.com:1443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@fre151.windmilltest.xyz:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@uk3-cdn-route.couldflare-cdn.com:1443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@usgp1-cdn-route.couldflare-cdn.com:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@usgp2-cdn-route.couldflare-cdn.com:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@uscera50-cdn-route.couldflare-cdn.com:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@usgp3-cdn-route.couldflare-cdn.com:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@jpgmo101-cdn-route.couldflare-cdn.com:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@jpgmo102-cdn-route.couldflare-cdn.com:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@jp104-cdn-route.couldflare-cdn.com:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@hkre1-cdn-route.couldflare-cdn.com:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@hkre3-cdn-route.couldflare-cdn.com:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@hkcera3-cdn-route.couldflare-cdn.com:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@ru1-cdn-route.couldflare-cdn.com:1443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@de1-cdn-route.couldflare-cdn.com:1443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@in1-cdn-route.couldflare-cdn.com:443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@sg2-cdn-route.couldflare-cdn.com:1443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@hkdt1-cdn-route.couldflare-cdn.com:1443
|
||||||
|
https://mrwdfNTD8M79LCukCieldrqZWqs=:exaxgqkKkd0TAMrCxeonWg==@infree1-cdn-route.couldflare-cdn.com:443
|
||||||
Reference in New Issue
Block a user