shadowsocks SIP002

This commit is contained in:
jebbs 2022-10-17 12:26:43 +08:00
parent 917a33a190
commit a66a89308a
2 changed files with 125 additions and 33 deletions

View File

@ -1,7 +1,6 @@
package link
import (
"fmt"
"net/url"
"strconv"
"strings"
@ -11,21 +10,52 @@ import (
E "github.com/sagernet/sing/common/exceptions"
)
var _ Link = (*SSLink)(nil)
var _ Link = (*ShadowSocks)(nil)
func init() {
common.Must(RegisterParser(&Parser{
Name: "Shadowsocks",
Scheme: []string{"ss"},
Parse: func(u *url.URL) (Link, error) {
link := &SSLink{}
link := &ShadowSocks{}
return link, link.Parse(u)
},
}))
}
// ShadowSocks represents a parsed shadowsocks link
type ShadowSocks struct {
Method string `json:"method,omitempty"`
Password string `json:"password,omitempty"`
Address string `json:"address,omitempty"`
Port uint16 `json:"port,omitempty"`
Ps string `json:"ps,omitempty"`
Plugin string `json:"plugin,omitempty"`
PluginOpts string `json:"plugin-opts,omitempty"`
}
// Options implements Link
func (l *ShadowSocks) Options() *option.Outbound {
return &option.Outbound{
Type: "shadowsocks",
Tag: l.Ps,
ShadowsocksOptions: option.ShadowsocksOutboundOptions{
ServerOptions: option.ServerOptions{
Server: l.Address,
ServerPort: l.Port,
},
Method: l.Method,
Password: l.Password,
Plugin: l.Plugin,
PluginOptions: l.PluginOpts,
},
}
}
// Parse implements Link
func (l *SSLink) Parse(u *url.URL) error {
//
// https://github.com/shadowsocks/shadowsocks-org/wiki/SIP002-URI-Scheme
func (l *ShadowSocks) Parse(u *url.URL) error {
if u.Scheme != "ss" {
return E.New("not a ss link")
}
@ -39,14 +69,26 @@ func (l *SSLink) Parse(u *url.URL) error {
queries := u.Query()
for key, values := range queries {
switch key {
default:
return fmt.Errorf("unsupported shadowsocks parameter: %s=%v", key, values)
case "plugin":
parts := strings.SplitN(values[0], ";", 2)
l.Plugin = parts[0]
if len(parts) == 2 {
l.PluginOpts = parts[1]
}
}
}
if uname := u.User.Username(); uname != "" {
if pass, ok := u.User.Password(); ok {
l.Method = uname
l.Password = pass
method, err := url.QueryUnescape(uname)
if err != nil {
return err
}
password, err := url.QueryUnescape(pass)
if err != nil {
return err
}
l.Method = method
l.Password = password
} else {
dec, err := base64Decode(uname)
if err != nil {
@ -61,28 +103,3 @@ func (l *SSLink) Parse(u *url.URL) error {
}
return nil
}
// SSLink represents a parsed shadowsocks link
type SSLink struct {
Method string `json:"method,omitempty"`
Password string `json:"password,omitempty"`
Address string `json:"address,omitempty"`
Port uint16 `json:"port,omitempty"`
Ps string `json:"ps,omitempty"`
}
// Options implements Link
func (l *SSLink) Options() *option.Outbound {
return &option.Outbound{
Type: "shadowsocks",
Tag: l.Ps,
ShadowsocksOptions: option.ShadowsocksOutboundOptions{
ServerOptions: option.ServerOptions{
Server: l.Address,
ServerPort: l.Port,
},
Method: l.Method,
Password: l.Password,
},
}
}

View File

@ -0,0 +1,75 @@
package link_test
import (
"net/url"
"testing"
"github.com/sagernet/sing-box/common/link"
)
func TestShadowSocks(t *testing.T) {
tests := []struct {
link string
want link.ShadowSocks
}{
{
link: "ss://YWVzLTEyOC1nY206dGVzdA@192.168.100.1:8888#Example1",
want: link.ShadowSocks{
Address: "192.168.100.1",
Port: 8888,
Ps: "Example1",
Method: "aes-128-gcm",
Password: "test",
},
},
{
link: "ss://cmM0LW1kNTpwYXNzd2Q@192.168.100.1:8888/?plugin=obfs-local%3Bobfs%3Dhttp%3Bobfs-host=abc.com#Example2",
want: link.ShadowSocks{
Address: "192.168.100.1",
Port: 8888,
Ps: "Example2",
Method: "rc4-md5",
Password: "passwd",
Plugin: "obfs-local",
PluginOpts: "obfs=http;obfs-host=abc.com",
},
},
{
link: "ss://2022-blake3-aes-256-gcm:YctPZ6U7xPPcU%2Bgp3u%2B0tx%2FtRizJN9K8y%2BuKlW2qjlI%3D@192.168.100.1:8888#Example3",
want: link.ShadowSocks{
Address: "192.168.100.1",
Port: 8888,
Ps: "Example3",
Method: "2022-blake3-aes-256-gcm",
Password: "YctPZ6U7xPPcU gp3u 0tx/tRizJN9K8y uKlW2qjlI=",
},
},
{
link: "ss://2022-blake3-aes-256-gcm:YctPZ6U7xPPcU%2Bgp3u%2B0tx%2FtRizJN9K8y%2BuKlW2qjlI%3D@192.168.100.1:8888/?plugin=v2ray-plugin%3Bserver&unsupported-arguments=should-be-ignored#Example3",
want: link.ShadowSocks{
Address: "192.168.100.1",
Port: 8888,
Ps: "Example3",
Method: "2022-blake3-aes-256-gcm",
Password: "YctPZ6U7xPPcU gp3u 0tx/tRizJN9K8y uKlW2qjlI=",
Plugin: "v2ray-plugin",
PluginOpts: "server",
},
},
}
for _, tt := range tests {
u, err := url.Parse(tt.link)
if err != nil {
t.Fatal(err)
}
link := link.ShadowSocks{}
err = link.Parse(u)
if err != nil {
t.Error(err)
return
}
if link != tt.want {
t.Errorf("want %v, got %v", tt.want, link)
}
}
}