github.com/chwjbn/xclash@v0.2.0/adapter/outbound/shadowsocksr.go (about) 1 package outbound 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "strconv" 8 9 "github.com/chwjbn/xclash/component/dialer" 10 C "github.com/chwjbn/xclash/constant" 11 "github.com/chwjbn/xclash/transport/ssr/obfs" 12 "github.com/chwjbn/xclash/transport/ssr/protocol" 13 14 "github.com/Dreamacro/go-shadowsocks2/core" 15 "github.com/Dreamacro/go-shadowsocks2/shadowaead" 16 "github.com/Dreamacro/go-shadowsocks2/shadowstream" 17 ) 18 19 type ShadowSocksR struct { 20 *Base 21 cipher core.Cipher 22 obfs obfs.Obfs 23 protocol protocol.Protocol 24 } 25 26 type ShadowSocksROption struct { 27 BasicOption 28 Name string `proxy:"name"` 29 Server string `proxy:"server"` 30 Port int `proxy:"port"` 31 Password string `proxy:"password"` 32 Cipher string `proxy:"cipher"` 33 Obfs string `proxy:"obfs"` 34 ObfsParam string `proxy:"obfs-param,omitempty"` 35 Protocol string `proxy:"protocol"` 36 ProtocolParam string `proxy:"protocol-param,omitempty"` 37 UDP bool `proxy:"udp,omitempty"` 38 } 39 40 // StreamConn implements C.ProxyAdapter 41 func (ssr *ShadowSocksR) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { 42 c = ssr.obfs.StreamConn(c) 43 c = ssr.cipher.StreamConn(c) 44 var ( 45 iv []byte 46 err error 47 ) 48 switch conn := c.(type) { 49 case *shadowstream.Conn: 50 iv, err = conn.ObtainWriteIV() 51 if err != nil { 52 return nil, err 53 } 54 case *shadowaead.Conn: 55 return nil, fmt.Errorf("invalid connection type") 56 } 57 c = ssr.protocol.StreamConn(c, iv) 58 _, err = c.Write(serializesSocksAddr(metadata)) 59 return c, err 60 } 61 62 // DialContext implements C.ProxyAdapter 63 func (ssr *ShadowSocksR) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { 64 c, err := dialer.DialContext(ctx, "tcp", ssr.addr, ssr.Base.DialOptions(opts...)...) 65 if err != nil { 66 return nil, fmt.Errorf("%s connect error: %w", ssr.addr, err) 67 } 68 tcpKeepAlive(c) 69 70 defer safeConnClose(c, err) 71 72 c, err = ssr.StreamConn(c, metadata) 73 return NewConn(c, ssr), err 74 } 75 76 // ListenPacketContext implements C.ProxyAdapter 77 func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { 78 pc, err := dialer.ListenPacket(ctx, "udp", "", ssr.Base.DialOptions(opts...)...) 79 if err != nil { 80 return nil, err 81 } 82 83 addr, err := resolveUDPAddr("udp", ssr.addr) 84 if err != nil { 85 pc.Close() 86 return nil, err 87 } 88 89 pc = ssr.cipher.PacketConn(pc) 90 pc = ssr.protocol.PacketConn(pc) 91 return newPacketConn(&ssPacketConn{PacketConn: pc, rAddr: addr}, ssr), nil 92 } 93 94 func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) { 95 addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) 96 cipher := option.Cipher 97 password := option.Password 98 coreCiph, err := core.PickCipher(cipher, nil, password) 99 if err != nil { 100 return nil, fmt.Errorf("ssr %s initialize error: %w", addr, err) 101 } 102 var ( 103 ivSize int 104 key []byte 105 ) 106 if option.Cipher == "dummy" { 107 ivSize = 0 108 key = core.Kdf(option.Password, 16) 109 } else { 110 ciph, ok := coreCiph.(*core.StreamCipher) 111 if !ok { 112 return nil, fmt.Errorf("%s is not dummy or a supported stream cipher in ssr", cipher) 113 } 114 ivSize = ciph.IVSize() 115 key = ciph.Key 116 } 117 118 obfs, obfsOverhead, err := obfs.PickObfs(option.Obfs, &obfs.Base{ 119 Host: option.Server, 120 Port: option.Port, 121 Key: key, 122 IVSize: ivSize, 123 Param: option.ObfsParam, 124 }) 125 if err != nil { 126 return nil, fmt.Errorf("ssr %s initialize obfs error: %w", addr, err) 127 } 128 129 protocol, err := protocol.PickProtocol(option.Protocol, &protocol.Base{ 130 Key: key, 131 Overhead: obfsOverhead, 132 Param: option.ProtocolParam, 133 }) 134 if err != nil { 135 return nil, fmt.Errorf("ssr %s initialize protocol error: %w", addr, err) 136 } 137 138 return &ShadowSocksR{ 139 Base: &Base{ 140 name: option.Name, 141 addr: addr, 142 tp: C.ShadowsocksR, 143 udp: option.UDP, 144 iface: option.Interface, 145 }, 146 cipher: coreCiph, 147 obfs: obfs, 148 protocol: protocol, 149 }, nil 150 }