mirror of https://github.com/ginuerzh/gost
16 changed files with 864 additions and 1796 deletions
@ -1,22 +0,0 @@ |
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2014 郑锐 |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
|
|||
@ -1,172 +0,0 @@ |
|||
gost - GO Simple Tunnel |
|||
==== |
|||
|
|||
### GO语言实现的安全隧道 |
|||
|
|||
#### 特性 |
|||
1. 支持设置上层代理(客户端,服务器端均可),支持上层代理认证。 |
|||
2. 客户端可用作http(s), socks5代理。 |
|||
3. 服务器端兼容标准的socks5协议, 可直接用作socks5代理, 并额外增加协商加密功能。 |
|||
4. Tunnel UDP over TCP, UDP数据包使用TCP通道传输,以解决防火墙的限制。 |
|||
5. 多种加密方式(tls,aes-256-cfb,des-cfb,rc4-md5等)。 |
|||
6. 客户端兼容shadowsocks协议,可作为shadowsocks服务器。 |
|||
|
|||
二进制文件下载:https://github.com/ginuerzh/gost/releases |
|||
|
|||
Google讨论组: https://groups.google.com/d/forum/go-gost |
|||
|
|||
#### 版本更新 |
|||
|
|||
##### v1.8 |
|||
* 支持tls tunnel(-tls参数),直接使用tls进行加密传输 |
|||
|
|||
##### v1.7 |
|||
* 支持认证功能,当作为http(s)代理时使用Basic Auth认证方式,当作为标准socks5代理时使用Username/Password认证方式 |
|||
|
|||
###### Bug fix: |
|||
* 修正当作为http代理时,POST请求出错问题 |
|||
|
|||
##### v1.6 |
|||
* 增加tls-auth加密方式,此方式必须设置认证密码(-p参数),原tls加密方式与v1.3版以前兼容 |
|||
|
|||
###### Bug fix: |
|||
* 修正当不设置上层代理时,连接出错问题 |
|||
|
|||
##### v1.5 |
|||
* 支持设置上层socks5代理(注: http tunnel不支持) |
|||
* 支持上层代理认证 |
|||
|
|||
##### V1.4 |
|||
* 支持http tunnel(-http参数),使用http协议来传输数据(注: 效率低,非特殊情况下,不推荐使用)。 |
|||
|
|||
##### v1.3 |
|||
* tls加密方式增加密码认证功能(与旧版本不兼容) |
|||
* 增加版本查看(-v参数) |
|||
* -p参数的默认值修改为空 |
|||
|
|||
##### v1.2 |
|||
* websocket tunnel增加加密功能。 |
|||
|
|||
##### v1.1 |
|||
* 支持websocket tunnel(-ws参数),使用websocket协议来传输数据。 |
|||
|
|||
#### 参数说明 |
|||
> -L=":8080": listen address |
|||
|
|||
> -P="": proxy for forward |
|||
|
|||
> -S="": the server that connect to |
|||
|
|||
> -cert="": tls cert file |
|||
|
|||
> -key="": tls key file |
|||
|
|||
> -m="": tunnel cipher method |
|||
|
|||
> -p="": tunnel cipher password |
|||
|
|||
> -sm="rc4-md5": shadowsocks cipher method |
|||
|
|||
> -sp="[email protected]": shadowsocks cipher password |
|||
|
|||
> -ss=false: run as shadowsocks server |
|||
|
|||
> -tls=false: use ssl/tls tunnel |
|||
|
|||
> -ws=false: use websocket tunnel |
|||
|
|||
> -http=false: use http tunnel |
|||
|
|||
> -v=false: print version |
|||
|
|||
|
|||
#### 使用方法 |
|||
##### 基本用法 |
|||
* 客户端: `gost -L=:8899 -S=server_ip:8080` |
|||
* 服务器: `gost -L=:8080` |
|||
|
|||
##### 设置认证信息 |
|||
* 客户端: `gost -L=admin:123456@:8899 -S=server_ip:8080` |
|||
* 服务器: `gost -L=admin:123456@:8080` |
|||
|
|||
注:当服务器端设置了认证,默认的无加密模式(-m为空)不可用, |
|||
即客户端或者使用认证方式(标准socks5模式),或者设置加密方式(gost兼容模式)。 |
|||
|
|||
##### 设置加密 |
|||
* 客户端: `gost -L=:8899 -S=server_ip:8080 -m=rc4-md5 -p=123456` |
|||
* 服务器: `gost -L=:8080 -m=rc4-md5 -p=123456` |
|||
|
|||
##### 设置上层代理 |
|||
* http代理: `gost -L=:8899 -P=http://127.0.0.1:8080` |
|||
* http代理(需认证): `gost -L=:8899 -P=http://admin:[email protected]:8080` |
|||
* socks5代理: `gost -L=:8899 -P=socks://127.0.0.1:1080` |
|||
* socks5代理(需认证): `gost -L=:8899 -P=socks://admin:[email protected]:1080` |
|||
|
|||
##### 使用tls tunnel (推荐) |
|||
* 客户端: `gost -L=:8899 -S=server_ip:8080 -tls` |
|||
* 服务器: `gost -L=:8080 -tls` |
|||
|
|||
注: 可通过-key, -cert参数手动指定自己的公钥与私钥文件。 |
|||
|
|||
##### 使用websocket tunnel |
|||
* 客户端: `gost -L=:8899 -S=server_ip:8080 -ws` |
|||
* 服务器: `gost -L=:8080 -ws` |
|||
|
|||
##### 使用http tunnel |
|||
* 客户端: `gost -L=:8899 -S=server_ip:8080 -http` |
|||
* 服务器: `gost -L=:8080 -http` |
|||
|
|||
注:websocket方式优先级高于http方式,即当-ws与-http参数同时存在时,-http参数无效。 |
|||
|
|||
##### 作为shadowsocks服务器 |
|||
gost支持作为shadowsocks服务器运行(-ss参数),这样就可以让android手机通过shadowsocks客户端(影梭)使用代理了。 |
|||
|
|||
###### 相关参数 |
|||
> -ss 开启shadowsocks模式 |
|||
|
|||
> -sm 设置shadowsocks加密方式(默认为rc4-md5) |
|||
|
|||
> -sp 设置shadowsocks加密密码(默认为[email protected]) |
|||
|
|||
当无-ss参数时,-sm, -sp参数无效。以上三个参数对服务端无效。 |
|||
|
|||
###### 相关命令 |
|||
* 客户端: `gost -L :8899 -S server_ip:port -sm=rc4-md5 [email protected] -ss` |
|||
* 服务器: 无需特殊设置,shadowsocks模式只与客户端有关,与服务端无关。 |
|||
|
|||
在手机的shadowsocks软件中设置好服务器IP(运行gost客户端电脑的IP),端口(8899),加密方法和密码就可以使用了。 |
|||
|
|||
注:shadowsocks模式与正常模式是不兼容的,当作为shadowsocks模式使用时(有-ss参数),浏览器不能使用。 |
|||
|
|||
|
|||
#### tunnel加密说明 |
|||
##### 目前支持的加密方法 |
|||
tls, tls-auth, aes-128-cfb, aes-192-cfb, aes-256-cfb, des-cfb, bf-cfb, cast5-cfb, rc4-md5, rc4, table |
|||
|
|||
##### Client |
|||
|
|||
Client端通过-m参数设置加密方式,默认为不加密(-m参数为空)。 |
|||
|
|||
如果设置的加密方式不被支持,则默认为不加密。 |
|||
|
|||
当设置的加密方式为tls时,-p参数无效。 |
|||
|
|||
当设置的加密方式为非tls时,通过-p参数设置加密密码,且不能为空;-p参数必须与Server端的-p参数相同。 |
|||
|
|||
##### Server |
|||
|
|||
Server端通过-m参数设置加密方式,默认为不加密(-m参数为空)。 |
|||
|
|||
如果设置的加密方式不被支持,默认为不处理。 |
|||
|
|||
如果没有设置加密方式(-m参数为空),则由client端控制加密方式,即client端可通过-m参数指定Server端使用哪种加密方式。 |
|||
|
|||
如果设置了加密方式(-m参数不为空),client端必须使用与Server端相同的加密方式。 |
|||
|
|||
当设置的加密方式为tls,tls-auth时,-key参数可手动指定公钥文件,-cert参数可手动指定私钥文件,如果未指定,则使用默认的公钥与私钥。 |
|||
|
|||
当设置的加密方式为tls时,-p参数无效;为tls-auth时,通过-p参数设置认证密码,且不能为空。 |
|||
|
|||
当设置的加密方式为非tls,tls-auth时,-key,-cert参数无效;通过-p参数设置加密密码,且不能为空。 |
|||
|
|||
|
|||
@ -0,0 +1,18 @@ |
|||
-----BEGIN CERTIFICATE----- |
|||
MIIC5jCCAdCgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD |
|||
bzAeFw0xNDAzMTcwNjIwNTFaFw0xNTAzMTcwNjIwNTFaMBIxEDAOBgNVBAoTB0Fj |
|||
bWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDccNO1xmd4lWSf |
|||
d/0/QS3E93cYIWHw831i/IKxigdRD/XMZonLdEHywW6lOiXazaP8e6CqPGSmnl0x |
|||
5k/3dvGCMj2JCVxM6+z7NpL+AiwvXmvkj/TOciCgwqssCwYS2CiVwjfazRjx1ZUJ |
|||
VDC5qiyRsfktQ2fVHrpnJGVSRagmiQgwGWBilVG9B8QvRtpQKN/GQGq17oIQm8aK |
|||
kOdPt93g93ojMIg7YJpgDgOirvVz/hDn7YD4ryrtPos9CMafFkJprymKpRHyvz7P |
|||
8a3+OkuPjFjPnwOHQ5u1U3+8vC44vfb1ExWzDLoT8Xp8Gndx39k0f7MVOol3GnYu |
|||
MN/dvNUdAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIAoDATBgNVHSUEDDAKBggrBgEF |
|||
BQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDALBgkqhkiG |
|||
9w0BAQUDggEBAIG8CJqvTIgJnNOK+i5/IUc/3yF/mSCWuG8qP+Fmo2t6T0PVOtc0 |
|||
8wiWH5iWtCAhjn0MRY9l/hIjWm6gUZGHCGuEgsOPpJDYGoNLjH9Xwokm4y3LFNRK |
|||
UBrrrDbKRNibApBHCapPf6gC5sXcjOwx7P2/kiHDgY7YH47jfcRhtAPNsM4gjsEO |
|||
RmwENY+hRUFHIRfQTyalqND+x6PWhRo3K6hpHs4DQEYPq4P2kFPqUqSBymH+Ny5/ |
|||
BcQ3wdMNmC6Bm/oiL1QV0M+/InOsAgQk/EDd0kmoU1ZT2lYHQduGmP099bOlHNpS |
|||
uqO3vXF3q8SPPr/A9TqSs7BKkBQbe0+cdsA= |
|||
-----END CERTIFICATE----- |
|||
@ -1,563 +0,0 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"bufio" |
|||
"bytes" |
|||
"crypto/tls" |
|||
"encoding/base64" |
|||
"encoding/binary" |
|||
//"encoding/hex"
|
|||
"errors" |
|||
"fmt" |
|||
"github.com/ginuerzh/gosocks5" |
|||
"github.com/gorilla/websocket" |
|||
"github.com/shadowsocks/shadowsocks-go/shadowsocks" |
|||
"io" |
|||
"io/ioutil" |
|||
"log" |
|||
"net" |
|||
"net/http" |
|||
"net/url" |
|||
"strconv" |
|||
"strings" |
|||
//"sync/atomic"
|
|||
) |
|||
|
|||
var ( |
|||
sessionCount int64 |
|||
clientConfig = &gosocks5.Config{ |
|||
MethodSelected: clientMethodSelected, |
|||
} |
|||
) |
|||
|
|||
func listenAndServe(addr string, handler func(net.Conn)) error { |
|||
laddr, err := net.ResolveTCPAddr("tcp", addr) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
ln, err := net.ListenTCP("tcp", laddr) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
defer ln.Close() |
|||
|
|||
for m, v := range Methods { |
|||
if Method == v { |
|||
clientConfig.Methods = []uint8{m} |
|||
} |
|||
} |
|||
|
|||
for { |
|||
conn, err := ln.AcceptTCP() |
|||
if err != nil { |
|||
log.Println("accept:", err) |
|||
continue |
|||
} |
|||
//log.Println("accept", conn.RemoteAddr())
|
|||
go handler(conn) |
|||
} |
|||
} |
|||
|
|||
func clientMethodSelected(method uint8, conn net.Conn) (net.Conn, error) { |
|||
switch method { |
|||
case gosocks5.MethodUserPass: |
|||
user, pass := parseUserPass(Password) |
|||
if err := clientSocksAuth(conn, user, pass); err != nil { |
|||
return nil, err |
|||
} |
|||
case MethodTLS, MethodTLSAuth: |
|||
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true}) |
|||
if method == MethodTLSAuth { |
|||
if len(Password) == 0 { |
|||
return nil, ErrEmptyAuth |
|||
} |
|||
if err := clientSocksAuth(conn, "", Password); err != nil { |
|||
return nil, err |
|||
} |
|||
} |
|||
case MethodAES128, MethodAES192, MethodAES256, |
|||
MethodDES, MethodBF, MethodCAST5, MethodRC4MD5, MethodRC4, MethodTable: |
|||
cipher, err := shadowsocks.NewCipher(Methods[method], Password) |
|||
if err != nil { |
|||
log.Println(err) |
|||
return nil, err |
|||
} |
|||
conn = shadowsocks.NewConn(conn, cipher) |
|||
case gosocks5.MethodNoAcceptable: |
|||
return nil, gosocks5.ErrBadMethod |
|||
} |
|||
|
|||
return conn, nil |
|||
} |
|||
|
|||
func makeTunnel() (c net.Conn, err error) { |
|||
if UseTLS || UseWebsocket || !UseHttp { |
|||
c, err = connect(Saddr) |
|||
} else { |
|||
addr := Saddr |
|||
if proxyURL != nil { |
|||
addr = proxyURL.Host |
|||
} |
|||
c, err = dial(addr) |
|||
} |
|||
if err != nil { |
|||
return |
|||
} |
|||
|
|||
if UseTLS { |
|||
config := &tls.Config{InsecureSkipVerify: true} |
|||
c = tls.Client(c, config) |
|||
} else if UseWebsocket { |
|||
ws, resp, err := websocket.NewClient(c, &url.URL{Host: Saddr}, nil, 8192, 8192) |
|||
if err != nil { |
|||
c.Close() |
|||
return nil, err |
|||
} |
|||
resp.Body.Close() |
|||
|
|||
c = NewWSConn(ws) |
|||
} else if UseHttp { |
|||
httpcli := NewHttpClientConn(c) |
|||
if err = httpcli.Handshake(); err != nil { |
|||
c.Close() |
|||
return nil, err |
|||
} |
|||
c = httpcli |
|||
//defer httpcli.Close()
|
|||
} |
|||
|
|||
sc := gosocks5.ClientConn(c, clientConfig) |
|||
if err = sc.Handleshake(); err != nil { |
|||
c.Close() |
|||
return nil, err |
|||
} |
|||
c = sc |
|||
|
|||
return |
|||
} |
|||
|
|||
func cliHandle(conn net.Conn) { |
|||
defer conn.Close() |
|||
|
|||
if Shadows { |
|||
cipher, _ := shadowsocks.NewCipher(SMethod, SPassword) |
|||
conn = shadowsocks.NewConn(conn, cipher) |
|||
handleShadow(conn) |
|||
return |
|||
} |
|||
|
|||
b := mpool.Take() |
|||
defer mpool.put(b) |
|||
|
|||
n, err := io.ReadAtLeast(conn, b, 2) |
|||
if err != nil { |
|||
return |
|||
} |
|||
|
|||
if b[0] == gosocks5.Ver5 { |
|||
mn := int(b[1]) // methods count
|
|||
length := 2 + mn |
|||
if n < length { |
|||
if _, err := io.ReadFull(conn, b[n:length]); err != nil { |
|||
return |
|||
} |
|||
} |
|||
|
|||
methods := b[2 : 2+mn] |
|||
handleSocks5(conn, methods) |
|||
return |
|||
} |
|||
|
|||
req, err := http.ReadRequest(bufio.NewReader(newReqReader(b[:n], conn))) |
|||
if err != nil { |
|||
//log.Println(hex.Dump(b[:n]))
|
|||
log.Println(err) |
|||
return |
|||
} |
|||
handleHttp(req, conn) |
|||
} |
|||
|
|||
func selectMethod(conn net.Conn, methods ...uint8) error { |
|||
m := gosocks5.MethodNoAuth |
|||
|
|||
if listenUrl.User != nil { |
|||
for _, method := range methods { |
|||
if method == gosocks5.MethodUserPass { |
|||
m = method |
|||
break |
|||
} |
|||
} |
|||
if m != gosocks5.MethodUserPass { |
|||
m = gosocks5.MethodNoAcceptable |
|||
} |
|||
} |
|||
if err := gosocks5.WriteMethod(m, conn); err != nil { |
|||
return err |
|||
} |
|||
|
|||
//log.Println(m)
|
|||
|
|||
switch m { |
|||
case gosocks5.MethodUserPass: |
|||
var username, password string |
|||
|
|||
if listenUrl != nil && listenUrl.User != nil { |
|||
username = listenUrl.User.Username() |
|||
password, _ = listenUrl.User.Password() |
|||
} |
|||
|
|||
if err := serverSocksAuth(conn, username, password); err != nil { |
|||
return err |
|||
} |
|||
case gosocks5.MethodNoAcceptable: |
|||
return gosocks5.ErrBadMethod |
|||
} |
|||
|
|||
return nil |
|||
} |
|||
|
|||
func handleSocks5(conn net.Conn, methods []uint8) { |
|||
if err := selectMethod(conn, methods...); err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
|
|||
req, err := gosocks5.ReadRequest(conn) |
|||
if err != nil { |
|||
return |
|||
} |
|||
|
|||
//log.Println(req)
|
|||
sconn, err := makeTunnel() |
|||
if err != nil { |
|||
gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) |
|||
log.Println(err) |
|||
return |
|||
} |
|||
defer sconn.Close() |
|||
|
|||
switch req.Cmd { |
|||
case gosocks5.CmdConnect, gosocks5.CmdBind: |
|||
if err := req.Write(sconn); err != nil { |
|||
return |
|||
} |
|||
Transport(conn, sconn) |
|||
case gosocks5.CmdUdp: |
|||
if err := req.Write(sconn); err != nil { |
|||
return |
|||
} |
|||
rep, err := gosocks5.ReadReply(sconn) |
|||
if err != nil || rep.Rep != gosocks5.Succeeded { |
|||
return |
|||
} |
|||
|
|||
uconn, err := net.ListenUDP("udp", nil) |
|||
if err != nil { |
|||
log.Println(err) |
|||
gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) |
|||
return |
|||
} |
|||
defer uconn.Close() |
|||
|
|||
addr := ToSocksAddr(uconn.LocalAddr()) |
|||
addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) |
|||
log.Println("udp:", addr) |
|||
|
|||
rep = gosocks5.NewReply(gosocks5.Succeeded, addr) |
|||
if err := rep.Write(conn); err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
|
|||
go cliTunnelUDP(uconn, sconn) |
|||
|
|||
// block, waiting for client exit
|
|||
ioutil.ReadAll(conn) |
|||
} |
|||
} |
|||
|
|||
func cliTunnelUDP(uconn *net.UDPConn, sconn net.Conn) { |
|||
var raddr *net.UDPAddr |
|||
|
|||
go func() { |
|||
b := lpool.Take() |
|||
defer lpool.put(b) |
|||
|
|||
for { |
|||
n, addr, err := uconn.ReadFromUDP(b) |
|||
if err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
raddr = addr |
|||
r := bytes.NewBuffer(b[:n]) |
|||
udp, err := gosocks5.ReadUDPDatagram(r) |
|||
if err != nil { |
|||
return |
|||
} |
|||
udp.Header.Rsv = uint16(len(udp.Data)) |
|||
//log.Println("r", raddr.String(), udp.Header)
|
|||
|
|||
if err := udp.Write(sconn); err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
} |
|||
}() |
|||
|
|||
for { |
|||
b := lpool.Take() |
|||
defer lpool.put(b) |
|||
|
|||
udp, err := gosocks5.ReadUDPDatagram(sconn) |
|||
if err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
//log.Println("w", udp.Header)
|
|||
udp.Header.Rsv = 0 |
|||
buf := bytes.NewBuffer(b[0:0]) |
|||
udp.Write(buf) |
|||
if _, err := uconn.WriteTo(buf.Bytes(), raddr); err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
} |
|||
} |
|||
|
|||
func clientHttpAuth(req *http.Request, conn net.Conn, username, password string) error { |
|||
u, p, ok := proxyBasicAuth(req.Header.Get("Proxy-Authorization")) |
|||
req.Header.Del("Proxy-Authorization") |
|||
if !ok || |
|||
(len(username) > 0 && u != username) || |
|||
(len(password) > 0 && p != password) { |
|||
conn.Write([]byte("HTTP/1.1 407 Proxy Authentication Required\r\n" + |
|||
"Proxy-Authenticate: Basic realm=\"gost\"\r\n" + |
|||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")) |
|||
|
|||
return errors.New("Proxy Authentication Required") |
|||
} |
|||
|
|||
return nil |
|||
} |
|||
|
|||
func proxyBasicAuth(auth string) (username, password string, ok bool) { |
|||
if auth == "" { |
|||
return |
|||
} |
|||
|
|||
if !strings.HasPrefix(auth, "Basic ") { |
|||
return |
|||
} |
|||
c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(auth, "Basic ")) |
|||
if err != nil { |
|||
return |
|||
} |
|||
cs := string(c) |
|||
s := strings.IndexByte(cs, ':') |
|||
if s < 0 { |
|||
return |
|||
} |
|||
|
|||
return cs[:s], cs[s+1:], true |
|||
} |
|||
|
|||
func handleHttp(req *http.Request, conn net.Conn) { |
|||
var host string |
|||
var port uint16 |
|||
|
|||
if listenUrl != nil && listenUrl.User != nil { |
|||
username := listenUrl.User.Username() |
|||
password, _ := listenUrl.User.Password() |
|||
|
|||
if err := clientHttpAuth(req, conn, username, password); err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
} |
|||
|
|||
s := strings.Split(req.Host, ":") |
|||
host = s[0] |
|||
port = 80 |
|||
if len(s) == 2 { |
|||
n, _ := strconv.ParseUint(s[1], 10, 16) |
|||
port = uint16(n) |
|||
} |
|||
|
|||
addr := &gosocks5.Addr{ |
|||
Type: gosocks5.AddrDomain, |
|||
Host: host, |
|||
Port: port, |
|||
} |
|||
r := gosocks5.NewRequest(gosocks5.CmdConnect, addr) |
|||
|
|||
sconn, err := makeTunnel() |
|||
if err != nil { |
|||
conn.Write([]byte("HTTP/1.1 503 Service unavailable\r\n" + |
|||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")) |
|||
log.Println(err) |
|||
return |
|||
} |
|||
defer sconn.Close() |
|||
|
|||
if err := r.Write(sconn); err != nil { |
|||
return |
|||
} |
|||
rep, err := gosocks5.ReadReply(sconn) |
|||
if err != nil || rep.Rep != gosocks5.Succeeded { |
|||
conn.Write([]byte("HTTP/1.1 503 Service unavailable\r\n" + |
|||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")) |
|||
return |
|||
} |
|||
|
|||
if req.Method == "CONNECT" { |
|||
if _, err = conn.Write( |
|||
[]byte("HTTP/1.1 200 Connection established\r\n" + |
|||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")); err != nil { |
|||
return |
|||
} |
|||
} else { |
|||
if err := req.Write(sconn); err != nil { |
|||
return |
|||
} |
|||
} |
|||
|
|||
if err := Transport(conn, sconn); err != nil { |
|||
//log.Println(err)
|
|||
} |
|||
} |
|||
|
|||
func handleShadow(conn net.Conn) { |
|||
addr, extra, err := getShadowRequest(conn) |
|||
if err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
|
|||
sconn, err := makeTunnel() |
|||
if err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
defer sconn.Close() |
|||
|
|||
req := gosocks5.NewRequest(gosocks5.CmdConnect, addr) |
|||
if err := req.Write(sconn); err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
rep, err := gosocks5.ReadReply(sconn) |
|||
if err != nil || rep.Rep != gosocks5.Succeeded { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
|
|||
if extra != nil { |
|||
if _, err := sconn.Write(extra); err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
} |
|||
|
|||
if err := Transport(conn, sconn); err != nil { |
|||
//log.Println(err)
|
|||
} |
|||
} |
|||
|
|||
func getShadowRequest(conn net.Conn) (addr *gosocks5.Addr, extra []byte, err error) { |
|||
const ( |
|||
idType = 0 // address type index
|
|||
idIP0 = 1 // ip addres start index
|
|||
idDmLen = 1 // domain address length index
|
|||
idDm0 = 2 // domain address start index
|
|||
|
|||
typeIPv4 = 1 // type is ipv4 address
|
|||
typeDm = 3 // type is domain address
|
|||
typeIPv6 = 4 // type is ipv6 address
|
|||
|
|||
lenIPv4 = 1 + net.IPv4len + 2 // 1addrType + ipv4 + 2port
|
|||
lenIPv6 = 1 + net.IPv6len + 2 // 1addrType + ipv6 + 2port
|
|||
lenDmBase = 1 + 1 + 2 // 1addrType + 1addrLen + 2port, plus addrLen
|
|||
) |
|||
|
|||
// buf size should at least have the same size with the largest possible
|
|||
// request size (when addrType is 3, domain name has at most 256 bytes)
|
|||
// 1(addrType) + 1(lenByte) + 256(max length address) + 2(port)
|
|||
buf := spool.Take() |
|||
defer spool.put(buf) |
|||
|
|||
var n int |
|||
// read till we get possible domain length field
|
|||
//shadowsocks.SetReadTimeout(conn)
|
|||
if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
|
|||
addr = &gosocks5.Addr{ |
|||
Type: buf[idType], |
|||
} |
|||
|
|||
reqLen := -1 |
|||
switch buf[idType] { |
|||
case typeIPv4: |
|||
reqLen = lenIPv4 |
|||
case typeIPv6: |
|||
reqLen = lenIPv6 |
|||
case typeDm: |
|||
reqLen = int(buf[idDmLen]) + lenDmBase |
|||
default: |
|||
err = fmt.Errorf("addr type %d not supported", buf[idType]) |
|||
return |
|||
} |
|||
|
|||
if n < reqLen { // rare case
|
|||
//ss.SetReadTimeout(conn)
|
|||
if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
} else if n > reqLen { |
|||
// it's possible to read more than just the request head
|
|||
extra = buf[reqLen:n] |
|||
} |
|||
|
|||
// Return string for typeIP is not most efficient, but browsers (Chrome,
|
|||
// Safari, Firefox) all seems using typeDm exclusively. So this is not a
|
|||
// big problem.
|
|||
switch buf[idType] { |
|||
case typeIPv4: |
|||
addr.Host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() |
|||
case typeIPv6: |
|||
addr.Host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() |
|||
case typeDm: |
|||
addr.Host = string(buf[idDm0 : idDm0+buf[idDmLen]]) |
|||
} |
|||
// parse port
|
|||
addr.Port = binary.BigEndian.Uint16(buf[reqLen-2 : reqLen]) |
|||
|
|||
return |
|||
} |
|||
|
|||
type reqReader struct { |
|||
b []byte |
|||
r io.Reader |
|||
} |
|||
|
|||
func newReqReader(b []byte, r io.Reader) *reqReader { |
|||
return &reqReader{ |
|||
b: b, |
|||
r: r, |
|||
} |
|||
} |
|||
|
|||
func (r *reqReader) Read(p []byte) (n int, err error) { |
|||
if len(r.b) == 0 { |
|||
return r.r.Read(p) |
|||
} |
|||
n = copy(p, r.b) |
|||
r.b = r.b[n:] |
|||
|
|||
return |
|||
} |
|||
@ -0,0 +1,176 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"bufio" |
|||
"crypto/tls" |
|||
"github.com/ginuerzh/gosocks5" |
|||
"github.com/golang/glog" |
|||
"io" |
|||
"net" |
|||
"net/http" |
|||
) |
|||
|
|||
func listenAndServe(arg Args) error { |
|||
var ln net.Listener |
|||
var err error |
|||
|
|||
switch arg.Transport { |
|||
case "ws": // websocket connection
|
|||
err = NewWs(arg).ListenAndServe() |
|||
if err != nil { |
|||
if glog.V(LFATAL) { |
|||
glog.Errorln(err) |
|||
} |
|||
} |
|||
return err |
|||
case "tls": // tls connection
|
|||
ln, err = tls.Listen("tcp", arg.Addr, |
|||
&tls.Config{Certificates: []tls.Certificate{arg.Cert}}) |
|||
default: |
|||
ln, err = net.Listen("tcp", arg.Addr) |
|||
} |
|||
|
|||
if err != nil { |
|||
if glog.V(LFATAL) { |
|||
glog.Errorln(err) |
|||
} |
|||
return err |
|||
} |
|||
defer ln.Close() |
|||
|
|||
for { |
|||
conn, err := ln.Accept() |
|||
if err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln(err) |
|||
} |
|||
continue |
|||
} |
|||
if glog.V(LINFO) { |
|||
glog.Infoln("accept", conn.RemoteAddr()) |
|||
} |
|||
go handleConn(conn, arg) |
|||
} |
|||
|
|||
return nil |
|||
} |
|||
|
|||
func handleConn(conn net.Conn, arg Args) { |
|||
defer conn.Close() |
|||
|
|||
selector := &serverSelector{ |
|||
methods: []uint8{ |
|||
gosocks5.MethodNoAuth, gosocks5.MethodUserPass, |
|||
MethodTLS, MethodTLSAuth, |
|||
}, |
|||
arg: arg, |
|||
} |
|||
|
|||
switch arg.Protocol { |
|||
case "ss": // shadowsocks
|
|||
return |
|||
case "http": |
|||
req, err := http.ReadRequest(bufio.NewReader(conn)) |
|||
if err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln(err) |
|||
} |
|||
return |
|||
} |
|||
handleHttpRequest(req, conn, arg) |
|||
return |
|||
case "socks", "socks5": |
|||
conn = gosocks5.ServerConn(conn, selector) |
|||
req, err := gosocks5.ReadRequest(conn) |
|||
if err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5:", err) |
|||
} |
|||
return |
|||
} |
|||
handleSocks5Request(req, conn, arg) |
|||
return |
|||
} |
|||
|
|||
// http + socks5
|
|||
|
|||
b := make([]byte, 16*1024) |
|||
|
|||
n, err := io.ReadAtLeast(conn, b, 2) |
|||
if err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln(err) |
|||
} |
|||
return |
|||
} |
|||
|
|||
if b[0] == gosocks5.Ver5 { |
|||
mn := int(b[1]) // methods count
|
|||
length := 2 + mn |
|||
if n < length { |
|||
if _, err := io.ReadFull(conn, b[n:length]); err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5:", err) |
|||
} |
|||
return |
|||
} |
|||
} |
|||
methods := b[2 : 2+mn] |
|||
method := selector.Select(methods...) |
|||
if _, err := conn.Write([]byte{gosocks5.Ver5, method}); err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5:", err) |
|||
} |
|||
return |
|||
} |
|||
c, err := selector.OnSelected(method, conn) |
|||
if err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5:", err) |
|||
} |
|||
return |
|||
} |
|||
conn = c |
|||
|
|||
req, err := gosocks5.ReadRequest(conn) |
|||
if err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5:", err) |
|||
} |
|||
return |
|||
} |
|||
handleSocks5Request(req, conn, arg) |
|||
return |
|||
} |
|||
|
|||
req, err := http.ReadRequest(bufio.NewReader(newReqReader(b[:n], conn))) |
|||
if err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln(err) |
|||
} |
|||
return |
|||
} |
|||
handleHttpRequest(req, conn, arg) |
|||
} |
|||
|
|||
type reqReader struct { |
|||
b []byte |
|||
r io.Reader |
|||
} |
|||
|
|||
func newReqReader(b []byte, r io.Reader) *reqReader { |
|||
return &reqReader{ |
|||
b: b, |
|||
r: r, |
|||
} |
|||
} |
|||
|
|||
func (r *reqReader) Read(p []byte) (n int, err error) { |
|||
if len(r.b) == 0 { |
|||
return r.r.Read(p) |
|||
} |
|||
n = copy(p, r.b) |
|||
r.b = r.b[n:] |
|||
|
|||
return |
|||
} |
|||
@ -1,285 +1,70 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"bufio" |
|||
"bytes" |
|||
"code.google.com/p/go-uuid/uuid" |
|||
"errors" |
|||
"github.com/ginuerzh/gosocks5" |
|||
"io" |
|||
"io/ioutil" |
|||
"log" |
|||
"encoding/base64" |
|||
"github.com/golang/glog" |
|||
"net" |
|||
"net/http" |
|||
"net/url" |
|||
"time" |
|||
"net/http/httputil" |
|||
"strings" |
|||
) |
|||
|
|||
const ( |
|||
s2cUri = "/s2c" |
|||
c2sUri = "/c2s" |
|||
) |
|||
|
|||
type HttpClientConn struct { |
|||
c net.Conn |
|||
token string |
|||
r io.ReadCloser |
|||
} |
|||
|
|||
func NewHttpClientConn(conn net.Conn) *HttpClientConn { |
|||
return &HttpClientConn{ |
|||
c: conn, |
|||
} |
|||
} |
|||
|
|||
func (conn *HttpClientConn) Handshake() (err error) { |
|||
//log.Println("remote", conn.c.RemoteAddr().String())
|
|||
req := &http.Request{ |
|||
Method: "GET", |
|||
URL: &url.URL{ |
|||
Host: Saddr, |
|||
Scheme: "http", |
|||
Path: s2cUri, |
|||
}, |
|||
Header: make(http.Header), |
|||
} |
|||
if proxyURL == nil { |
|||
err = req.Write(conn.c) |
|||
} else { |
|||
setBasicAuth(req) |
|||
err = req.WriteProxy(conn.c) |
|||
} |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
resp, err := http.ReadResponse(bufio.NewReader(conn.c), req) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
if resp.StatusCode != http.StatusOK { |
|||
return errors.New(resp.Status) |
|||
} |
|||
|
|||
b := make([]byte, 36) |
|||
if _, err = io.ReadFull(resp.Body, b); err != nil { |
|||
return err |
|||
} |
|||
if uuid.Parse(string(b)) == nil { |
|||
return errors.New("Handshake: wrong token") |
|||
} |
|||
conn.token = string(b) |
|||
conn.r = resp.Body |
|||
//log.Println(conn.token, "connected")
|
|||
|
|||
return nil |
|||
} |
|||
|
|||
func (conn *HttpClientConn) Read(b []byte) (n int, err error) { |
|||
n, err = conn.r.Read(b) |
|||
//log.Println("http r:", n)
|
|||
return |
|||
} |
|||
|
|||
func (conn *HttpClientConn) Write(b []byte) (n int, err error) { |
|||
q := url.Values{} |
|||
q.Set("token", conn.token) |
|||
req := &http.Request{ |
|||
Method: "POST", |
|||
Body: ioutil.NopCloser(bytes.NewReader(b)), |
|||
ContentLength: int64(len(b)), |
|||
URL: &url.URL{ |
|||
Host: Saddr, |
|||
Scheme: "http", |
|||
Path: c2sUri, |
|||
RawQuery: q.Encode(), |
|||
}, |
|||
Header: make(http.Header), |
|||
} |
|||
resp, err := doRequest(req) |
|||
if err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
resp.Body.Close() |
|||
|
|||
if resp.StatusCode != http.StatusOK { |
|||
//log.Println(resp.Status)
|
|||
return 0, errors.New(resp.Status) |
|||
} |
|||
//log.Println("http w:", len(b))
|
|||
return len(b), nil |
|||
} |
|||
|
|||
func (conn *HttpClientConn) Close() error { |
|||
conn.Write(nil) |
|||
return conn.r.Close() |
|||
} |
|||
|
|||
func (conn *HttpClientConn) LocalAddr() net.Addr { |
|||
return conn.c.LocalAddr() |
|||
} |
|||
|
|||
func (conn *HttpClientConn) RemoteAddr() net.Addr { |
|||
return conn.c.RemoteAddr() |
|||
} |
|||
|
|||
func (conn *HttpClientConn) SetDeadline(t time.Time) error { |
|||
return conn.c.SetDeadline(t) |
|||
} |
|||
|
|||
func (conn *HttpClientConn) SetReadDeadline(t time.Time) error { |
|||
return conn.c.SetReadDeadline(t) |
|||
} |
|||
|
|||
func (conn *HttpClientConn) SetWriteDeadline(t time.Time) error { |
|||
return conn.c.SetWriteDeadline(t) |
|||
} |
|||
|
|||
type HttpServerConn struct { |
|||
w http.ResponseWriter |
|||
c chan []byte |
|||
closed bool |
|||
rb []byte |
|||
} |
|||
|
|||
func NewHttpServerConn(w http.ResponseWriter, c chan []byte) *HttpServerConn { |
|||
return &HttpServerConn{ |
|||
w: w, |
|||
c: c, |
|||
} |
|||
} |
|||
|
|||
func (conn *HttpServerConn) Read(b []byte) (n int, err error) { |
|||
if len(conn.rb) == 0 { |
|||
var ok bool |
|||
if conn.rb, ok = <-conn.c; !ok { |
|||
return 0, io.EOF |
|||
func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) { |
|||
if glog.V(LDEBUG) { |
|||
dump, err := httputil.DumpRequest(req, false) |
|||
if err != nil { |
|||
glog.Infoln(err) |
|||
} else { |
|||
glog.Infoln(string(dump)) |
|||
} |
|||
} |
|||
n = copy(b, conn.rb) |
|||
conn.rb = conn.rb[n:] |
|||
|
|||
//log.Println("http r:", n)
|
|||
|
|||
return |
|||
} |
|||
|
|||
func (conn *HttpServerConn) Write(b []byte) (n int, err error) { |
|||
n, err = conn.w.Write(b) |
|||
if f, ok := conn.w.(http.Flusher); ok { |
|||
f.Flush() |
|||
} |
|||
//log.Println("http w:", n)
|
|||
return |
|||
} |
|||
|
|||
func (conn *HttpServerConn) Close() error { |
|||
if !conn.closed { |
|||
close(conn.c) |
|||
conn.closed = true |
|||
var username, password string |
|||
if arg.User != nil { |
|||
username = arg.User.Username() |
|||
password, _ = arg.User.Password() |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func (conn *HttpServerConn) LocalAddr() net.Addr { |
|||
return nil |
|||
} |
|||
|
|||
func (conn *HttpServerConn) RemoteAddr() net.Addr { |
|||
return nil |
|||
} |
|||
u, p, _ := proxyBasicAuth(req.Header.Get("Proxy-Authorization")) |
|||
req.Header.Del("Proxy-Authorization") |
|||
|
|||
func (conn *HttpServerConn) SetDeadline(t time.Time) error { |
|||
return nil |
|||
} |
|||
if (username != "" && u != username) || (password != "" && p != password) { |
|||
resp := "HTTP/1.1 407 Proxy Authentication Required\r\n" + |
|||
"Proxy-Authenticate: Basic realm=\"gost\"\r\n" + |
|||
"Proxy-Agent: gost/" + Version + "\r\n\r\n" |
|||
|
|||
func (conn *HttpServerConn) SetReadDeadline(t time.Time) error { |
|||
return nil |
|||
} |
|||
|
|||
func (conn *HttpServerConn) SetWriteDeadline(t time.Time) error { |
|||
return nil |
|||
} |
|||
|
|||
type HttpServer struct { |
|||
Addr string |
|||
conns map[string]*HttpServerConn |
|||
} |
|||
|
|||
func (s *HttpServer) s2c(w http.ResponseWriter, r *http.Request) { |
|||
token := uuid.New() |
|||
ch := make(chan []byte, 8) |
|||
|
|||
conn := NewHttpServerConn(w, ch) |
|||
if _, err := conn.Write([]byte(token)); err != nil { |
|||
if _, err := conn.Write([]byte(resp)); err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln(err) |
|||
} |
|||
} |
|||
if glog.V(LDEBUG) { |
|||
glog.Infoln(resp) |
|||
} |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("http: proxy authentication required") |
|||
} |
|||
return |
|||
} |
|||
|
|||
s.conns[token] = conn |
|||
defer delete(s.conns, token) |
|||
|
|||
serveSocks5(gosocks5.ServerConn(conn, serverConfig)) |
|||
} |
|||
|
|||
func (s *HttpServer) c2s(w http.ResponseWriter, r *http.Request) { |
|||
defer func() { |
|||
if err := recover(); err != nil { |
|||
log.Println(err) |
|||
} |
|||
}() |
|||
|
|||
if r.Method != "POST" { |
|||
w.WriteHeader(http.StatusMethodNotAllowed) |
|||
func proxyBasicAuth(authInfo string) (username, password string, ok bool) { |
|||
if authInfo == "" { |
|||
return |
|||
} |
|||
|
|||
token := r.FormValue("token") |
|||
conn := s.conns[token] |
|||
if conn == nil { |
|||
w.WriteHeader(http.StatusBadRequest) |
|||
if !strings.HasPrefix(authInfo, "Basic ") { |
|||
return |
|||
} |
|||
b, err := ioutil.ReadAll(r.Body) |
|||
if err != nil || len(b) == 0 { |
|||
conn.Close() |
|||
delete(s.conns, token) |
|||
//log.Println(token, "disconnected")
|
|||
c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(authInfo, "Basic ")) |
|||
if err != nil { |
|||
return |
|||
} |
|||
conn.c <- b |
|||
} |
|||
|
|||
func (s *HttpServer) ListenAndServe() error { |
|||
s.conns = make(map[string]*HttpServerConn) |
|||
http.HandleFunc(s2cUri, s.s2c) |
|||
http.HandleFunc(c2sUri, s.c2s) |
|||
return http.ListenAndServe(s.Addr, nil) |
|||
} |
|||
|
|||
func doRequest(req *http.Request) (*http.Response, error) { |
|||
if proxyURL != nil { |
|||
c, err := dial(proxyURL.Host) |
|||
if err != nil { |
|||
log.Println(err) |
|||
return nil, err |
|||
} |
|||
defer c.Close() |
|||
|
|||
setBasicAuth(req) |
|||
if err := req.WriteProxy(c); err != nil { |
|||
log.Println(err) |
|||
return nil, err |
|||
} |
|||
/* |
|||
b, err := ioutil.ReadAll(c) |
|||
if err != nil { |
|||
log.Println(err) |
|||
return nil, err |
|||
} |
|||
*/ |
|||
return http.ReadResponse(bufio.NewReader(c), req) |
|||
cs := string(c) |
|||
s := strings.IndexByte(cs, ':') |
|||
if s < 0 { |
|||
return |
|||
} |
|||
|
|||
return http.DefaultClient.Do(req) |
|||
return cs[:s], cs[s+1:], true |
|||
} |
|||
|
|||
@ -0,0 +1,27 @@ |
|||
-----BEGIN RSA PRIVATE KEY----- |
|||
MIIEowIBAAKCAQEA3HDTtcZneJVkn3f9P0EtxPd3GCFh8PN9YvyCsYoHUQ/1zGaJ |
|||
y3RB8sFupTol2s2j/Hugqjxkpp5dMeZP93bxgjI9iQlcTOvs+zaS/gIsL15r5I/0 |
|||
znIgoMKrLAsGEtgolcI32s0Y8dWVCVQwuaoskbH5LUNn1R66ZyRlUkWoJokIMBlg |
|||
YpVRvQfEL0baUCjfxkBqte6CEJvGipDnT7fd4Pd6IzCIO2CaYA4Doq71c/4Q5+2A |
|||
+K8q7T6LPQjGnxZCaa8piqUR8r8+z/Gt/jpLj4xYz58Dh0ObtVN/vLwuOL329RMV |
|||
swy6E/F6fBp3cd/ZNH+zFTqJdxp2LjDf3bzVHQIDAQABAoIBAHal26147nQ+pHwY |
|||
jxwers3XDCjWvup7g79lfcqlKi79UiUEA6KYHm7UogMYewt7p4nb2KwH+XycvDiB |
|||
aAUf5flXpTs+6IkWauUDiLZi4PlV7uiEexUq5FjirlL0U/6MjbudX4bK4WQ4uxDc |
|||
WaV07Kw2iJFOOHLDKT0en9JaX5jtJNc4ZnE9efFoQ5jfypPWtRw65G1rULEg6nvc |
|||
GDh+1ce+4foCkpLRC9c24xAwJONZG6x3UqrSS9qfAsb73nWRQrTfUcO3nhoN8VvL |
|||
kL9skn1+S06NyUN0KoEtyRBp+RcpXSsBWAo6qZmo/WqhB/gjzWrxVwn20+yJSm35 |
|||
ZsMc6QECgYEA8GS+Mp9xfB2szWHz6YTOO1Uu4lHM1ccZMwS1G+dL0KO3uGAiPdvp |
|||
woVot6v6w88t7onXsLo5pgz7SYug0CpkF3K/MRd1Ar4lH7PK7IBQ6rFr9ppVxDbx |
|||
AEWRswUoPbKCr7W6HU8LbQHDavsDlEIwc6+DiwnL4BzlKjb7RpgQEz0CgYEA6sB5 |
|||
uHvx3Y5FDcGk1n73leQSAcq14l3ZLNpjrs8msoREDil/j5WmuSN58/7PGMiMgHEi |
|||
1vLm3H796JmvGr9OBvspOjHyk07ui2/We/j9Hoxm1VWhyi8HkLNDj70HKalTTFMz |
|||
RHO4O+0xCva+h9mKZrRMVktXr2jjdFn/0MYIZ2ECgYAIIsC1IeRLWQ3CHbCNlKsO |
|||
IwHlMvOFwKk/qsceXKOaOhA7szU1dr3gkXdL0Aw6mEZrrkqYdpUA46uVf54/rU+Z |
|||
445I8QxKvXiwK/uQKX+TkdGflPWWIG3jnnch4ejMvb/ihnn4B/bRB6A/fKNQXzUY |
|||
lTYUfI5j1VaEKTwz1W2l2QKBgByFCcSp+jZqhGUpc3dDsZyaOr3Q/Mvlju7uEVI5 |
|||
hIAHpaT60a6GBd1UPAqymEJwivFHzW3D0NxU6VAK68UaHMaoWNfjHY9b9YsnKS2i |
|||
kE3XzN56Ks+/avHfdYPO+UHMenw5V28nh+hv5pdoZrlmanQTz3pkaOC8o3WNQZEB |
|||
nh/BAoGBAMY5z2f1pmMhrvtPDSlEVjgjELbaInxFaxPLR4Pdyzn83gtIIU14+R8X |
|||
2LPs6PPwrNjWnIgrUSVXncIFL3pa45B+Mx1pYCpOAB1+nCZjIBQmpeo4Y0dwA/XH |
|||
85EthKPvoszm+OPbyI16OcePV5ocX7lupRYuAo0pek7bomhmHWHz |
|||
-----END RSA PRIVATE KEY----- |
|||
@ -3,75 +3,57 @@ package main |
|||
|
|||
import ( |
|||
"flag" |
|||
"log" |
|||
"net/url" |
|||
"time" |
|||
"github.com/golang/glog" |
|||
"sync" |
|||
) |
|||
|
|||
const ( |
|||
LFATAL = iota |
|||
LERROR |
|||
LWARNING |
|||
LINFO |
|||
LDEBUG |
|||
) |
|||
|
|||
var ( |
|||
Laddr, Saddr, Proxy string |
|||
UseWebsocket, UseHttp, UseTLS bool |
|||
Shadows bool |
|||
SMethod, SPassword string |
|||
Method, Password string |
|||
CertFile, KeyFile string |
|||
PrintVersion bool |
|||
listenUrl, proxyUrl, forwardUrl string |
|||
|
|||
proxyURL *url.URL |
|||
listenUrl *url.URL |
|||
listenArgs []Args |
|||
proxyArgs []Args |
|||
forwardArgs []Args |
|||
) |
|||
|
|||
func init() { |
|||
flag.StringVar(&Proxy, "P", "", "proxy for forward") |
|||
flag.StringVar(&Saddr, "S", "", "the server that connect to") |
|||
flag.StringVar(&Laddr, "L", ":8080", "listen address") |
|||
flag.StringVar(&Method, "m", "", "tunnel cipher method") |
|||
flag.StringVar(&Password, "p", "", "tunnel cipher password") |
|||
flag.StringVar(&CertFile, "cert", "", "tls cert file") |
|||
flag.StringVar(&KeyFile, "key", "", "tls key file") |
|||
flag.BoolVar(&Shadows, "ss", false, "run as shadowsocks server") |
|||
flag.BoolVar(&UseTLS, "tls", false, "use ssl/tls tunnel") |
|||
flag.BoolVar(&UseWebsocket, "ws", false, "use websocket tunnel") |
|||
flag.BoolVar(&UseHttp, "http", false, "use http tunnel") |
|||
flag.StringVar(&SMethod, "sm", "rc4-md5", "shadowsocks cipher method") |
|||
flag.StringVar(&SPassword, "sp", "[email protected]", "shadowsocks cipher password") |
|||
flag.BoolVar(&PrintVersion, "v", false, "print version") |
|||
flag.Parse() |
|||
flag.StringVar(&listenUrl, "L", ":http", "local address") |
|||
flag.StringVar(&forwardUrl, "S", "", "remote address") |
|||
flag.StringVar(&proxyUrl, "P", "", "proxy address") |
|||
|
|||
log.SetFlags(log.LstdFlags | log.Lshortfile) |
|||
flag.Parse() |
|||
|
|||
proxyURL, _ = parseURL(Proxy) |
|||
listenUrl, _ = parseURL(Laddr) |
|||
listenArgs = parseArgs(listenUrl) |
|||
proxyArgs = parseArgs(proxyUrl) |
|||
forwardArgs = parseArgs(forwardUrl) |
|||
} |
|||
|
|||
var ( |
|||
spool = NewMemPool(1024, 120*time.Minute, 1024) // 1k size buffer pool
|
|||
mpool = NewMemPool(16*1024, 60*time.Minute, 512) // 16k size buffer pool
|
|||
lpool = NewMemPool(32*1024, 30*time.Minute, 256) // 32k size buffer pool
|
|||
) |
|||
|
|||
func main() { |
|||
if PrintVersion { |
|||
printVersion() |
|||
return |
|||
} |
|||
defer glog.Flush() |
|||
|
|||
laddr := listenUrl.Host |
|||
|
|||
if len(Saddr) == 0 { |
|||
var server Server |
|||
if UseTLS { |
|||
server = &TlsServer{Addr: laddr, CertFile: CertFile, KeyFile: KeyFile} |
|||
} else if UseWebsocket { |
|||
server = &WSServer{Addr: laddr} |
|||
} else if UseHttp { |
|||
server = &HttpServer{Addr: laddr} |
|||
} else { |
|||
server = &Socks5Server{Addr: laddr} |
|||
} |
|||
log.Fatal(server.ListenAndServe()) |
|||
return |
|||
if len(listenArgs) == 0 { |
|||
glog.Fatalln("no listen addr") |
|||
} |
|||
|
|||
log.Fatal(listenAndServe(laddr, cliHandle)) |
|||
var wg sync.WaitGroup |
|||
|
|||
for _, arg := range listenArgs { |
|||
wg.Add(1) |
|||
go func() { |
|||
defer wg.Done() |
|||
if err := listenAndServe(arg); err != nil { |
|||
if glog.V(LFATAL) { |
|||
glog.Errorln(err) |
|||
} |
|||
} |
|||
}() |
|||
} |
|||
wg.Wait() |
|||
} |
|||
|
|||
@ -1,108 +0,0 @@ |
|||
// pool for buffer
|
|||
package main |
|||
|
|||
import ( |
|||
"container/list" |
|||
//"log"
|
|||
"time" |
|||
) |
|||
|
|||
type poolItem struct { |
|||
when time.Time |
|||
item interface{} |
|||
} |
|||
|
|||
type pool struct { |
|||
quque *list.List |
|||
takeChan, putChan chan interface{} |
|||
age time.Duration |
|||
max int |
|||
} |
|||
|
|||
func (p *pool) run() { |
|||
for { |
|||
if p.size() == 0 { |
|||
select { |
|||
case b := <-p.putChan: |
|||
p.put(b) |
|||
} |
|||
continue |
|||
} |
|||
|
|||
i := p.quque.Front() |
|||
timeout := time.NewTimer(p.age) |
|||
|
|||
select { |
|||
case b := <-p.putChan: |
|||
timeout.Stop() |
|||
p.put(b) |
|||
case p.takeChan <- i.Value.(*poolItem).item: |
|||
timeout.Stop() |
|||
p.quque.Remove(i) |
|||
case <-timeout.C: |
|||
i = p.quque.Back() |
|||
for i != nil { |
|||
if time.Since(i.Value.(*poolItem).when) < p.age { |
|||
break |
|||
} |
|||
e := i.Prev() |
|||
p.quque.Remove(i) |
|||
i = e |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
func (p *pool) size() int { |
|||
return p.quque.Len() |
|||
} |
|||
|
|||
func (p *pool) put(v interface{}) { |
|||
if p.size() < p.max { |
|||
p.quque.PushFront(&poolItem{when: time.Now(), item: v}) |
|||
return |
|||
} |
|||
} |
|||
|
|||
type MemPool struct { |
|||
pool |
|||
bs int |
|||
} |
|||
|
|||
func NewMemPool(bs int, age time.Duration, max int) *MemPool { |
|||
if bs <= 0 { |
|||
bs = 8192 |
|||
} |
|||
|
|||
if age == 0 { |
|||
age = 1 * time.Minute |
|||
} |
|||
|
|||
p := &MemPool{ |
|||
pool: pool{ |
|||
quque: list.New(), |
|||
takeChan: make(chan interface{}), |
|||
putChan: make(chan interface{}), |
|||
age: age, |
|||
max: max, |
|||
}, |
|||
bs: bs, |
|||
} |
|||
|
|||
go p.run() |
|||
|
|||
return p |
|||
} |
|||
|
|||
func (p *MemPool) Take() []byte { |
|||
select { |
|||
case v := <-p.takeChan: |
|||
return v.([]byte) |
|||
default: |
|||
return make([]byte, p.bs) |
|||
} |
|||
} |
|||
|
|||
func (p *MemPool) Put(b []byte) { |
|||
p.putChan <- b |
|||
} |
|||
@ -1,6 +0,0 @@ |
|||
package main |
|||
|
|||
type Server interface { |
|||
ListenAndServe() error |
|||
} |
|||
|
|||
@ -0,0 +1,400 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"crypto/tls" |
|||
"github.com/ginuerzh/gosocks5" |
|||
"github.com/golang/glog" |
|||
"net" |
|||
"strconv" |
|||
) |
|||
|
|||
const ( |
|||
MethodTLS uint8 = 0x80 // extended method for tls
|
|||
MethodTLSAuth uint8 = 0x82 // extended method for tls+auth
|
|||
) |
|||
|
|||
type clientSelector struct { |
|||
arg Args |
|||
} |
|||
|
|||
func (selector *clientSelector) Methods() []uint8 { |
|||
return nil |
|||
} |
|||
|
|||
func (selector *clientSelector) Select(methods ...uint8) (method uint8) { |
|||
return |
|||
} |
|||
|
|||
func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) { |
|||
switch method { |
|||
case MethodTLS: |
|||
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true}) |
|||
|
|||
case gosocks5.MethodUserPass, MethodTLSAuth: |
|||
if method == MethodTLSAuth { |
|||
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true}) |
|||
} |
|||
|
|||
var username, password string |
|||
if selector.arg.User != nil { |
|||
username = selector.arg.User.Username() |
|||
password, _ = selector.arg.User.Password() |
|||
} |
|||
|
|||
req := gosocks5.NewUserPassRequest(gosocks5.UserPassVer, username, password) |
|||
if err := req.Write(conn); err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln(err) |
|||
} |
|||
return nil, err |
|||
} |
|||
if glog.V(LDEBUG) { |
|||
glog.Infoln(req) |
|||
} |
|||
|
|||
res, err := gosocks5.ReadUserPassResponse(conn) |
|||
if err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln(err) |
|||
} |
|||
return nil, err |
|||
} |
|||
if glog.V(LDEBUG) { |
|||
glog.Infoln(res) |
|||
} |
|||
|
|||
if res.Status != gosocks5.Succeeded { |
|||
return nil, gosocks5.ErrAuthFailure |
|||
} |
|||
case gosocks5.MethodNoAcceptable: |
|||
return nil, gosocks5.ErrBadMethod |
|||
} |
|||
|
|||
return conn, nil |
|||
} |
|||
|
|||
type serverSelector struct { |
|||
methods []uint8 |
|||
arg Args |
|||
} |
|||
|
|||
func (selector *serverSelector) Methods() []uint8 { |
|||
return selector.methods |
|||
} |
|||
|
|||
func (selector *serverSelector) Select(methods ...uint8) (method uint8) { |
|||
if glog.V(LDEBUG) { |
|||
glog.Infof("%x %x % x", gosocks5.Ver5, len(methods), methods) |
|||
} |
|||
|
|||
method = gosocks5.MethodNoAcceptable |
|||
|
|||
for _, m := range methods { |
|||
for _, mm := range selector.methods { |
|||
if m == mm { |
|||
method = m |
|||
break |
|||
} |
|||
} |
|||
} |
|||
|
|||
if method == gosocks5.MethodNoAcceptable { |
|||
return |
|||
} |
|||
// when user/pass is set, auth is mandatory
|
|||
if selector.arg.User != nil { |
|||
if method == gosocks5.MethodNoAuth { |
|||
method = gosocks5.MethodUserPass |
|||
} |
|||
if method == MethodTLS { |
|||
method = MethodTLSAuth |
|||
} |
|||
} |
|||
|
|||
return |
|||
} |
|||
|
|||
func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) { |
|||
if glog.V(LDEBUG) { |
|||
glog.Infof("%x %x", gosocks5.Ver5, method) |
|||
} |
|||
|
|||
switch method { |
|||
case MethodTLS: |
|||
conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{selector.arg.Cert}}) |
|||
|
|||
case gosocks5.MethodUserPass, MethodTLSAuth: |
|||
if method == MethodTLSAuth { |
|||
conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{selector.arg.Cert}}) |
|||
} |
|||
|
|||
req, err := gosocks5.ReadUserPassRequest(conn) |
|||
if err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln(err) |
|||
} |
|||
return nil, err |
|||
} |
|||
if glog.V(LDEBUG) { |
|||
glog.Infoln(req) |
|||
} |
|||
|
|||
var username, password string |
|||
if selector.arg.User != nil { |
|||
username = selector.arg.User.Username() |
|||
password, _ = selector.arg.User.Password() |
|||
} |
|||
|
|||
if (username != "" && req.Username != username) || (password != "" && req.Password != password) { |
|||
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure) |
|||
if err := resp.Write(conn); err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln(err) |
|||
} |
|||
return nil, err |
|||
} |
|||
if glog.V(LDEBUG) { |
|||
glog.Infoln(resp) |
|||
} |
|||
return nil, gosocks5.ErrAuthFailure |
|||
} |
|||
|
|||
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded) |
|||
if err := resp.Write(conn); err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln(err) |
|||
} |
|||
return nil, err |
|||
} |
|||
if glog.V(LDEBUG) { |
|||
glog.Infoln(resp) |
|||
} |
|||
|
|||
case gosocks5.MethodNoAcceptable: |
|||
return nil, gosocks5.ErrBadMethod |
|||
} |
|||
|
|||
return conn, nil |
|||
} |
|||
|
|||
func handleSocks5Request(req *gosocks5.Request, conn net.Conn, arg Args) { |
|||
if glog.V(LDEBUG) { |
|||
glog.Infoln(req) |
|||
} |
|||
|
|||
switch req.Cmd { |
|||
case gosocks5.CmdConnect: |
|||
if glog.V(LINFO) { |
|||
glog.Infoln("socks5 connect:", req.Addr.String()) |
|||
} |
|||
tconn, err := connect(req.Addr.String()) |
|||
if err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5 connect:", err) |
|||
} |
|||
rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil) |
|||
if err := rep.Write(conn); err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5 connect:", err) |
|||
} |
|||
} else { |
|||
if glog.V(LDEBUG) { |
|||
glog.Infoln(rep) |
|||
} |
|||
} |
|||
return |
|||
} |
|||
defer tconn.Close() |
|||
|
|||
rep := gosocks5.NewReply(gosocks5.Succeeded, nil) |
|||
if err := rep.Write(conn); err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5 connect:", err) |
|||
} |
|||
return |
|||
} else { |
|||
if glog.V(LDEBUG) { |
|||
glog.Infoln(rep) |
|||
} |
|||
} |
|||
|
|||
if err := Transport(conn, tconn); err != nil { |
|||
//log.Println(err)
|
|||
} |
|||
case gosocks5.CmdBind: |
|||
l, err := net.ListenTCP("tcp", nil) |
|||
if err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5 bind listen:", err) |
|||
} |
|||
rep := gosocks5.NewReply(gosocks5.Failure, nil) |
|||
if err := rep.Write(conn); err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5 bind listen:", err) |
|||
} |
|||
} else { |
|||
if glog.V(LDEBUG) { |
|||
glog.Infoln(rep) |
|||
} |
|||
} |
|||
return |
|||
} |
|||
|
|||
addr := ToSocksAddr(l.Addr()) |
|||
addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) |
|||
if glog.V(LINFO) { |
|||
glog.Infoln("socks5 bind:", addr) |
|||
} |
|||
rep := gosocks5.NewReply(gosocks5.Succeeded, addr) |
|||
if err := rep.Write(conn); err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5 bind:", err) |
|||
} |
|||
l.Close() |
|||
return |
|||
} else { |
|||
if glog.V(LDEBUG) { |
|||
glog.Infoln(rep) |
|||
} |
|||
} |
|||
|
|||
tconn, err := l.AcceptTCP() |
|||
l.Close() // only accept one peer
|
|||
if err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5 bind accept:", err) |
|||
} |
|||
rep = gosocks5.NewReply(gosocks5.Failure, nil) |
|||
if err := rep.Write(conn); err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5 bind accept:", err) |
|||
} |
|||
} else { |
|||
if glog.V(LDEBUG) { |
|||
glog.Infoln(rep) |
|||
} |
|||
} |
|||
return |
|||
} |
|||
defer tconn.Close() |
|||
|
|||
addr = ToSocksAddr(tconn.RemoteAddr()) |
|||
if glog.V(LINFO) { |
|||
glog.Infoln("socks5 bind accept:", addr.String()) |
|||
} |
|||
rep = gosocks5.NewReply(gosocks5.Succeeded, addr) |
|||
if err := rep.Write(conn); err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5 bind accept:", err) |
|||
} |
|||
return |
|||
} else { |
|||
if glog.V(LDEBUG) { |
|||
glog.Infoln(rep) |
|||
} |
|||
} |
|||
|
|||
if err := Transport(conn, tconn); err != nil { |
|||
//log.Println(err)
|
|||
} |
|||
case gosocks5.CmdUdp: |
|||
uconn, err := net.ListenUDP("udp", nil) |
|||
if err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5 udp listen:", err) |
|||
} |
|||
rep := gosocks5.NewReply(gosocks5.Failure, nil) |
|||
if err := rep.Write(conn); err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5 udp listen:", err) |
|||
} |
|||
} else { |
|||
if glog.V(LDEBUG) { |
|||
glog.Infoln(rep) |
|||
} |
|||
} |
|||
return |
|||
} |
|||
defer uconn.Close() |
|||
|
|||
addr := ToSocksAddr(uconn.LocalAddr()) |
|||
addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) |
|||
if glog.V(LINFO) { |
|||
glog.Infoln("socks5 udp:", addr) |
|||
} |
|||
rep := gosocks5.NewReply(gosocks5.Succeeded, addr) |
|||
if err := rep.Write(conn); err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln("socks5 udp:", err) |
|||
} |
|||
return |
|||
} else { |
|||
if glog.V(LDEBUG) { |
|||
glog.Infoln(rep) |
|||
} |
|||
} |
|||
srvTunnelUDP(conn, uconn) |
|||
} |
|||
} |
|||
|
|||
func srvTunnelUDP(conn net.Conn, uconn *net.UDPConn) { |
|||
go func() { |
|||
b := make([]byte, 16*1024) |
|||
|
|||
for { |
|||
n, addr, err := uconn.ReadFromUDP(b) |
|||
if err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln(err) |
|||
} |
|||
return |
|||
} |
|||
|
|||
udp := gosocks5.NewUDPDatagram( |
|||
gosocks5.NewUDPHeader(uint16(n), 0, ToSocksAddr(addr)), b[:n]) |
|||
//log.Println("r", udp.Header)
|
|||
if err := udp.Write(conn); err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln(err) |
|||
} |
|||
return |
|||
} |
|||
} |
|||
}() |
|||
|
|||
for { |
|||
udp, err := gosocks5.ReadUDPDatagram(conn) |
|||
if err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln(err) |
|||
} |
|||
return |
|||
} |
|||
//log.Println("w", udp.Header)
|
|||
addr, err := net.ResolveUDPAddr("udp", udp.Header.Addr.String()) |
|||
if err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln(err) |
|||
} |
|||
continue // drop silently
|
|||
} |
|||
|
|||
if _, err := uconn.WriteToUDP(udp.Data, addr); err != nil { |
|||
if glog.V(LWARNING) { |
|||
glog.Warningln(err) |
|||
} |
|||
return |
|||
} |
|||
} |
|||
} |
|||
|
|||
func ToSocksAddr(addr net.Addr) *gosocks5.Addr { |
|||
host, port, _ := net.SplitHostPort(addr.String()) |
|||
p, _ := strconv.Atoi(port) |
|||
|
|||
return &gosocks5.Addr{ |
|||
Type: gosocks5.AddrIPv4, |
|||
Host: host, |
|||
Port: uint16(p), |
|||
} |
|||
} |
|||
@ -1,296 +0,0 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"github.com/ginuerzh/gosocks5" |
|||
"github.com/shadowsocks/shadowsocks-go/shadowsocks" |
|||
"net" |
|||
//"strconv"
|
|||
"crypto/tls" |
|||
"log" |
|||
) |
|||
|
|||
const ( |
|||
rawCert = `-----BEGIN CERTIFICATE----- |
|||
MIIC5jCCAdCgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD |
|||
bzAeFw0xNDAzMTcwNjIwNTFaFw0xNTAzMTcwNjIwNTFaMBIxEDAOBgNVBAoTB0Fj |
|||
bWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDccNO1xmd4lWSf |
|||
d/0/QS3E93cYIWHw831i/IKxigdRD/XMZonLdEHywW6lOiXazaP8e6CqPGSmnl0x |
|||
5k/3dvGCMj2JCVxM6+z7NpL+AiwvXmvkj/TOciCgwqssCwYS2CiVwjfazRjx1ZUJ |
|||
VDC5qiyRsfktQ2fVHrpnJGVSRagmiQgwGWBilVG9B8QvRtpQKN/GQGq17oIQm8aK |
|||
kOdPt93g93ojMIg7YJpgDgOirvVz/hDn7YD4ryrtPos9CMafFkJprymKpRHyvz7P |
|||
8a3+OkuPjFjPnwOHQ5u1U3+8vC44vfb1ExWzDLoT8Xp8Gndx39k0f7MVOol3GnYu |
|||
MN/dvNUdAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIAoDATBgNVHSUEDDAKBggrBgEF |
|||
BQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDALBgkqhkiG |
|||
9w0BAQUDggEBAIG8CJqvTIgJnNOK+i5/IUc/3yF/mSCWuG8qP+Fmo2t6T0PVOtc0 |
|||
8wiWH5iWtCAhjn0MRY9l/hIjWm6gUZGHCGuEgsOPpJDYGoNLjH9Xwokm4y3LFNRK |
|||
UBrrrDbKRNibApBHCapPf6gC5sXcjOwx7P2/kiHDgY7YH47jfcRhtAPNsM4gjsEO |
|||
RmwENY+hRUFHIRfQTyalqND+x6PWhRo3K6hpHs4DQEYPq4P2kFPqUqSBymH+Ny5/ |
|||
BcQ3wdMNmC6Bm/oiL1QV0M+/InOsAgQk/EDd0kmoU1ZT2lYHQduGmP099bOlHNpS |
|||
uqO3vXF3q8SPPr/A9TqSs7BKkBQbe0+cdsA= |
|||
-----END CERTIFICATE-----` |
|||
|
|||
rawKey = `-----BEGIN RSA PRIVATE KEY----- |
|||
MIIEowIBAAKCAQEA3HDTtcZneJVkn3f9P0EtxPd3GCFh8PN9YvyCsYoHUQ/1zGaJ |
|||
y3RB8sFupTol2s2j/Hugqjxkpp5dMeZP93bxgjI9iQlcTOvs+zaS/gIsL15r5I/0 |
|||
znIgoMKrLAsGEtgolcI32s0Y8dWVCVQwuaoskbH5LUNn1R66ZyRlUkWoJokIMBlg |
|||
YpVRvQfEL0baUCjfxkBqte6CEJvGipDnT7fd4Pd6IzCIO2CaYA4Doq71c/4Q5+2A |
|||
+K8q7T6LPQjGnxZCaa8piqUR8r8+z/Gt/jpLj4xYz58Dh0ObtVN/vLwuOL329RMV |
|||
swy6E/F6fBp3cd/ZNH+zFTqJdxp2LjDf3bzVHQIDAQABAoIBAHal26147nQ+pHwY |
|||
jxwers3XDCjWvup7g79lfcqlKi79UiUEA6KYHm7UogMYewt7p4nb2KwH+XycvDiB |
|||
aAUf5flXpTs+6IkWauUDiLZi4PlV7uiEexUq5FjirlL0U/6MjbudX4bK4WQ4uxDc |
|||
WaV07Kw2iJFOOHLDKT0en9JaX5jtJNc4ZnE9efFoQ5jfypPWtRw65G1rULEg6nvc |
|||
GDh+1ce+4foCkpLRC9c24xAwJONZG6x3UqrSS9qfAsb73nWRQrTfUcO3nhoN8VvL |
|||
kL9skn1+S06NyUN0KoEtyRBp+RcpXSsBWAo6qZmo/WqhB/gjzWrxVwn20+yJSm35 |
|||
ZsMc6QECgYEA8GS+Mp9xfB2szWHz6YTOO1Uu4lHM1ccZMwS1G+dL0KO3uGAiPdvp |
|||
woVot6v6w88t7onXsLo5pgz7SYug0CpkF3K/MRd1Ar4lH7PK7IBQ6rFr9ppVxDbx |
|||
AEWRswUoPbKCr7W6HU8LbQHDavsDlEIwc6+DiwnL4BzlKjb7RpgQEz0CgYEA6sB5 |
|||
uHvx3Y5FDcGk1n73leQSAcq14l3ZLNpjrs8msoREDil/j5WmuSN58/7PGMiMgHEi |
|||
1vLm3H796JmvGr9OBvspOjHyk07ui2/We/j9Hoxm1VWhyi8HkLNDj70HKalTTFMz |
|||
RHO4O+0xCva+h9mKZrRMVktXr2jjdFn/0MYIZ2ECgYAIIsC1IeRLWQ3CHbCNlKsO |
|||
IwHlMvOFwKk/qsceXKOaOhA7szU1dr3gkXdL0Aw6mEZrrkqYdpUA46uVf54/rU+Z |
|||
445I8QxKvXiwK/uQKX+TkdGflPWWIG3jnnch4ejMvb/ihnn4B/bRB6A/fKNQXzUY |
|||
lTYUfI5j1VaEKTwz1W2l2QKBgByFCcSp+jZqhGUpc3dDsZyaOr3Q/Mvlju7uEVI5 |
|||
hIAHpaT60a6GBd1UPAqymEJwivFHzW3D0NxU6VAK68UaHMaoWNfjHY9b9YsnKS2i |
|||
kE3XzN56Ks+/avHfdYPO+UHMenw5V28nh+hv5pdoZrlmanQTz3pkaOC8o3WNQZEB |
|||
nh/BAoGBAMY5z2f1pmMhrvtPDSlEVjgjELbaInxFaxPLR4Pdyzn83gtIIU14+R8X |
|||
2LPs6PPwrNjWnIgrUSVXncIFL3pa45B+Mx1pYCpOAB1+nCZjIBQmpeo4Y0dwA/XH |
|||
85EthKPvoszm+OPbyI16OcePV5ocX7lupRYuAo0pek7bomhmHWHz |
|||
-----END RSA PRIVATE KEY-----` |
|||
) |
|||
|
|||
var ( |
|||
serverConfig = &gosocks5.Config{ |
|||
SelectMethod: serverSelectMethod, |
|||
MethodSelected: serverMethodSelected, |
|||
} |
|||
) |
|||
|
|||
type Socks5Server struct { |
|||
Addr string // TCP address to listen on
|
|||
} |
|||
|
|||
func (s *Socks5Server) ListenAndServe() error { |
|||
addr, err := net.ResolveTCPAddr("tcp", s.Addr) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
ln, err := net.ListenTCP("tcp", addr) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
defer ln.Close() |
|||
|
|||
for { |
|||
conn, err := ln.AcceptTCP() |
|||
if err != nil { |
|||
log.Println("accept:", err) |
|||
continue |
|||
} |
|||
//log.Println("accept", conn.RemoteAddr())
|
|||
|
|||
go serveSocks5(gosocks5.ServerConn(conn, serverConfig)) |
|||
} |
|||
} |
|||
|
|||
func serverSelectMethod(methods ...uint8) uint8 { |
|||
//log.Println(methods)
|
|||
m := gosocks5.MethodNoAuth |
|||
|
|||
for _, method := range methods { |
|||
if _, ok := Methods[method]; ok { |
|||
m = method |
|||
} |
|||
} |
|||
|
|||
// when user/pass is set for proxy auth, the NoAuth method is disabled
|
|||
if len(Method) == 0 && m == gosocks5.MethodNoAuth && listenUrl.User != nil { |
|||
return gosocks5.MethodNoAcceptable |
|||
} |
|||
|
|||
if len(Method) == 0 || Methods[m] == Method { |
|||
return m |
|||
} |
|||
|
|||
return gosocks5.MethodNoAcceptable |
|||
} |
|||
|
|||
func serverMethodSelected(method uint8, conn net.Conn) (net.Conn, error) { |
|||
//log.Println(method)
|
|||
switch method { |
|||
case gosocks5.MethodUserPass: |
|||
var username, password string |
|||
|
|||
if listenUrl != nil && listenUrl.User != nil { |
|||
username = listenUrl.User.Username() |
|||
password, _ = listenUrl.User.Password() |
|||
} |
|||
|
|||
if err := serverSocksAuth(conn, username, password); err != nil { |
|||
return nil, err |
|||
} |
|||
case MethodTLS, MethodTLSAuth: |
|||
var cert tls.Certificate |
|||
var err error |
|||
|
|||
if len(CertFile) == 0 || len(KeyFile) == 0 { |
|||
cert, err = tls.X509KeyPair([]byte(rawCert), []byte(rawKey)) |
|||
} else { |
|||
cert, err = tls.LoadX509KeyPair(CertFile, KeyFile) |
|||
} |
|||
|
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{cert}}) |
|||
if method == MethodTLSAuth { |
|||
// password is mandatory
|
|||
if len(Password) == 0 { |
|||
return nil, ErrEmptyAuth |
|||
} |
|||
if err := serverSocksAuth(conn, "", Password); err != nil { |
|||
return nil, err |
|||
} |
|||
} |
|||
case MethodAES128, MethodAES192, MethodAES256, |
|||
MethodDES, MethodBF, MethodCAST5, MethodRC4MD5, MethodRC4, MethodTable: |
|||
cipher, err := shadowsocks.NewCipher(Methods[method], Password) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
conn = shadowsocks.NewConn(conn, cipher) |
|||
case gosocks5.MethodNoAcceptable: |
|||
return nil, gosocks5.ErrBadMethod |
|||
} |
|||
|
|||
return conn, nil |
|||
} |
|||
|
|||
func serveSocks5(conn net.Conn) { |
|||
defer conn.Close() |
|||
|
|||
req, err := gosocks5.ReadRequest(conn) |
|||
if err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
|
|||
switch req.Cmd { |
|||
case gosocks5.CmdConnect: |
|||
//log.Println("connect", req.Addr.String())
|
|||
tconn, err := connect(req.Addr.String()) |
|||
if err != nil { |
|||
log.Println("connect", req.Addr.String(), err) |
|||
gosocks5.NewReply(gosocks5.HostUnreachable, nil).Write(conn) |
|||
return |
|||
} |
|||
defer tconn.Close() |
|||
|
|||
rep := gosocks5.NewReply(gosocks5.Succeeded, nil) |
|||
if err := rep.Write(conn); err != nil { |
|||
return |
|||
} |
|||
|
|||
if err := Transport(conn, tconn); err != nil { |
|||
//log.Println(err)
|
|||
} |
|||
case gosocks5.CmdBind: |
|||
l, err := net.ListenTCP("tcp", nil) |
|||
if err != nil { |
|||
gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) |
|||
log.Println("bind listen", err) |
|||
return |
|||
} |
|||
|
|||
addr := ToSocksAddr(l.Addr()) |
|||
addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) |
|||
log.Println("bind:", addr) |
|||
rep := gosocks5.NewReply(gosocks5.Succeeded, addr) |
|||
if err := rep.Write(conn); err != nil { |
|||
log.Println(err) |
|||
l.Close() |
|||
return |
|||
} |
|||
|
|||
tconn, err := l.AcceptTCP() |
|||
l.Close() // only accept one peer
|
|||
if err != nil { |
|||
log.Println("accept:", err) |
|||
gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) |
|||
return |
|||
} |
|||
defer tconn.Close() |
|||
|
|||
addr = ToSocksAddr(tconn.RemoteAddr()) |
|||
log.Println("accept peer:", addr.String()) |
|||
rep = gosocks5.NewReply(gosocks5.Succeeded, addr) |
|||
if err := rep.Write(conn); err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
|
|||
if err := Transport(conn, tconn); err != nil { |
|||
//log.Println(err)
|
|||
} |
|||
case gosocks5.CmdUdp: |
|||
uconn, err := net.ListenUDP("udp", nil) |
|||
if err != nil { |
|||
log.Println("udp listen", err) |
|||
gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) |
|||
return |
|||
} |
|||
defer uconn.Close() |
|||
|
|||
addr := ToSocksAddr(uconn.LocalAddr()) |
|||
addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) |
|||
log.Println("udp:", addr) |
|||
rep := gosocks5.NewReply(gosocks5.Succeeded, addr) |
|||
if err := rep.Write(conn); err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
srvTunnelUDP(conn, uconn) |
|||
} |
|||
} |
|||
|
|||
func srvTunnelUDP(conn net.Conn, uconn *net.UDPConn) { |
|||
go func() { |
|||
b := lpool.Take() |
|||
defer lpool.put(b) |
|||
|
|||
for { |
|||
n, addr, err := uconn.ReadFromUDP(b) |
|||
if err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
|
|||
udp := gosocks5.NewUDPDatagram( |
|||
gosocks5.NewUDPHeader(uint16(n), 0, ToSocksAddr(addr)), b[:n]) |
|||
//log.Println("r", udp.Header)
|
|||
if err := udp.Write(conn); err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
} |
|||
}() |
|||
|
|||
for { |
|||
udp, err := gosocks5.ReadUDPDatagram(conn) |
|||
if err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
//log.Println("w", udp.Header)
|
|||
addr, err := net.ResolveUDPAddr("udp", udp.Header.Addr.String()) |
|||
if err != nil { |
|||
log.Println(err) |
|||
continue // drop silently
|
|||
} |
|||
|
|||
if _, err := uconn.WriteToUDP(udp.Data, addr); err != nil { |
|||
log.Println(err) |
|||
return |
|||
} |
|||
} |
|||
} |
|||
@ -1,51 +0,0 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"crypto/tls" |
|||
"github.com/ginuerzh/gosocks5" |
|||
"net" |
|||
) |
|||
|
|||
type TlsServer struct { |
|||
Addr string |
|||
CertFile, KeyFile string |
|||
} |
|||
|
|||
func (s *TlsServer) ListenAndServe() error { |
|||
return s.listenAndServeTLS() |
|||
} |
|||
|
|||
func (s *TlsServer) listenAndServeTLS() error { |
|||
var cert tls.Certificate |
|||
var err error |
|||
|
|||
if len(s.CertFile) == 0 || len(s.KeyFile) == 0 { |
|||
cert, err = tls.X509KeyPair([]byte(rawCert), []byte(rawKey)) |
|||
} else { |
|||
cert, err = tls.LoadX509KeyPair(s.CertFile, s.KeyFile) |
|||
} |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
config := &tls.Config{Certificates: []tls.Certificate{cert}} |
|||
l, err := tls.Listen("tcp", s.Addr, config) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
defer l.Close() |
|||
|
|||
for { |
|||
conn, err := l.Accept() |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
go func(c net.Conn) { |
|||
c = gosocks5.ServerConn(c, serverConfig) |
|||
serveSocks5(c) |
|||
}(conn) |
|||
} |
|||
|
|||
return nil |
|||
} |
|||
@ -1,82 +1,119 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"github.com/ginuerzh/gosocks5" |
|||
//"github.com/ginuerzh/gosocks5"
|
|||
"github.com/golang/glog" |
|||
"github.com/gorilla/websocket" |
|||
"log" |
|||
"net" |
|||
"net/http" |
|||
"net/http/httputil" |
|||
"net/url" |
|||
"time" |
|||
) |
|||
|
|||
type WSConn struct { |
|||
*websocket.Conn |
|||
rb []byte |
|||
type wsConn struct { |
|||
conn *websocket.Conn |
|||
rb []byte |
|||
} |
|||
|
|||
func NewWSConn(conn *websocket.Conn) *WSConn { |
|||
c := &WSConn{ |
|||
Conn: conn, |
|||
func wsClient(conn net.Conn, host string) (*wsConn, error) { |
|||
c, resp, err := websocket.NewClient(conn, &url.URL{Host: host, Path: "/ws"}, nil, 1024, 1024) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
resp.Body.Close() |
|||
|
|||
return c |
|||
return &wsConn{conn: c}, nil |
|||
} |
|||
|
|||
func (conn *WSConn) Read(b []byte) (n int, err error) { |
|||
if len(conn.rb) == 0 { |
|||
_, conn.rb, err = conn.ReadMessage() |
|||
func wsServer(conn *websocket.Conn) *wsConn { |
|||
return &wsConn{ |
|||
conn: conn, |
|||
} |
|||
n = copy(b, conn.rb) |
|||
conn.rb = conn.rb[n:] |
|||
} |
|||
|
|||
func (c *wsConn) Read(b []byte) (n int, err error) { |
|||
if len(c.rb) == 0 { |
|||
_, c.rb, err = c.conn.ReadMessage() |
|||
} |
|||
n = copy(b, c.rb) |
|||
c.rb = c.rb[n:] |
|||
|
|||
//log.Println("ws r:", n)
|
|||
|
|||
return |
|||
} |
|||
|
|||
func (conn *WSConn) Write(b []byte) (n int, err error) { |
|||
err = conn.WriteMessage(websocket.BinaryMessage, b) |
|||
func (c *wsConn) Write(b []byte) (n int, err error) { |
|||
err = c.conn.WriteMessage(websocket.BinaryMessage, b) |
|||
n = len(b) |
|||
//log.Println("ws w:", n)
|
|||
|
|||
return |
|||
} |
|||
|
|||
func (conn *WSConn) SetDeadline(t time.Time) error { |
|||
func (c *wsConn) Close() error { |
|||
return c.conn.Close() |
|||
} |
|||
|
|||
func (c *wsConn) LocalAddr() net.Addr { |
|||
return c.conn.LocalAddr() |
|||
} |
|||
|
|||
func (c *wsConn) RemoteAddr() net.Addr { |
|||
return c.conn.RemoteAddr() |
|||
} |
|||
|
|||
func (conn *wsConn) SetDeadline(t time.Time) error { |
|||
if err := conn.SetReadDeadline(t); err != nil { |
|||
return err |
|||
} |
|||
return conn.SetWriteDeadline(t) |
|||
} |
|||
func (c *wsConn) SetReadDeadline(t time.Time) error { |
|||
return c.conn.SetReadDeadline(t) |
|||
} |
|||
|
|||
type WSServer struct { |
|||
Addr string |
|||
func (c *wsConn) SetWriteDeadline(t time.Time) error { |
|||
return c.conn.SetWriteDeadline(t) |
|||
} |
|||
|
|||
var upgrader = websocket.Upgrader{ |
|||
ReadBufferSize: 8192, |
|||
WriteBufferSize: 8192, |
|||
CheckOrigin: func(r *http.Request) bool { return true }, |
|||
type ws struct { |
|||
upgrader websocket.Upgrader |
|||
arg Args |
|||
} |
|||
|
|||
func (s *WSServer) handle(w http.ResponseWriter, r *http.Request) { |
|||
conn, err := upgrader.Upgrade(w, r, nil) |
|||
if err != nil { |
|||
log.Println(err) |
|||
return |
|||
func NewWs(arg Args) *ws { |
|||
return &ws{ |
|||
arg: arg, |
|||
upgrader: websocket.Upgrader{ |
|||
ReadBufferSize: 1024, |
|||
WriteBufferSize: 1024, |
|||
CheckOrigin: func(r *http.Request) bool { return true }, |
|||
}, |
|||
} |
|||
//defer conn.Close()
|
|||
} |
|||
|
|||
c := gosocks5.ServerConn(NewWSConn(conn), serverConfig) |
|||
/* |
|||
if err := c.Handleshake(); err != nil { |
|||
log.Println(err) |
|||
return |
|||
func (s *ws) handle(w http.ResponseWriter, r *http.Request) { |
|||
if glog.V(LDEBUG) { |
|||
dump, err := httputil.DumpRequest(r, false) |
|||
if err != nil { |
|||
glog.Infoln(err) |
|||
} else { |
|||
glog.Infoln(string(dump)) |
|||
} |
|||
*/ |
|||
serveSocks5(c) |
|||
} |
|||
conn, err := s.upgrader.Upgrade(w, r, nil) |
|||
if err != nil { |
|||
if glog.V(LERROR) { |
|||
glog.Errorln(err) |
|||
} |
|||
return |
|||
} |
|||
handleConn(wsServer(conn), s.arg) |
|||
} |
|||
|
|||
func (s *WSServer) ListenAndServe() error { |
|||
http.HandleFunc("/", s.handle) |
|||
return http.ListenAndServe(s.Addr, nil) |
|||
func (s *ws) ListenAndServe() error { |
|||
http.HandleFunc("/ws", s.handle) |
|||
return http.ListenAndServe(s.arg.Addr, nil) |
|||
} |
|||
|
|||
Loading…
Reference in new issue