From 74ef5ab744ecdce2a600fcb54915ee5ddaf2fe7c Mon Sep 17 00:00:00 2001 From: thinkoaa Date: Thu, 12 Sep 2024 00:00:44 +0800 Subject: [PATCH] go mod tidy --- config.toml | 28 +++++++++-------- go.mod | 11 +++++-- go.sum | 16 ++++++++-- main.go | 58 ++++++++++++++++++++-------------- utils/globals.go | 2 +- utils/load_config.go | 75 ++++++++++++++++++++++++++++++++++++++++++++ utils/netspace.go | 62 +++++++++++++++++++++--------------- utils/utils.go | 72 ++++++++++++++++++++++++------------------ 8 files changed, 227 insertions(+), 97 deletions(-) create mode 100644 utils/load_config.go diff --git a/config.toml b/config.toml index 864d8f0..adf91ab 100644 --- a/config.toml +++ b/config.toml @@ -1,13 +1,15 @@ [listener]#******非特殊情况,默认即可******本地监听端口,其他工具中的SOCKS5代理指定为该IP:PORT,即可轮询使用其他代理 IP='127.0.0.1' -PORT='10086' +PORT=10086 +userName=''#用户名和密码都不为空时,会进行认证,防止部署到vps上时,被其他人使用 +password='' [checkSocks]#******非特殊情况,默认即可****** #通过访问实际url来验证代理的可用性 checkURL='https://www.baidu.com'#可以配置为要访问的目标地址,确保所有代理都能访问到目标地址 checkRspKeywords='百度一下'#上面地址原始响应中的某个字符串,用来验证通过代理访问目标时有无因某种原因被ban掉。 -maxConcurrentReq='15'#同时最多N个并发通过代理访问上面的地址,检测socks5代理是否可用,可根据网络环境调整。云主机的话开500、1000都可以,本机的话,开三五十差不多。 -timeout='6'#单位秒,验证socks5代理的超时时间,建议保持在5或6,检查及使用代理访问上面的地址时,超过这个时间,判定无效 +maxConcurrentReq=500 #同时最多N个并发通过代理访问上面的地址,检测socks5代理是否可用,可根据网络环境调整。云主机的话开500、1000都可以,本机的话,开三五十差不多。 +timeout=6 #单位秒,验证socks5代理的超时时间,建议保持在5或6,检查及使用代理访问上面的地址时,超过这个时间,判定无效 [checkSocks.checkGeolocate]##******非特殊情况,默认即可******通过访问返回IP归属地信息的URL和关键字判断,来排除某些代理,如:某些情况下,真正要访问的系统限制只有大陆地区IP可以访问 switch='close' #open:启用,非open:禁用 @@ -19,21 +21,21 @@ includeKeywords=['中国']#格式如:['中国','北京']则只获取中国北 [FOFA] switch = 'close' #open:启用,非open:禁用 apiUrl='https://fofa.info/api/v1/search/all' -email = 'xxxxx@xxx.com' -key = '54eddce1xxxxxxxxxxxxxxxx49836612' -queryString = 'protocol=="socks5" && country="CN" && banner="Method:No Authentication"'#官方语法 -resultSize='500' #此处最大为10000,需小于等于从官网web界面看到的结果数量 +email = 'xxxx@xxx.com' +key = '54eddxxxxxx8axxxxxxxxdfb4xxxxxx612' +queryString = 'protocol=="socks5" && country="CN" && banner="Method:No Authentication"' +resultSize=300 #此处最大为10000,需小于等于从官网web界面看到的结果数量 [QUAKE] switch = 'close' apiUrl='https://quake.360.net/api/v3/search/quake_service' -key = '962xxx36-xxxxxxxxxxxxxxxx-5efxxxxfc90b0a' -queryString = 'service:socks5 AND country: "CN" AND response:"No authentication"'#官方语法 -resultSize='500' #此处最大为10000,需小于等于从官网web界面看到的结果数量 +key = '9621xxx6-2xxxxxx-8c98-xxxxxxx90b0a' +queryString = 'service:socks5 AND country: "CN" AND response:"No authentication"' +resultSize=300 #此处最大为10000,需小于等于从官网web界面看到的结果数量 [HUNTER] switch = 'close' apiUrl='https://hunter.qianxin.com/openApi/search' -key = '9c1698e0xxxxxxxxxxxxxxxxa6e90758edcxxxx23533f9xxxxxxxxxxxxxxxx9ce18' -queryString = 'protocol=="socks5"&&protocol.banner="No authentication"&&ip.country="CN"'#官方语法 -resultSize='300' #最小为100,按100条/每页翻页,最大值需小于从官网web界面看到的结果数量,值需为100的整倍数,如200、300、1000、2000等 \ No newline at end of file +key = '9c1698e044xxxa6e90xxxxdc2xxxxx2048f7xxxxx8' +queryString = 'protocol=="socks5"&&protocol.banner="No authentication"&&ip.country="CN"' +resultSize=300 #最小为100,按100条/每页翻页,最大值需小于从官网web界面看到的结果数量,值需为100的整倍数,如200、300、1000、2000等 \ No newline at end of file diff --git a/go.mod b/go.mod index 351a90a..9e9079e 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,12 @@ module Deadpool -go 1.20 +go 1.21.0 -require github.com/BurntSushi/toml v1.4.0 +toolchain go1.23.0 + +require ( + github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 + github.com/pelletier/go-toml/v2 v2.2.3 +) + +require golang.org/x/net v0.29.0 diff --git a/go.sum b/go.sum index 8bc10f6..ee72e28 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,14 @@ -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 430786b..6d7a3ab 100644 --- a/main.go +++ b/main.go @@ -3,42 +3,39 @@ package main import ( "Deadpool/utils" "fmt" - "net" + "io" + "log" "os" + "strconv" + "strings" "time" - "github.com/BurntSushi/toml" + "github.com/armon/go-socks5" ) func main() { + utils.Banner() fmt.Println("By:thinkoaa GitHub:https://github.com/thinkoaa/Deadpool\n\n") //读取配置文件 - var config map[string]interface{} - if _, err := toml.DecodeFile("config.toml", &config); err != nil { - fmt.Println("读取config.toml失败,请检查配置,格式一定要正确", err) - os.Exit(1) - } - // 开启监听 - listenerConfig := config["listener"].(map[string]interface{}) - listener := listenerConfig["IP"].(string) + ":" + listenerConfig["PORT"].(string) - socketServer, err := net.Listen("tcp", listener) + config, err := utils.LoadConfig("config.toml") if err != nil { - fmt.Printf("本地监听服务启动失败:%v\n", err) + fmt.Printf("读取config.toml失败,请检查配置,格式一定要正确%w\n", err) os.Exit(1) } + //从本地文件中取socks代理 fmt.Println("***直接使用fmt打印当前使用的代理,若高并发时,命令行打印可能会阻塞,不对打印做特殊处理,可忽略,不会影响实际的请求转发***\n") utils.GetSocksFromFile(utils.LastDataFile) //从fofa获取 utils.Wg.Add(1) - go utils.GetSocksFromFofa(config["FOFA"].(map[string]interface{})) + go utils.GetSocksFromFofa(config.FOFA) //从hunter获取 utils.Wg.Add(1) - go utils.GetSocksFromHunter(config["HUNTER"].(map[string]interface{})) + go utils.GetSocksFromHunter(config.HUNTER) //从quake中取 utils.Wg.Add(1) - go utils.GetSocksFromQuake(config["QUAKE"].(map[string]interface{})) + go utils.GetSocksFromQuake(config.QUAKE) utils.Wg.Wait() //等待所有 goroutine 完成 if len(utils.SocksList) == 0 { @@ -52,7 +49,8 @@ func main() { //开始检测代理存活性 startTime := time.Now() - utils.CheckSocks(config["checkSocks"].(map[string]interface{})) + utils.Timeout = config.CheckSocks.Timeout + utils.CheckSocks(config.CheckSocks) sec := int(time.Since(startTime).Seconds()) if sec == 0 { @@ -66,13 +64,25 @@ func main() { utils.WriteLinesToFile() //存活代理写入硬盘,以备下次启动直接读取 - fmt.Printf("======其他工具通过配置 socks5://%v 使用收集的代理,此处若提示0.0.0.0:xxxx,使用时需指定为具体地址======\n", listener) - for { //持续监听请求 - reqFromClient, err := socketServer.Accept() - if err != nil { - fmt.Printf("本次客户端发起的请求出错:%v\n", err) - continue - } - go utils.TransmitReqFromClient(reqFromClient) + // 开启监听 + conf := &socks5.Config{ + Dial: utils.DefineDial, + Logger: log.New(io.Discard, "", log.LstdFlags), + } + userName := strings.TrimSpace(config.Listener.UserName) + password := strings.TrimSpace(config.Listener.Password) + if userName != "" && password != "" { + cator := socks5.UserPassAuthenticator{Credentials: socks5.StaticCredentials{ + userName: password, + }} + conf.AuthMethods = []socks5.Authenticator{cator} + } + server, _ := socks5.New(conf) + listener := config.Listener.IP + ":" + strconv.Itoa(config.Listener.Port) + fmt.Printf("======其他工具通过配置 socks5://%v 使用收集的代理,如有账号密码,记得配置======\n", listener) + if err := server.ListenAndServe("tcp", listener); err != nil { + fmt.Printf("本地监听服务启动失败:%v\n", err) + os.Exit(1) } + } diff --git a/utils/globals.go b/utils/globals.go index 27465f7..cf74b62 100644 --- a/utils/globals.go +++ b/utils/globals.go @@ -6,7 +6,7 @@ var ( SocksList []string EffectiveList []string proxyIndex int - timeout int + Timeout int LastDataFile = "lastData.txt" Wg sync.WaitGroup mu sync.Mutex diff --git a/utils/load_config.go b/utils/load_config.go new file mode 100644 index 0000000..d0e00e4 --- /dev/null +++ b/utils/load_config.go @@ -0,0 +1,75 @@ +package utils + +import ( + "os" + + toml "github.com/pelletier/go-toml/v2" +) + +type Config struct { + Listener ListenerConfig `toml:"listener"` + CheckSocks CheckSocksConfig `toml:"checkSocks"` + FOFA FOFAConfig `toml:"FOFA"` + QUAKE QUAKEConfig `toml:"QUAKE"` + HUNTER HUNTERConfig `toml:"HUNTER"` +} + +type ListenerConfig struct { + IP string `toml:"IP"` + Port int `toml:"PORT"` + UserName string `toml:"userName"` + Password string `toml:"password"` +} + +type CheckSocksConfig struct { + CheckURL string `toml:"checkURL"` + CheckRspKeywords string `toml:"checkRspKeywords"` + MaxConcurrentReq int `toml:"maxConcurrentReq"` + Timeout int `toml:"timeout"` + CheckGeolocate CheckGeolocateConfig `toml:"checkGeolocate"` +} + +type CheckGeolocateConfig struct { + Switch string `toml:"switch"` + CheckURL string `toml:"checkURL"` + ExcludeKeywords []string `toml:"excludeKeywords"` + IncludeKeywords []string `toml:"includeKeywords"` +} + +type FOFAConfig struct { + Switch string `toml:"switch"` + APIURL string `toml:"apiUrl"` + Email string `toml:"email"` + Key string `toml:"key"` + QueryString string `toml:"queryString"` + ResultSize int `toml:"resultSize"` +} + +type QUAKEConfig struct { + Switch string `toml:"switch"` + APIURL string `toml:"apiUrl"` + Key string `toml:"key"` + QueryString string `toml:"queryString"` + ResultSize int `toml:"resultSize"` +} + +type HUNTERConfig struct { + Switch string `toml:"switch"` + APIURL string `toml:"apiUrl"` + Key string `toml:"key"` + QueryString string `toml:"queryString"` + ResultSize int `toml:"resultSize"` +} + +func LoadConfig(path string) (Config, error) { + var config Config + // 读取并解析 TOML 文件 + data, err := os.ReadFile(path) + if err != nil { + return config, err + } + + err = toml.Unmarshal(data, &config) + + return config, err +} diff --git a/utils/netspace.go b/utils/netspace.go index 1974bdd..29b5938 100644 --- a/utils/netspace.go +++ b/utils/netspace.go @@ -12,19 +12,18 @@ import ( ) // 从quake获取,结果为IP:PORT -func GetSocksFromQuake(config map[string]interface{}) { +func GetSocksFromQuake(quake QUAKEConfig) { defer Wg.Done() - if config["switch"] != "open" { + if quake.Switch != "open" { fmt.Println("---未开启quake---") return } - - fmt.Println("***已开启quake,将根据配置条件从quake中获取" + config["resultSize"].(string) + "条数据,然后进行有效性检测***") - jsonCondition := "{\"query\": \"" + strings.Replace(config["queryString"].(string), `"`, `\"`, -1) + "\",\"start\": 0,\"size\": " + config["resultSize"].(string) + ",\"include\":[\"ip\",\"port\"]}" + fmt.Printf("***已开启quake,将根据配置条件从quake中获取%d条数据,然后进行有效性检测***\n", quake.ResultSize) + jsonCondition := "{\"query\": \"" + strings.Replace(quake.QueryString, `"`, `\"`, -1) + "\",\"start\": 0,\"size\": " + strconv.Itoa(quake.ResultSize) + ",\"include\":[\"ip\",\"port\"]}" headers := map[string]string{ - "X-QuakeToken": config["key"].(string), + "X-QuakeToken": quake.Key, "Content-Type": "application/json"} - content, err := fetchContent(config["apiUrl"].(string), "POST", 1500, nil, headers, jsonCondition) + content, err := fetchContent(quake.APIURL, "POST", 60, nil, headers, jsonCondition) if err != nil { fmt.Println("quake异常", err) return @@ -47,21 +46,21 @@ func GetSocksFromQuake(config map[string]interface{}) { } // 从FOFA获取,结果为IP:PORT -func GetSocksFromFofa(config map[string]interface{}) { +func GetSocksFromFofa(fofa FOFAConfig) { defer Wg.Done() - if config["switch"] != "open" { + if fofa.Switch != "open" { fmt.Println("---未开启fofa---") return } - fmt.Println("***已开启fofa,将根据配置条件从fofa中获取" + config["resultSize"].(string) + "条数据,然后进行有效性检测***") + fmt.Printf("***已开启fofa,将根据配置条件从fofa中获取%d条数据,然后进行有效性检测***\n", fofa.ResultSize) params := map[string]string{ - "email": config["email"].(string), - "key": config["key"].(string), + "email": fofa.Email, + "key": fofa.Key, "fields": "ip,port", - "qbase64": base64.StdEncoding.EncodeToString([]byte(config["queryString"].(string))), - "size": config["resultSize"].(string)} - content, err := fetchContent(config["apiUrl"].(string), "GET", 1500, params, nil, "") + "qbase64": base64.URLEncoding.EncodeToString([]byte(fofa.QueryString)), + "size": strconv.Itoa(fofa.ResultSize)} + content, err := fetchContent(fofa.APIURL, "GET", 60, params, nil, "") if err != nil { fmt.Println("访问fofa异常", err) return @@ -82,23 +81,24 @@ func GetSocksFromFofa(config map[string]interface{}) { } // 从鹰图获取,结果为IP:PORT -func GetSocksFromHunter(config map[string]interface{}) { +func GetSocksFromHunter(hunter HUNTERConfig) { defer Wg.Done() - if config["switch"] != "open" { + if hunter.Switch != "open" { fmt.Println("---未开启hunter---") return } - fmt.Println("***已开启hunter,将根据配置条件从hunter中获取" + config["resultSize"].(string) + "条数据,然后进行有效性检测***") - hunterResultSize, _ := strconv.Atoi(config["resultSize"].(string)) + fmt.Printf("***已开启hunter,将根据配置条件从hunter中获取%d条数据,然后进行有效性检测***\n", hunter.ResultSize) - for i := 1; i <= hunterResultSize/100; i++ { + var exeData int //记录处理了几条 + end := hunter.ResultSize / 100 + for i := 1; i <= end; i++ { params := map[string]string{ - "api-key": config["key"].(string), - "search": base64.StdEncoding.EncodeToString([]byte(config["queryString"].(string))), + "api-key": hunter.Key, + "search": base64.URLEncoding.EncodeToString([]byte(hunter.QueryString)), "page": strconv.Itoa(i), "page_size": "100"} - fmt.Printf("HUNTER:每页100条,正在查询第%v页\n", i) - content, err := fetchContent(config["apiUrl"].(string), "GET", 30, params, nil, "") + fmt.Printf("HUNTER:每页100条,正在查询第%v页\n", i) + content, err := fetchContent(hunter.APIURL, "GET", 60, params, nil, "") if err != nil { fmt.Println("访问hunter异常", err) return @@ -110,15 +110,27 @@ func GetSocksFromHunter(config map[string]interface{}) { fmt.Println("HUNTER:", data["message"]) return } + rsData := data["data"].(map[string]interface{}) + total := rsData["total"].(float64) + if total == 0 { + fmt.Println("HUNTER:xxx根据配置语法,未取到数据xxx") + break + } arr := rsData["arr"].([]interface{}) for _, item := range arr { itemMap := item.(map[string]interface{}) ip := itemMap["ip"].(string) port := itemMap["port"].(float64) + exeData++ addSocks(ip + ":" + strconv.FormatFloat(port, 'f', -1, 64)) } - time.Sleep(3 * time.Second) //防止hunter提示访问过快获取不到结果 + if float64(exeData) >= total { + break + } + if end > 1 && i != end { + time.Sleep(3 * time.Second) //防止hunter提示访问过快获取不到结果 + } } fmt.Println("+++hunter数据已取+++") } diff --git a/utils/utils.go b/utils/utils.go index 4dc36f2..7a90a40 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -3,6 +3,7 @@ package utils import ( "bufio" "bytes" + "context" "crypto/tls" "fmt" "io" @@ -10,9 +11,10 @@ import ( "net/http" "net/url" "os" - "strconv" "strings" "time" + + "golang.org/x/net/proxy" ) // 防止goroutine 异步处理问题 @@ -82,19 +84,19 @@ func RemoveDuplicates() { SocksList = result } -func CheckSocks(config map[string]interface{}) { - maxConcurrentReq, _ := strconv.Atoi(config["maxConcurrentReq"].(string)) - timeout, _ = strconv.Atoi(config["timeout"].(string)) +func CheckSocks(checkSocks CheckSocksConfig) { + maxConcurrentReq := checkSocks.MaxConcurrentReq + timeout := checkSocks.Timeout semaphore = make(chan struct{}, maxConcurrentReq) - checkRspKeywords := config["checkRspKeywords"].(string) - checkGeolocateConfig := config["checkGeolocate"].(map[string]interface{}) - checkGeolocateSwitch := checkGeolocateConfig["switch"].(string) + checkRspKeywords := checkSocks.CheckRspKeywords + checkGeolocateConfig := checkSocks.CheckGeolocate + checkGeolocateSwitch := checkGeolocateConfig.Switch isOpenGeolocateSwitch := false - reqUrl := config["checkURL"].(string) + reqUrl := checkSocks.CheckURL if checkGeolocateSwitch == "open" { isOpenGeolocateSwitch = true - reqUrl = checkGeolocateConfig["checkURL"].(string) + reqUrl = checkGeolocateConfig.CheckURL } fmt.Printf("并发:[ %v ],超时标准:[ %vs ]\n", maxConcurrentReq, timeout) for index, proxyAddr := range SocksList { @@ -148,15 +150,15 @@ func CheckSocks(config map[string]interface{}) { } } else { //直接循环要排除的关键字,任一命中就返回 - for _, keyword := range checkGeolocateConfig["excludeKeywords"].([]interface{}) { - if strings.Contains(stringBody, keyword.(string)) { + for _, keyword := range checkGeolocateConfig.ExcludeKeywords { + if strings.Contains(stringBody, keyword) { // fmt.Println("忽略:" + proxyAddr + "包含:" + keyword.(string)) return } } //直接循环要必须包含的关键字,任一未命中就返回 - for _, keyword := range checkGeolocateConfig["includeKeywords"].([]interface{}) { - if !strings.Contains(stringBody, keyword.(string)) { + for _, keyword := range checkGeolocateConfig.IncludeKeywords { + if !strings.Contains(stringBody, keyword) { // fmt.Println("忽略:" + proxyAddr + "未包含:" + keyword.(string)) return } @@ -188,32 +190,42 @@ func WriteLinesToFile() error { return writer.Flush() } -func TransmitReqFromClient(reqFromClient net.Conn) { - defer reqFromClient.Close() - tmpProxy := getNextProxy() - fmt.Println(time.Now().Format("2006-01-02 15:04:05") + " " + tmpProxy) - if len(EffectiveList) == 0 { - fmt.Println("***已无可用代理,程序退出***") - os.Exit(1) - } - if len(EffectiveList) <= 1 { - fmt.Printf("***可用代理已仅剩%v个,%v,***\n", len(EffectiveList), EffectiveList) +func DefineDial(ctx context.Context, network, address string) (net.Conn, error) { + + return transmitReqFromClient(network, address) +} + +func transmitReqFromClient(network string, address string) (net.Conn, error) { + tempProxy := getNextProxy() + fmt.Println(time.Now().Format("2006-01-02 15:04:05") + "\t" + tempProxy) + // 超时时间设置为 5 秒 + timeout := time.Duration(Timeout) * time.Second + + dialer := &net.Dialer{ + Timeout: timeout, } - conn, err := net.DialTimeout("tcp", tmpProxy, time.Duration(timeout)*time.Second) + dialect, _ := proxy.SOCKS5(network, tempProxy, nil, dialer) + conn, err := dialect.Dial(network, address) if err != nil { - delInvalidProxy(tmpProxy) //从临时列表中删除该代理 - TransmitReqFromClient(reqFromClient) - return + delInvalidProxy(tempProxy) + fmt.Printf("%s无效,自动切换下一个......\n", tempProxy) + return transmitReqFromClient(network, address) } - defer conn.Close() - go io.Copy(conn, reqFromClient) - io.Copy(reqFromClient, conn) + + return conn, nil } func getNextProxy() string { mu.Lock() defer mu.Unlock() + if len(EffectiveList) == 0 { + fmt.Println("***已无可用代理,程序退出***") + os.Exit(1) + } + if len(EffectiveList) <= 2 { + fmt.Printf("***可用代理已仅剩%v个,%v,***\n", len(EffectiveList), EffectiveList) + } proxy := EffectiveList[proxyIndex] proxyIndex = (proxyIndex + 1) % len(EffectiveList) // 循环访问 return proxy