golang实现http,socks5代理扫描工具
本篇文章主要介绍使用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的文件
需要使用到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 = "http://www.baidu.com"
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 / 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: Copied 153978 bytes to client error= — PASS: TestCheckHttpProxy (2.15s) PASS
socksproxy_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]
- 原文作者:iamdev
- 原文链接:https://blog.iamdev.cn/post/golang-proxy/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止转载 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。