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
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"code.google.com/p/go-uuid/uuid"
|
||||
"errors"
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"encoding/base64"
|
||||
"github.com/golang/glog"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
s2cUri = "/s2c"
|
||||
c2sUri = "/c2s"
|
||||
)
|
||||
|
||||
type HttpClientConn struct {
|
||||
c net.Conn
|
||||
token string
|
||||
r io.ReadCloser
|
||||
}
|
||||
|
||||
func NewHttpClientConn(conn net.Conn) *HttpClientConn {
|
||||
return &HttpClientConn{
|
||||
c: conn,
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *HttpClientConn) Handshake() (err error) {
|
||||
//log.Println("remote", conn.c.RemoteAddr().String())
|
||||
req := &http.Request{
|
||||
Method: "GET",
|
||||
URL: &url.URL{
|
||||
Host: Saddr,
|
||||
Scheme: "http",
|
||||
Path: s2cUri,
|
||||
},
|
||||
Header: make(http.Header),
|
||||
}
|
||||
if proxyURL == nil {
|
||||
err = req.Write(conn.c)
|
||||
} else {
|
||||
setBasicAuth(req)
|
||||
err = req.WriteProxy(conn.c)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(conn.c), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New(resp.Status)
|
||||
}
|
||||
|
||||
b := make([]byte, 36)
|
||||
if _, err = io.ReadFull(resp.Body, b); err != nil {
|
||||
return err
|
||||
}
|
||||
if uuid.Parse(string(b)) == nil {
|
||||
return errors.New("Handshake: wrong token")
|
||||
}
|
||||
conn.token = string(b)
|
||||
conn.r = resp.Body
|
||||
//log.Println(conn.token, "connected")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conn *HttpClientConn) Read(b []byte) (n int, err error) {
|
||||
n, err = conn.r.Read(b)
|
||||
//log.Println("http r:", n)
|
||||
return
|
||||
}
|
||||
|
||||
func (conn *HttpClientConn) Write(b []byte) (n int, err error) {
|
||||
q := url.Values{}
|
||||
q.Set("token", conn.token)
|
||||
req := &http.Request{
|
||||
Method: "POST",
|
||||
Body: ioutil.NopCloser(bytes.NewReader(b)),
|
||||
ContentLength: int64(len(b)),
|
||||
URL: &url.URL{
|
||||
Host: Saddr,
|
||||
Scheme: "http",
|
||||
Path: c2sUri,
|
||||
RawQuery: q.Encode(),
|
||||
},
|
||||
Header: make(http.Header),
|
||||
}
|
||||
resp, err := doRequest(req)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
//log.Println(resp.Status)
|
||||
return 0, errors.New(resp.Status)
|
||||
}
|
||||
//log.Println("http w:", len(b))
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (conn *HttpClientConn) Close() error {
|
||||
conn.Write(nil)
|
||||
return conn.r.Close()
|
||||
}
|
||||
|
||||
func (conn *HttpClientConn) LocalAddr() net.Addr {
|
||||
return conn.c.LocalAddr()
|
||||
}
|
||||
|
||||
func (conn *HttpClientConn) RemoteAddr() net.Addr {
|
||||
return conn.c.RemoteAddr()
|
||||
}
|
||||
|
||||
func (conn *HttpClientConn) SetDeadline(t time.Time) error {
|
||||
return conn.c.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (conn *HttpClientConn) SetReadDeadline(t time.Time) error {
|
||||
return conn.c.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (conn *HttpClientConn) SetWriteDeadline(t time.Time) error {
|
||||
return conn.c.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
type HttpServerConn struct {
|
||||
w http.ResponseWriter
|
||||
c chan []byte
|
||||
closed bool
|
||||
rb []byte
|
||||
}
|
||||
|
||||
func NewHttpServerConn(w http.ResponseWriter, c chan []byte) *HttpServerConn {
|
||||
return &HttpServerConn{
|
||||
w: w,
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *HttpServerConn) Read(b []byte) (n int, err error) {
|
||||
if len(conn.rb) == 0 {
|
||||
var ok bool
|
||||
if conn.rb, ok = <-conn.c; !ok {
|
||||
return 0, io.EOF
|
||||
}
|
||||
}
|
||||
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)
|
||||
func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) {
|
||||
if glog.V(LDEBUG) {
|
||||
dump, err := httputil.DumpRequest(req, false)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
glog.Infoln(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 (
|
||||
"flag"
|
||||
"log"
|
||||
"net/url"
|
||||
"time"
|
||||
"github.com/golang/glog"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
LFATAL = iota
|
||||
LERROR
|
||||
LWARNING
|
||||
LINFO
|
||||
LDEBUG
|
||||
)
|
||||
|
||||
var (
|
||||
Laddr, Saddr, Proxy string
|
||||
UseWebsocket, UseHttp, UseTLS bool
|
||||
Shadows bool
|
||||
SMethod, SPassword string
|
||||
Method, Password string
|
||||
CertFile, KeyFile string
|
||||
PrintVersion bool
|
||||
listenUrl, proxyUrl, forwardUrl string
|
||||
|
||||
proxyURL *url.URL
|
||||
listenUrl *url.URL
|
||||
listenArgs []Args
|
||||
proxyArgs []Args
|
||||
forwardArgs []Args
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&Proxy, "P", "", "proxy for forward")
|
||||
flag.StringVar(&Saddr, "S", "", "the server that connect to")
|
||||
flag.StringVar(&Laddr, "L", ":8080", "listen address")
|
||||
flag.StringVar(&Method, "m", "", "tunnel cipher method")
|
||||
flag.StringVar(&Password, "p", "", "tunnel cipher password")
|
||||
flag.StringVar(&CertFile, "cert", "", "tls cert file")
|
||||
flag.StringVar(&KeyFile, "key", "", "tls key file")
|
||||
flag.BoolVar(&Shadows, "ss", false, "run as shadowsocks server")
|
||||
flag.BoolVar(&UseTLS, "tls", false, "use ssl/tls tunnel")
|
||||
flag.BoolVar(&UseWebsocket, "ws", false, "use websocket tunnel")
|
||||
flag.BoolVar(&UseHttp, "http", false, "use http tunnel")
|
||||
flag.StringVar(&SMethod, "sm", "rc4-md5", "shadowsocks cipher method")
|
||||
flag.StringVar(&SPassword, "sp", "ginuerzh@gmail.com", "shadowsocks cipher password")
|
||||
flag.BoolVar(&PrintVersion, "v", false, "print version")
|
||||
flag.StringVar(&listenUrl, "L", ":http", "local address")
|
||||
flag.StringVar(&forwardUrl, "S", "", "remote address")
|
||||
flag.StringVar(&proxyUrl, "P", "", "proxy address")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
proxyURL, _ = parseURL(Proxy)
|
||||
listenUrl, _ = parseURL(Laddr)
|
||||
listenArgs = parseArgs(listenUrl)
|
||||
proxyArgs = parseArgs(proxyUrl)
|
||||
forwardArgs = parseArgs(forwardUrl)
|
||||
}
|
||||
|
||||
var (
|
||||
spool = NewMemPool(1024, 120*time.Minute, 1024) // 1k size buffer pool
|
||||
mpool = NewMemPool(16*1024, 60*time.Minute, 512) // 16k size buffer pool
|
||||
lpool = NewMemPool(32*1024, 30*time.Minute, 256) // 32k size buffer pool
|
||||
)
|
||||
|
||||
func main() {
|
||||
if PrintVersion {
|
||||
printVersion()
|
||||
return
|
||||
defer glog.Flush()
|
||||
|
||||
if len(listenArgs) == 0 {
|
||||
glog.Fatalln("no listen addr")
|
||||
}
|
||||
|
||||
laddr := listenUrl.Host
|
||||
var wg sync.WaitGroup
|
||||
|
||||
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
|
||||
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)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
log.Fatal(listenAndServe(laddr, cliHandle))
|
||||
wg.Wait()
|
||||
}
|
||||
|
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
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
//"bytes"
|
||||
"encoding/base64"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
"fmt"
|
||||
"github.com/golang/glog"
|
||||
"io"
|
||||
//"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
MethodTLS uint8 = 0x80 + iota
|
||||
MethodAES128
|
||||
MethodAES192
|
||||
MethodAES256
|
||||
MethodDES
|
||||
MethodBF
|
||||
MethodCAST5
|
||||
MethodRC4MD5
|
||||
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
|
||||
// socks://admin:123456@localhost:8080
|
||||
type Args struct {
|
||||
Addr string // host:port
|
||||
Protocol string // protocol: hs/http/socks/socks5/ss, default is hs(http+socks5)
|
||||
Transport string // transport: tcp/ws/tls, default is tcp(raw tcp)
|
||||
User *url.Userinfo
|
||||
EncMeth string // data encryption method
|
||||
EncPass string // data encryption password
|
||||
Cert tls.Certificate // tls certificate
|
||||
}
|
||||
|
||||
func parseURL(rawurl string) (*url.URL, error) {
|
||||
if len(rawurl) == 0 {
|
||||
return nil, nil
|
||||
func (args Args) String() string {
|
||||
var authUser, authPass string
|
||||
if args.User != nil {
|
||||
authUser = args.User.Username()
|
||||
authPass, _ = args.User.Password()
|
||||
}
|
||||
if !strings.HasPrefix(rawurl, "http://") &&
|
||||
!strings.HasPrefix(rawurl, "socks://") {
|
||||
rawurl = "http://" + rawurl
|
||||
}
|
||||
return url.Parse(rawurl)
|
||||
return fmt.Sprintf("host: %s, proto: %s, trans: %s, auth: %s:%s, enc: %s:%s",
|
||||
args.Addr, args.Protocol, args.Transport, authUser, authPass,
|
||||
args.EncMeth, args.EncPass)
|
||||
}
|
||||
|
||||
func parseUserPass(key string) (username string, password string) {
|
||||
sep := ":"
|
||||
i := strings.Index(key, sep)
|
||||
if i < 0 {
|
||||
return key, ""
|
||||
func parseArgs(rawurl string) (args []Args) {
|
||||
ss := strings.Split(rawurl, ",")
|
||||
if rawurl == "" || len(ss) == 0 {
|
||||
return nil
|
||||
}
|
||||
return key[0:i], key[i+len(sep):]
|
||||
}
|
||||
|
||||
func ToSocksAddr(addr net.Addr) *gosocks5.Addr {
|
||||
host, port, _ := net.SplitHostPort(addr.String())
|
||||
p, _ := strconv.Atoi(port)
|
||||
for _, s := range ss {
|
||||
if !strings.Contains(s, "://") {
|
||||
s = "hs://" + s
|
||||
}
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
if glog.V(LWARNING) {
|
||||
glog.Warningln(err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
return &gosocks5.Addr{
|
||||
Type: gosocks5.AddrIPv4,
|
||||
Host: host,
|
||||
Port: uint16(p),
|
||||
arg := Args{
|
||||
Addr: u.Host,
|
||||
User: u.User,
|
||||
}
|
||||
|
||||
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) {
|
||||
taddr, err := net.ResolveTCPAddr("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return net.DialTCP("tcp", nil, taddr)
|
||||
return
|
||||
}
|
||||
|
||||
func connect(addr string) (net.Conn, error) {
|
||||
if !strings.Contains(addr, ":") {
|
||||
addr += ":80"
|
||||
}
|
||||
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 proxyURL == nil {
|
||||
return dial(addr)
|
||||
}
|
||||
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 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())))
|
||||
}
|
||||
*/
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// based on io.Copy
|
||||
func Copy(dst io.Writer, src io.Reader) (written int64, err error) {
|
||||
buf := lpool.Take()
|
||||
defer lpool.put(buf)
|
||||
buf := make([]byte, 32*1024)
|
||||
|
||||
for {
|
||||
nr, er := src.Read(buf)
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
Version = "1.8"
|
||||
Version = "2.0"
|
||||
)
|
||||
|
||||
func printVersion() {
|
||||
|
117
ws.go
117
ws.go
@ -1,82 +1,119 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
//"github.com/ginuerzh/gosocks5"
|
||||
"github.com/golang/glog"
|
||||
"github.com/gorilla/websocket"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WSConn struct {
|
||||
*websocket.Conn
|
||||
rb []byte
|
||||
type wsConn struct {
|
||||
conn *websocket.Conn
|
||||
rb []byte
|
||||
}
|
||||
|
||||
func NewWSConn(conn *websocket.Conn) *WSConn {
|
||||
c := &WSConn{
|
||||
Conn: conn,
|
||||
func wsClient(conn net.Conn, host string) (*wsConn, error) {
|
||||
c, resp, err := websocket.NewClient(conn, &url.URL{Host: host, Path: "/ws"}, nil, 1024, 1024)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
return c
|
||||
return &wsConn{conn: c}, nil
|
||||
}
|
||||
|
||||
func (conn *WSConn) Read(b []byte) (n int, err error) {
|
||||
if len(conn.rb) == 0 {
|
||||
_, conn.rb, err = conn.ReadMessage()
|
||||
func wsServer(conn *websocket.Conn) *wsConn {
|
||||
return &wsConn{
|
||||
conn: conn,
|
||||
}
|
||||
n = copy(b, conn.rb)
|
||||
conn.rb = conn.rb[n:]
|
||||
}
|
||||
|
||||
func (c *wsConn) Read(b []byte) (n int, err error) {
|
||||
if len(c.rb) == 0 {
|
||||
_, c.rb, err = c.conn.ReadMessage()
|
||||
}
|
||||
n = copy(b, c.rb)
|
||||
c.rb = c.rb[n:]
|
||||
|
||||
//log.Println("ws r:", n)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (conn *WSConn) Write(b []byte) (n int, err error) {
|
||||
err = conn.WriteMessage(websocket.BinaryMessage, b)
|
||||
func (c *wsConn) Write(b []byte) (n int, err error) {
|
||||
err = c.conn.WriteMessage(websocket.BinaryMessage, b)
|
||||
n = len(b)
|
||||
//log.Println("ws w:", n)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (conn *WSConn) SetDeadline(t time.Time) error {
|
||||
func (c *wsConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *wsConn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *wsConn) RemoteAddr() net.Addr {
|
||||
return c.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (conn *wsConn) SetDeadline(t time.Time) error {
|
||||
if err := conn.SetReadDeadline(t); err != nil {
|
||||
return err
|
||||
}
|
||||
return conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
type WSServer struct {
|
||||
Addr string
|
||||
func (c *wsConn) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 8192,
|
||||
WriteBufferSize: 8192,
|
||||
CheckOrigin: func(r *http.Request) bool { return true },
|
||||
func (c *wsConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func (s *WSServer) handle(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
type ws struct {
|
||||
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 {
|
||||
log.Println(err)
|
||||
if glog.V(LERROR) {
|
||||
glog.Errorln(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
//defer conn.Close()
|
||||
|
||||
c := gosocks5.ServerConn(NewWSConn(conn), serverConfig)
|
||||
/*
|
||||
if err := c.Handleshake(); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
*/
|
||||
serveSocks5(c)
|
||||
handleConn(wsServer(conn), s.arg)
|
||||
}
|
||||
|
||||
func (s *WSServer) ListenAndServe() error {
|
||||
http.HandleFunc("/", s.handle)
|
||||
return http.ListenAndServe(s.Addr, nil)
|
||||
func (s *ws) ListenAndServe() error {
|
||||
http.HandleFunc("/ws", s.handle)
|
||||
return http.ListenAndServe(s.arg.Addr, nil)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user