support socks5 proxy
This commit is contained in:
parent
7cb9f91de0
commit
253b18ddf2
64
README.md
64
README.md
@ -4,20 +4,24 @@ gost - GO Simple Tunnel
|
|||||||
### GO语言实现的安全隧道
|
### GO语言实现的安全隧道
|
||||||
|
|
||||||
#### 特性
|
#### 特性
|
||||||
1. 支持设置上层http代理。
|
1. 支持设置上层代理(客户端,服务器端均可)。
|
||||||
2. 客户端可用作http(s), socks5代理。
|
2. 客户端可用作http(s), socks5代理。
|
||||||
3. 服务器端兼容标准的socks5协议, 可直接用作socks5代理, 并额外增加协商加密功能。
|
3. 服务器端兼容标准的socks5协议, 可直接用作socks5代理, 并额外增加协商加密功能。
|
||||||
4. Tunnel UDP over TCP, UDP数据包使用TCP通道传输,以解决防火墙的限制。
|
4. Tunnel UDP over TCP, UDP数据包使用TCP通道传输,以解决防火墙的限制。
|
||||||
5. 多种加密方式(tls,aes-256-cfb,des-cfb,rc4-md5等)。
|
5. 多种加密方式(tls,aes-256-cfb,des-cfb,rc4-md5等)。
|
||||||
6. 客户端兼容shadowsocks协议,可作为shadowsocks服务器。
|
6. 客户端兼容shadowsocks协议,可作为shadowsocks服务器。
|
||||||
|
|
||||||
二进制文件下载:https://bintray.com/ginuerzh/gost/gost/v1.3/view
|
二进制文件下载:https://bintray.com/ginuerzh/gost/gost/view
|
||||||
|
|
||||||
Google讨论组: https://groups.google.com/forum/#!forum/go-gost
|
Google讨论组: https://groups.google.com/d/forum/go-gost
|
||||||
|
|
||||||
#### 版本更新
|
#### 版本更新
|
||||||
|
##### v1.5
|
||||||
|
* 支持设置上层socks5代理(注: http tunnel不支持)
|
||||||
|
* 支持上层代理用户名密码验证
|
||||||
|
|
||||||
##### V1.4
|
##### V1.4
|
||||||
* 支持http tunnel (-http参数),使用http协议来传输数据(注:效率低,非特殊情况下,不推荐使用)。
|
* 支持http tunnel(-http参数),使用http协议来传输数据(注: 效率低,非特殊情况下,不推荐使用)。
|
||||||
|
|
||||||
##### v1.3
|
##### v1.3
|
||||||
* tls加密方式增加密码认证功能(与旧版本不兼容)
|
* tls加密方式增加密码认证功能(与旧版本不兼容)
|
||||||
@ -59,42 +63,34 @@ Google讨论组: https://groups.google.com/forum/#!forum/go-gost
|
|||||||
|
|
||||||
|
|
||||||
#### 使用方法
|
#### 使用方法
|
||||||
#####服务器端:
|
##### 基本用法
|
||||||
`gost -L=:8080`
|
* 客户端: `gost -L=:8899 -S=server_ip:8080`
|
||||||
|
* 服务器: `gost -L=:8080`
|
||||||
|
|
||||||
#####服务器端设置加密:
|
##### 设置加密
|
||||||
`gost -L=:8080 -m=rc4-md5 -p=123456`
|
* 客户端: `gost -L=:8899 -S=server_ip:8080 -m=rc4-md5 -p=123456`
|
||||||
|
* 服务器: `gost -L=:8080 -m=rc4-md5 -p=123456`
|
||||||
|
|
||||||
#####服务器端有上层http代理:
|
##### 设置上层代理
|
||||||
`gost -L=:8080 -m=rc4-md5 -p=123456 -P=proxy_ip:port`
|
* http代理: `gost -L=:8899 -P=http://127.0.0.1:8080`
|
||||||
|
* http代理(需认证): `gost -L=:8899 -P=http://admin:123456@127.0.0.1:8080`
|
||||||
#####客户端:
|
* socks5代理: `gost -L=:8899 -P=socks://127.0.0.1:1080`
|
||||||
`gost -L=:8899 -S=your_server_ip:8080`
|
* socks5代理(需认证): `gost -L=:8899 -P=socks://admin:123456@127.0.0.1:1080`
|
||||||
|
|
||||||
#####客户端设置加密:
|
|
||||||
`gost -L=:8899 -S=your_server_ip:8080 -m=rc4-md5 -p=123456`
|
|
||||||
|
|
||||||
#####客户端有上层http代理:
|
|
||||||
`gost -L=:8899 -S=your_server_ip:8080 -m=rc4-md5 -p=123456 -P=proxy_ip:port`
|
|
||||||
|
|
||||||
##### 使用websocket tunnel
|
##### 使用websocket tunnel
|
||||||
* 服务器端
|
* 客户端: `gost -L=:8899 -S=server_ip:8080 -ws`
|
||||||
`gost -L=:8080 -m=aes-256-cfb -p=123456 -ws`
|
* 服务器: `gost -L=:8080 -ws`
|
||||||
* 客户端
|
|
||||||
`gost -L=:8899 -S=your_server_ip:8080 -m=rc4-md5 -p=123456 -ws`
|
|
||||||
|
|
||||||
##### 使用http tunnel
|
##### 使用http tunnel
|
||||||
* 服务器端
|
* 客户端: `gost -L=:8899 -S=server_ip:8080 -http`
|
||||||
`gost -L=:8080 -m=rc4-md5 -p=123456 -http`
|
* 服务器: `gost -L=:8080 -http`
|
||||||
* 客户端
|
|
||||||
`gost -L=:8899 -S=your_server_ip:8080 -m=rc4-md5 -p=123456 -http`
|
|
||||||
|
|
||||||
注:websocket方式优先级高于http方式,即当-ws与-http参数同时存在时,-http参数默认无效。
|
注:websocket方式优先级高于http方式,即当-ws与-http参数同时存在时,-http参数无效。
|
||||||
|
|
||||||
#####作为shadowsocks服务器:
|
##### 作为shadowsocks服务器
|
||||||
gost支持作为shadowsocks服务器运行(-ss参数),这样就可以让android手机通过shadowsocks客户端(影梭)使用代理了。
|
gost支持作为shadowsocks服务器运行(-ss参数),这样就可以让android手机通过shadowsocks客户端(影梭)使用代理了。
|
||||||
|
|
||||||
######相关参数:
|
###### 相关参数
|
||||||
> -ss 开启shadowsocks模式
|
> -ss 开启shadowsocks模式
|
||||||
|
|
||||||
> -sm 设置shadowsocks加密方式(默认为rc4-md5)
|
> -sm 设置shadowsocks加密方式(默认为rc4-md5)
|
||||||
@ -103,11 +99,11 @@ gost支持作为shadowsocks服务器运行(-ss参数),这样就可以让androi
|
|||||||
|
|
||||||
当无-ss参数时,-sm, -sp参数无效。以上三个参数对服务端无效。
|
当无-ss参数时,-sm, -sp参数无效。以上三个参数对服务端无效。
|
||||||
|
|
||||||
######相关命令:
|
###### 相关命令
|
||||||
* 服务端:无需特殊设置,shadowsocks模式只与客户端有关,与服务端无关。
|
* 客户端: `gost -L :8899 -S server_ip:port -sm=rc4-md5 -sp=ginuerzh@gmail.com -ss`
|
||||||
* 客户端:`gost -L :8899 -S demo-project-gostwebsocket.c9.io -sm=rc4-md5 -sp=ginuerzh@gmail.com -ss`
|
* 服务器: 无需特殊设置,shadowsocks模式只与客户端有关,与服务端无关。
|
||||||
|
|
||||||
在手机的shadowsocks软件中设置好服务器(运行gost电脑的IP),端口(8899),加密方法和密码就可以使用了。
|
在手机的shadowsocks软件中设置好服务器IP(运行gost客户端电脑的IP),端口(8899),加密方法和密码就可以使用了。
|
||||||
|
|
||||||
注:shadowsocks模式与正常模式是不兼容的,当作为shadowsocks模式使用时(有-ss参数),浏览器不能使用。
|
注:shadowsocks模式与正常模式是不兼容的,当作为shadowsocks模式使用时(有-ss参数),浏览器不能使用。
|
||||||
|
|
||||||
|
@ -108,13 +108,13 @@ func cliHandle(conn net.Conn) {
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
if UseWebsocket || !UseHttp {
|
if UseWebsocket || !UseHttp {
|
||||||
c, err = ConnectProxy(Saddr, Proxy)
|
c, err = connect(Saddr)
|
||||||
} else {
|
} else {
|
||||||
addr := Saddr
|
addr := Saddr
|
||||||
if len(Proxy) > 0 {
|
if proxyURL != nil {
|
||||||
addr = Proxy
|
addr = proxyURL.Host
|
||||||
}
|
}
|
||||||
c, err = Connect(addr)
|
c, err = dial(addr)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
17
http.go
17
http.go
@ -41,10 +41,12 @@ func (conn *HttpClientConn) Handshake() (err error) {
|
|||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Path: s2cUri,
|
Path: s2cUri,
|
||||||
},
|
},
|
||||||
|
Header: make(http.Header),
|
||||||
}
|
}
|
||||||
if len(Proxy) == 0 {
|
if proxyURL == nil {
|
||||||
err = req.Write(conn.c)
|
err = req.Write(conn.c)
|
||||||
} else {
|
} else {
|
||||||
|
setBasicAuth(req)
|
||||||
err = req.WriteProxy(conn.c)
|
err = req.WriteProxy(conn.c)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -55,6 +57,9 @@ func (conn *HttpClientConn) Handshake() (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return errors.New(resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
b := make([]byte, 36)
|
b := make([]byte, 36)
|
||||||
if _, err = io.ReadFull(resp.Body, b); err != nil {
|
if _, err = io.ReadFull(resp.Body, b); err != nil {
|
||||||
@ -89,8 +94,9 @@ func (conn *HttpClientConn) Write(b []byte) (n int, err error) {
|
|||||||
Path: c2sUri,
|
Path: c2sUri,
|
||||||
RawQuery: q.Encode(),
|
RawQuery: q.Encode(),
|
||||||
},
|
},
|
||||||
|
Header: make(http.Header),
|
||||||
}
|
}
|
||||||
resp, err := doRequest(req, Proxy)
|
resp, err := doRequest(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return
|
return
|
||||||
@ -251,15 +257,16 @@ func (s *HttpServer) ListenAndServe() error {
|
|||||||
return http.ListenAndServe(s.Addr, nil)
|
return http.ListenAndServe(s.Addr, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func doRequest(req *http.Request, proxy string) (*http.Response, error) {
|
func doRequest(req *http.Request) (*http.Response, error) {
|
||||||
if len(proxy) > 0 {
|
if proxyURL != nil {
|
||||||
c, err := Connect(proxy)
|
c, err := dial(proxyURL.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
|
setBasicAuth(req)
|
||||||
if err := req.WriteProxy(c); err != nil {
|
if err := req.WriteProxy(c); err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
14
main.go
14
main.go
@ -5,6 +5,8 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
//"github.com/ginuerzh/gosocks5"
|
//"github.com/ginuerzh/gosocks5"
|
||||||
"log"
|
"log"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,6 +18,8 @@ var (
|
|||||||
Method, Password string
|
Method, Password string
|
||||||
CertFile, KeyFile string
|
CertFile, KeyFile string
|
||||||
PrintVersion bool
|
PrintVersion bool
|
||||||
|
|
||||||
|
proxyURL *url.URL
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -35,6 +39,8 @@ func init() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||||
|
|
||||||
|
proxyURL, _ = parseURL(Proxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -64,3 +70,11 @@ func main() {
|
|||||||
|
|
||||||
log.Fatal(listenAndServe(Laddr, cliHandle))
|
log.Fatal(listenAndServe(Laddr, cliHandle))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseURL(rawurl string) (*url.URL, error) {
|
||||||
|
if !strings.HasPrefix(rawurl, "http://") &&
|
||||||
|
!strings.HasPrefix(rawurl, "socks://") {
|
||||||
|
rawurl = "http://" + rawurl
|
||||||
|
}
|
||||||
|
return url.Parse(rawurl)
|
||||||
|
}
|
||||||
|
@ -176,8 +176,9 @@ func socks5Handle(conn net.Conn) {
|
|||||||
switch req.Cmd {
|
switch req.Cmd {
|
||||||
case gosocks5.CmdConnect:
|
case gosocks5.CmdConnect:
|
||||||
//log.Println("connect", req.Addr.String())
|
//log.Println("connect", req.Addr.String())
|
||||||
tconn, err := ConnectProxy(req.Addr.String(), Proxy)
|
tconn, err := connect(req.Addr.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
gosocks5.NewReply(gosocks5.HostUnreachable, nil).Write(conn)
|
gosocks5.NewReply(gosocks5.HostUnreachable, nil).Write(conn)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
141
util.go
141
util.go
@ -3,10 +3,11 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
//"bytes"
|
//"bytes"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/ginuerzh/gosocks5"
|
"github.com/ginuerzh/gosocks5"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
//"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -52,59 +53,145 @@ func ToSocksAddr(addr net.Addr) *gosocks5.Addr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Connect(addr string) (net.Conn, error) {
|
func dial(addr string) (net.Conn, error) {
|
||||||
taddr, err := net.ResolveTCPAddr("tcp", addr)
|
taddr, err := net.ResolveTCPAddr("tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return net.DialTCP("tcp", nil, taddr)
|
return net.DialTCP("tcp", nil, taddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConnectProxy(addr, proxy string) (net.Conn, error) {
|
func connect(addr string) (net.Conn, error) {
|
||||||
if !strings.Contains(addr, ":") {
|
if !strings.Contains(addr, ":") {
|
||||||
addr += ":80"
|
addr += ":80"
|
||||||
}
|
}
|
||||||
if len(proxy) == 0 {
|
if proxyURL == nil {
|
||||||
return Connect(addr)
|
return dial(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
paddr, err := net.ResolveTCPAddr("tcp", proxy)
|
switch proxyURL.Scheme {
|
||||||
if err != nil {
|
case "socks": // socks5 proxy
|
||||||
return nil, err
|
return connectSocks5Proxy(addr)
|
||||||
}
|
case "http": // http proxy
|
||||||
pconn, err := net.DialTCP("tcp", nil, paddr)
|
fallthrough
|
||||||
if err != nil {
|
default:
|
||||||
log.Println(err)
|
return connectHTTPProxy(addr)
|
||||||
return nil, err
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectHTTPProxy(addr string) (conn net.Conn, err error) {
|
||||||
|
conn, err = dial(proxyURL.Host)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
header := http.Header{}
|
|
||||||
header.Set("Proxy-Connection", "keep-alive")
|
|
||||||
req := &http.Request{
|
req := &http.Request{
|
||||||
Method: "CONNECT",
|
Method: "CONNECT",
|
||||||
URL: &url.URL{Host: addr},
|
URL: &url.URL{Host: addr},
|
||||||
Host: addr,
|
Host: addr,
|
||||||
Header: header,
|
Header: make(http.Header),
|
||||||
}
|
}
|
||||||
if err := req.Write(pconn); err != nil {
|
req.Header.Set("Proxy-Connection", "keep-alive")
|
||||||
log.Println(err)
|
setBasicAuth(req)
|
||||||
pconn.Close()
|
|
||||||
return nil, err
|
if err = req.Write(conn); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := http.ReadResponse(bufio.NewReader(pconn), req)
|
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
conn.Close()
|
||||||
pconn.Close()
|
return
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
pconn.Close()
|
conn.Close()
|
||||||
|
//log.Println(resp.Status)
|
||||||
return nil, errors.New(resp.Status)
|
return nil, errors.New(resp.Status)
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return pconn, nil
|
func connectSocks5Proxy(addr string) (conn net.Conn, err error) {
|
||||||
|
conn, err = dial(proxyURL.Host)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conf := &gosocks5.Config{
|
||||||
|
Methods: []uint8{gosocks5.MethodNoAuth, gosocks5.MethodUserPass},
|
||||||
|
MethodSelected: proxyMethodSelected,
|
||||||
|
}
|
||||||
|
|
||||||
|
c := gosocks5.ClientConn(conn, conf)
|
||||||
|
if err := c.Handleshake(); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conn = c
|
||||||
|
|
||||||
|
s := strings.Split(addr, ":")
|
||||||
|
host := s[0]
|
||||||
|
port := 80
|
||||||
|
if len(s) == 2 {
|
||||||
|
n, _ := strconv.ParseUint(s[1], 10, 16)
|
||||||
|
port = int(n)
|
||||||
|
}
|
||||||
|
a := &gosocks5.Addr{
|
||||||
|
Type: gosocks5.AddrDomain,
|
||||||
|
Host: host,
|
||||||
|
Port: uint16(port),
|
||||||
|
}
|
||||||
|
if err := gosocks5.NewRequest(gosocks5.CmdConnect, a).Write(conn); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rep, err := gosocks5.ReadReply(conn)
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if rep.Rep != gosocks5.Succeeded {
|
||||||
|
conn.Close()
|
||||||
|
return nil, errors.New("Socks Failture")
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func proxyMethodSelected(method uint8, conn net.Conn) (net.Conn, error) {
|
||||||
|
switch method {
|
||||||
|
case gosocks5.MethodUserPass:
|
||||||
|
if proxyURL == nil || proxyURL.User == nil {
|
||||||
|
return nil, gosocks5.ErrAuthFailure
|
||||||
|
}
|
||||||
|
pwd, _ := proxyURL.User.Password()
|
||||||
|
if err := gosocks5.NewUserPassRequest(gosocks5.UserPassVer,
|
||||||
|
proxyURL.User.Username(), pwd).Write(conn); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := gosocks5.ReadUserPassResponse(conn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resp.Status != gosocks5.Succeeded {
|
||||||
|
return nil, gosocks5.ErrAuthFailure
|
||||||
|
}
|
||||||
|
case gosocks5.MethodNoAcceptable:
|
||||||
|
return nil, gosocks5.ErrBadMethod
|
||||||
|
|
||||||
|
//case gosocks5.MethodNoAuth:
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setBasicAuth(r *http.Request) {
|
||||||
|
if proxyURL != nil && proxyURL.User != nil {
|
||||||
|
r.Header.Set("Proxy-Authorization",
|
||||||
|
"Basic "+base64.StdEncoding.EncodeToString([]byte(proxyURL.User.String())))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// based on io.Copy
|
// based on io.Copy
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "1.4"
|
Version = "1.5"
|
||||||
)
|
)
|
||||||
|
|
||||||
func printVersion() {
|
func printVersion() {
|
||||||
|
Loading…
Reference in New Issue
Block a user