github.com/laof/lite-speed-test@v0.0.0-20230930011949-1f39b7037845/outbound/shadowsocks.go (about) 1 package outbound 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "net" 9 "strconv" 10 11 "github.com/laof/lite-speed-test/stats" 12 "github.com/laof/lite-speed-test/transport/dialer" 13 "github.com/laof/lite-speed-test/transport/socks5" 14 15 "github.com/laof/go2/core" 16 "github.com/laof/lite-speed-test/common/structure" 17 C "github.com/laof/lite-speed-test/constant" 18 "github.com/laof/lite-speed-test/log" 19 ) 20 21 type ShadowSocks struct { 22 *Base 23 cipher core.Cipher 24 25 // obfs 26 obfsMode string 27 obfsOption *simpleObfsOption 28 // v2rayOption *v2rayObfs.Option 29 } 30 31 type ShadowSocksOption struct { 32 Name string `proxy:"name,omitempty"` 33 Server string `proxy:"server"` 34 Port int `proxy:"port"` 35 Password string `proxy:"password"` 36 Cipher string `proxy:"cipher"` 37 UDP bool `proxy:"udp,omitempty"` 38 Plugin string `proxy:"plugin,omitempty"` 39 PluginOpts map[string]interface{} `proxy:"plugin-opts,omitempty"` 40 Remarks string `proxy:"remarks,omitempty"` 41 } 42 43 type simpleObfsOption struct { 44 Mode string `obfs:"mode"` 45 Host string `obfs:"host,omitempty"` 46 } 47 48 type v2rayObfsOption struct { 49 Mode string `obfs:"mode"` 50 Host string `obfs:"host,omitempty"` 51 Path string `obfs:"path,omitempty"` 52 TLS bool `obfs:"tls,omitempty"` 53 Headers map[string]string `obfs:"headers,omitempty"` 54 SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` 55 Mux bool `obfs:"mux,omitempty"` 56 } 57 58 func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { 59 // switch ss.obfsMode { 60 // case "tls": 61 // c = obfs.NewTLSObfs(c, ss.obfsOption.Host) 62 // case "http": 63 // _, port, _ := net.SplitHostPort(ss.addr) 64 // c = obfs.NewHTTPObfs(c, ss.obfsOption.Host, port) 65 // case "websocket": 66 // var err error 67 // c, err = v2rayObfs.NewV2rayObfs(c, ss.v2rayOption) 68 // if err != nil { 69 // return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) 70 // } 71 // } 72 c = ss.cipher.StreamConn(c) 73 _, err := c.Write(serializesSocksAddr(metadata)) 74 return c, err 75 } 76 77 func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata) (net.Conn, error) { 78 log.I("start dial from", ss.addr, "to", metadata.RemoteAddress()) 79 c, err := dialer.DialContext(ctx, "tcp", ss.addr) 80 if err != nil { 81 return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) 82 } 83 tcpKeepAlive(c) 84 log.I("start StreamConn from", ss.addr, "to", metadata.RemoteAddress()) 85 sc := stats.NewStatsConn(c) 86 return ss.StreamConn(sc, metadata) 87 } 88 89 func (ss *ShadowSocks) DialUDP(metadata *C.Metadata) (net.PacketConn, error) { 90 pc, err := dialer.ListenPacket("udp", "") 91 if err != nil { 92 return nil, err 93 } 94 95 addr, err := resolveUDPAddr("udp", ss.addr) 96 if err != nil { 97 return nil, err 98 } 99 100 spc := stats.NewStatsPacketConn(pc) 101 pc = ss.cipher.PacketConn(spc) 102 return &ssPacketConn{PacketConn: pc, rAddr: addr}, nil 103 } 104 105 func (ss *ShadowSocks) MarshalJSON() ([]byte, error) { 106 return json.Marshal(map[string]string{ 107 "type": "shadowsocks", 108 }) 109 } 110 111 func NewShadowSocks(option *ShadowSocksOption) (*ShadowSocks, error) { 112 addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) 113 cipher := option.Cipher 114 password := option.Password 115 ciph, err := core.PickCipher(cipher, nil, password) 116 if err != nil { 117 return nil, fmt.Errorf("ss %s initialize error: %w", addr, err) 118 } 119 120 // var v2rayOption *v2rayObfs.Option 121 var obfsOption *simpleObfsOption 122 obfsMode := "" 123 124 decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true}) 125 if option.Plugin == "obfs" { 126 opts := simpleObfsOption{Host: "bing.com"} 127 if err := decoder.Decode(option.PluginOpts, &opts); err != nil { 128 return nil, fmt.Errorf("ss %s initialize obfs error: %w", addr, err) 129 } 130 131 if opts.Mode != "tls" && opts.Mode != "http" { 132 return nil, fmt.Errorf("ss %s obfs mode error: %s", addr, opts.Mode) 133 } 134 obfsMode = opts.Mode 135 obfsOption = &opts 136 } else if option.Plugin == "v2ray-plugin" { 137 opts := v2rayObfsOption{Host: "bing.com", Mux: true} 138 if err := decoder.Decode(option.PluginOpts, &opts); err != nil { 139 return nil, fmt.Errorf("ss %s initialize v2ray-plugin error: %w", addr, err) 140 } 141 142 if opts.Mode != "websocket" { 143 return nil, fmt.Errorf("ss %s obfs mode error: %s", addr, opts.Mode) 144 } 145 obfsMode = opts.Mode 146 // v2rayOption = &v2rayObfs.Option{ 147 // Host: opts.Host, 148 // Path: opts.Path, 149 // Headers: opts.Headers, 150 // Mux: opts.Mux, 151 // } 152 153 // if opts.TLS { 154 // v2rayOption.TLS = true 155 // v2rayOption.SkipCertVerify = opts.SkipCertVerify 156 // v2rayOption.SessionCache = getClientSessionCache() 157 // } 158 } 159 160 return &ShadowSocks{ 161 Base: &Base{ 162 name: option.Name, 163 addr: addr, 164 udp: option.UDP, 165 }, 166 cipher: ciph, 167 168 obfsMode: obfsMode, 169 // v2rayOption: v2rayOption, 170 obfsOption: obfsOption, 171 }, nil 172 } 173 174 type ssPacketConn struct { 175 net.PacketConn 176 rAddr net.Addr 177 } 178 179 func (spc *ssPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { 180 packet, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b) 181 if err != nil { 182 return 183 } 184 return spc.PacketConn.WriteTo(packet[3:], spc.rAddr) 185 } 186 187 func (spc *ssPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { 188 n, _, e := spc.PacketConn.ReadFrom(b) 189 if e != nil { 190 return 0, nil, e 191 } 192 193 addr := socks5.SplitAddr(b[:n]) 194 if addr == nil { 195 return 0, nil, errors.New("parse addr error") 196 } 197 198 udpAddr := addr.UDPAddr() 199 if udpAddr == nil { 200 return 0, nil, errors.New("parse addr error") 201 } 202 203 copy(b, b[len(addr):]) 204 return n - len(addr), udpAddr, e 205 }