Skip to content

Commit

Permalink
Merge branch 'feature/socsk5'
Browse files Browse the repository at this point in the history
  • Loading branch information
wweir committed Mar 23, 2019
2 parents d35115b + 53748cf commit 64778f6
Show file tree
Hide file tree
Showing 8 changed files with 245 additions and 39 deletions.
2 changes: 1 addition & 1 deletion conf/conf_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

func initArgs() {
flag.StringVar(&Conf.ConfigFile, "f", filepath.Dir(os.Args[0])+"/sower.toml", "config file location")
flag.StringVar(&Conf.NetType, "n", "TCP", "proxy net type: "+strings.Join(transport.ListTransports(), ","))
flag.StringVar(&Conf.NetType, "n", "TCP", "net type (socks5 client only): "+strings.Join(transport.ListTransports(), ","))
flag.StringVar(&Conf.Cipher, "C", "AES_128_GCM", "cipher type: "+strings.Join(shadow.ListCiphers(), ","))
flag.StringVar(&Conf.Password, "p", "12345678", "password")
flag.StringVar(&Conf.ServerPort, "P", "5533", "server mode listen port")
Expand Down
2 changes: 1 addition & 1 deletion conf/sower.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
net_type="TCP" # TCP, KCP, QUIC
net_type="TCP" # TCP, KCP, QUIC, SOCKS5(client only)
cipher="AES_128_GCM" # AES_128_GCM, AES_192_GCM, AES_256_GCM, CHACHA20_IETF_POLY1305, XCHACHA20_IETF_POLY1305
password="12345678"
server_port="5533"
Expand Down
7 changes: 4 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ func main() {
return "load rules", nil
})

isSocks5 := (cfg.NetType == "SOCKS5")

if cfg.HTTPProxy != "" {
go proxy.StartHttpProxy(tran, cfg.ServerAddr,
cfg.Cipher, cfg.Password, cfg.HTTPProxy)
go proxy.StartHttpProxy(tran, isSocks5, cfg.ServerAddr, cfg.Cipher, cfg.Password, cfg.HTTPProxy)
}

go dns.StartDNS(cfg.DNSServer, cfg.ClientIP, conf.SuggestCh, cfg.SuggestLevel)
proxy.StartClient(tran, cfg.ServerAddr, cfg.Cipher, cfg.Password, cfg.ClientIP)
proxy.StartClient(tran, isSocks5, cfg.ServerAddr, cfg.Cipher, cfg.Password, cfg.ClientIP)
}
}
22 changes: 11 additions & 11 deletions proxy/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,14 @@ import (
"github.com/wweir/sower/proxy/transport"
)

