diff --git a/parser/shadowsocks.go b/parser/shadowsocks.go index c7f4092..56c1d02 100644 --- a/parser/shadowsocks.go +++ b/parser/shadowsocks.go @@ -2,6 +2,7 @@ package parser import ( "net/url" + "strconv" "strings" "sub2sing-box/constant" "sub2sing-box/model" @@ -9,90 +10,78 @@ import ( ) func ParseShadowsocks(proxy string) (model.Outbound, error) { - if !strings.HasPrefix(proxy, constant.ShadowsocksPrefix) { + link, err := url.Parse(proxy) + if err != nil { + return model.Outbound{}, &ParseError{ + Type: ErrInvalidStruct, + Message: "url parse error", + Raw: proxy, + } + } + + server := link.Hostname() + if server == "" { + return model.Outbound{}, &ParseError{ + Type: ErrInvalidStruct, + Message: "missing server host", + Raw: proxy, + } + } + + if link.Scheme+"://" != constant.ShadowsocksPrefix { return model.Outbound{}, &ParseError{Type: ErrInvalidPrefix, Raw: proxy} } - proxy = strings.TrimPrefix(proxy, constant.ShadowsocksPrefix) - urlParts := strings.SplitN(proxy, "@", 2) - if len(urlParts) != 2 { - return model.Outbound{}, &ParseError{ - Type: ErrInvalidStruct, - Message: "missing character '@' in url", - Raw: proxy, - } - } - - if !strings.Contains(urlParts[0], ":") { - decoded, err := util.DecodeBase64(urlParts[0]) - if err != nil { - return model.Outbound{}, &ParseError{ - Type: ErrInvalidStruct, - Message: "invalid base64 encoded", - Raw: proxy, - } - } - urlParts[0] = decoded - } - credentials := strings.SplitN(urlParts[0], ":", 2) - if len(credentials) != 2 { - return model.Outbound{}, &ParseError{ - Type: ErrInvalidStruct, - Message: "missing server host or port", - Raw: proxy, - } - } - method, password := credentials[0], credentials[1] - - serverInfoAndTag := strings.SplitN(urlParts[1], "#", 2) - serverAndPort := serverInfoAndTag[0] - - lastColonIndex := strings.LastIndex(serverAndPort, ":") - if lastColonIndex == -1 { - return model.Outbound{}, &ParseError{ - Type: ErrInvalidStruct, - Message: "missing port in address", - Raw: proxy, - } - } - - server := serverAndPort[:lastColonIndex] - portStr := serverAndPort[lastColonIndex+1:] - - port, err := ParsePort(portStr) + port, err := strconv.Atoi(link.Port()) if err != nil { return model.Outbound{}, &ParseError{ - Type: ErrInvalidPort, - Message: err.Error(), + Type: ErrInvalidStruct, + Message: "missing server port", Raw: proxy, } } - var remarks string - if len(serverInfoAndTag) == 2 { - unescape, err := url.QueryUnescape(serverInfoAndTag[1]) - if err != nil { - return model.Outbound{}, &ParseError{ - Type: ErrInvalidStruct, - Message: "cannot unescape remarks", - Raw: proxy, - } + user, err := util.DecodeBase64(link.User.Username()) + if user == "" { + return model.Outbound{}, &ParseError{ + Type: ErrInvalidStruct, + Message: "missing method and password", + Raw: proxy, + } + } + methodAndPass := strings.SplitN(user, ":", 2) + if len(methodAndPass) != 2 { + return model.Outbound{}, &ParseError{ + Type: ErrInvalidStruct, + Message: "missing method and password", + Raw: proxy, + } + } + + query := link.Query() + pluginStr := query.Get("plugin") + plugin := "" + options := "" + if pluginStr != "" { + arr := strings.SplitN(pluginStr, ";", 2) + if len(arr) == 2 { + plugin = arr[0] + options = arr[1] } - remarks = strings.TrimSpace(unescape) - } else { - remarks = strings.TrimSpace(server + ":" + portStr) } result := model.Outbound{ Type: "shadowsocks", - Tag: remarks, + Tag: link.Fragment, ShadowsocksOptions: model.ShadowsocksOutboundOptions{ ServerOptions: model.ServerOptions{ Server: server, - ServerPort: port, + ServerPort: uint16(port), }, - Method: method, - Password: password, + Method: methodAndPass[0], + Password: methodAndPass[1], + Plugin: plugin, + PluginOptions: options, }, } return result, nil