gost v2.0 init

This commit is contained in:
rui.zheng 2015-09-25 17:00:06 +08:00
parent 82e624d8ce
commit 037a79a00d
16 changed files with 882 additions and 1814 deletions

22
LICENSE
View File

@ -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
View File

@ -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端相同的加密方式。
当设置的加密方式为tlstls-auth时-key参数可手动指定公钥文件-cert参数可手动指定私钥文件如果未指定则使用默认的公钥与私钥。
当设置的加密方式为tls时-p参数无效为tls-auth时通过-p参数设置认证密码且不能为空。
当设置的加密方式为非tlstls-auth时-key-cert参数无效通过-p参数设置加密密码且不能为空。

18
cert2.pem Normal file
View 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
View File

@ -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
View 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
}

323
http.go
View File

@ -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)
func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) {
if glog.V(LDEBUG) {
dump, err := httputil.DumpRequest(req, false)
if err != nil {
glog.Infoln(err)
} else {
setBasicAuth(req)
err = req.WriteProxy(conn.c)
glog.Infoln(string(dump))
}
}
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 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 {
cs := string(c)
s := strings.IndexByte(cs, ':')
if s < 0 {
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 {
log.Println(err)
return nil, err
}
defer c.Close()
setBasicAuth(req)
if err := req.WriteProxy(c); err != nil {
log.Println(err)
return nil, err
}
/*
b, err := ioutil.ReadAll(c)
if err != nil {
log.Println(err)
return nil, err
}
*/
return http.ReadResponse(bufio.NewReader(c), req)
}
return http.DefaultClient.Do(req)
return cs[:s], cs[s+1:], true
}

27
key.pem Normal file
View 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-----

90
main.go
View File

@ -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}
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(server.ListenAndServe())
return
}
log.Fatal(listenAndServe(laddr, cliHandle))
}()
}
wg.Wait()
}

108
pool.go
View File

@ -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
}

View File

@ -1,6 +0,0 @@
package main
type Server interface {
ListenAndServe() error
}

400
socks.go Normal file
View 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
View File

@ -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
View File

@ -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
}

295
util.go
View File

@ -1,95 +1,104 @@
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)
return &gosocks5.Addr{
Type: gosocks5.AddrIPv4,
Host: host,
Port: uint16(p),
for _, s := range ss {
if !strings.Contains(s, "://") {
s = "hs://" + s
}
}
func dial(addr string) (net.Conn, error) {
taddr, err := net.ResolveTCPAddr("tcp", addr)
u, err := url.Parse(s)
if err != nil {
return nil, err
if glog.V(LWARNING) {
glog.Warningln(err)
}
return net.DialTCP("tcp", nil, taddr)
continue
}
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)
}
return
}
func connect(addr string) (net.Conn, error) {
if !strings.Contains(addr, ":") {
addr += ":80"
}
/*
if proxyURL == nil {
return dial(addr)
}
@ -102,161 +111,13 @@ func connect(addr string) (net.Conn, error) {
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
}
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)

View File

@ -5,7 +5,7 @@ import (
)
const (
Version = "1.8"
Version = "2.0"
)
func printVersion() {

113
ws.go
View File

@ -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
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,
func (c *wsConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}
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 *WSServer) handle(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
func (s *ws) handle(w http.ResponseWriter, r *http.Request) {
if glog.V(LDEBUG) {
dump, err := httputil.DumpRequest(r, false)
if err != nil {
log.Println(err)
glog.Infoln(err)
} else {
glog.Infoln(string(dump))
}
}
conn, err := s.upgrader.Upgrade(w, r, nil)
if err != nil {
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)
}