github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/node/parser/shadowsocks.go (about)

     1  package parser
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"fmt"
     7  	"net"
     8  	"net/url"
     9  	"os"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/Asutorufa/yuhaiin/pkg/log"
    14  	"github.com/Asutorufa/yuhaiin/pkg/protos/node/point"
    15  	"github.com/Asutorufa/yuhaiin/pkg/protos/node/protocol"
    16  	"github.com/Asutorufa/yuhaiin/pkg/protos/node/subscribe"
    17  )
    18  
    19  func init() {
    20  	store.Store(subscribe.Type_shadowsocks, func(data []byte) (*point.Point, error) {
    21  		ssUrl, err := url.Parse(string(data))
    22  		if err != nil {
    23  			return nil, fmt.Errorf("parse url failed: %w", err)
    24  		}
    25  
    26  		server, portstr := ssUrl.Hostname(), ssUrl.Port()
    27  
    28  		var method, password string
    29  		mps, err := base64.RawURLEncoding.DecodeString(ssUrl.User.String())
    30  		if err != nil {
    31  			log.Warn("parse shadowsocks user failed", "err", err)
    32  		}
    33  		if i := bytes.IndexByte(mps, ':'); i != -1 {
    34  			method, password = string(mps[:i]), string(mps[i+1:])
    35  		}
    36  
    37  		port, err := strconv.ParseUint(portstr, 10, 16)
    38  		if err != nil {
    39  			return nil, fmt.Errorf("parse port failed: %w", err)
    40  		}
    41  
    42  		simple := &protocol.Simple{
    43  			Host: server,
    44  			Port: int32(port),
    45  		}
    46  
    47  		var plugin []*protocol.Protocol
    48  		pluginopts := parseOpts(ssUrl.Query().Get("plugin"))
    49  		switch {
    50  		case pluginopts["obfs-local"] == "true":
    51  			plugin, err = parseObfs(pluginopts)
    52  		case pluginopts["v2ray"] == "true":
    53  			plugin, err = parseV2ray(pluginopts, simple)
    54  		default:
    55  		}
    56  		if err != nil {
    57  			return nil, fmt.Errorf("parse plugin failed: %w", err)
    58  		}
    59  
    60  		protocols := append([]*protocol.Protocol{
    61  			{
    62  				Protocol: &protocol.Protocol_Simple{
    63  					Simple: simple,
    64  				},
    65  			},
    66  		}, plugin...)
    67  
    68  		return &point.Point{
    69  			Origin: point.Origin_remote,
    70  			Name:   "[ss]" + ssUrl.Fragment,
    71  			Protocols: append(protocols, &protocol.Protocol{
    72  				Protocol: &protocol.Protocol_Shadowsocks{
    73  					Shadowsocks: &protocol.Shadowsocks{
    74  						Method:   method,
    75  						Password: password,
    76  					},
    77  				},
    78  			}),
    79  		}, nil
    80  	})
    81  }
    82  
    83  func parseV2ray(store map[string]string, simple *protocol.Simple) ([]*protocol.Protocol, error) {
    84  	// fastOpen := false
    85  	// path := "/"
    86  	// host := "cloudfront.com"
    87  	// tlsEnabled := false
    88  	// cert := ""
    89  	// certRaw := ""
    90  	// mode := "websocket"
    91  
    92  	var err error
    93  	var cert []byte
    94  	if store["cert"] != "" {
    95  		cert, err = os.ReadFile(store["cert"])
    96  		if err != nil {
    97  			log.Warn("read cert file failed", "err", err)
    98  		}
    99  	}
   100  
   101  	ns, _, err := net.SplitHostPort(store["host"])
   102  	if err != nil {
   103  		log.Warn("split host and port failed", "err", err)
   104  		ns = store["host"]
   105  	}
   106  
   107  	switch store["mode"] {
   108  	case "websocket":
   109  		var protocols []*protocol.Protocol
   110  		protocols = append(protocols, &protocol.Protocol{
   111  			Protocol: &protocol.Protocol_Tls{
   112  				Tls: &protocol.TlsConfig{
   113  					ServerNames: []string{ns},
   114  					Enable:      store["tls"] == "true",
   115  					CaCert:      [][]byte{cert},
   116  				},
   117  			},
   118  		})
   119  		return append(protocols, &protocol.Protocol{
   120  			Protocol: &protocol.Protocol_Websocket{
   121  				Websocket: &protocol.Websocket{
   122  					Host: store["host"],
   123  					Path: store["path"],
   124  				},
   125  			},
   126  		}), nil
   127  	case "quic":
   128  		return []*protocol.Protocol{
   129  			{
   130  				Protocol: &protocol.Protocol_Quic{
   131  					Quic: &protocol.Quic{
   132  						Tls: &protocol.TlsConfig{
   133  							ServerNames: []string{ns},
   134  							CaCert:      [][]byte{cert},
   135  						},
   136  					},
   137  				},
   138  			},
   139  		}, nil
   140  	}
   141  
   142  	return nil, fmt.Errorf("unsupported mode: %v", store["mode"])
   143  }
   144  
   145  func parseObfs(args map[string]string) ([]*protocol.Protocol, error) {
   146  	hostname, port, err := net.SplitHostPort(args["obfs-host"])
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  	return []*protocol.Protocol{
   151  		{
   152  			Protocol: &protocol.Protocol_ObfsHttp{
   153  				ObfsHttp: &protocol.ObfsHttp{
   154  					Host: hostname,
   155  					Port: port,
   156  				},
   157  			},
   158  		},
   159  	}, nil
   160  }
   161  
   162  func parseOpts(options string) map[string]string {
   163  	store := make(map[string]string)
   164  	for _, x := range strings.Split(options, ";") {
   165  		i := strings.IndexByte(x, '=')
   166  		if i == -1 {
   167  			store[x] = "true"
   168  		} else {
   169  			key, value := x[:i], x[i+1:]
   170  			store[key] = value
   171  		}
   172  	}
   173  	return store
   174  }