Skip to content

Commit

Permalink
add socks5
Browse files Browse the repository at this point in the history
  • Loading branch information
StinkyPeach committed Jan 19, 2021
1 parent 705f442 commit 2b41262
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 21 deletions.
14 changes: 7 additions & 7 deletions adapter/outbound/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ func ParseProxy(mapping map[string]interface{}) (C.Proxy, error) {
break
}
proxy, err = NewShadowSocksR(*ssrOption)
//case "socks5":
// socksOption := &Socks5Option{}
// err = decoder.Decode(mapping, socksOption)
// if err != nil {
// break
// }
// proxy = NewSocks5(*socksOption)
case "socks5":
socksOption := &Socks5Option{}
err = decoder.Decode(mapping, socksOption)
if err != nil {
break
}
proxy = NewSocks5(*socksOption)
//case "http":
// httpOption := &HttpOption{}
// err = decoder.Decode(mapping, httpOption)
Expand Down
190 changes: 190 additions & 0 deletions adapter/outbound/socks5.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package outbound

import (
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"strconv"

"github.com/StinkyPeach/bridge/component/dialer"
"github.com/StinkyPeach/bridge/component/socks5"
C "github.com/StinkyPeach/bridge/constant"
)

type Socks5 struct {
*Base
user string
pass string
tls bool
skipCertVerify bool
tlsConfig *tls.Config
}

type Socks5Option struct {
Name string `proxy:"name"`
Server string `proxy:"server"`
Port int `proxy:"port"`
UserName string `proxy:"username,omitempty"`
Password string `proxy:"password,omitempty"`
TLS bool `proxy:"tls,omitempty"`
UDP bool `proxy:"udp,omitempty"`
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
}

func (ss *Socks5) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
if ss.tls {
cc := tls.Client(c, ss.tlsConfig)
err := cc.Handshake()
c = cc
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
}
}

var user *socks5.User
if ss.user != "" {
user = &socks5.User{
Username: ss.user,
Password: ss.pass,
}
}
if _, err := socks5.ClientHandshake(c, serializesSocksAddr(metadata), socks5.CmdConnect, user); err != nil {
return nil, err
}
return c, nil
}

func (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) {
c, err := dialer.DialContext(ctx, "tcp", ss.addr)
if err != nil {
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
}
tcpKeepAlive(c)

c, err = ss.StreamConn(c, metadata)
if err != nil {
return nil, err
}

return NewConn(c, ss), nil
}

func (ss *Socks5) DialUDP(metadata *C.Metadata) (_ C.PacketConn, err error) {
ctx, cancel := context.WithTimeout(context.Background(), tcpTimeout)
defer cancel()
c, err := dialer.DialContext(ctx, "tcp", ss.addr)
if err != nil {
err = fmt.Errorf("%s connect error: %w", ss.addr, err)
return
}

if ss.tls {
cc := tls.Client(c, ss.tlsConfig)
err = cc.Handshake()
c = cc
}

defer func() {
if err != nil {
c.Close()
}
}()

tcpKeepAlive(c)
var user *socks5.User
if ss.user != "" {
user = &socks5.User{
Username: ss.user,
Password: ss.pass,
}
}

bindAddr, err := socks5.ClientHandshake(c, serializesSocksAddr(metadata), socks5.CmdUDPAssociate, user)
if err != nil {
err = fmt.Errorf("client hanshake error: %w", err)
return
}

pc, err := dialer.ListenPacket("udp", "")
if err != nil {
return
}

go func() {
io.Copy(ioutil.Discard, c)
c.Close()
// A UDP association terminates when the TCP connection that the UDP
// ASSOCIATE request arrived on terminates. RFC1928
pc.Close()
}()

return newPacketConn(&socksPacketConn{PacketConn: pc, rAddr: bindAddr.UDPAddr(), tcpConn: c}, ss), nil
}

func NewSocks5(option Socks5Option) *Socks5 {
var tlsConfig *tls.Config
if option.TLS {
tlsConfig = &tls.Config{
InsecureSkipVerify: option.SkipCertVerify,
ClientSessionCache: getClientSessionCache(),
ServerName: option.Server,
}
}

return &Socks5{
Base: &Base{
name: option.Name,
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
tp: C.Socks5,
udp: option.UDP,
},
user: option.UserName,
pass: option.Password,
tls: option.TLS,
skipCertVerify: option.SkipCertVerify,
tlsConfig: tlsConfig,
}
}

type socksPacketConn struct {
net.PacketConn
rAddr net.Addr
tcpConn net.Conn
}

func (uc *socksPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
packet, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)
if err != nil {
return
}
return uc.PacketConn.WriteTo(packet, uc.rAddr)
}

func (uc *socksPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
n, _, e := uc.PacketConn.ReadFrom(b)
if e != nil {
return 0, nil, e
}
addr, payload, err := socks5.DecodeUDPPacket(b)
if err != nil {
return 0, nil, err
}

udpAddr := addr.UDPAddr()
if udpAddr == nil {
return 0, nil, errors.New("parse udp addr error")
}

// due to DecodeUDPPacket is mutable, record addr length
copy(b, payload)
return n - len(addr) - 3, udpAddr, nil
}

func (uc *socksPacketConn) Close() error {
uc.tcpConn.Close()
return uc.PacketConn.Close()
}
40 changes: 26 additions & 14 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,32 @@ import (
func main() {
log.Info("this is ssr test")

ssrNode := make(map[string]interface{})
ssrNode["name"] = "test"
ssrNode["type"] = "ssr"
ssrNode["server"] = "127.0.0.1"
ssrNode["port"] = 8388
ssrNode["password"] = "password"
ssrNode["cipher"] = "rc4-md5"
ssrNode["obfs"] = "http_post"
ssrNode["obfs-param"] = ""
ssrNode["protocol"] = "origin"
ssrNode["protocol-param"] = ""
ssrNode["udp"] = true

p, err := outbound.ParseProxy(ssrNode)
//ssrNode := make(map[string]interface{})
//ssrNode["name"] = "test"
//ssrNode["type"] = "ssr"
//ssrNode["server"] = "127.0.0.1"
//ssrNode["port"] = 8388
//ssrNode["password"] = "password"
//ssrNode["cipher"] = "rc4-md5"
//ssrNode["obfs"] = "http_post"
//ssrNode["obfs-param"] = ""
//ssrNode["protocol"] = "origin"
//ssrNode["protocol-param"] = ""
//ssrNode["udp"] = true
//
//p, err := outbound.ParseProxy(ssrNode)


socks5Node := make(map[string]interface{})
socks5Node["name"] = "test"
socks5Node["type"] = "socks5"
socks5Node["server"] = "127.0.0.1"
socks5Node["port"] = 7890
socks5Node["udp"] = false
socks5Node["skip-cert-verify"] = true

p, err := outbound.ParseProxy(socks5Node)


if err != nil {
fmt.Println(err)
Expand Down

0 comments on commit 2b41262

Please sign in to comment.