chore: shadowsocks listener support old cipher

This commit is contained in:
gVisor bot 2022-11-11 22:44:44 +08:00
parent 4abbe8ce65
commit e4d211d0df
7 changed files with 261 additions and 11 deletions

View file

@ -1,7 +1,15 @@
package constant
import "net"
type Listener interface {
RawAddress() string
Address() string
Close() error
}
type AdvanceListener interface {
Close()
Config() string
HandleConn(conn net.Conn, in chan<- ConnContext)
}

View file

@ -41,7 +41,7 @@ var (
mixedListener *mixed.Listener
mixedUDPLister *socks.UDPListener
tunLister *sing_tun.Listener
shadowSocksListener *sing_shadowsocks.Listener
shadowSocksListener C.AdvanceListener
vmessListener *sing_vmess.Listener
tcpTunListener *tunnel.Listener
udpTunListener *tunnel.UdpListener

105
listener/shadowsocks/tcp.go Normal file
View file

@ -0,0 +1,105 @@
package shadowsocks
import (
"net"
"strings"
"github.com/Dreamacro/clash/adapter/inbound"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/shadowsocks/core"
"github.com/Dreamacro/clash/transport/socks5"
)
type Listener struct {
closed bool
config string
listeners []net.Listener
udpListeners []*UDPListener
pickCipher core.Cipher
}
var _listener *Listener
func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) {
addr, cipher, password, err := parseSSURL(config)
if err != nil {
return nil, err
}
pickCipher, err := core.PickCipher(cipher, nil, password)
if err != nil {
return nil, err
}
sl := &Listener{false, config, nil, nil, pickCipher}
_listener = sl
for _, addr := range strings.Split(addr, ",") {
addr := addr
//UDP
ul, err := NewUDP(addr, pickCipher, udpIn)
if err != nil {
return nil, err
}
sl.udpListeners = append(sl.udpListeners, ul)
//TCP
l, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
sl.listeners = append(sl.listeners, l)
go func() {
log.Infoln("ShadowSocks proxy listening at: %s", l.Addr().String())
for {
c, err := l.Accept()
if err != nil {
if sl.closed {
break
}
continue
}
_ = c.(*net.TCPConn).SetKeepAlive(true)
go sl.HandleConn(c, tcpIn)
}
}()
}
return sl, nil
}
func (l *Listener) Close() {
l.closed = true
for _, lis := range l.listeners {
_ = lis.Close()
}
for _, lis := range l.udpListeners {
_ = lis.Close()
}
}
func (l *Listener) Config() string {
return l.config
}
func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) {
conn = l.pickCipher.StreamConn(conn)
target, err := socks5.ReadAddr(conn, make([]byte, socks5.MaxAddrLen))
if err != nil {
_ = conn.Close()
return
}
in <- inbound.NewSocket(target, conn, C.SHADOWSOCKS)
}
func HandleShadowSocks(conn net.Conn, in chan<- C.ConnContext) bool {
if _listener != nil && _listener.pickCipher != nil {
go _listener.HandleConn(conn, in)
return true
}
return false
}

View file

@ -0,0 +1,76 @@
package shadowsocks
import (
"net"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/common/sockopt"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/shadowsocks/core"
"github.com/Dreamacro/clash/transport/socks5"
)
type UDPListener struct {
packetConn net.PacketConn
closed bool
}
func NewUDP(addr string, pickCipher core.Cipher, in chan<- *inbound.PacketAdapter) (*UDPListener, error) {
l, err := net.ListenPacket("udp", addr)
if err != nil {
return nil, err
}
err = sockopt.UDPReuseaddr(l.(*net.UDPConn))
if err != nil {
log.Warnln("Failed to Reuse UDP Address: %s", err)
}
sl := &UDPListener{l, false}
conn := pickCipher.PacketConn(l)
go func() {
for {
buf := pool.Get(pool.RelayBufferSize)
n, remoteAddr, err := conn.ReadFrom(buf)
if err != nil {
pool.Put(buf)
if sl.closed {
break
}
continue
}
handleSocksUDP(conn, in, buf[:n], remoteAddr)
}
}()
return sl, nil
}
func (l *UDPListener) Close() error {
l.closed = true
return l.packetConn.Close()
}
func handleSocksUDP(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, addr net.Addr) {
tgtAddr := socks5.SplitAddr(buf)
if tgtAddr == nil {
// Unresolved UDP packet, return buffer to the pool
pool.Put(buf)
return
}
target := socks5.ParseAddr(tgtAddr.String())
payload := buf[len(tgtAddr):]
packet := &packet{
pc: pc,
rAddr: addr,
payload: payload,
bufRef: buf,
}
select {
case in <- inbound.NewPacket(target, packet, C.SHADOWSOCKS):
default:
}
}