func StartClient(tran transport.Transport, server, cipher, password, listenIP string) {
func StartClient(tran transport.Transport, isSocks5 bool, server, cipher, password, listenIP string) {
connCh := listenLocal(listenIP, []string{"80", "443"})
resolved := false

glog.Infoln("Client started.")
for {
conn := <-connCh

if !resolved {
if addr, err := net.ResolveTCPAddr("tcp", server); err != nil {
glog.Errorln(err)
} else {
server = addr.String()
resolved = true
}
}
resolveAddr(&server)
glog.V(1).Infof("new conn from (%s) to (%s)", conn.RemoteAddr(), server)

rc, err := tran.Dial(server)
Expand All @@ -32,7 +24,15 @@ func StartClient(tran transport.Transport, server, cipher, password, listenIP st
glog.Errorln(err)
continue
}
rc = shadow.Shadow(rc, cipher, password)

if isSocks5 {
if rc, conn, err = buildSocks5Conn(rc, conn); err != nil {
glog.Errorln(err)
continue
}
} else {
rc = shadow.Shadow(rc, cipher, password)
}

go relay(conn, rc)
}
Expand Down
52 changes: 30 additions & 22 deletions proxy/http_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,24 @@ import (
"io"
"net"
"net/http"
"net/url"
"time"

"github.com/golang/glog"
"github.com/wweir/sower/proxy/shadow"
"github.com/wweir/sower/proxy/transport"
)

func StartHttpProxy(tran transport.Transport, server, cipher, password, addr string) {
resolved := false

func StartHttpProxy(tran transport.Transport, isSocks5 bool, server, cipher, password, addr string) {
srv := &http.Server{
Addr: addr,
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !resolved {
if addr, err := net.ResolveTCPAddr("tcp", server); err != nil {
glog.Errorln(err)
} else {
server = addr.String()
resolved = true
}
}
resolveAddr(&server)

if r.Method == http.MethodConnect {
httpsProxy(w, r, tran, server, cipher, password)
httpsProxy(w, r, tran, isSocks5, server, cipher, password)
} else {
httpProxy(w, r, tran, server, cipher, password)
httpProxy(w, r, tran, isSocks5, server, cipher, password)
}
}),
// Disable HTTP/2.
Expand All @@ -43,20 +35,28 @@ func StartHttpProxy(tran transport.Transport, server, cipher, password, addr str
}

func httpProxy(w http.ResponseWriter, req *http.Request,
tran transport.Transport, server, cipher, password string) {
tran transport.Transport, isSocks5 bool, server, cipher, password string) {

roundTripper := &http.Transport{
DialContext: func(context.Context, string, string) (net.Conn, error) {
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}

if isSocks5 {
roundTripper.Proxy = func(*http.Request) (*url.URL, error) {
return url.Parse("socks5://" + server)
}

} else {
roundTripper.DialContext = func(context.Context, string, string) (net.Conn, error) {
conn, err := tran.Dial(server)
if err != nil {
return nil, err
}
return shadow.Shadow(conn, cipher, password), nil
},
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
}

resp, err := roundTripper.RoundTrip(req)
Expand All @@ -77,7 +77,7 @@ func httpProxy(w http.ResponseWriter, req *http.Request,
}

func httpsProxy(w http.ResponseWriter, r *http.Request,
tran transport.Transport, server, cipher, password string) {
tran transport.Transport, isSocks5 bool, server, cipher, password string) {

// local conn
conn, _, err := w.(http.Hijacker).Hijack()
Expand All @@ -101,7 +101,15 @@ func httpsProxy(w http.ResponseWriter, r *http.Request,
glog.Errorln("serve https proxy, dial remote fail:", err)
return
}
rc = shadow.Shadow(rc, cipher, password)

if isSocks5 {
if rc, conn, err = buildSocks5Conn(rc, conn); err != nil {
glog.Errorln(err)
return
}
} else {
rc = shadow.Shadow(rc, cipher, password)
}

relay(rc, conn)
}
154 changes: 154 additions & 0 deletions proxy/socks5/socks5.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package socks5

import (
"encoding/binary"
"io"
"net"

"github.com/pkg/errors"
)

func ToSocks5(c net.Conn, domain, port string) net.Conn {
switch port {
case "80":
return &conn{init: make(chan struct{}), Conn: c, domain: domain, port: []byte{0x00, 0x50}}
case "443":
return &conn{init: make(chan struct{}), Conn: c, domain: domain, port: []byte{0x01, 0xbb}}
default:
panic("invalid port: " + port)
}
}

type conn struct {
init chan struct{}
domain string
port []byte
net.Conn
}

func (c *conn) Read(b []byte) (n int, err error) {
<-c.init
return c.Conn.Read(b)
}

func (c *conn) Write(b []byte) (n int, err error) {
select {
case <-c.init:
return c.Conn.Write(b)
default:
}

{
req := &authReq{
VER: 5,
NMETHODS: 1,
METHODS: [1]byte{0}, // NO AUTHENTICATION REQUIRED
}
if err := binary.Write(c.Conn, binary.BigEndian, req); err != nil {
return 0, err
}
}
{
resp := &authResp{}
if err := binary.Read(c.Conn, binary.BigEndian, resp); err != nil {
return 0, err
}
}
{
req := &request{
req: req{
VER: 5, // socks5
CMD: 1, // CONNECT
RSV: 0, // RESERVED
ATYP: 3, // DOMAINNAME
},
DST_ADDR: append([]byte{byte(len(c.domain))}, []byte(c.domain)...),
DST_PORT: c.port,
}

if _, err := c.Conn.Write(req.Bytes()); err != nil {
return 0, err
}
}
{
resp := &response{}
if err := binary.Read(c.Conn, binary.BigEndian, &(resp.resp)); err != nil {
return 0, err
}

switch resp.REP {
case 0x00:
default:
return 0, errors.Errorf("socks5 handshake fail, return code: %d", resp.REP)
}

switch resp.ATYP {
case 0x01: // IPv4
resp.DST_ADDR = make([]byte, net.IPv4len)
if _, err := io.ReadFull(c.Conn, resp.DST_ADDR); err != nil {
return 0, err
}
case 0x03: // domain name
if _, err := io.ReadFull(c.Conn, resp.DST_ADDR[:1]); err != nil {
return 0, err
}
if _, err := io.ReadFull(c.Conn, resp.DST_ADDR[1:1+int(resp.DST_ADDR[0])]); err != nil {
return 0, err
}
case 0x04:
resp.DST_ADDR = make([]byte, net.IPv6len)
if _, err := io.ReadFull(c.Conn, resp.DST_ADDR); err != nil {
return 0, err
}
}

resp.DST_PORT = make([]byte, 2)
if _, err := io.ReadFull(c.Conn, resp.DST_PORT); err != nil {
return 0, err
}
}

close(c.init)
return c.Conn.Write(b)
}

type authReq struct {
VER byte
NMETHODS byte
METHODS [1]byte // 1 to 255, fix to no authentication
}

type authResp struct {
VER byte
METHOD byte
}

type request struct {
req
DST_ADDR []byte // first byte is length
DST_PORT []byte // two bytes
}
type req struct {
VER byte
CMD byte
RSV byte
ATYP byte
}

func (r *request) Bytes() []byte {
out := []byte{r.VER, r.CMD, r.RSV, r.ATYP}
out = append(out, r.DST_ADDR...)
return append(out, r.DST_PORT...)
}

type response struct {
resp
DST_ADDR []byte // first byte is length
DST_PORT []byte // two bytes
}
type resp struct {
VER byte
REP byte
RSV byte
ATYP byte
}
11 changes: 10 additions & 1 deletion proxy/transport/tcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ import (

type tcp struct {
DialTimeout time.Duration
isSocks5 bool
}

func init() {
transports["TCP"] = &tcp{
DialTimeout: 5 * time.Second,
}
transports["SOCKS5"] = &tcp{
DialTimeout: 5 * time.Second,
isSocks5: true,
}
}

func (t *tcp) Dial(server string) (net.Conn, error) {
Expand All @@ -27,7 +32,11 @@ func (t *tcp) Dial(server string) (net.Conn, error) {
return conn, nil
}

func (*tcp) Listen(port string) (<-chan net.Conn, error) {
func (t *tcp) Listen(port string) (<-chan net.Conn, error) {
if t.isSocks5 {
panic("not support run as socks5 server")
}

ln, err := net.Listen("tcp", port)
if err != nil {
return nil, err
Expand Down
Loading

0 comments on commit 64778f6

Please sign in to comment.