本篇文章主要介绍使用golang实现一个代理扫描工具,目前主流常用的代理有http代理,和socks5代理,当然还有什么vpn,ss这种代理,不在本文讨论范畴,因为这些是加密的。
扫描出来可以被我们使用的代理,当然是不需要密码,免费的代理了。
首先是httpProxy的实现:新建一个go文件httpproxy.go
下面的CheckProxy在后面会实现
package proxy import ( "fmt" "net/http" "net/url" "time" ) type HttpProxy struct { } func (HttpProxy) IsProxy(proxyIp string, proxyPort int) (isProxy bool, err error) { proxyUrl := fmt.Sprintf("http://%s:%d", proxyIp, proxyPort) proxy, err := url.Parse(proxyUrl) if err != nil { return false, err } netTransport := &http.Transport{ Proxy: http.ProxyURL(proxy), } client := &http.Client{ Timeout: time.Second * 20,//设置连接超时时间 Transport: netTransport, } return CheckProxy(client) }其次是SocksProxy的实现:新建socksproxy.go的文件
需要使用到http://golang.org/x/net/proxy这个包
http client访问网址的超时时间可以自行设置
package proxy import ( "context" "fmt" "golang.org/x/net/proxy" "net" "net/http" "time" ) type SocksProxy struct { } func (SocksProxy) IsProxy(proxyIp string, proxyPort int) (isProxy bool, err error) { proxyAddr := fmt.Sprintf("%s:%d", proxyIp, proxyPort) dialSocksProxy, err := proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct) if err != nil { return false, nil } netTransport := &http.Transport{DialContext: func(ctx context.Context, network, addr string) (conn net.Conn, e error) { c, e := dialSocksProxy.Dial(network, addr) return c, e }} client := &http.Client{ Timeout: time.Second * 10, Transport: netTransport, } return CheckProxy(client) }新建basic.go实现CheckProxy方法,通过访问一个url,测试一下是否可以连通,达到检测代理是否可用的目的
这里使用百度的地址,作为检测的url,如果需要的是可以fq的代理,可以选择谷歌首页地址
package proxy import ( "io/ioutil" "net/http" "strings" ) const TestUrl = "" type Proxyer interface { IsProxy(proxyIp string, proxyPort int) (isProxy bool, err error) } func CheckProxy(client *http.Client) (isProxy bool, err error){ res, err := client.Get(TestUrl) if err != nil { return false, err } else { defer res.Body.Close() if res.StatusCode == 200 { body, err := ioutil.ReadAll(res.Body) if err == nil && strings.Contains(string(body), "baidu") { return true, nil } else { return false, err } } else { return false, nil } } }新建单元测试用例来测试写好的httpproxy和socksproxy
httpproxy_test.go
package proxy import ( "github.com/elazarl/goproxy" "log" "net/http" "testing" "time" ) func TestCheckHttpProxy(t *testing.T) { go func() { proxy := goproxy.NewProxyHttpServer() proxy.Verbose = true log.Fatal(http.ListenAndServe(":8080", proxy)) }() time.Sleep(time.Second * 2) isProxy, err := HttpProxy{}.IsProxy("127.0.0.1", 8080) if !isProxy { t.Error("should be a proxy", err) } } 运行结果: === RUN TestCheckHttpProxy 2019/07/07 14:35:17 [001] INFO: Got request / http://www.baidu.com GET http://www.baidu.com/ 2019/07/07 14:35:17 [001] INFO: Sending request GET http://www.baidu.com/ 2019/07/07 14:35:17 [001] INFO: Received response 200 OK 2019/07/07 14:35:17 [001] INFO: Copying response to client 200 OK [200] 2019/07/07 14:35:17 [001] INFO: Copiedbytes to client error= --- PASS: TestCheckHttpProxy (2.15s) PASSsocksproxy_test.go
package proxy import ( "github.com/armon/go-socks5" "testing" "time" ) func TestIsSocksProxy(t *testing.T) { go func() { conf := &socks5.Config{} server, err := socks5.New(conf) if err != nil { panic(err) } if err := server.ListenAndServe("tcp", "127.0.0.1:8002"); err != nil { panic(err) } }() time.Sleep(time.Second*2) isProxy, err := SocksProxy{}.IsProxy("127.0.0.1", 8002) if !isProxy { t.Error("should be a proxy", err) } } 运行结果: === RUN TestIsSocksProxy --- PASS: TestIsSocksProxy (2.28s) PASS测试用例显示都通过了,接下来写个main函数来具体实现扫描逻辑
网上找个可以用的代理 183.30.204.91 9999,随便百度搜索的,测试也发现是可用的。那么用代理扫描工具扫一下,看一下效果
定义好ip,和扫描端口范围,8000-10000这个端口范围,并发控制在500以内,也就是最多同时500个线程在执行扫描端口。
注意这个并发数,会涉及到打开的文件数,因为需要不断发起tcp连接,在unix哲学上,一切皆文件。
所以在mac上或者在linux上执行扫描的时候,需要修改文件打开数的限制,可以使用ulimit命令修改当前用户可以打开的文件数。
使用一个map来存放我们扫出来的代理,最后记录下扫描用时。
package main import ( "fmt" "sync" "time" ) import "scanproxy/proxy" var ( Threads = make(chan int, 500) //控制在500以内 ) func main() { var start = 8000 var end = 10000 var proxyIp = "183.30.204.91" var now = time.Now().Unix() var Map = make(map[int]bool) var waitGroup = sync.WaitGroup{} for port := start; port < end; port++ { Threads <- 1 go func(port int) { waitGroup.Add(1) defer waitGroup.Add(-1) isProxy, err := proxy.HttpProxy{}.IsProxy(proxyIp, port) if isProxy { fmt.Printf("%s:%d\n", proxyIp, port) Map[port] = true } if err != nil { fmt.Println(err) } <-Threads }(port) } waitGroup.Wait() fmt.Printf("用时%d秒\n",time.Now().Unix()-now) fmt.Println(Map) }结果如下:
183.30.204.91:9999 Get http://www.baidu.com: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers) Get http://www.baidu.com: net/http: request canceled (Client.Timeout exceeded while awaiting headers) 用时20秒 map[9999:true]最后发现扫描出9999是代理,为什么用时是20秒呢?
这是因为在http_proxy.go里面我设置的超时时间是20秒
client := &http.Client{ Timeout: time.Second * 20, Transport: netTransport, }修改成5秒后,可以看到效果,用时5秒。
可以感受到go的并发是真的快。
183.30.204.91:9999 Get http://www.baidu.com: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers) Get http://www.baidu.com: net/http: request canceled (Client.Timeout exceeded while awaiting headers) 用时5秒 map[9999:true]