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 |
package main |
||||
|
|
||||
import ( |
import ( |
||||
"bufio" |
"encoding/base64" |
||||
"bytes" |
"github.com/golang/glog" |
||||
"code.google.com/p/go-uuid/uuid" |
|
||||
"errors" |
|
||||
"github.com/ginuerzh/gosocks5" |
|
||||
"io" |
|
||||
"io/ioutil" |
|
||||
"log" |
|
||||
"net" |
"net" |
||||
"net/http" |
"net/http" |
||||
"net/url" |
"net/http/httputil" |
||||
"time" |
"strings" |
||||
) |
) |
||||
|
|
||||
const ( |
func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) { |
||||
s2cUri = "/s2c" |
if glog.V(LDEBUG) { |
||||
c2sUri = "/c2s" |
dump, err := httputil.DumpRequest(req, false) |
||||
) |
if err != nil { |
||||
|
glog.Infoln(err) |
||||
type HttpClientConn struct { |
} else { |
||||
c net.Conn |
glog.Infoln(string(dump)) |
||||
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 |
|
||||
} |
} |
||||
} |
} |
||||
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 { |
var username, password string |
||||
if !conn.closed { |
if arg.User != nil { |
||||
close(conn.c) |
username = arg.User.Username() |
||||
conn.closed = true |
password, _ = arg.User.Password() |
||||
} |
} |
||||
return nil |
|
||||
} |
|
||||
|
|
||||
func (conn *HttpServerConn) LocalAddr() net.Addr { |
|
||||
return nil |
|
||||
} |
|
||||
|
|
||||
func (conn *HttpServerConn) RemoteAddr() net.Addr { |
u, p, _ := proxyBasicAuth(req.Header.Get("Proxy-Authorization")) |
||||
return nil |
req.Header.Del("Proxy-Authorization") |
||||
} |
|
||||
|
|
||||
func (conn *HttpServerConn) SetDeadline(t time.Time) error { |
if (username != "" && u != username) || (password != "" && p != password) { |
||||
return nil |
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 { |
if _, err := conn.Write([]byte(resp)); err != nil { |
||||
return nil |
if glog.V(LWARNING) { |
||||
} |
glog.Warningln(err) |
||||
|
} |
||||
func (conn *HttpServerConn) SetWriteDeadline(t time.Time) error { |
} |
||||
return nil |
if glog.V(LDEBUG) { |
||||
} |
glog.Infoln(resp) |
||||
|
} |
||||
type HttpServer struct { |
if glog.V(LWARNING) { |
||||
Addr string |
glog.Warningln("http: proxy authentication required") |
||||
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 { |
|
||||
return |
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) { |
func proxyBasicAuth(authInfo string) (username, password string, ok bool) { |
||||
defer func() { |
if authInfo == "" { |
||||
if err := recover(); err != nil { |
|
||||
log.Println(err) |
|
||||
} |
|
||||
}() |
|
||||
|
|
||||
if r.Method != "POST" { |
|
||||
w.WriteHeader(http.StatusMethodNotAllowed) |
|
||||
return |
return |
||||
} |
} |
||||
|
|
||||
token := r.FormValue("token") |
if !strings.HasPrefix(authInfo, "Basic ") { |
||||
conn := s.conns[token] |
|
||||
if conn == nil { |
|
||||
w.WriteHeader(http.StatusBadRequest) |
|
||||
return |
return |
||||
} |
} |
||||
b, err := ioutil.ReadAll(r.Body) |
c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(authInfo, "Basic ")) |
||||
if err != nil || len(b) == 0 { |
if err != nil { |
||||
conn.Close() |
|
||||
delete(s.conns, token) |
|
||||
//log.Println(token, "disconnected")
|
|
||||
return |
return |
||||
} |
} |
||||
conn.c <- b |
cs := string(c) |
||||
} |
s := strings.IndexByte(cs, ':') |
||||
|
if s < 0 { |
||||
func (s *HttpServer) ListenAndServe() error { |
return |
||||
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) |
|
||||
} |
} |
||||
|
|
||||
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 ( |
import ( |
||||
"flag" |
"flag" |
||||
"log" |
"github.com/golang/glog" |
||||
"net/url" |
"sync" |
||||
"time" |
) |
||||
|
|
||||
|
const ( |
||||
|
LFATAL = iota |
||||
|
LERROR |
||||
|
LWARNING |
||||
|
LINFO |
||||
|
LDEBUG |
||||
) |
) |
||||
|
|
||||
var ( |
var ( |
||||
Laddr, Saddr, Proxy string |
listenUrl, proxyUrl, forwardUrl string |
||||
UseWebsocket, UseHttp, UseTLS bool |
|
||||
Shadows bool |
|
||||
SMethod, SPassword string |
|
||||
Method, Password string |
|
||||
CertFile, KeyFile string |
|
||||
PrintVersion bool |
|
||||
|
|
||||
proxyURL *url.URL |
listenArgs []Args |
||||
listenUrl *url.URL |
proxyArgs []Args |
||||
|
forwardArgs []Args |
||||
) |
) |
||||
|
|
||||
func init() { |
func init() { |
||||
flag.StringVar(&Proxy, "P", "", "proxy for forward") |
flag.StringVar(&listenUrl, "L", ":http", "local address") |
||||
flag.StringVar(&Saddr, "S", "", "the server that connect to") |
flag.StringVar(&forwardUrl, "S", "", "remote address") |
||||
flag.StringVar(&Laddr, "L", ":8080", "listen address") |
flag.StringVar(&proxyUrl, "P", "", "proxy 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() |
|
||||
|
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile) |
flag.Parse() |
||||
|
|
||||
proxyURL, _ = parseURL(Proxy) |
listenArgs = parseArgs(listenUrl) |
||||
listenUrl, _ = parseURL(Laddr) |
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() { |
func main() { |
||||
if PrintVersion { |
defer glog.Flush() |
||||
printVersion() |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
laddr := listenUrl.Host |
if len(listenArgs) == 0 { |
||||
|
glog.Fatalln("no listen addr") |
||||
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 |
|
||||
} |
} |
||||
|
|
||||
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 |
package main |
||||
|
|
||||
import ( |
import ( |
||||
"github.com/ginuerzh/gosocks5" |
//"github.com/ginuerzh/gosocks5"
|
||||
|
"github.com/golang/glog" |
||||
"github.com/gorilla/websocket" |
"github.com/gorilla/websocket" |
||||
"log" |
"net" |
||||
"net/http" |
"net/http" |
||||
|
"net/http/httputil" |
||||
|
"net/url" |
||||
"time" |
"time" |
||||
) |
) |
||||
|
|
||||
type WSConn struct { |
type wsConn struct { |
||||
*websocket.Conn |
conn *websocket.Conn |
||||
rb []byte |
rb []byte |
||||
} |
} |
||||
|
|
||||
func NewWSConn(conn *websocket.Conn) *WSConn { |
func wsClient(conn net.Conn, host string) (*wsConn, error) { |
||||
c := &WSConn{ |
c, resp, err := websocket.NewClient(conn, &url.URL{Host: host, Path: "/ws"}, nil, 1024, 1024) |
||||
Conn: conn, |
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) { |
func wsServer(conn *websocket.Conn) *wsConn { |
||||
if len(conn.rb) == 0 { |
return &wsConn{ |
||||
_, conn.rb, err = conn.ReadMessage() |
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)
|
//log.Println("ws r:", n)
|
||||
|
|
||||
return |
return |
||||
} |
} |
||||
|
|
||||
func (conn *WSConn) Write(b []byte) (n int, err error) { |
func (c *wsConn) Write(b []byte) (n int, err error) { |
||||
err = conn.WriteMessage(websocket.BinaryMessage, b) |
err = c.conn.WriteMessage(websocket.BinaryMessage, b) |
||||
n = len(b) |
n = len(b) |
||||
//log.Println("ws w:", n)
|
//log.Println("ws w:", n)
|
||||
|
|
||||
return |
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 { |
if err := conn.SetReadDeadline(t); err != nil { |
||||
return err |
return err |
||||
} |
} |
||||
return conn.SetWriteDeadline(t) |
return conn.SetWriteDeadline(t) |
||||
} |
} |
||||
|
func (c *wsConn) SetReadDeadline(t time.Time) error { |
||||
|
return c.conn.SetReadDeadline(t) |
||||
|
} |
||||
|
|
||||
type WSServer struct { |
func (c *wsConn) SetWriteDeadline(t time.Time) error { |
||||
Addr string |
return c.conn.SetWriteDeadline(t) |
||||
} |
} |
||||
|
|
||||
var upgrader = websocket.Upgrader{ |
type ws struct { |
||||
ReadBufferSize: 8192, |
upgrader websocket.Upgrader |
||||
WriteBufferSize: 8192, |
arg Args |
||||
CheckOrigin: func(r *http.Request) bool { return true }, |
|
||||
} |
} |
||||
|
|
||||
func (s *WSServer) handle(w http.ResponseWriter, r *http.Request) { |
func NewWs(arg Args) *ws { |
||||
conn, err := upgrader.Upgrade(w, r, nil) |
return &ws{ |
||||
if err != nil { |
arg: arg, |
||||
log.Println(err) |
upgrader: websocket.Upgrader{ |
||||
return |
ReadBufferSize: 1024, |
||||
|
WriteBufferSize: 1024, |
||||
|
CheckOrigin: func(r *http.Request) bool { return true }, |
||||
|
}, |
||||
} |
} |
||||
//defer conn.Close()
|
} |
||||
|
|
||||
c := gosocks5.ServerConn(NewWSConn(conn), serverConfig) |
func (s *ws) handle(w http.ResponseWriter, r *http.Request) { |
||||
/* |
if glog.V(LDEBUG) { |
||||
if err := c.Handleshake(); err != nil { |
dump, err := httputil.DumpRequest(r, false) |
||||
log.Println(err) |
if err != nil { |
||||
return |
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 { |
func (s *ws) ListenAndServe() error { |
||||
http.HandleFunc("/", s.handle) |
http.HandleFunc("/ws", s.handle) |
||||
return http.ListenAndServe(s.Addr, nil) |
return http.ListenAndServe(s.arg.Addr, nil) |
||||
} |
} |
||||
|
|||||
Loading…
Reference in new issue