gost v2.0 init
This commit is contained in:
parent
82e624d8ce
commit
037a79a00d
22
LICENSE
22
LICENSE
@ -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.
|
|
||||||
|
|
172
README.md
172
README.md
@ -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="ginuerzh@gmail.com": 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:123456@127.0.0.1:8080`
|
|
||||||
* socks5代理: `gost -L=:8899 -P=socks://127.0.0.1:1080`
|
|
||||||
* socks5代理(需认证): `gost -L=:8899 -P=socks://admin:123456@127.0.0.1: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加密密码(默认为ginuerzh@gmail.com)
|
|
||||||
|
|
||||||
当无-ss参数时,-sm, -sp参数无效。以上三个参数对服务端无效。
|
|
||||||
|
|
||||||
###### 相关命令
|
|
||||||
* 客户端: `gost -L :8899 -S server_ip:port -sm=rc4-md5 -sp=ginuerzh@gmail.com -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参数设置加密密码,且不能为空。
|
|
||||||
|
|
||||||
|
|
18
cert2.pem
Normal file
18
cert2.pem
Normal file
@ -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-----
|
563
client.go
563
client.go
@ -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
|
|
||||||
}
|
|
176
conn.go
Normal file
176
conn.go
Normal file
@ -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
|
||||||
|
}
|
331
http.go
331
http.go
@ -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)
|
||||||
)
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (conn *HttpServerConn) LocalAddr() net.Addr {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (conn *HttpServerConn) RemoteAddr() net.Addr {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (conn *HttpServerConn) SetDeadline(t time.Time) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
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)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
token := r.FormValue("token")
|
|
||||||
conn := s.conns[token]
|
|
||||||
if conn == nil {
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b, err := ioutil.ReadAll(r.Body)
|
|
||||||
if err != nil || len(b) == 0 {
|
|
||||||
conn.Close()
|
|
||||||
delete(s.conns, token)
|
|
||||||
//log.Println(token, "disconnected")
|
|
||||||
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 {
|
if err != nil {
|
||||||
log.Println(err)
|
glog.Infoln(err)
|
||||||
return nil, err
|
} else {
|
||||||
|
glog.Infoln(string(dump))
|
||||||
}
|
}
|
||||||
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)
|
var username, password string
|
||||||
|
if arg.User != nil {
|
||||||
|
username = arg.User.Username()
|
||||||
|
password, _ = arg.User.Password()
|
||||||
|
}
|
||||||
|
|
||||||
|
u, p, _ := proxyBasicAuth(req.Header.Get("Proxy-Authorization"))
|
||||||
|
req.Header.Del("Proxy-Authorization")
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func proxyBasicAuth(authInfo string) (username, password string, ok bool) {
|
||||||
|
if authInfo == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(authInfo, "Basic ") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(authInfo, "Basic "))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cs := string(c)
|
||||||
|
s := strings.IndexByte(cs, ':')
|
||||||
|
if s < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return cs[:s], cs[s+1:], true
|
||||||
}
|
}
|
||||||
|
27
key.pem
Normal file
27
key.pem
Normal file
@ -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-----
|
92
main.go
92
main.go
@ -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", "ginuerzh@gmail.com", "shadowsocks cipher password")
|
|
||||||
flag.BoolVar(&PrintVersion, "v", false, "print version")
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
listenArgs = parseArgs(listenUrl)
|
||||||
|
proxyArgs = parseArgs(proxyUrl)
|
||||||
proxyURL, _ = parseURL(Proxy)
|
forwardArgs = parseArgs(forwardUrl)
|
||||||
listenUrl, _ = parseURL(Laddr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
if len(listenArgs) == 0 {
|
||||||
|
glog.Fatalln("no listen addr")
|
||||||
}
|
}
|
||||||
|
|
||||||
laddr := listenUrl.Host
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
if len(Saddr) == 0 {
|
for _, arg := range listenArgs {
|
||||||
var server Server
|
wg.Add(1)
|
||||||
if UseTLS {
|
go func() {
|
||||||
server = &TlsServer{Addr: laddr, CertFile: CertFile, KeyFile: KeyFile}
|
defer wg.Done()
|
||||||
} else if UseWebsocket {
|
if err := listenAndServe(arg); err != nil {
|
||||||
server = &WSServer{Addr: laddr}
|
if glog.V(LFATAL) {
|
||||||
} else if UseHttp {
|
glog.Errorln(err)
|
||||||
server = &HttpServer{Addr: laddr}
|
}
|
||||||
} else {
|
}
|
||||||
server = &Socks5Server{Addr: laddr}
|
}()
|
||||||
}
|
|
||||||
log.Fatal(server.ListenAndServe())
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
wg.Wait()
|
||||||
log.Fatal(listenAndServe(laddr, cliHandle))
|
|
||||||
}
|
}
|
||||||
|
108
pool.go
108
pool.go
@ -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
|
|
||||||
}
|
|
400
socks.go
Normal file
400
socks.go
Normal file
@ -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),
|
||||||
|
}
|
||||||
|
}
|
296
socks5.go
296
socks5.go
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
51
tls.go
51
tls.go
@ -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
|
|
||||||
}
|
|
315
util.go
315
util.go
@ -1,262 +1,123 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"crypto/tls"
|
||||||
//"bytes"
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/ginuerzh/gosocks5"
|
"fmt"
|
||||||
|
"github.com/golang/glog"
|
||||||
"io"
|
"io"
|
||||||
//"log"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// socks://admin:123456@localhost:8080
|
||||||
MethodTLS uint8 = 0x80 + iota
|
type Args struct {
|
||||||
MethodAES128
|
Addr string // host:port
|
||||||
MethodAES192
|
Protocol string // protocol: hs/http/socks/socks5/ss, default is hs(http+socks5)
|
||||||
MethodAES256
|
Transport string // transport: tcp/ws/tls, default is tcp(raw tcp)
|
||||||
MethodDES
|
User *url.Userinfo
|
||||||
MethodBF
|
EncMeth string // data encryption method
|
||||||
MethodCAST5
|
EncPass string // data encryption password
|
||||||
MethodRC4MD5
|
Cert tls.Certificate // tls certificate
|
||||||
MethodRC4
|
|
||||||
MethodTable
|
|
||||||
MethodTLSAuth
|
|
||||||
)
|
|
||||||
|
|
||||||
var ErrEmptyAuth = errors.New("empty auth")
|
|
||||||
|
|
||||||
var Methods = map[uint8]string{
|
|
||||||
//gosocks5.MethodNoAuth: "", // 0x00
|
|
||||||
gosocks5.MethodUserPass: "userpass", // 0x02
|
|
||||||
MethodTLS: "tls", // 0x80
|
|
||||||
MethodAES128: "aes-128-cfb", // 0x81
|
|
||||||
MethodAES192: "aes-192-cfb", // 0x82
|
|
||||||
MethodAES256: "aes-256-cfb", // 0x83
|
|
||||||
MethodDES: "des-cfb", // 0x84
|
|
||||||
MethodBF: "bf-cfb", // 0x85
|
|
||||||
MethodCAST5: "cast5-cfb", // 0x86
|
|
||||||
MethodRC4MD5: "rc4-md5", // 8x87
|
|
||||||
MethodRC4: "rc4", // 0x88
|
|
||||||
MethodTable: "table", // 0x89
|
|
||||||
MethodTLSAuth: "tls-auth", // 0x90
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseURL(rawurl string) (*url.URL, error) {
|
func (args Args) String() string {
|
||||||
if len(rawurl) == 0 {
|
var authUser, authPass string
|
||||||
return nil, nil
|
if args.User != nil {
|
||||||
|
authUser = args.User.Username()
|
||||||
|
authPass, _ = args.User.Password()
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(rawurl, "http://") &&
|
return fmt.Sprintf("host: %s, proto: %s, trans: %s, auth: %s:%s, enc: %s:%s",
|
||||||
!strings.HasPrefix(rawurl, "socks://") {
|
args.Addr, args.Protocol, args.Transport, authUser, authPass,
|
||||||
rawurl = "http://" + rawurl
|
args.EncMeth, args.EncPass)
|
||||||
}
|
|
||||||
return url.Parse(rawurl)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseUserPass(key string) (username string, password string) {
|
func parseArgs(rawurl string) (args []Args) {
|
||||||
sep := ":"
|
ss := strings.Split(rawurl, ",")
|
||||||
i := strings.Index(key, sep)
|
if rawurl == "" || len(ss) == 0 {
|
||||||
if i < 0 {
|
return nil
|
||||||
return key, ""
|
|
||||||
}
|
}
|
||||||
return key[0:i], key[i+len(sep):]
|
|
||||||
}
|
|
||||||
|
|
||||||
func ToSocksAddr(addr net.Addr) *gosocks5.Addr {
|
for _, s := range ss {
|
||||||
host, port, _ := net.SplitHostPort(addr.String())
|
if !strings.Contains(s, "://") {
|
||||||
p, _ := strconv.Atoi(port)
|
s = "hs://" + s
|
||||||
|
}
|
||||||
|
u, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
if glog.V(LWARNING) {
|
||||||
|
glog.Warningln(err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
return &gosocks5.Addr{
|
arg := Args{
|
||||||
Type: gosocks5.AddrIPv4,
|
Addr: u.Host,
|
||||||
Host: host,
|
User: u.User,
|
||||||
Port: uint16(p),
|
}
|
||||||
|
|
||||||
|
schemes := strings.Split(u.Scheme, "+")
|
||||||
|
if len(schemes) == 1 {
|
||||||
|
switch schemes[0] {
|
||||||
|
case "http", "socks", "socks5", "ss":
|
||||||
|
arg.Protocol = schemes[0]
|
||||||
|
case "ws", "tls", "tcp":
|
||||||
|
arg.Transport = schemes[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(schemes) == 2 {
|
||||||
|
arg.Protocol = schemes[0]
|
||||||
|
arg.Transport = schemes[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
arg.Cert, err = tls.LoadX509KeyPair("cert.pem", "key.pem")
|
||||||
|
if err != nil {
|
||||||
|
if glog.V(LFATAL) {
|
||||||
|
glog.Errorln(err, ", tls will not be supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mp := strings.Split(strings.Trim(u.Path, "/"), ":")
|
||||||
|
if len(mp) == 1 {
|
||||||
|
arg.EncMeth = mp[0]
|
||||||
|
}
|
||||||
|
if len(mp) == 2 {
|
||||||
|
arg.EncMeth = mp[0]
|
||||||
|
arg.EncPass = mp[1]
|
||||||
|
}
|
||||||
|
if glog.V(LINFO) {
|
||||||
|
glog.Infoln(arg)
|
||||||
|
}
|
||||||
|
args = append(args, arg)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func dial(addr string) (net.Conn, error) {
|
return
|
||||||
taddr, err := net.ResolveTCPAddr("tcp", addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return net.DialTCP("tcp", nil, taddr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func connect(addr string) (net.Conn, error) {
|
func connect(addr string) (net.Conn, error) {
|
||||||
if !strings.Contains(addr, ":") {
|
if !strings.Contains(addr, ":") {
|
||||||
addr += ":80"
|
addr += ":80"
|
||||||
}
|
}
|
||||||
if proxyURL == nil {
|
/*
|
||||||
return dial(addr)
|
if proxyURL == nil {
|
||||||
}
|
return dial(addr)
|
||||||
|
|
||||||
switch proxyURL.Scheme {
|
|
||||||
case "socks": // socks5 proxy
|
|
||||||
return connectSocks5Proxy(addr)
|
|
||||||
case "http": // http proxy
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
return connectHTTPProxy(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func connectHTTPProxy(addr string) (conn net.Conn, err error) {
|
|
||||||
conn, err = dial(proxyURL.Host)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req := &http.Request{
|
|
||||||
Method: "CONNECT",
|
|
||||||
URL: &url.URL{Host: addr},
|
|
||||||
Host: addr,
|
|
||||||
Header: make(http.Header),
|
|
||||||
}
|
|
||||||
req.Header.Set("Proxy-Connection", "keep-alive")
|
|
||||||
setBasicAuth(req)
|
|
||||||
|
|
||||||
if err = req.Write(conn); err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
conn.Close()
|
|
||||||
//log.Println(resp.Status)
|
|
||||||
return nil, errors.New(resp.Status)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func connectSocks5Proxy(addr string) (conn net.Conn, err error) {
|
|
||||||
conn, err = dial(proxyURL.Host)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
conf := &gosocks5.Config{
|
|
||||||
// Methods: []uint8{gosocks5.MethodNoAuth, gosocks5.MethodUserPass},
|
|
||||||
MethodSelected: proxyMethodSelected,
|
|
||||||
}
|
|
||||||
if proxyURL.User != nil {
|
|
||||||
conf.Methods = []uint8{gosocks5.MethodUserPass}
|
|
||||||
}
|
|
||||||
|
|
||||||
c := gosocks5.ClientConn(conn, conf)
|
|
||||||
if err := c.Handleshake(); err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
conn = c
|
|
||||||
|
|
||||||
s := strings.Split(addr, ":")
|
|
||||||
host := s[0]
|
|
||||||
port := 80
|
|
||||||
if len(s) == 2 {
|
|
||||||
n, _ := strconv.ParseUint(s[1], 10, 16)
|
|
||||||
port = int(n)
|
|
||||||
}
|
|
||||||
a := &gosocks5.Addr{
|
|
||||||
Type: gosocks5.AddrDomain,
|
|
||||||
Host: host,
|
|
||||||
Port: uint16(port),
|
|
||||||
}
|
|
||||||
if err := gosocks5.NewRequest(gosocks5.CmdConnect, a).Write(conn); err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rep, err := gosocks5.ReadReply(conn)
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if rep.Rep != gosocks5.Succeeded {
|
|
||||||
conn.Close()
|
|
||||||
return nil, errors.New("Socks Failture")
|
|
||||||
}
|
|
||||||
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func proxyMethodSelected(method uint8, conn net.Conn) (net.Conn, error) {
|
|
||||||
switch method {
|
|
||||||
case gosocks5.MethodUserPass:
|
|
||||||
var user, pass string
|
|
||||||
|
|
||||||
if proxyURL != nil && proxyURL.User != nil {
|
|
||||||
user = proxyURL.User.Username()
|
|
||||||
pass, _ = proxyURL.User.Password()
|
|
||||||
}
|
}
|
||||||
if err := clientSocksAuth(conn, user, pass); err != nil {
|
|
||||||
return nil, err
|
switch proxyURL.Scheme {
|
||||||
|
case "socks": // socks5 proxy
|
||||||
|
return connectSocks5Proxy(addr)
|
||||||
|
case "http": // http proxy
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
return connectHTTPProxy(addr)
|
||||||
}
|
}
|
||||||
case gosocks5.MethodNoAcceptable:
|
*/
|
||||||
return nil, gosocks5.ErrBadMethod
|
return nil, errors.New("not implemented")
|
||||||
}
|
|
||||||
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func clientSocksAuth(conn net.Conn, username, password string) error {
|
|
||||||
if err := gosocks5.NewUserPassRequest(
|
|
||||||
gosocks5.UserPassVer, username, password).Write(conn); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
res, err := gosocks5.ReadUserPassResponse(conn)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if res.Status != gosocks5.Succeeded {
|
|
||||||
return gosocks5.ErrAuthFailure
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func serverSocksAuth(conn net.Conn, username, password string) error {
|
|
||||||
req, err := gosocks5.ReadUserPassRequest(conn)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len(username) > 0 && req.Username != username) ||
|
|
||||||
(len(password) > 0 && req.Password != password) {
|
|
||||||
if err := gosocks5.NewUserPassResponse(
|
|
||||||
gosocks5.UserPassVer, gosocks5.Failure).Write(conn); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return gosocks5.ErrAuthFailure
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := gosocks5.NewUserPassResponse(
|
|
||||||
gosocks5.UserPassVer, gosocks5.Succeeded).Write(conn); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setBasicAuth(r *http.Request) {
|
|
||||||
if proxyURL != nil && proxyURL.User != nil {
|
|
||||||
r.Header.Set("Proxy-Authorization",
|
|
||||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(proxyURL.User.String())))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// based on io.Copy
|
// based on io.Copy
|
||||||
func Copy(dst io.Writer, src io.Reader) (written int64, err error) {
|
func Copy(dst io.Writer, src io.Reader) (written int64, err error) {
|
||||||
buf := lpool.Take()
|
buf := make([]byte, 32*1024)
|
||||||
defer lpool.put(buf)
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
nr, er := src.Read(buf)
|
nr, er := src.Read(buf)
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "1.8"
|
Version = "2.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
func printVersion() {
|
func printVersion() {
|
||||||
|
117
ws.go
117
ws.go
@ -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 {
|
||||||
type WSServer struct {
|
return c.conn.SetReadDeadline(t)
|
||||||
Addr string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var upgrader = websocket.Upgrader{
|
func (c *wsConn) SetWriteDeadline(t time.Time) error {
|
||||||
ReadBufferSize: 8192,
|
return c.conn.SetWriteDeadline(t)
|
||||||
WriteBufferSize: 8192,
|
|
||||||
CheckOrigin: func(r *http.Request) bool { return true },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WSServer) handle(w http.ResponseWriter, r *http.Request) {
|
type ws struct {
|
||||||
conn, err := upgrader.Upgrade(w, r, nil)
|
upgrader websocket.Upgrader
|
||||||
|
arg Args
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWs(arg Args) *ws {
|
||||||
|
return &ws{
|
||||||
|
arg: arg,
|
||||||
|
upgrader: websocket.Upgrader{
|
||||||
|
ReadBufferSize: 1024,
|
||||||
|
WriteBufferSize: 1024,
|
||||||
|
CheckOrigin: func(r *http.Request) bool { return true },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn, err := s.upgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
if glog.V(LERROR) {
|
||||||
|
glog.Errorln(err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//defer conn.Close()
|
handleConn(wsServer(conn), s.arg)
|
||||||
|
|
||||||
c := gosocks5.ServerConn(NewWSConn(conn), serverConfig)
|
|
||||||
/*
|
|
||||||
if err := c.Handleshake(); err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
serveSocks5(c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
Block a user