├── makefile ├── pics └── curl-run.jpg ├── cmd └── rotateproxy │ ├── rotateproxy │ ├── rotateproxy.zip │ └── main.go ├── go.mod ├── util.go ├── README.md ├── go.sum ├── crawler.go ├── check.go ├── db.go └── traffic_redirect.go /makefile: -------------------------------------------------------------------------------- 1 | build: 2 | cd cmd/rotateproxy && go build -trimpath -ldflags="-s -w" -------------------------------------------------------------------------------- /pics/curl-run.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Liang2580/rotateproxy/HEAD/pics/curl-run.jpg -------------------------------------------------------------------------------- /cmd/rotateproxy/rotateproxy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Liang2580/rotateproxy/HEAD/cmd/rotateproxy/rotateproxy -------------------------------------------------------------------------------- /cmd/rotateproxy/rotateproxy.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Liang2580/rotateproxy/HEAD/cmd/rotateproxy/rotateproxy.zip -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/akkuman/rotateproxy 2 | 3 | go 1.17 4 | 5 | require ( 6 | gorm.io/driver/sqlite v1.1.6 7 | gorm.io/gorm v1.21.16 8 | ) 9 | 10 | require ( 11 | github.com/jinzhu/inflection v1.0.0 // indirect 12 | github.com/jinzhu/now v1.1.2 // indirect 13 | github.com/mattn/go-sqlite3 v1.14.8 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | package rotateproxy 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func init() { 11 | rand.Seed(time.Now().Unix()) 12 | } 13 | 14 | func RandomSyncMap(sMap sync.Map) (key, value interface{}) { 15 | var tmp [][2]interface{} 16 | sMap.Range(func(key, value interface{}) bool { 17 | if value.(int) == 0 { 18 | tmp = append(tmp, [2]interface{}{key, value}) 19 | } 20 | return true 21 | }) 22 | element := tmp[rand.Intn(len(tmp))] 23 | return element[0], element[1] 24 | } 25 | 26 | func IsProxyURLBlank() bool { 27 | proxies, err := QueryAvailProxyURL() 28 | if err != nil { 29 | fmt.Printf("[!] Error: %v\n", err) 30 | return false 31 | } 32 | return len(proxies) == 0 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rotateproxy 2 | 3 | 利用fofa搜索socks5开放代理进行代理池轮切的工具 4 | 5 | ## 帮助 6 | 7 | ```shell 8 | └──╼ #./cmd/rotateproxy/rotateproxy -h 9 | Usage of ./cmd/rotateproxy/rotateproxy: 10 | -email string 11 | email address 12 | -l string 13 | listen address (default ":9999") 14 | -page int 15 | the page count you want to crawl (default 5) 16 | -region int 17 | 0: all 1: cannot bypass gfw 2: bypass gfw 18 | -rule string 19 | search rule (default "protocol==\"socks5\" && \"Version:5 Method:No Authentication(0x00)\" && after=\"2021-11-01\"") 20 | -time int 21 | Not used for more than milliseconds (default 8000) 22 | -token string 23 | token 24 | 25 | ``` 26 | 27 | 28 | 使用方法 29 | 30 | ./cmd/rotateproxy/rotateproxy -email=qqq.com -token=xxx 31 | 32 | time 代表。毫秒值。默认为超过8000毫秒则不使用当前代理 33 | 34 | ![proxy](https://user-images.githubusercontent.com/27684409/142030959-588afe68-3a1a-4734-86e4-7ec347e18e21.png) 35 | 36 | 37 | 38 | 39 | ##此项目还欠缺一部分功能 40 | 41 | 42 | 1.当前代理中断后的自动连接耗时最短的代理 [2.0已完成] 43 | 44 | 2.调度的权重 45 | 46 | 3.暂只编译了Linux amd64 47 | -------------------------------------------------------------------------------- /cmd/rotateproxy/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/akkuman/rotateproxy" 7 | ) 8 | 9 | var ( 10 | baseCfg rotateproxy.BaseConfig 11 | email string 12 | token string 13 | rule string 14 | pageCount int 15 | timeout int 16 | ) 17 | 18 | func init() { 19 | flag.StringVar(&email, "email", "", "email address") 20 | flag.StringVar(&token, "token", "", "token") 21 | flag.StringVar(&baseCfg.ListenAddr, "l", ":9999", "listen address") 22 | flag.IntVar(&timeout, "time", 9900, "Not used for more than milliseconds") 23 | // && country="CN" 24 | flag.StringVar(&rule, "rule", `protocol=="socks5" && "Version:5 Method:No Authentication(0x00)" && after="2021-10-01" && country="CN"`, "search rule") 25 | flag.IntVar(&baseCfg.IPRegionFlag, "region", 0, "0: all 1: cannot bypass gfw 2: bypass gfw") 26 | flag.IntVar(&pageCount, "page", 10, "the page count you want to crawl") 27 | flag.Parse() 28 | } 29 | 30 | func isFlagPassed(name string) bool { 31 | found := false 32 | flag.Visit(func(f *flag.Flag) { 33 | if f.Name == name { 34 | found = true 35 | } 36 | }) 37 | return found 38 | } 39 | 40 | func main() { 41 | if !isFlagPassed("email") || !isFlagPassed("token") { 42 | flag.Usage() 43 | return 44 | } 45 | rotateproxy.StartRunCrawler(token, email, rule, pageCount) 46 | rotateproxy.StartCheckProxyAlive(timeout) 47 | c := rotateproxy.NewRedirectClient(rotateproxy.WithConfig(&baseCfg)) 48 | c.Serve() 49 | select {} 50 | } 51 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 4 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 5 | github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= 6 | github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 7 | github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU= 8 | github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= 9 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 10 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 11 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 13 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 14 | gorm.io/driver/sqlite v1.1.6 h1:p3U8WXkVFTOLPED4JjrZExfndjOtya3db8w9/vEMNyI= 15 | gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8= 16 | gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= 17 | gorm.io/gorm v1.21.16 h1:YBIQLtP5PLfZQz59qfrq7xbrK7KWQ+JsXXCH/THlMqs= 18 | gorm.io/gorm v1.21.16/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= 19 | -------------------------------------------------------------------------------- /crawler.go: -------------------------------------------------------------------------------- 1 | package rotateproxy 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/json" 6 | "fmt" 7 | "net/http" 8 | "time" 9 | ) 10 | 11 | var crawlDone = make(chan struct{}) 12 | 13 | type fofaAPIResponse struct { 14 | Err bool `json:"error"` 15 | Mode string `json:"mode"` 16 | Page int `json:"page"` 17 | Query string `json:"query"` 18 | Results [][]string `json:"results"` 19 | Size int `json:"size"` 20 | } 21 | 22 | func addProxyURL(url string) { 23 | CreateProxyURL(url) 24 | } 25 | 26 | func RunCrawler(fofaApiKey, fofaEmail, rule string, pageNum int) (err error) { 27 | req, err := http.NewRequest("GET", "https://fofa.so/api/v1/search/all", nil) 28 | if err != nil { 29 | return err 30 | } 31 | rule = base64.StdEncoding.EncodeToString([]byte(rule)) 32 | q := req.URL.Query() 33 | q.Add("email", fofaEmail) 34 | q.Add("key", fofaApiKey) 35 | q.Add("qbase64", rule) 36 | q.Add("size", "500") 37 | q.Add("page", fmt.Sprintf("%d", pageNum)) 38 | q.Add("fields", "host,title,ip,domain,port,country,city,server,protocol") 39 | req.URL.RawQuery = q.Encode() 40 | resp, err := http.DefaultClient.Do(req) 41 | if err != nil { 42 | return err 43 | } 44 | fmt.Printf("start to parse proxy url from response\n") 45 | defer resp.Body.Close() 46 | var res fofaAPIResponse 47 | err = json.NewDecoder(resp.Body).Decode(&res) 48 | if err != nil { 49 | return err 50 | } 51 | fmt.Printf("get %d host\n", len(res.Results)) 52 | for _, value := range res.Results { 53 | host := value[0] 54 | addProxyURL(fmt.Sprintf("socks5://%s", host)) 55 | } 56 | crawlDone <- struct{}{} 57 | return 58 | } 59 | 60 | func StartRunCrawler(fofaApiKey, fofaEmail, rule string, pageCount int) { 61 | go func() { 62 | for i := 1; i <= pageCount; i++ { 63 | RunCrawler(fofaApiKey, fofaEmail, rule, i) 64 | } 65 | ticker := time.NewTicker(600 * time.Second) 66 | for range ticker.C { 67 | for i := 1; i <= pageCount; i++ { 68 | RunCrawler(fofaApiKey, fofaEmail, rule, i) 69 | } 70 | } 71 | }() 72 | } 73 | -------------------------------------------------------------------------------- /check.go: -------------------------------------------------------------------------------- 1 | package rotateproxy 2 | 3 | import ( 4 | "crypto/tls" 5 | "encoding/json" 6 | "fmt" 7 | "net/http" 8 | "net/url" 9 | "time" 10 | ) 11 | 12 | type IPResponse struct { 13 | ORIGIN string `json:"origin"` 14 | } 15 | 16 | 17 | type IPInfo struct { 18 | Status string `json:"status"` 19 | Country string `json:"country"` 20 | CountryCode string `json:"countryCode"` 21 | Region string `json:"region"` 22 | RegionName string `json:"regionName"` 23 | City string `json:"city"` 24 | Zip string `json:"zip"` 25 | Lat float64 `json:"lat"` 26 | Lon float64 `json:"lon"` 27 | Timezone string `json:"timezone"` 28 | Isp string `json:"isp"` 29 | Org string `json:"org"` 30 | As string `json:"as"` 31 | Query string `json:"query"` 32 | } 33 | 34 | func CheckProxyAlive(proxyURL,ip string,timeout int) (respBody string, avail bool,time_config int) { 35 | startTime := time.Now().UnixNano() 36 | proxy, _ := url.Parse(proxyURL) 37 | httpclient := &http.Client{ 38 | Transport: &http.Transport{ 39 | Proxy: http.ProxyURL(proxy), 40 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 41 | }, 42 | Timeout: 20 * time.Second, 43 | } 44 | resp, err := httpclient.Get("http://httpbin.org/ip") 45 | if err != nil { 46 | return "", false,10000 47 | } 48 | defer resp.Body.Close() 49 | var res IPResponse 50 | err = json.NewDecoder(resp.Body).Decode(&res) 51 | if len(res.ORIGIN)<2{ 52 | return "", false,10000 53 | } 54 | if err != nil { 55 | return "", false,10000 56 | } 57 | endTime := time.Now().UnixNano() 58 | Milliseconds:= int((endTime - startTime) / 1e6)// 毫秒 59 | fmt.Println("IP : ",ip," 用时毫秒 ",Milliseconds," 可用") 60 | if Milliseconds>timeout{ 61 | return "", false,10000 62 | } 63 | return res.ORIGIN, true,Milliseconds 64 | } 65 | 66 | func StartCheckProxyAlive(timeout int) { 67 | go func() { 68 | ticker := time.NewTicker(120 * time.Second) 69 | for { 70 | select { 71 | case <-crawlDone: 72 | fmt.Println("Checking") 73 | checkAlive(timeout) 74 | fmt.Println("Check done") 75 | case <-ticker.C: 76 | checkAlive(timeout) 77 | } 78 | } 79 | }() 80 | } 81 | 82 | func checkAlive(timeout int) { 83 | proxies, err := QueryProxyURL() 84 | if err != nil { 85 | fmt.Printf("[!] query db error: %v\n", err) 86 | } 87 | for i := range proxies { 88 | proxy := proxies[i] 89 | if proxy.Available{ 90 | continue 91 | } 92 | if proxy.Retry >5{ 93 | continue 94 | } 95 | go func() { 96 | _, _,time_conig := CheckProxyAlive(proxy.URL,proxy.IP,timeout) 97 | if time_conig !=10000 { 98 | SetProxytime(proxy.URL, time_conig) 99 | }else{ 100 | AddProxyURLRetry(proxy.URL) 101 | } 102 | }() 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /db.go: -------------------------------------------------------------------------------- 1 | package rotateproxy 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | 7 | "gorm.io/driver/sqlite" 8 | "gorm.io/gorm" 9 | "gorm.io/gorm/logger" 10 | ) 11 | 12 | var DB *gorm.DB 13 | 14 | type ProxyURL struct { 15 | gorm.Model 16 | URL string `gorm:"uniqueIndex;column:url"` 17 | IP string `gorm:"column:ip"` 18 | CONSUMING int `gorm:"column:time"` 19 | COUNT int `gorm:"column:count"` 20 | WEIGHT int `gorm:"column:weight"` 21 | Retry int `gorm:"column:retry"` 22 | Available bool `gorm:"column:available"` 23 | CanBypassGFW bool `gorm:"column:can_bypass_gfw"` 24 | } 25 | 26 | func (ProxyURL) TableName() string { 27 | return "proxy_urls" 28 | } 29 | 30 | func checkErr(err error) { 31 | if err != nil { 32 | panic(err) 33 | } 34 | } 35 | 36 | func init() { 37 | var err error 38 | DB, err = gorm.Open(sqlite.Open("db.db"), &gorm.Config{ 39 | Logger: logger.Discard, 40 | }) 41 | checkErr(err) 42 | DB.AutoMigrate(&ProxyURL{}) 43 | } 44 | 45 | func CreateProxyURL(url string) error { 46 | regstr := `\d+\.\d+\.\d+\.\d+` 47 | reg, _ := regexp.Compile(regstr) 48 | ip := reg.Find([]byte(url)) 49 | tx := DB.Create(&ProxyURL{ 50 | URL: url, 51 | IP:string(ip), 52 | CONSUMING:0, 53 | COUNT:0, 54 | WEIGHT:1, 55 | Retry: 0, 56 | Available: false, 57 | }) 58 | return tx.Error 59 | } 60 | 61 | func QueryAvailProxyURL() (proxyURLs []ProxyURL, err error) { 62 | tx := DB.Where("available = ?", true).Find(&proxyURLs) 63 | err = tx.Error 64 | return 65 | } 66 | 67 | func QueryProxyURL() (proxyURLs []ProxyURL, err error) { 68 | tx := DB.Find(&proxyURLs) 69 | err = tx.Error 70 | return 71 | } 72 | 73 | func SetProxyURLAvail(url string, canBypassGFW bool) error { 74 | tx := DB.Model(&ProxyURL{}).Where("url = ?", url).Updates(ProxyURL{Retry: 0, Available: true, CanBypassGFW: canBypassGFW}) 75 | return tx.Error 76 | } 77 | func StopProxy(url string) error { 78 | tx := DB.Model(&ProxyURL{}).Where("url = ?", url).Update("Available",false) 79 | return tx.Error 80 | } 81 | 82 | func SetProxytime(url string,timeconfig int) error { 83 | tx := DB.Model(&ProxyURL{}).Where("url = ?", url).Updates(ProxyURL{CONSUMING: timeconfig,Available: true}) 84 | return tx.Error 85 | } 86 | 87 | func AddProxyURLRetry(url string) error { 88 | tx := DB.Model(&ProxyURL{}).Where("url = ?", url).Update("retry", gorm.Expr("retry + 1")) 89 | return tx.Error 90 | } 91 | 92 | 93 | func RandomProxyURL(regionFlag int) (string, error) { 94 | var proxyURL ProxyURL 95 | var tx *gorm.DB 96 | switch regionFlag { 97 | case 1: 98 | tx = DB.Raw(fmt.Sprintf("SELECT * FROM %s WHERE available = ? AND can_bypass_gfw = ? ORDER BY RANDOM() LIMIT 1;", proxyURL.TableName()), true, false).Scan(&proxyURL) 99 | case 2: 100 | tx = DB.Raw(fmt.Sprintf("SELECT * FROM %s WHERE available = ? AND can_bypass_gfw = ? ORDER BY RANDOM() LIMIT 1;", proxyURL.TableName()), true, true).Scan(&proxyURL) 101 | default: 102 | tx = DB.Raw(fmt.Sprintf("SELECT * FROM %s WHERE available = 1 ORDER BY RANDOM() LIMIT 1;", proxyURL.TableName())).Scan(&proxyURL) 103 | } 104 | return proxyURL.URL, tx.Error 105 | } 106 | 107 | 108 | func RandomProxyURL2() (string, error) { 109 | var proxyURL ProxyURL 110 | var tx *gorm.DB 111 | tx = DB.Raw(fmt.Sprintf("SELECT * FROM %s WHERE available = 1 ORDER BY time ASC LIMIT 1;", proxyURL.TableName())).Scan(&proxyURL) 112 | return proxyURL.URL, tx.Error 113 | } 114 | -------------------------------------------------------------------------------- /traffic_redirect.go: -------------------------------------------------------------------------------- 1 | package rotateproxy 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | var ( 12 | largeBufferSize = 32 * 1024 // 32KB large buffer 13 | ) 14 | 15 | type BaseConfig struct { 16 | ListenAddr string 17 | IPRegionFlag int // 0: all 1: cannot bypass gfw 2: bypass gfw 18 | } 19 | 20 | type RedirectClient struct { 21 | config *BaseConfig 22 | } 23 | 24 | type RedirectClientOption func(*RedirectClient) 25 | 26 | func WithConfig(config *BaseConfig) RedirectClientOption { 27 | return func(c *RedirectClient) { 28 | c.config = config 29 | } 30 | } 31 | 32 | func NewRedirectClient(opts ...RedirectClientOption) *RedirectClient { 33 | c := &RedirectClient{} 34 | for _, opt := range opts { 35 | opt(c) 36 | } 37 | return c 38 | } 39 | 40 | func (c *RedirectClient) Serve() error { 41 | l, err := net.Listen("tcp", c.config.ListenAddr) 42 | if err != nil { 43 | return err 44 | } 45 | //for IsProxyURLBlank() { 46 | // fmt.Println("[*] waiting for crawl proxy...") 47 | // time.Sleep(3 * time.Second) 48 | //} 49 | for { 50 | conn, err := l.Accept() 51 | if err != nil { 52 | fmt.Printf("[!] accept error: %v\n", err) 53 | continue 54 | } 55 | go c.HandleConn(conn) 56 | } 57 | } 58 | 59 | 60 | func (c *RedirectClient) HandleConn(conn net.Conn) { 61 | startTime := time.Now().UnixNano() 62 | key, err := RandomProxyURL(c.config.IPRegionFlag) 63 | if err != nil { 64 | errConn := closeConn(conn) 65 | if errConn != nil { 66 | fmt.Printf("[!] close connect error : %v\n", errConn) 67 | } 68 | return 69 | } 70 | key2 := strings.TrimPrefix(key, "socks5://") 71 | fmt.Println(key) 72 | cc, err := net.DialTimeout("tcp", key2, 500*time.Millisecond) 73 | flag :=false 74 | if err != nil { 75 | //如果超时。则踢出这个节点 76 | fmt.Printf("连接失败\n") 77 | flag=true 78 | go StopProxy(key) 79 | go closeConn(conn) 80 | } 81 | if flag{ 82 | key2 = strings.TrimPrefix(key, "socks5://") 83 | fmt.Printf("[!] 连接代理失败了。重新选择节点中 %v\n", key2) 84 | cc, err = net.DialTimeout("tcp", key2, 500*time.Millisecond) 85 | if err != nil { 86 | StopProxy(key) 87 | closeConn(conn) 88 | return 89 | } 90 | } 91 | 92 | endTime := time.Now().UnixNano() 93 | Milliseconds:= int((endTime - startTime) / 1e6)// 毫秒 94 | if Milliseconds>30000{ 95 | fmt.Printf("[!] cannot connect to error2 %v\n", key2) 96 | StopProxy(key) 97 | closeConn(conn) 98 | return 99 | } 100 | go func() { 101 | err = transport(conn, cc) 102 | if err != nil { 103 | fmt.Printf("[!] connect error: %v\n", err) 104 | errConn := closeConn(conn) 105 | if errConn != nil { 106 | fmt.Printf("[!] close connect error: %v\n", errConn) 107 | } 108 | errConn = closeConn(cc) 109 | if errConn != nil { 110 | fmt.Printf("[!] close upstream connect error: %v\n", errConn) 111 | } 112 | } 113 | }() 114 | 115 | } 116 | 117 | func (c *RedirectClient) HandleConn2(conn net.Conn) { 118 | key, err := RandomProxyURL(c.config.IPRegionFlag) 119 | if err != nil { 120 | errConn := closeConn(conn) 121 | if errConn != nil { 122 | fmt.Printf("[!] close connect error: %v\n", errConn) 123 | } 124 | return 125 | } 126 | key2 := strings.TrimPrefix(key, "socks5://") 127 | fmt.Println(key) 128 | cc, err := net.DialTimeout("tcp", key2, 10*time.Second) 129 | flag:=true 130 | if err != nil { 131 | //如果超时。则踢出这个节点 132 | StopProxy(key) 133 | flag=flag 134 | fmt.Printf("[!] cannot connect to error %v\n", key2) 135 | } 136 | if !flag{ 137 | url2, err := RandomProxyURL2() 138 | if err != nil { 139 | errConn := closeConn(conn) 140 | if errConn != nil { 141 | fmt.Printf("[!] close connect error2: %v\n", errConn) 142 | } 143 | return 144 | } 145 | key2 := strings.TrimPrefix(url2, "socks5://") 146 | fmt.Println(key) 147 | cc, err := net.DialTimeout("tcp", key2, 5*time.Second) 148 | if err != nil { 149 | //如果超时。则踢出这个节点 150 | StopProxy(key) 151 | flag=flag 152 | fmt.Printf("[!] cannot connect111 to %v\n", key2) 153 | } 154 | go func() { 155 | err = transport(conn, cc) 156 | if err != nil { 157 | fmt.Printf("[!] connect error222: %v\n", err) 158 | errConn := closeConn(conn) 159 | if errConn != nil { 160 | fmt.Printf("[!] close connect error333: %v\n", errConn) 161 | } 162 | errConn = closeConn(cc) 163 | if errConn != nil { 164 | fmt.Printf("[!] close upstream connect error44: %v\n", errConn) 165 | } 166 | } 167 | }() 168 | 169 | }else{ 170 | go func() { 171 | err = transport(conn, cc) 172 | if err != nil { 173 | fmt.Printf("[!] connect error: %v\n", err) 174 | errConn := closeConn(conn) 175 | if errConn != nil { 176 | fmt.Printf("[!] close connect error: %v\n", errConn) 177 | } 178 | errConn = closeConn(cc) 179 | if errConn != nil { 180 | fmt.Printf("[!] close upstream connect error: %v\n", errConn) 181 | } 182 | } 183 | }() 184 | } 185 | } 186 | 187 | func closeConn(conn net.Conn) (err error) { 188 | defer func() { 189 | if e := recover(); e != nil { 190 | err = fmt.Errorf("%v", e) 191 | } 192 | }() 193 | err = conn.Close() 194 | return err 195 | } 196 | 197 | func transport(rw1, rw2 io.ReadWriter) error { 198 | errc := make(chan error, 1) 199 | go func() { 200 | errc <- copyBuffer(rw1, rw2) 201 | }() 202 | 203 | go func() { 204 | errc <- copyBuffer(rw2, rw1) 205 | }() 206 | 207 | err := <-errc 208 | if err != nil && err == io.EOF { 209 | err = nil 210 | } 211 | return err 212 | } 213 | 214 | func copyBuffer(dst io.Writer, src io.Reader) (err error) { 215 | defer func() { 216 | if e := recover(); e != nil { 217 | err = fmt.Errorf("%v", e) 218 | } 219 | }() 220 | buf := make([]byte, largeBufferSize) 221 | 222 | _, err = io.CopyBuffer(dst, src, buf) 223 | return err 224 | } 225 | --------------------------------------------------------------------------------