View file

@ -0,0 +1,59 @@
package shadowsocks
import (
"bytes"
"errors"
"net"
"net/url"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/transport/socks5"
)
type packet struct {
pc net.PacketConn
rAddr net.Addr
payload []byte
bufRef []byte
}
func (c *packet) Data() []byte {
return c.payload
}
// WriteBack wirtes UDP packet with source(ip, port) = `addr`
func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
if addr == nil {
err = errors.New("address is invalid")
return
}
packet := bytes.Join([][]byte{socks5.ParseAddrToSocksAddr(addr), b}, []byte{})
return c.pc.WriteTo(packet, c.rAddr)
}
// LocalAddr returns the source IP/Port of UDP Packet
func (c *packet) LocalAddr() net.Addr {
return c.rAddr
}
func (c *packet) Drop() {
pool.Put(c.bufRef)
}
func (c *packet) InAddr() net.Addr {
return c.pc.LocalAddr()
}
func parseSSURL(s string) (addr, cipher, password string, err error) {
u, err := url.Parse(s)
if err != nil {
return
}
addr = u.Host
if u.User != nil {
cipher = u.User.Username()
password, _ = u.User.Password()
}
return
}

View file

@ -10,6 +10,7 @@ import (
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/sockopt"
C "github.com/Dreamacro/clash/constant"
embedSS "github.com/Dreamacro/clash/listener/shadowsocks"
"github.com/Dreamacro/clash/listener/sing"
"github.com/Dreamacro/clash/log"
@ -32,7 +33,7 @@ type Listener struct {
var _listener *Listener
func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) {
func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (C.AdvanceListener, error) {
addr, cipher, password, err := parseSSURL(config)
if err != nil {
return nil, err
@ -56,6 +57,7 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.Packet
sl.service, err = shadowaead_2022.NewServiceWithPassword(cipher, password, udpTimeout, h)
default:
err = fmt.Errorf("shadowsocks: unsupported method: %s", cipher)
return embedSS.New(config, tcpIn, udpIn)
}
if err != nil {
return nil, err
@ -117,7 +119,7 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.Packet
}
_ = c.(*net.TCPConn).SetKeepAlive(true)
go sl.HandleConn(c)
go sl.HandleConn(c, tcpIn)
}
}()
}
@ -139,7 +141,7 @@ func (l *Listener) Config() string {
return l.config
}
func (l *Listener) HandleConn(conn net.Conn) {
func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) {
err := l.service.NewConnection(context.TODO(), conn, metadata.Metadata{
Protocol: "shadowsocks",
Source: metadata.ParseSocksaddr(conn.RemoteAddr().String()),
@ -150,12 +152,12 @@ func (l *Listener) HandleConn(conn net.Conn) {
}
}
func HandleShadowSocks(conn net.Conn) bool {
func HandleShadowSocks(conn net.Conn, in chan<- C.ConnContext) bool {
if _listener != nil && _listener.service != nil {
go _listener.HandleConn(conn)
go _listener.HandleConn(conn, in)
return true
}
return false
return embedSS.HandleShadowSocks(conn, in)
}
func parseSSURL(s string) (addr, cipher, password string, err error) {

View file

@ -72,7 +72,7 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.Packet
}
_ = c.(*net.TCPConn).SetKeepAlive(true)
go sl.HandleConn(c)
go sl.HandleConn(c, tcpIn)
}
}()
}
@ -92,7 +92,7 @@ func (l *Listener) Config() string {
return l.config
}
func (l *Listener) HandleConn(conn net.Conn) {
func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) {
err := l.service.NewConnection(context.TODO(), conn, metadata.Metadata{
Protocol: "vmess",
Source: metadata.ParseSocksaddr(conn.RemoteAddr().String()),
@ -103,9 +103,9 @@ func (l *Listener) HandleConn(conn net.Conn) {
}
}
func HandleVmess(conn net.Conn) bool {
func HandleVmess(conn net.Conn, in chan<- C.ConnContext) bool {
if _listener != nil && _listener.service != nil {
go _listener.HandleConn(conn)
go _listener.HandleConn(conn, in)
return true
}
return false