mirror of
https://github.com/nitezs/sub2sing-box.git
synced 2024-12-23 22:04:41 -05:00
重构部分代码
fix: vless ws 配置缺少 path 字段
This commit is contained in:
parent
91571affdd
commit
688f8287af
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@
|
|||||||
dist
|
dist
|
||||||
*test.go
|
*test.go
|
||||||
template.json
|
template.json
|
||||||
|
.idea
|
@ -9,7 +9,8 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
model2 "sub2sing-box/model"
|
C "sub2sing-box/constant"
|
||||||
|
"sub2sing-box/model"
|
||||||
"sub2sing-box/parser"
|
"sub2sing-box/parser"
|
||||||
"sub2sing-box/util"
|
"sub2sing-box/util"
|
||||||
)
|
)
|
||||||
@ -28,7 +29,7 @@ func Convert(
|
|||||||
result := ""
|
result := ""
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
proxyList, err := ConvertSubscriptionsToSProxy(subscriptions)
|
outbounds, err := ConvertSubscriptionsToSProxy(subscriptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -37,18 +38,18 @@ func Convert(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
proxyList = append(proxyList, p)
|
outbounds = append(outbounds, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
if delete != "" {
|
if delete != "" {
|
||||||
proxyList, err = DeleteProxy(proxyList, delete)
|
outbounds, err = DeleteProxy(outbounds, delete)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range rename {
|
for k, v := range rename {
|
||||||
proxyList, err = RenameProxy(proxyList, k, v)
|
outbounds, err = RenameProxy(outbounds, k, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -56,14 +57,14 @@ func Convert(
|
|||||||
|
|
||||||
keep := make(map[int]bool)
|
keep := make(map[int]bool)
|
||||||
set := make(map[string]struct {
|
set := make(map[string]struct {
|
||||||
Proxy model2.Proxy
|
Proxy model.Outbound
|
||||||
Count int
|
Count int
|
||||||
})
|
})
|
||||||
for i, p := range proxyList {
|
for i, p := range outbounds {
|
||||||
if _, exists := set[p.Tag]; !exists {
|
if _, exists := set[p.Tag]; !exists {
|
||||||
keep[i] = true
|
keep[i] = true
|
||||||
set[p.Tag] = struct {
|
set[p.Tag] = struct {
|
||||||
Proxy model2.Proxy
|
Proxy model.Outbound
|
||||||
Count int
|
Count int
|
||||||
}{p, 0}
|
}{p, 0}
|
||||||
} else {
|
} else {
|
||||||
@ -71,32 +72,16 @@ func Convert(
|
|||||||
p2, _ := json.Marshal(set[p.Tag])
|
p2, _ := json.Marshal(set[p.Tag])
|
||||||
if string(p1) != string(p2) {
|
if string(p1) != string(p2) {
|
||||||
set[p.Tag] = struct {
|
set[p.Tag] = struct {
|
||||||
Proxy model2.Proxy
|
Proxy model.Outbound
|
||||||
Count int
|
Count int
|
||||||
}{p, set[p.Tag].Count + 1}
|
}{p, set[p.Tag].Count + 1}
|
||||||
keep[i] = true
|
keep[i] = true
|
||||||
proxyList[i].Tag = fmt.Sprintf("%s %d", p.Tag, set[p.Tag].Count)
|
outbounds[i].Tag = fmt.Sprintf("%s %d", p.Tag, set[p.Tag].Count)
|
||||||
} else {
|
} else {
|
||||||
keep[i] = false
|
keep[i] = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var newProxyList []model2.Proxy
|
|
||||||
for i, p := range proxyList {
|
|
||||||
if keep[i] {
|
|
||||||
newProxyList = append(newProxyList, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
proxyList = newProxyList
|
|
||||||
var outbounds []model2.Outbound
|
|
||||||
ps, err := json.Marshal(&proxyList)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(ps, &outbounds)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if group {
|
if group {
|
||||||
outbounds = AddCountryGroup(outbounds, groupType, sortKey, sortType)
|
outbounds = AddCountryGroup(outbounds, groupType, sortKey, sortType)
|
||||||
}
|
}
|
||||||
@ -116,52 +101,65 @@ func Convert(
|
|||||||
return string(result), nil
|
return string(result), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddCountryGroup(proxies []model2.Outbound, groupType string, sortKey string, sortType string) []model2.Outbound {
|
func AddCountryGroup(proxies []model.Outbound, groupType string, sortKey string, sortType string) []model.Outbound {
|
||||||
newGroup := make(map[string]model2.Outbound)
|
newGroup := make(map[string]model.Outbound)
|
||||||
for _, p := range proxies {
|
for _, p := range proxies {
|
||||||
if p.Type != "selector" && p.Type != "urltest" {
|
if p.Type != C.TypeSelector && p.Type != C.TypeURLTest {
|
||||||
country := model2.GetContryName(p.Tag)
|
country := model.GetContryName(p.Tag)
|
||||||
if group, ok := newGroup[country]; ok {
|
if group, ok := newGroup[country]; ok {
|
||||||
group.Outbounds = append(group.Outbounds, p.Tag)
|
group.SetOutbounds(append(group.GetOutbounds(), p.Tag))
|
||||||
newGroup[country] = group
|
newGroup[country] = group
|
||||||
} else {
|
} else {
|
||||||
newGroup[country] = model2.Outbound{
|
if groupType == C.TypeSelector {
|
||||||
Tag: country,
|
newGroup[country] = model.Outbound{
|
||||||
Type: groupType,
|
Tag: country,
|
||||||
Outbounds: []string{p.Tag},
|
Type: groupType,
|
||||||
InterruptExistConnections: true,
|
SelectorOptions: model.SelectorOutboundOptions{
|
||||||
|
Outbounds: []string{p.Tag},
|
||||||
|
InterruptExistConnections: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else if groupType == C.TypeURLTest {
|
||||||
|
newGroup[country] = model.Outbound{
|
||||||
|
Tag: country,
|
||||||
|
Type: groupType,
|
||||||
|
URLTestOptions: model.URLTestOutboundOptions{
|
||||||
|
Outbounds: []string{p.Tag},
|
||||||
|
InterruptExistConnections: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var groups []model2.Outbound
|
var groups []model.Outbound
|
||||||
for _, p := range newGroup {
|
for _, p := range newGroup {
|
||||||
groups = append(groups, p)
|
groups = append(groups, p)
|
||||||
}
|
}
|
||||||
if sortType == "asc" {
|
if sortType == "asc" {
|
||||||
switch sortKey {
|
switch sortKey {
|
||||||
case "tag":
|
case "tag":
|
||||||
sort.Sort(model2.SortByTag(groups))
|
sort.Sort(model.SortByTag(groups))
|
||||||
case "num":
|
case "num":
|
||||||
sort.Sort(model2.SortByNumber(groups))
|
sort.Sort(model.SortByNumber(groups))
|
||||||
default:
|
default:
|
||||||
sort.Sort(model2.SortByTag(groups))
|
sort.Sort(model.SortByTag(groups))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch sortKey {
|
switch sortKey {
|
||||||
case "tag":
|
case "tag":
|
||||||
sort.Sort(sort.Reverse(model2.SortByTag(groups)))
|
sort.Sort(sort.Reverse(model.SortByTag(groups)))
|
||||||
case "num":
|
case "num":
|
||||||
sort.Sort(sort.Reverse(model2.SortByNumber(groups)))
|
sort.Sort(sort.Reverse(model.SortByNumber(groups)))
|
||||||
default:
|
default:
|
||||||
sort.Sort(sort.Reverse(model2.SortByTag(groups)))
|
sort.Sort(sort.Reverse(model.SortByTag(groups)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return append(proxies, groups...)
|
return append(proxies, groups...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MergeTemplate(outbounds []model2.Outbound, template string) (string, error) {
|
func MergeTemplate(outbounds []model.Outbound, template string) (string, error) {
|
||||||
var config model2.Config
|
var config model.Config
|
||||||
var err error
|
var err error
|
||||||
if strings.HasPrefix(template, "http") {
|
if strings.HasPrefix(template, "http") {
|
||||||
data, err := util.Fetch(template, 3)
|
data, err := util.Fetch(template, 3)
|
||||||
@ -179,15 +177,15 @@ func MergeTemplate(outbounds []model2.Outbound, template string) (string, error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
config, err = ReadTemplate(template)
|
config, err = ReadTemplate(template)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
proxyTags := make([]string, 0)
|
proxyTags := make([]string, 0)
|
||||||
groupTags := make([]string, 0)
|
groupTags := make([]string, 0)
|
||||||
groups := make(map[string]model2.Outbound)
|
groups := make(map[string]model.Outbound)
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
for _, p := range outbounds {
|
for _, p := range outbounds {
|
||||||
if model2.IsCountryGroup(p.Tag) {
|
if model.IsCountryGroup(p.Tag) {
|
||||||
groupTags = append(groupTags, p.Tag)
|
groupTags = append(groupTags, p.Tag)
|
||||||
reg := regexp.MustCompile("[A-Za-z]{2}")
|
reg := regexp.MustCompile("[A-Za-z]{2}")
|
||||||
country := reg.FindString(p.Tag)
|
country := reg.FindString(p.Tag)
|
||||||
@ -198,22 +196,24 @@ func MergeTemplate(outbounds []model2.Outbound, template string) (string, error)
|
|||||||
}
|
}
|
||||||
reg := regexp.MustCompile("<[A-Za-z]{2}>")
|
reg := regexp.MustCompile("<[A-Za-z]{2}>")
|
||||||
for i, outbound := range config.Outbounds {
|
for i, outbound := range config.Outbounds {
|
||||||
var parsedOutbound []string = make([]string, 0)
|
if outbound.Type == C.TypeSelector || outbound.Type == C.TypeURLTest {
|
||||||
for _, o := range outbound.Outbounds {
|
var parsedOutbound []string = make([]string, 0)
|
||||||
if o == "<all-proxy-tags>" {
|
for _, o := range outbound.GetOutbounds() {
|
||||||
parsedOutbound = append(parsedOutbound, proxyTags...)
|
if o == "<all-proxy-tags>" {
|
||||||
} else if o == "<all-country-tags>" {
|
parsedOutbound = append(parsedOutbound, proxyTags...)
|
||||||
parsedOutbound = append(parsedOutbound, groupTags...)
|
} else if o == "<all-country-tags>" {
|
||||||
} else if reg.MatchString(o) {
|
parsedOutbound = append(parsedOutbound, groupTags...)
|
||||||
country := strings.ToUpper(strings.Trim(reg.FindString(o), "<>"))
|
} else if reg.MatchString(o) {
|
||||||
if group, ok := groups[country]; ok {
|
country := strings.ToUpper(strings.Trim(reg.FindString(o), "<>"))
|
||||||
parsedOutbound = append(parsedOutbound, group.Outbounds...)
|
if group, ok := groups[country]; ok {
|
||||||
|
parsedOutbound = append(parsedOutbound, group.GetOutbounds()...)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parsedOutbound = append(parsedOutbound, o)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
parsedOutbound = append(parsedOutbound, o)
|
|
||||||
}
|
}
|
||||||
|
config.Outbounds[i].SetOutbounds(parsedOutbound)
|
||||||
}
|
}
|
||||||
config.Outbounds[i].Outbounds = parsedOutbound
|
|
||||||
}
|
}
|
||||||
config.Outbounds = append(config.Outbounds, outbounds...)
|
config.Outbounds = append(config.Outbounds, outbounds...)
|
||||||
data, err := json.Marshal(config)
|
data, err := json.Marshal(config)
|
||||||
@ -223,17 +223,17 @@ func MergeTemplate(outbounds []model2.Outbound, template string) (string, error)
|
|||||||
return string(data), nil
|
return string(data), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConvertCProxyToSProxy(proxy string) (model2.Proxy, error) {
|
func ConvertCProxyToSProxy(proxy string) (model.Outbound, error) {
|
||||||
for prefix, parseFunc := range parser.ParserMap {
|
for prefix, parseFunc := range parser.ParserMap {
|
||||||
if strings.HasPrefix(proxy, prefix) {
|
if strings.HasPrefix(proxy, prefix) {
|
||||||
proxy, err := parseFunc(proxy)
|
proxy, err := parseFunc(proxy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, err
|
return model.Outbound{}, err
|
||||||
}
|
}
|
||||||
return proxy, nil
|
return proxy, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return model2.Proxy{}, errors.New("Unknown proxy format")
|
return model.Outbound{}, errors.New("Unknown proxy format")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConvertCProxyToJson(proxy string) (string, error) {
|
func ConvertCProxyToJson(proxy string) (string, error) {
|
||||||
@ -248,8 +248,8 @@ func ConvertCProxyToJson(proxy string) (string, error) {
|
|||||||
return string(data), nil
|
return string(data), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConvertSubscriptionsToSProxy(urls []string) ([]model2.Proxy, error) {
|
func ConvertSubscriptionsToSProxy(urls []string) ([]model.Outbound, error) {
|
||||||
proxyList := make([]model2.Proxy, 0)
|
proxyList := make([]model.Outbound, 0)
|
||||||
for _, url := range urls {
|
for _, url := range urls {
|
||||||
data, err := util.Fetch(url, 3)
|
data, err := util.Fetch(url, 3)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -287,25 +287,25 @@ func ConvertSubscriptionsToJson(urls []string) (string, error) {
|
|||||||
return string(result), nil
|
return string(result), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadTemplate(path string) (model2.Config, error) {
|
func ReadTemplate(path string) (model.Config, error) {
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Config{}, err
|
return model.Config{}, err
|
||||||
}
|
}
|
||||||
var res model2.Config
|
var res model.Config
|
||||||
err = json.Unmarshal(data, &res)
|
err = json.Unmarshal(data, &res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Config{}, err
|
return model.Config{}, err
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteProxy(proxies []model2.Proxy, regex string) ([]model2.Proxy, error) {
|
func DeleteProxy(proxies []model.Outbound, regex string) ([]model.Outbound, error) {
|
||||||
reg, err := regexp.Compile(regex)
|
reg, err := regexp.Compile(regex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var newProxies []model2.Proxy
|
var newProxies []model.Outbound
|
||||||
for _, p := range proxies {
|
for _, p := range proxies {
|
||||||
if !reg.MatchString(p.Tag) {
|
if !reg.MatchString(p.Tag) {
|
||||||
newProxies = append(newProxies, p)
|
newProxies = append(newProxies, p)
|
||||||
@ -314,7 +314,7 @@ func DeleteProxy(proxies []model2.Proxy, regex string) ([]model2.Proxy, error) {
|
|||||||
return newProxies, nil
|
return newProxies, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RenameProxy(proxies []model2.Proxy, regex string, replaceText string) ([]model2.Proxy, error) {
|
func RenameProxy(proxies []model.Outbound, regex string, replaceText string) ([]model.Outbound, error) {
|
||||||
reg, err := regexp.Compile(regex)
|
reg, err := regexp.Compile(regex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
31
constant/proxy.go
Normal file
31
constant/proxy.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package constant
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeTun = "tun"
|
||||||
|
TypeRedirect = "redirect"
|
||||||
|
TypeTProxy = "tproxy"
|
||||||
|
TypeDirect = "direct"
|
||||||
|
TypeBlock = "block"
|
||||||
|
TypeDNS = "dns"
|
||||||
|
TypeSOCKS = "socks"
|
||||||
|
TypeHTTP = "http"
|
||||||
|
TypeMixed = "mixed"
|
||||||
|
TypeShadowsocks = "shadowsocks"
|
||||||
|
TypeVMess = "vmess"
|
||||||
|
TypeTrojan = "trojan"
|
||||||
|
TypeNaive = "naive"
|
||||||
|
TypeWireGuard = "wireguard"
|
||||||
|
TypeHysteria = "hysteria"
|
||||||
|
TypeTor = "tor"
|
||||||
|
TypeSSH = "ssh"
|
||||||
|
TypeShadowTLS = "shadowtls"
|
||||||
|
TypeShadowsocksR = "shadowsocksr"
|
||||||
|
TypeVLESS = "vless"
|
||||||
|
TypeTUIC = "tuic"
|
||||||
|
TypeHysteria2 = "hysteria2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeSelector = "selector"
|
||||||
|
TypeURLTest = "urltest"
|
||||||
|
)
|
6
go.mod
6
go.mod
@ -2,7 +2,10 @@ module sub2sing-box
|
|||||||
|
|
||||||
go 1.21.5
|
go 1.21.5
|
||||||
|
|
||||||
require github.com/spf13/cobra v1.8.0
|
require (
|
||||||
|
github.com/spf13/cobra v1.8.0
|
||||||
|
golang.org/x/text v0.9.0
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bytedance/sonic v1.9.1 // indirect
|
github.com/bytedance/sonic v1.9.1 // indirect
|
||||||
@ -26,7 +29,6 @@ require (
|
|||||||
golang.org/x/crypto v0.9.0 // indirect
|
golang.org/x/crypto v0.9.0 // indirect
|
||||||
golang.org/x/net v0.10.0 // indirect
|
golang.org/x/net v0.10.0 // indirect
|
||||||
golang.org/x/sys v0.8.0 // indirect
|
golang.org/x/sys v0.8.0 // indirect
|
||||||
golang.org/x/text v0.9.0 // indirect
|
|
||||||
google.golang.org/protobuf v1.30.0 // indirect
|
google.golang.org/protobuf v1.30.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
169
model/config.go
169
model/config.go
@ -51,8 +51,8 @@ type Config struct {
|
|||||||
Log *LogOptions `json:"log,omitempty"`
|
Log *LogOptions `json:"log,omitempty"`
|
||||||
DNS *DNSOptions `json:"dns,omitempty"`
|
DNS *DNSOptions `json:"dns,omitempty"`
|
||||||
NTP *NTPOptions `json:"ntp,omitempty"`
|
NTP *NTPOptions `json:"ntp,omitempty"`
|
||||||
Inbounds Listable[Inbound] `json:"inbounds,omitempty"`
|
Inbounds []Inbound `json:"inbounds,omitempty"`
|
||||||
Outbounds Listable[Outbound] `json:"outbounds,omitempty"`
|
Outbounds []Outbound `json:"outbounds,omitempty"`
|
||||||
Route *RouteOptions `json:"route,omitempty"`
|
Route *RouteOptions `json:"route,omitempty"`
|
||||||
Experimental *ExperimentalOptions `json:"experimental,omitempty"`
|
Experimental *ExperimentalOptions `json:"experimental,omitempty"`
|
||||||
}
|
}
|
||||||
@ -65,12 +65,12 @@ type LogOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DNSOptions struct {
|
type DNSOptions struct {
|
||||||
Servers Listable[DNSServerOptions] `json:"servers,omitempty"`
|
Servers Listable[DNSServerOptions] `json:"servers,omitempty"`
|
||||||
Rules Listable[DNSRule] `json:"rules,omitempty"`
|
Rules Listable[DNSRule] `json:"rules,omitempty"`
|
||||||
Final string `json:"final,omitempty"`
|
Final string `json:"final,omitempty"`
|
||||||
ReverseMapping bool `json:"reverse_mapping,omitempty"`
|
ReverseMapping bool `json:"reverse_mapping,omitempty"`
|
||||||
FakeIP *DNSFakeIPOptions `json:"fakeip,omitempty"`
|
FakeIP *DNSFakeIPOptions `json:"fakeip,omitempty"`
|
||||||
Strategy string `json:"strategy,omitempty"`
|
Strategy string `json:"strategy,omitempty"`
|
||||||
DisableCache bool `json:"disable_cache,omitempty"`
|
DisableCache bool `json:"disable_cache,omitempty"`
|
||||||
DisableExpire bool `json:"disable_expire,omitempty"`
|
DisableExpire bool `json:"disable_expire,omitempty"`
|
||||||
IndependentCache bool `json:"independent_cache,omitempty"`
|
IndependentCache bool `json:"independent_cache,omitempty"`
|
||||||
@ -162,29 +162,29 @@ type Inbound struct {
|
|||||||
Tag string `json:"tag,omitempty"`
|
Tag string `json:"tag,omitempty"`
|
||||||
InterfaceName string `json:"interface_name,omitempty"`
|
InterfaceName string `json:"interface_name,omitempty"`
|
||||||
MTU uint32 `json:"mtu,omitempty"`
|
MTU uint32 `json:"mtu,omitempty"`
|
||||||
GSO bool `json:"gso,omitempty"`
|
GSO bool `json:"gso,omitempty"`
|
||||||
Inet4Address Listable[string] `json:"inet4_address,omitempty"`
|
Inet4Address Listable[string] `json:"inet4_address,omitempty"`
|
||||||
Inet6Address Listable[string] `json:"inet6_address,omitempty"`
|
Inet6Address Listable[string] `json:"inet6_address,omitempty"`
|
||||||
AutoRoute bool `json:"auto_route,omitempty"`
|
AutoRoute bool `json:"auto_route,omitempty"`
|
||||||
StrictRoute bool `json:"strict_route,omitempty"`
|
StrictRoute bool `json:"strict_route,omitempty"`
|
||||||
Inet4RouteAddress Listable[string] `json:"inet4_route_address,omitempty"`
|
Inet4RouteAddress Listable[string] `json:"inet4_route_address,omitempty"`
|
||||||
Inet6RouteAddress Listable[string] `json:"inet6_route_address,omitempty"`
|
Inet6RouteAddress Listable[string] `json:"inet6_route_address,omitempty"`
|
||||||
Inet4RouteExcludeAddress Listable[string] `json:"inet4_route_exclude_address,omitempty"`
|
Inet4RouteExcludeAddress Listable[string] `json:"inet4_route_exclude_address,omitempty"`
|
||||||
Inet6RouteExcludeAddress Listable[string] `json:"inet6_route_exclude_address,omitempty"`
|
Inet6RouteExcludeAddress Listable[string] `json:"inet6_route_exclude_address,omitempty"`
|
||||||
IncludeInterface Listable[string] `json:"include_interface,omitempty"`
|
IncludeInterface Listable[string] `json:"include_interface,omitempty"`
|
||||||
ExcludeInterface Listable[string] `json:"exclude_interface,omitempty"`
|
ExcludeInterface Listable[string] `json:"exclude_interface,omitempty"`
|
||||||
IncludeUID Listable[uint32] `json:"include_uid,omitempty"`
|
IncludeUID Listable[uint32] `json:"include_uid,omitempty"`
|
||||||
IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"`
|
IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"`
|
||||||
ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"`
|
ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"`
|
||||||
ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"`
|
ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"`
|
||||||
IncludeAndroidUser Listable[int] `json:"include_android_user,omitempty"`
|
IncludeAndroidUser Listable[int] `json:"include_android_user,omitempty"`
|
||||||
IncludePackage Listable[string] `json:"include_package,omitempty"`
|
IncludePackage Listable[string] `json:"include_package,omitempty"`
|
||||||
ExcludePackage Listable[string] `json:"exclude_package,omitempty"`
|
ExcludePackage Listable[string] `json:"exclude_package,omitempty"`
|
||||||
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
|
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
|
||||||
UDPTimeout string `json:"udp_timeout,omitempty"`
|
UDPTimeout string `json:"udp_timeout,omitempty"`
|
||||||
Stack string `json:"stack,omitempty"`
|
Stack string `json:"stack,omitempty"`
|
||||||
Platform *TunPlatformOptions `json:"platform,omitempty"`
|
Platform *TunPlatformOptions `json:"platform,omitempty"`
|
||||||
SniffEnabled bool `json:"sniff,omitempty"`
|
SniffEnabled bool `json:"sniff,omitempty"`
|
||||||
SniffOverrideDestination bool `json:"sniff_override_destination,omitempty"`
|
SniffOverrideDestination bool `json:"sniff_override_destination,omitempty"`
|
||||||
SniffTimeout string `json:"sniff_timeout,omitempty"`
|
SniffTimeout string `json:"sniff_timeout,omitempty"`
|
||||||
DomainStrategy string `json:"domain_strategy,omitempty"`
|
DomainStrategy string `json:"domain_strategy,omitempty"`
|
||||||
@ -203,111 +203,12 @@ type HTTPProxyOptions struct {
|
|||||||
MatchDomain Listable[string] `json:"match_domain,omitempty"`
|
MatchDomain Listable[string] `json:"match_domain,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Outbound struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Tag string `json:"tag,omitempty"`
|
|
||||||
Detour string `json:"detour,omitempty"`
|
|
||||||
BindInterface string `json:"bind_interface,omitempty"`
|
|
||||||
Inet4BindAddress string `json:"inet4_bind_address,omitempty"`
|
|
||||||
Inet6BindAddress string `json:"inet6_bind_address,omitempty"`
|
|
||||||
ProtectPath string `json:"protect_path,omitempty"`
|
|
||||||
RoutingMark int `json:"routing_mark,omitempty"`
|
|
||||||
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
|
||||||
ConnectTimeout string `json:"connect_timeout,omitempty"`
|
|
||||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
|
||||||
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
|
||||||
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
|
||||||
DomainStrategy string `json:"domain_strategy,omitempty"`
|
|
||||||
FallbackDelay string `json:"fallback_delay,omitempty"`
|
|
||||||
OverrideAddress string `json:"override_address,omitempty"`
|
|
||||||
OverridePort uint16 `json:"override_port,omitempty"`
|
|
||||||
ProxyProtocol uint8 `json:"proxy_protocol,omitempty"`
|
|
||||||
Server string `json:"server,omitempty"`
|
|
||||||
ServerPort uint16 `json:"server_port,omitempty"`
|
|
||||||
Version string `json:"version,omitempty"`
|
|
||||||
Username string `json:"username,omitempty"`
|
|
||||||
Password string `json:"password,omitempty"`
|
|
||||||
Network string `json:"network,omitempty"`
|
|
||||||
UDPOverTCP *UDPOverTCPOptions `json:"udp_over_tcp,omitempty"`
|
|
||||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
|
||||||
Path string `json:"path,omitempty"`
|
|
||||||
Headers map[string]Listable[string] `json:"headers,omitempty"`
|
|
||||||
Method string `json:"method,omitempty"`
|
|
||||||
Plugin string `json:"plugin,omitempty"`
|
|
||||||
PluginOptions string `json:"plugin_opts,omitempty"`
|
|
||||||
Multiplex *OutboundMultiplexOptions `json:"multiplex,omitempty"`
|
|
||||||
UUID string `json:"uuid,omitempty"`
|
|
||||||
Security string `json:"security,omitempty"`
|
|
||||||
AlterId int `json:"alter_id,omitempty"`
|
|
||||||
GlobalPadding bool `json:"global_padding,omitempty"`
|
|
||||||
AuthenticatedLength bool `json:"authenticated_length,omitempty"`
|
|
||||||
PacketEncoding string `json:"packet_encoding,omitempty"`
|
|
||||||
Transport *V2RayTransportOptions `json:"transport,omitempty"`
|
|
||||||
SystemInterface bool `json:"system_interface,omitempty"`
|
|
||||||
GSO bool `json:"gso,omitempty"`
|
|
||||||
InterfaceName string `json:"interface_name,omitempty"`
|
|
||||||
LocalAddress Listable[string] `json:"local_address,omitempty"`
|
|
||||||
PrivateKey string `json:"private_key,omitempty"`
|
|
||||||
Peers Listable[WireGuardPeer] `json:"peers,omitempty"`
|
|
||||||
PeerPublicKey string `json:"peer_public_key,omitempty"`
|
|
||||||
PreSharedKey string `json:"pre_shared_key,omitempty"`
|
|
||||||
Reserved Listable[uint8] `json:"reserved,omitempty"`
|
|
||||||
Workers int `json:"workers,omitempty"`
|
|
||||||
MTU uint32 `json:"mtu,omitempty"`
|
|
||||||
Up string `json:"up,omitempty"`
|
|
||||||
UpMbps int `json:"up_mbps,omitempty"`
|
|
||||||
Down string `json:"down,omitempty"`
|
|
||||||
DownMbps int `json:"down_mbps,omitempty"`
|
|
||||||
Obfs *Obfs `json:"obfs,omitempty"`
|
|
||||||
Auth Listable[byte] `json:"auth,omitempty"`
|
|
||||||
AuthString string `json:"auth_str,omitempty"`
|
|
||||||
ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"`
|
|
||||||
ReceiveWindow uint64 `json:"recv_window,omitempty"`
|
|
||||||
DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"`
|
|
||||||
ExecutablePath string `json:"executable_path,omitempty"`
|
|
||||||
ExtraArgs Listable[string] `json:"extra_args,omitempty"`
|
|
||||||
DataDirectory string `json:"data_directory,omitempty"`
|
|
||||||
Options map[string]string `json:"torrc,omitempty"`
|
|
||||||
User string `json:"user,omitempty"`
|
|
||||||
PrivateKeyPath string `json:"private_key_path,omitempty"`
|
|
||||||
PrivateKeyPassphrase string `json:"private_key_passphrase,omitempty"`
|
|
||||||
HostKey Listable[string] `json:"host_key,omitempty"`
|
|
||||||
HostKeyAlgorithms Listable[string] `json:"host_key_algorithms,omitempty"`
|
|
||||||
ClientVersion string `json:"client_version,omitempty"`
|
|
||||||
ObfsParam string `json:"obfs_param,omitempty"`
|
|
||||||
Protocol string `json:"protocol,omitempty"`
|
|
||||||
ProtocolParam string `json:"protocol_param,omitempty"`
|
|
||||||
Flow string `json:"flow,omitempty"`
|
|
||||||
CongestionControl string `json:"congestion_control,omitempty"`
|
|
||||||
UDPRelayMode string `json:"udp_relay_mode,omitempty"`
|
|
||||||
UDPOverStream bool `json:"udp_over_stream,omitempty"`
|
|
||||||
ZeroRTTHandshake bool `json:"zero_rtt_handshake,omitempty"`
|
|
||||||
Heartbeat string `json:"heartbeat,omitempty"`
|
|
||||||
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
|
||||||
Default string `json:"default,omitempty"`
|
|
||||||
Outbounds Listable[string] `json:"outbounds,omitempty"`
|
|
||||||
URL string `json:"url,omitempty"`
|
|
||||||
Interval string `json:"interval,omitempty"`
|
|
||||||
Tolerance uint16 `json:"tolerance,omitempty"`
|
|
||||||
IdleTimeout string `json:"idle_timeout,omitempty"`
|
|
||||||
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type WireGuardPeer struct {
|
|
||||||
Server string `json:"server"`
|
|
||||||
ServerPort uint16 `json:"server_port"`
|
|
||||||
PublicKey string `json:"public_key,omitempty"`
|
|
||||||
PreSharedKey string `json:"pre_shared_key,omitempty"`
|
|
||||||
AllowedIPs Listable[string] `json:"allowed_ips,omitempty"`
|
|
||||||
Reserved Listable[uint8] `json:"reserved,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RouteOptions struct {
|
type RouteOptions struct {
|
||||||
GeoIP *GeoIPOptions `json:"geoip,omitempty"`
|
GeoIP *GeoIPOptions `json:"geoip,omitempty"`
|
||||||
Geosite *GeositeOptions `json:"geosite,omitempty"`
|
Geosite *GeositeOptions `json:"geosite,omitempty"`
|
||||||
Rules Listable[Rule] `json:"rules,omitempty"`
|
Rules Listable[Rule] `json:"rules,omitempty"`
|
||||||
RuleSet Listable[RuleSet] `json:"rule_set,omitempty"`
|
RuleSet Listable[RuleSet] `json:"rule_set,omitempty"`
|
||||||
Final string `json:"final,omitempty"`
|
Final string `json:"final,omitempty"`
|
||||||
FindProcess bool `json:"find_process,omitempty"`
|
FindProcess bool `json:"find_process,omitempty"`
|
||||||
AutoDetectInterface bool `json:"auto_detect_interface,omitempty"`
|
AutoDetectInterface bool `json:"auto_detect_interface,omitempty"`
|
||||||
OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"`
|
OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"`
|
||||||
|
8
model/direct.go
Normal file
8
model/direct.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type DirectOutboundOptions struct {
|
||||||
|
DialerOptions
|
||||||
|
OverrideAddress string `json:"override_address,omitempty"`
|
||||||
|
OverridePort uint16 `json:"override_port,omitempty"`
|
||||||
|
ProxyProtocol uint8 `json:"proxy_protocol,omitempty"`
|
||||||
|
}
|
16
model/group.go
Normal file
16
model/group.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type SelectorOutboundOptions struct {
|
||||||
|
Outbounds []string `json:"outbounds"`
|
||||||
|
Default string `json:"default,omitempty"`
|
||||||
|
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type URLTestOutboundOptions struct {
|
||||||
|
Outbounds []string `json:"outbounds"`
|
||||||
|
URL string `json:"url,omitempty"`
|
||||||
|
Interval string `json:"interval,omitempty"`
|
||||||
|
Tolerance uint16 `json:"tolerance,omitempty"`
|
||||||
|
IdleTimeout string `json:"idle_timeout,omitempty"`
|
||||||
|
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
||||||
|
}
|
@ -1,18 +1,18 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
type Hysteria struct {
|
type HysteriaOutboundOptions struct {
|
||||||
Server string `json:"server"`
|
DialerOptions
|
||||||
ServerPort uint16 `json:"server_port"`
|
ServerOptions
|
||||||
Up string `json:"up,omitempty"`
|
Up string `json:"up,omitempty"`
|
||||||
UpMbps int `json:"up_mbps,omitempty"`
|
UpMbps int `json:"up_mbps,omitempty"`
|
||||||
Down string `json:"down,omitempty"`
|
Down string `json:"down,omitempty"`
|
||||||
DownMbps int `json:"down_mbps,omitempty"`
|
DownMbps int `json:"down_mbps,omitempty"`
|
||||||
Obfs string `json:"obfs,omitempty"`
|
Obfs string `json:"obfs,omitempty"`
|
||||||
Auth []byte `json:"auth,omitempty"`
|
Auth []byte `json:"auth,omitempty"`
|
||||||
AuthString string `json:"auth_str,omitempty"`
|
AuthString string `json:"auth_str,omitempty"`
|
||||||
ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"`
|
ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"`
|
||||||
ReceiveWindow uint64 `json:"recv_window,omitempty"`
|
ReceiveWindow uint64 `json:"recv_window,omitempty"`
|
||||||
DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"`
|
DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"`
|
||||||
Network string `json:"network,omitempty"`
|
Network string `json:"network,omitempty"`
|
||||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
OutboundTLSOptionsContainer
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,14 @@ type Hysteria2Obfs struct {
|
|||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Hysteria2 struct {
|
type Hysteria2OutboundOptions struct {
|
||||||
Server string `json:"server"`
|
DialerOptions
|
||||||
ServerPort uint16 `json:"server_port"`
|
ServerOptions
|
||||||
UpMbps int `json:"up_mbps,omitempty"`
|
UpMbps int `json:"up_mbps,omitempty"`
|
||||||
DownMbps int `json:"down_mbps,omitempty"`
|
DownMbps int `json:"down_mbps,omitempty"`
|
||||||
Obfs *Hysteria2Obfs `json:"obfs,omitempty"`
|
Obfs *Hysteria2Obfs `json:"obfs,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
Network string `json:"network,omitempty"`
|
Network string `json:"network,omitempty"`
|
||||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
OutboundTLSOptionsContainer
|
||||||
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
||||||
}
|
}
|
||||||
|
167
model/outbound.go
Normal file
167
model/outbound.go
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
C "sub2sing-box/constant"
|
||||||
|
"sub2sing-box/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _Outbound struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Tag string `json:"tag,omitempty"`
|
||||||
|
DirectOptions DirectOutboundOptions `json:"-"`
|
||||||
|
SocksOptions SocksOutboundOptions `json:"-"`
|
||||||
|
HTTPOptions HTTPOutboundOptions `json:"-"`
|
||||||
|
ShadowsocksOptions ShadowsocksOutboundOptions `json:"-"`
|
||||||
|
VMessOptions VMessOutboundOptions `json:"-"`
|
||||||
|
TrojanOptions TrojanOutboundOptions `json:"-"`
|
||||||
|
WireGuardOptions WireGuardOutboundOptions `json:"-"`
|
||||||
|
HysteriaOptions HysteriaOutboundOptions `json:"-"`
|
||||||
|
TorOptions TorOutboundOptions `json:"-"`
|
||||||
|
SSHOptions SSHOutboundOptions `json:"-"`
|
||||||
|
ShadowTLSOptions ShadowTLSOutboundOptions `json:"-"`
|
||||||
|
ShadowsocksROptions ShadowsocksROutboundOptions `json:"-"`
|
||||||
|
VLESSOptions VLESSOutboundOptions `json:"-"`
|
||||||
|
TUICOptions TUICOutboundOptions `json:"-"`
|
||||||
|
Hysteria2Options Hysteria2OutboundOptions `json:"-"`
|
||||||
|
SelectorOptions SelectorOutboundOptions `json:"-"`
|
||||||
|
URLTestOptions URLTestOutboundOptions `json:"-"`
|
||||||
|
}
|
||||||
|
type Outbound _Outbound
|
||||||
|
|
||||||
|
func (h *Outbound) RawOptions() (any, error) {
|
||||||
|
var rawOptionsPtr any
|
||||||
|
switch h.Type {
|
||||||
|
case C.TypeDirect:
|
||||||
|
rawOptionsPtr = &h.DirectOptions
|
||||||
|
case C.TypeBlock, C.TypeDNS:
|
||||||
|
rawOptionsPtr = nil
|
||||||
|
case C.TypeSOCKS:
|
||||||
|
rawOptionsPtr = &h.SocksOptions
|
||||||
|
case C.TypeHTTP:
|
||||||
|
rawOptionsPtr = &h.HTTPOptions
|
||||||
|
case C.TypeShadowsocks:
|
||||||
|
rawOptionsPtr = &h.ShadowsocksOptions
|
||||||
|
case C.TypeVMess:
|
||||||
|
rawOptionsPtr = &h.VMessOptions
|
||||||
|
case C.TypeTrojan:
|
||||||
|
rawOptionsPtr = &h.TrojanOptions
|
||||||
|
case C.TypeWireGuard:
|
||||||
|
rawOptionsPtr = &h.WireGuardOptions
|
||||||
|
case C.TypeHysteria:
|
||||||
|
rawOptionsPtr = &h.HysteriaOptions
|
||||||
|
case C.TypeTor:
|
||||||
|
rawOptionsPtr = &h.TorOptions
|
||||||
|
case C.TypeSSH:
|
||||||
|
rawOptionsPtr = &h.SSHOptions
|
||||||
|
case C.TypeShadowTLS:
|
||||||
|
rawOptionsPtr = &h.ShadowTLSOptions
|
||||||
|
case C.TypeShadowsocksR:
|
||||||
|
rawOptionsPtr = &h.ShadowsocksROptions
|
||||||
|
case C.TypeVLESS:
|
||||||
|
rawOptionsPtr = &h.VLESSOptions
|
||||||
|
case C.TypeTUIC:
|
||||||
|
rawOptionsPtr = &h.TUICOptions
|
||||||
|
case C.TypeHysteria2:
|
||||||
|
rawOptionsPtr = &h.Hysteria2Options
|
||||||
|
case C.TypeSelector:
|
||||||
|
rawOptionsPtr = &h.SelectorOptions
|
||||||
|
case C.TypeURLTest:
|
||||||
|
rawOptionsPtr = &h.URLTestOptions
|
||||||
|
case "":
|
||||||
|
return nil, errors.New("missing outbound type")
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unknown outbound type: " + h.Type)
|
||||||
|
}
|
||||||
|
return rawOptionsPtr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Outbound) MarshalJSON() ([]byte, error) {
|
||||||
|
rawOptions, err := h.RawOptions()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result, err := util.MergeAndMarshal(struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Tag string `json:"tag,omitempty"`
|
||||||
|
}{
|
||||||
|
Type: h.Type,
|
||||||
|
Tag: h.Tag,
|
||||||
|
}, rawOptions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return []byte(result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Outbound) UnmarshalJSON(bytes []byte) error {
|
||||||
|
err := json.Unmarshal(bytes, (*_Outbound)(h))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rawOptions, err := h.RawOptions()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rawOptions == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(bytes, rawOptions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rawOptionsType := reflect.TypeOf(rawOptions).Elem()
|
||||||
|
hValue := reflect.ValueOf(h).Elem()
|
||||||
|
for i := 0; i < hValue.NumField(); i++ {
|
||||||
|
fieldType := hValue.Field(i).Type()
|
||||||
|
if fieldType == rawOptionsType {
|
||||||
|
hValue.Field(i).Set(reflect.ValueOf(rawOptions).Elem())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors.New("unknown outbound type: " + h.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Outbound) GetOutbounds() []string {
|
||||||
|
if h.Type == C.TypeSelector {
|
||||||
|
return h.SelectorOptions.Outbounds
|
||||||
|
}
|
||||||
|
if h.Type == C.TypeURLTest {
|
||||||
|
return h.URLTestOptions.Outbounds
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Outbound) SetOutbounds(outbounds []string) {
|
||||||
|
if h.Type == C.TypeSelector {
|
||||||
|
h.SelectorOptions.Outbounds = outbounds
|
||||||
|
}
|
||||||
|
if h.Type == C.TypeURLTest {
|
||||||
|
h.URLTestOptions.Outbounds = outbounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DialerOptions struct {
|
||||||
|
Detour string `json:"detour,omitempty"`
|
||||||
|
BindInterface string `json:"bind_interface,omitempty"`
|
||||||
|
Inet4BindAddress string `json:"inet4_bind_address,omitempty"`
|
||||||
|
Inet6BindAddress string `json:"inet6_bind_address,omitempty"`
|
||||||
|
ProtectPath string `json:"protect_path,omitempty"`
|
||||||
|
RoutingMark int `json:"routing_mark,omitempty"`
|
||||||
|
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
||||||
|
ConnectTimeout string `json:"connect_timeout,omitempty"`
|
||||||
|
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||||
|
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
||||||
|
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
||||||
|
UDPFragmentDefault bool `json:"-"`
|
||||||
|
DomainStrategy string `json:"domain_strategy,omitempty"`
|
||||||
|
FallbackDelay string `json:"fallback_delay,omitempty"`
|
||||||
|
IsWireGuardListener bool `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerOptions struct {
|
||||||
|
Server string `json:"server"`
|
||||||
|
ServerPort uint16 `json:"server_port"`
|
||||||
|
}
|
@ -1,94 +0,0 @@
|
|||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Proxy struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Tag string `json:"tag,omitempty"`
|
|
||||||
Shadowsocks `json:"-"`
|
|
||||||
VMess `json:"-"`
|
|
||||||
VLESS `json:"-"`
|
|
||||||
Trojan `json:"-"`
|
|
||||||
TUIC `json:"-"`
|
|
||||||
Hysteria `json:"-"`
|
|
||||||
Hysteria2 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Proxy) MarshalJSON() ([]byte, error) {
|
|
||||||
switch p.Type {
|
|
||||||
case "shadowsocks":
|
|
||||||
return json.Marshal(&struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Tag string `json:"tag,omitempty"`
|
|
||||||
Shadowsocks
|
|
||||||
}{
|
|
||||||
Type: p.Type,
|
|
||||||
Tag: p.Tag,
|
|
||||||
Shadowsocks: p.Shadowsocks,
|
|
||||||
})
|
|
||||||
case "vmess":
|
|
||||||
return json.Marshal(&struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Tag string `json:"tag,omitempty"`
|
|
||||||
VMess
|
|
||||||
}{
|
|
||||||
Type: p.Type,
|
|
||||||
Tag: p.Tag,
|
|
||||||
VMess: p.VMess,
|
|
||||||
})
|
|
||||||
case "vless":
|
|
||||||
return json.Marshal(&struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Tag string `json:"tag,omitempty"`
|
|
||||||
VLESS
|
|
||||||
}{
|
|
||||||
Type: p.Type,
|
|
||||||
Tag: p.Tag,
|
|
||||||
VLESS: p.VLESS,
|
|
||||||
})
|
|
||||||
case "trojan":
|
|
||||||
return json.Marshal(&struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Tag string `json:"tag,omitempty"`
|
|
||||||
Trojan
|
|
||||||
}{
|
|
||||||
Type: p.Type,
|
|
||||||
Tag: p.Tag,
|
|
||||||
Trojan: p.Trojan,
|
|
||||||
})
|
|
||||||
case "tuic":
|
|
||||||
return json.Marshal(&struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Tag string `json:"tag,omitempty"`
|
|
||||||
TUIC
|
|
||||||
}{
|
|
||||||
Type: p.Type,
|
|
||||||
Tag: p.Tag,
|
|
||||||
TUIC: p.TUIC,
|
|
||||||
})
|
|
||||||
case "hysteria":
|
|
||||||
return json.Marshal(&struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Tag string `json:"tag,omitempty"`
|
|
||||||
Hysteria
|
|
||||||
}{
|
|
||||||
Type: p.Type,
|
|
||||||
Tag: p.Tag,
|
|
||||||
Hysteria: p.Hysteria,
|
|
||||||
})
|
|
||||||
case "hysteria2":
|
|
||||||
return json.Marshal(&struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Tag string `json:"tag,omitempty"`
|
|
||||||
Hysteria2
|
|
||||||
}{
|
|
||||||
Type: p.Type,
|
|
||||||
Tag: p.Tag,
|
|
||||||
Hysteria2: p.Hysteria2,
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
return json.Marshal(p)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,8 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
type Shadowsocks struct {
|
type ShadowsocksOutboundOptions struct {
|
||||||
Server string `json:"server"`
|
DialerOptions
|
||||||
ServerPort uint16 `json:"server_port"`
|
ServerOptions
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Plugin string `json:"plugin,omitempty"`
|
Plugin string `json:"plugin,omitempty"`
|
||||||
|
13
model/shadowsocksr.go
Normal file
13
model/shadowsocksr.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type ShadowsocksROutboundOptions struct {
|
||||||
|
DialerOptions
|
||||||
|
ServerOptions
|
||||||
|
Method string `json:"method"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Obfs string `json:"obfs,omitempty"`
|
||||||
|
ObfsParam string `json:"obfs_param,omitempty"`
|
||||||
|
Protocol string `json:"protocol,omitempty"`
|
||||||
|
ProtocolParam string `json:"protocol_param,omitempty"`
|
||||||
|
Network string `json:"network,omitempty"`
|
||||||
|
}
|
19
model/shadowtls.go
Normal file
19
model/shadowtls.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type ShadowTLSUser struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShadowTLSHandshakeOptions struct {
|
||||||
|
ServerOptions
|
||||||
|
DialerOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShadowTLSOutboundOptions struct {
|
||||||
|
DialerOptions
|
||||||
|
ServerOptions
|
||||||
|
Version int `json:"version,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
OutboundTLSOptionsContainer
|
||||||
|
}
|
21
model/simple.go
Normal file
21
model/simple.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type SocksOutboundOptions struct {
|
||||||
|
DialerOptions
|
||||||
|
ServerOptions
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
Network Listable[string] `json:"network,omitempty"`
|
||||||
|
UDPOverTCP *UDPOverTCPOptions `json:"udp_over_tcp,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPOutboundOptions struct {
|
||||||
|
DialerOptions
|
||||||
|
ServerOptions
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
OutboundTLSOptionsContainer
|
||||||
|
Path string `json:"path,omitempty"`
|
||||||
|
Headers map[string]string `json:"headers,omitempty"`
|
||||||
|
}
|
@ -1,15 +1,32 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
C "sub2sing-box/constant"
|
||||||
|
|
||||||
"golang.org/x/text/collate"
|
"golang.org/x/text/collate"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SortByNumber []Outbound
|
type SortByNumber []Outbound
|
||||||
|
|
||||||
func (a SortByNumber) Len() int { return len(a) }
|
func (a SortByNumber) Len() int { return len(a) }
|
||||||
func (a SortByNumber) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
func (a SortByNumber) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
func (a SortByNumber) Less(i, j int) bool { return len(a[i].Outbounds) < len(a[j].Outbounds) }
|
func (a SortByNumber) Less(i, j int) bool {
|
||||||
|
var size1, size2 int
|
||||||
|
if a[i].Type == C.TypeSelector {
|
||||||
|
size1 = len(a[i].SelectorOptions.Outbounds)
|
||||||
|
}
|
||||||
|
if a[i].Type == C.TypeURLTest {
|
||||||
|
size1 = len(a[j].URLTestOptions.Outbounds)
|
||||||
|
}
|
||||||
|
if a[j].Type == C.TypeSelector {
|
||||||
|
size2 = len(a[j].SelectorOptions.Outbounds)
|
||||||
|
}
|
||||||
|
if a[j].Type == C.TypeURLTest {
|
||||||
|
size2 = len(a[j].URLTestOptions.Outbounds)
|
||||||
|
}
|
||||||
|
return size1 < size2
|
||||||
|
}
|
||||||
|
|
||||||
type SortByTag []Outbound
|
type SortByTag []Outbound
|
||||||
|
|
||||||
|
14
model/ssh.go
Normal file
14
model/ssh.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type SSHOutboundOptions struct {
|
||||||
|
DialerOptions
|
||||||
|
ServerOptions
|
||||||
|
User string `json:"user,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
PrivateKey Listable[string] `json:"private_key,omitempty"`
|
||||||
|
PrivateKeyPath string `json:"private_key_path,omitempty"`
|
||||||
|
PrivateKeyPassphrase string `json:"private_key_passphrase,omitempty"`
|
||||||
|
HostKey Listable[string] `json:"host_key,omitempty"`
|
||||||
|
HostKeyAlgorithms Listable[string] `json:"host_key_algorithms,omitempty"`
|
||||||
|
ClientVersion string `json:"client_version,omitempty"`
|
||||||
|
}
|
@ -34,3 +34,7 @@ type OutboundRealityOptions struct {
|
|||||||
PublicKey string `json:"public_key,omitempty"`
|
PublicKey string `json:"public_key,omitempty"`
|
||||||
ShortID string `json:"short_id,omitempty"`
|
ShortID string `json:"short_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OutboundTLSOptionsContainer struct {
|
||||||
|
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
||||||
|
}
|
||||||
|
9
model/tor.go
Normal file
9
model/tor.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type TorOutboundOptions struct {
|
||||||
|
DialerOptions
|
||||||
|
ExecutablePath string `json:"executable_path,omitempty"`
|
||||||
|
ExtraArgs []string `json:"extra_args,omitempty"`
|
||||||
|
DataDirectory string `json:"data_directory,omitempty"`
|
||||||
|
Options map[string]string `json:"torrc,omitempty"`
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
type Trojan struct {
|
type TrojanOutboundOptions struct {
|
||||||
Server string `json:"server"`
|
DialerOptions
|
||||||
ServerPort uint16 `json:"server_port"`
|
ServerOptions
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Network string `json:"network,omitempty"`
|
Network string `json:"network,omitempty"`
|
||||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
OutboundTLSOptionsContainer
|
||||||
Multiplex *OutboundMultiplexOptions `json:"multiplex,omitempty"`
|
Multiplex *OutboundMultiplexOptions `json:"multiplex,omitempty"`
|
||||||
Transport *V2RayTransportOptions `json:"transport,omitempty"`
|
Transport *V2RayTransportOptions `json:"transport,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
type TUIC struct {
|
type TUICOutboundOptions struct {
|
||||||
Server string `json:"server"`
|
DialerOptions
|
||||||
ServerPort uint16 `json:"server_port"`
|
ServerOptions
|
||||||
UUID string `json:"uuid,omitempty"`
|
UUID string `json:"uuid,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
CongestionControl string `json:"congestion_control,omitempty"`
|
CongestionControl string `json:"congestion_control,omitempty"`
|
||||||
UDPRelayMode string `json:"udp_relay_mode,omitempty"`
|
UDPRelayMode string `json:"udp_relay_mode,omitempty"`
|
||||||
UDPOverStream bool `json:"udp_over_stream,omitempty"`
|
UDPOverStream bool `json:"udp_over_stream,omitempty"`
|
||||||
ZeroRTTHandshake bool `json:"zero_rtt_handshake,omitempty"`
|
ZeroRTTHandshake bool `json:"zero_rtt_handshake,omitempty"`
|
||||||
Heartbeat string `json:"heartbeat,omitempty"`
|
Heartbeat string `json:"heartbeat,omitempty"`
|
||||||
Network string `json:"network,omitempty"`
|
Network string `json:"network,omitempty"`
|
||||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
OutboundTLSOptionsContainer
|
||||||
}
|
}
|
||||||
|
@ -53,8 +53,8 @@ func (o *V2RayTransportOptions) MarshalJSON() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type V2RayHTTPOptions struct {
|
type V2RayHTTPOptions struct {
|
||||||
Host Listable[string] `json:"host,omitempty"`
|
Host Listable[string] `json:"host,omitempty"`
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
Method string `json:"method,omitempty"`
|
Method string `json:"method,omitempty"`
|
||||||
Headers map[string]string `json:"headers,omitempty"`
|
Headers map[string]string `json:"headers,omitempty"`
|
||||||
IdleTimeout string `json:"idle_timeout,omitempty"`
|
IdleTimeout string `json:"idle_timeout,omitempty"`
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
type VLESS struct {
|
type VLESSOutboundOptions struct {
|
||||||
Server string `json:"server"`
|
DialerOptions
|
||||||
ServerPort uint16 `json:"server_port"`
|
ServerOptions
|
||||||
UUID string `json:"uuid"`
|
UUID string `json:"uuid"`
|
||||||
Flow string `json:"flow,omitempty"`
|
Flow string `json:"flow,omitempty"`
|
||||||
Network string `json:"network,omitempty"`
|
Network string `json:"network,omitempty"`
|
||||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
OutboundTLSOptionsContainer
|
||||||
Multiplex *OutboundMultiplexOptions `json:"multiplex,omitempty"`
|
Multiplex *OutboundMultiplexOptions `json:"multiplex,omitempty"`
|
||||||
Transport *V2RayTransportOptions `json:"transport,omitempty"`
|
Transport *V2RayTransportOptions `json:"transport,omitempty"`
|
||||||
PacketEncoding *string `json:"packet_encoding,omitempty"`
|
PacketEncoding *string `json:"packet_encoding,omitempty"`
|
||||||
|
@ -18,17 +18,17 @@ type VmessJson struct {
|
|||||||
Fp string `json:"fp"`
|
Fp string `json:"fp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type VMess struct {
|
type VMessOutboundOptions struct {
|
||||||
Server string `json:"server"`
|
DialerOptions
|
||||||
ServerPort uint16 `json:"server_port"`
|
ServerOptions
|
||||||
UUID string `json:"uuid"`
|
UUID string `json:"uuid"`
|
||||||
Security string `json:"security"`
|
Security string `json:"security"`
|
||||||
AlterId int `json:"alter_id,omitempty"`
|
AlterId int `json:"alter_id,omitempty"`
|
||||||
GlobalPadding bool `json:"global_padding,omitempty"`
|
GlobalPadding bool `json:"global_padding,omitempty"`
|
||||||
AuthenticatedLength bool `json:"authenticated_length,omitempty"`
|
AuthenticatedLength bool `json:"authenticated_length,omitempty"`
|
||||||
Network string `json:"network,omitempty"`
|
Network string `json:"network,omitempty"`
|
||||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
OutboundTLSOptionsContainer
|
||||||
PacketEncoding string `json:"packet_encoding,omitempty"`
|
PacketEncoding string `json:"packet_encoding,omitempty"`
|
||||||
Multiplex *OutboundMultiplexOptions `json:"multiplex,omitempty"`
|
Multiplex *OutboundMultiplexOptions `json:"multiplex,omitempty"`
|
||||||
Transport *V2RayTransportOptions `json:"transport,omitempty"`
|
Transport *V2RayTransportOptions `json:"transport,omitempty"`
|
||||||
}
|
}
|
||||||
|
28
model/wireguard.go
Normal file
28
model/wireguard.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "net/netip"
|
||||||
|
|
||||||
|
type WireGuardOutboundOptions struct {
|
||||||
|
DialerOptions
|
||||||
|
SystemInterface bool `json:"system_interface,omitempty"`
|
||||||
|
GSO bool `json:"gso,omitempty"`
|
||||||
|
InterfaceName string `json:"interface_name,omitempty"`
|
||||||
|
LocalAddress Listable[netip.Prefix] `json:"local_address"`
|
||||||
|
PrivateKey string `json:"private_key"`
|
||||||
|
Peers []WireGuardPeer `json:"peers,omitempty"`
|
||||||
|
ServerOptions
|
||||||
|
PeerPublicKey string `json:"peer_public_key"`
|
||||||
|
PreSharedKey string `json:"pre_shared_key,omitempty"`
|
||||||
|
Reserved []uint8 `json:"reserved,omitempty"`
|
||||||
|
Workers int `json:"workers,omitempty"`
|
||||||
|
MTU uint32 `json:"mtu,omitempty"`
|
||||||
|
Network string `json:"network,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WireGuardPeer struct {
|
||||||
|
ServerOptions
|
||||||
|
PublicKey string `json:"public_key,omitempty"`
|
||||||
|
PreSharedKey string `json:"pre_shared_key,omitempty"`
|
||||||
|
AllowedIPs Listable[string] `json:"allowed_ips,omitempty"`
|
||||||
|
Reserved []uint8 `json:"reserved,omitempty"`
|
||||||
|
}
|
@ -5,7 +5,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
model2 "sub2sing-box/model"
|
"sub2sing-box/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
//hysteria://host:port?protocol=udp&auth=123456&peer=sni.domain&insecure=1&upmbps=100&downmbps=100&alpn=hysteria&obfs=xplus&obfsParam=123456#remarks
|
//hysteria://host:port?protocol=udp&auth=123456&peer=sni.domain&insecure=1&upmbps=100&downmbps=100&alpn=hysteria&obfs=xplus&obfsParam=123456#remarks
|
||||||
@ -23,23 +23,23 @@ import (
|
|||||||
//- obfsParam: Obfuscation password (optional)
|
//- obfsParam: Obfuscation password (optional)
|
||||||
//- remarks: remarks (optional)
|
//- remarks: remarks (optional)
|
||||||
|
|
||||||
func ParseHysteria(proxy string) (model2.Proxy, error) {
|
func ParseHysteria(proxy string) (model.Outbound, error) {
|
||||||
if !strings.HasPrefix(proxy, "hysteria://") {
|
if !strings.HasPrefix(proxy, "hysteria://") {
|
||||||
return model2.Proxy{}, errors.New("invalid hysteria Url")
|
return model.Outbound{}, errors.New("invalid hysteria Url")
|
||||||
}
|
}
|
||||||
parts := strings.SplitN(strings.TrimPrefix(proxy, "hysteria://"), "?", 2)
|
parts := strings.SplitN(strings.TrimPrefix(proxy, "hysteria://"), "?", 2)
|
||||||
serverInfo := strings.SplitN(parts[0], ":", 2)
|
serverInfo := strings.SplitN(parts[0], ":", 2)
|
||||||
if len(serverInfo) != 2 {
|
if len(serverInfo) != 2 {
|
||||||
return model2.Proxy{}, errors.New("invalid hysteria Url")
|
return model.Outbound{}, errors.New("invalid hysteria Url")
|
||||||
}
|
}
|
||||||
params, err := url.ParseQuery(parts[1])
|
params, err := url.ParseQuery(parts[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, errors.New("invalid hysteria Url")
|
return model.Outbound{}, errors.New("invalid hysteria Url")
|
||||||
}
|
}
|
||||||
host := serverInfo[0]
|
host := serverInfo[0]
|
||||||
port, err := strconv.Atoi(serverInfo[1])
|
port, err := strconv.Atoi(serverInfo[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, errors.New("invalid hysteria Url")
|
return model.Outbound{}, errors.New("invalid hysteria Url")
|
||||||
}
|
}
|
||||||
protocol := params.Get("protocol")
|
protocol := params.Get("protocol")
|
||||||
auth := params.Get("auth")
|
auth := params.Get("auth")
|
||||||
@ -64,23 +64,27 @@ func ParseHysteria(proxy string) (model2.Proxy, error) {
|
|||||||
}
|
}
|
||||||
insecureBool, err := strconv.ParseBool(insecure)
|
insecureBool, err := strconv.ParseBool(insecure)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, errors.New("invalid hysteria Url")
|
return model.Outbound{}, errors.New("invalid hysteria Url")
|
||||||
}
|
}
|
||||||
result := model2.Proxy{
|
result := model.Outbound{
|
||||||
Type: "hysteria",
|
Type: "hysteria",
|
||||||
Tag: remarks,
|
Tag: remarks,
|
||||||
Hysteria: model2.Hysteria{
|
HysteriaOptions: model.HysteriaOutboundOptions{
|
||||||
Server: host,
|
ServerOptions: model.ServerOptions{
|
||||||
ServerPort: uint16(port),
|
Server: host,
|
||||||
Up: upmbps,
|
ServerPort: uint16(port),
|
||||||
Down: downmbps,
|
},
|
||||||
Auth: []byte(auth),
|
Up: upmbps,
|
||||||
Obfs: obfs,
|
Down: downmbps,
|
||||||
Network: protocol,
|
Auth: []byte(auth),
|
||||||
TLS: &model2.OutboundTLSOptions{
|
Obfs: obfs,
|
||||||
Enabled: true,
|
Network: protocol,
|
||||||
Insecure: insecureBool,
|
OutboundTLSOptionsContainer: model.OutboundTLSOptionsContainer{
|
||||||
ALPN: alpn,
|
TLS: &model.OutboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
Insecure: insecureBool,
|
||||||
|
ALPN: alpn,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,14 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
model2 "sub2sing-box/model"
|
"sub2sing-box/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// hysteria2://letmein@example.com/?insecure=1&obfs=salamander&obfs-password=gawrgura&pinSHA256=deadbeef&sni=real.example.com
|
// hysteria2://letmein@example.com/?insecure=1&obfs=salamander&obfs-password=gawrgura&pinSHA256=deadbeef&sni=real.example.com
|
||||||
|
|
||||||
func ParseHysteria2(proxy string) (model2.Proxy, error) {
|
func ParseHysteria2(proxy string) (model.Outbound, error) {
|
||||||
if !strings.HasPrefix(proxy, "hysteria2://") && !strings.HasPrefix(proxy, "hy2://") {
|
if !strings.HasPrefix(proxy, "hysteria2://") && !strings.HasPrefix(proxy, "hy2://") {
|
||||||
return model2.Proxy{}, errors.New("invalid hysteria2 Url")
|
return model.Outbound{}, errors.New("invalid hysteria2 Url")
|
||||||
}
|
}
|
||||||
parts := strings.SplitN(strings.TrimPrefix(proxy, "hysteria2://"), "@", 2)
|
parts := strings.SplitN(strings.TrimPrefix(proxy, "hysteria2://"), "@", 2)
|
||||||
serverInfo := strings.SplitN(parts[1], "/?", 2)
|
serverInfo := strings.SplitN(parts[1], "/?", 2)
|
||||||
@ -20,36 +20,38 @@ func ParseHysteria2(proxy string) (model2.Proxy, error) {
|
|||||||
if len(serverAndPort) == 1 {
|
if len(serverAndPort) == 1 {
|
||||||
serverAndPort = append(serverAndPort, "443")
|
serverAndPort = append(serverAndPort, "443")
|
||||||
} else if len(serverAndPort) != 2 {
|
} else if len(serverAndPort) != 2 {
|
||||||
return model2.Proxy{}, errors.New("invalid hysteria2 Url")
|
return model.Outbound{}, errors.New("invalid hysteria2 Url")
|
||||||
}
|
}
|
||||||
params, err := url.ParseQuery(serverInfo[1])
|
params, err := url.ParseQuery(serverInfo[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, errors.New("invalid hysteria2 Url")
|
return model.Outbound{}, errors.New("invalid hysteria2 Url")
|
||||||
}
|
}
|
||||||
port, err := strconv.Atoi(serverAndPort[1])
|
port, err := strconv.Atoi(serverAndPort[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, errors.New("invalid hysteria2 Url")
|
return model.Outbound{}, errors.New("invalid hysteria2 Url")
|
||||||
}
|
}
|
||||||
remarks := params.Get("name")
|
remarks := params.Get("name")
|
||||||
server := serverAndPort[0]
|
server := serverAndPort[0]
|
||||||
password := parts[0]
|
password := parts[0]
|
||||||
network := params.Get("network")
|
network := params.Get("network")
|
||||||
result := model2.Proxy{
|
result := model.Outbound{
|
||||||
Type: "hysteria2",
|
Type: "hysteria2",
|
||||||
Tag: remarks,
|
Tag: remarks,
|
||||||
Hysteria2: model2.Hysteria2{
|
Hysteria2Options: model.Hysteria2OutboundOptions{
|
||||||
Server: server,
|
ServerOptions: model.ServerOptions{
|
||||||
ServerPort: uint16(port),
|
Server: server,
|
||||||
Password: password,
|
ServerPort: uint16(port),
|
||||||
Obfs: &model2.Hysteria2Obfs{
|
},
|
||||||
|
Password: password,
|
||||||
|
Obfs: &model.Hysteria2Obfs{
|
||||||
Type: params.Get("obfs"),
|
Type: params.Get("obfs"),
|
||||||
Password: params.Get("obfs-password"),
|
Password: params.Get("obfs-password"),
|
||||||
},
|
},
|
||||||
TLS: &model2.OutboundTLSOptions{
|
OutboundTLSOptionsContainer: model.OutboundTLSOptionsContainer{
|
||||||
Enabled: params.Get("pinSHA256") != "",
|
TLS: &model.OutboundTLSOptions{Enabled: params.Get("pinSHA256") != "",
|
||||||
Insecure: params.Get("insecure") == "1",
|
Insecure: params.Get("insecure") == "1",
|
||||||
ServerName: params.Get("sni"),
|
ServerName: params.Get("sni"),
|
||||||
Certificate: []string{params.Get("pinSHA256")},
|
Certificate: []string{params.Get("pinSHA256")}},
|
||||||
},
|
},
|
||||||
Network: network,
|
Network: network,
|
||||||
},
|
},
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"sub2sing-box/model"
|
"sub2sing-box/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ParserMap map[string]func(string) (model.Proxy, error) = map[string]func(string) (model.Proxy, error){
|
var ParserMap map[string]func(string) (model.Outbound, error) = map[string]func(string) (model.Outbound, error){
|
||||||
"ss://": ParseShadowsocks,
|
"ss://": ParseShadowsocks,
|
||||||
"vmess://": ParseVmess,
|
"vmess://": ParseVmess,
|
||||||
"trojan://": ParseTrojan,
|
"trojan://": ParseTrojan,
|
||||||
|
@ -5,43 +5,43 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
model2 "sub2sing-box/model"
|
"sub2sing-box/model"
|
||||||
"sub2sing-box/util"
|
"sub2sing-box/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseShadowsocks(proxy string) (model2.Proxy, error) {
|
func ParseShadowsocks(proxy string) (model.Outbound, error) {
|
||||||
if !strings.HasPrefix(proxy, "ss://") {
|
if !strings.HasPrefix(proxy, "ss://") {
|
||||||
return model2.Proxy{}, errors.New("invalid ss Url")
|
return model.Outbound{}, errors.New("invalid ss Url")
|
||||||
}
|
}
|
||||||
parts := strings.SplitN(strings.TrimPrefix(proxy, "ss://"), "@", 2)
|
parts := strings.SplitN(strings.TrimPrefix(proxy, "ss://"), "@", 2)
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
return model2.Proxy{}, errors.New("invalid ss Url")
|
return model.Outbound{}, errors.New("invalid ss Url")
|
||||||
}
|
}
|
||||||
if !strings.Contains(parts[0], ":") {
|
if !strings.Contains(parts[0], ":") {
|
||||||
decoded, err := util.DecodeBase64(parts[0])
|
decoded, err := util.DecodeBase64(parts[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, errors.New("invalid ss Url" + err.Error())
|
return model.Outbound{}, errors.New("invalid ss Url" + err.Error())
|
||||||
}
|
}
|
||||||
parts[0] = decoded
|
parts[0] = decoded
|
||||||
}
|
}
|
||||||
credentials := strings.SplitN(parts[0], ":", 2)
|
credentials := strings.SplitN(parts[0], ":", 2)
|
||||||
if len(credentials) != 2 {
|
if len(credentials) != 2 {
|
||||||
return model2.Proxy{}, errors.New("invalid ss Url")
|
return model.Outbound{}, errors.New("invalid ss Url")
|
||||||
}
|
}
|
||||||
serverInfo := strings.SplitN(parts[1], "#", 2)
|
serverInfo := strings.SplitN(parts[1], "#", 2)
|
||||||
serverAndPort := strings.SplitN(serverInfo[0], ":", 2)
|
serverAndPort := strings.SplitN(serverInfo[0], ":", 2)
|
||||||
if len(serverAndPort) != 2 {
|
if len(serverAndPort) != 2 {
|
||||||
return model2.Proxy{}, errors.New("invalid ss Url")
|
return model.Outbound{}, errors.New("invalid ss Url")
|
||||||
}
|
}
|
||||||
port, err := strconv.Atoi(strings.TrimSpace(serverAndPort[1]))
|
port, err := strconv.Atoi(strings.TrimSpace(serverAndPort[1]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, errors.New("invalid ss Url" + err.Error())
|
return model.Outbound{}, errors.New("invalid ss Url" + err.Error())
|
||||||
}
|
}
|
||||||
remarks := ""
|
remarks := ""
|
||||||
if len(serverInfo) == 2 {
|
if len(serverInfo) == 2 {
|
||||||
unescape, err := url.QueryUnescape(serverInfo[1])
|
unescape, err := url.QueryUnescape(serverInfo[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, errors.New("invalid ss Url" + err.Error())
|
return model.Outbound{}, errors.New("invalid ss Url" + err.Error())
|
||||||
}
|
}
|
||||||
remarks = strings.TrimSpace(unescape)
|
remarks = strings.TrimSpace(unescape)
|
||||||
} else {
|
} else {
|
||||||
@ -50,14 +50,16 @@ func ParseShadowsocks(proxy string) (model2.Proxy, error) {
|
|||||||
method := credentials[0]
|
method := credentials[0]
|
||||||
password := credentials[1]
|
password := credentials[1]
|
||||||
server := strings.TrimSpace(serverAndPort[0])
|
server := strings.TrimSpace(serverAndPort[0])
|
||||||
result := model2.Proxy{
|
result := model.Outbound{
|
||||||
Type: "shadowsocks",
|
Type: "shadowsocks",
|
||||||
Tag: remarks,
|
Tag: remarks,
|
||||||
Shadowsocks: model2.Shadowsocks{
|
ShadowsocksOptions: model.ShadowsocksOutboundOptions{
|
||||||
Method: method,
|
ServerOptions: model.ServerOptions{
|
||||||
Password: password,
|
Server: server,
|
||||||
Server: server,
|
ServerPort: uint16(port),
|
||||||
ServerPort: uint16(port),
|
},
|
||||||
|
Method: method,
|
||||||
|
Password: password,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
|
@ -5,30 +5,30 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
model2 "sub2sing-box/model"
|
"sub2sing-box/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseTrojan(proxy string) (model2.Proxy, error) {
|
func ParseTrojan(proxy string) (model.Outbound, error) {
|
||||||
if !strings.HasPrefix(proxy, "trojan://") {
|
if !strings.HasPrefix(proxy, "trojan://") {
|
||||||
return model2.Proxy{}, errors.New("invalid trojan Url")
|
return model.Outbound{}, errors.New("invalid trojan Url")
|
||||||
}
|
}
|
||||||
parts := strings.SplitN(strings.TrimPrefix(proxy, "trojan://"), "@", 2)
|
parts := strings.SplitN(strings.TrimPrefix(proxy, "trojan://"), "@", 2)
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
return model2.Proxy{}, errors.New("invalid trojan Url")
|
return model.Outbound{}, errors.New("invalid trojan Url")
|
||||||
}
|
}
|
||||||
serverInfo := strings.SplitN(parts[1], "#", 2)
|
serverInfo := strings.SplitN(parts[1], "#", 2)
|
||||||
serverAndPortAndParams := strings.SplitN(serverInfo[0], "?", 2)
|
serverAndPortAndParams := strings.SplitN(serverInfo[0], "?", 2)
|
||||||
serverAndPort := strings.SplitN(serverAndPortAndParams[0], ":", 2)
|
serverAndPort := strings.SplitN(serverAndPortAndParams[0], ":", 2)
|
||||||
params, err := url.ParseQuery(serverAndPortAndParams[1])
|
params, err := url.ParseQuery(serverAndPortAndParams[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, err
|
return model.Outbound{}, err
|
||||||
}
|
}
|
||||||
if len(serverAndPort) != 2 {
|
if len(serverAndPort) != 2 {
|
||||||
return model2.Proxy{}, errors.New("invalid trojan Url")
|
return model.Outbound{}, errors.New("invalid trojan Url")
|
||||||
}
|
}
|
||||||
port, err := strconv.Atoi(strings.TrimSpace(serverAndPort[1]))
|
port, err := strconv.Atoi(strings.TrimSpace(serverAndPort[1]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, err
|
return model.Outbound{}, err
|
||||||
}
|
}
|
||||||
remarks := ""
|
remarks := ""
|
||||||
if len(serverInfo) == 2 {
|
if len(serverInfo) == 2 {
|
||||||
@ -38,14 +38,16 @@ func ParseTrojan(proxy string) (model2.Proxy, error) {
|
|||||||
}
|
}
|
||||||
server := strings.TrimSpace(serverAndPort[0])
|
server := strings.TrimSpace(serverAndPort[0])
|
||||||
password := strings.TrimSpace(parts[0])
|
password := strings.TrimSpace(parts[0])
|
||||||
result := model2.Proxy{
|
result := model.Outbound{
|
||||||
Type: "trojan",
|
Type: "trojan",
|
||||||
Tag: remarks,
|
Tag: remarks,
|
||||||
Trojan: model2.Trojan{
|
TrojanOptions: model.TrojanOutboundOptions{
|
||||||
Server: server,
|
ServerOptions: model.ServerOptions{
|
||||||
ServerPort: uint16(port),
|
Server: server,
|
||||||
Password: password,
|
ServerPort: uint16(port),
|
||||||
Network: params.Get("type"),
|
},
|
||||||
|
Password: password,
|
||||||
|
Network: params.Get("type"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if params.Get("security") == "xtls" || params.Get("security") == "tls" {
|
if params.Get("security") == "xtls" || params.Get("security") == "tls" {
|
||||||
@ -55,31 +57,35 @@ func ParseTrojan(proxy string) (model2.Proxy, error) {
|
|||||||
} else {
|
} else {
|
||||||
alpn = nil
|
alpn = nil
|
||||||
}
|
}
|
||||||
result.Trojan.TLS = &model2.OutboundTLSOptions{
|
result.TrojanOptions.OutboundTLSOptionsContainer = model.OutboundTLSOptionsContainer{
|
||||||
Enabled: true,
|
TLS: &model.OutboundTLSOptions{
|
||||||
ALPN: alpn,
|
Enabled: true,
|
||||||
ServerName: params.Get("sni"),
|
ALPN: alpn,
|
||||||
|
ServerName: params.Get("sni"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if params.Get("security") == "reality" {
|
if params.Get("security") == "reality" {
|
||||||
result.Trojan.TLS = &model2.OutboundTLSOptions{
|
result.TrojanOptions.OutboundTLSOptionsContainer = model.OutboundTLSOptionsContainer{
|
||||||
Enabled: true,
|
TLS: &model.OutboundTLSOptions{
|
||||||
ServerName: params.Get("sni"),
|
Enabled: true,
|
||||||
Reality: &model2.OutboundRealityOptions{
|
ServerName: params.Get("sni"),
|
||||||
Enabled: true,
|
Reality: &model.OutboundRealityOptions{
|
||||||
PublicKey: params.Get("pbk"),
|
Enabled: true,
|
||||||
ShortID: params.Get("sid"),
|
PublicKey: params.Get("pbk"),
|
||||||
},
|
ShortID: params.Get("sid"),
|
||||||
UTLS: &model2.OutboundUTLSOptions{
|
},
|
||||||
Enabled: params.Get("fp") != "",
|
UTLS: &model.OutboundUTLSOptions{
|
||||||
Fingerprint: params.Get("fp"),
|
Enabled: params.Get("fp") != "",
|
||||||
|
Fingerprint: params.Get("fp"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if params.Get("type") == "ws" {
|
if params.Get("type") == "ws" {
|
||||||
result.Trojan.Transport = &model2.V2RayTransportOptions{
|
result.TrojanOptions.Transport = &model.V2RayTransportOptions{
|
||||||
Type: "ws",
|
Type: "ws",
|
||||||
WebsocketOptions: model2.V2RayWebsocketOptions{
|
WebsocketOptions: model.V2RayWebsocketOptions{
|
||||||
Path: params.Get("path"),
|
Path: params.Get("path"),
|
||||||
Headers: map[string]string{
|
Headers: map[string]string{
|
||||||
"Host": params.Get("host"),
|
"Host": params.Get("host"),
|
||||||
@ -88,24 +94,24 @@ func ParseTrojan(proxy string) (model2.Proxy, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if params.Get("type") == "http" {
|
if params.Get("type") == "http" {
|
||||||
result.Trojan.Transport = &model2.V2RayTransportOptions{
|
result.TrojanOptions.Transport = &model.V2RayTransportOptions{
|
||||||
Type: "http",
|
Type: "http",
|
||||||
HTTPOptions: model2.V2RayHTTPOptions{
|
HTTPOptions: model.V2RayHTTPOptions{
|
||||||
Host: []string{params.Get("host")},
|
Host: []string{params.Get("host")},
|
||||||
Path: params.Get("path"),
|
Path: params.Get("path"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if params.Get("type") == "quic" {
|
if params.Get("type") == "quic" {
|
||||||
result.Trojan.Transport = &model2.V2RayTransportOptions{
|
result.TrojanOptions.Transport = &model.V2RayTransportOptions{
|
||||||
Type: "quic",
|
Type: "quic",
|
||||||
QUICOptions: model2.V2RayQUICOptions{},
|
QUICOptions: model.V2RayQUICOptions{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if params.Get("type") == "grpc" {
|
if params.Get("type") == "grpc" {
|
||||||
result.Trojan.Transport = &model2.V2RayTransportOptions{
|
result.TrojanOptions.Transport = &model.V2RayTransportOptions{
|
||||||
Type: "grpc",
|
Type: "grpc",
|
||||||
GRPCOptions: model2.V2RayGRPCOptions{
|
GRPCOptions: model.V2RayGRPCOptions{
|
||||||
ServiceName: params.Get("serviceName"),
|
ServiceName: params.Get("serviceName"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -5,30 +5,30 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
model2 "sub2sing-box/model"
|
"sub2sing-box/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseVless(proxy string) (model2.Proxy, error) {
|
func ParseVless(proxy string) (model.Outbound, error) {
|
||||||
if !strings.HasPrefix(proxy, "vless://") {
|
if !strings.HasPrefix(proxy, "vless://") {
|
||||||
return model2.Proxy{}, errors.New("invalid vless Url")
|
return model.Outbound{}, errors.New("invalid vless Url")
|
||||||
}
|
}
|
||||||
parts := strings.SplitN(strings.TrimPrefix(proxy, "vless://"), "@", 2)
|
parts := strings.SplitN(strings.TrimPrefix(proxy, "vless://"), "@", 2)
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
return model2.Proxy{}, errors.New("invalid vless Url")
|
return model.Outbound{}, errors.New("invalid vless Url")
|
||||||
}
|
}
|
||||||
serverInfo := strings.SplitN(parts[1], "#", 2)
|
serverInfo := strings.SplitN(parts[1], "#", 2)
|
||||||
serverAndPortAndParams := strings.SplitN(serverInfo[0], "?", 2)
|
serverAndPortAndParams := strings.SplitN(serverInfo[0], "?", 2)
|
||||||
serverAndPort := strings.SplitN(serverAndPortAndParams[0], ":", 2)
|
serverAndPort := strings.SplitN(serverAndPortAndParams[0], ":", 2)
|
||||||
params, err := url.ParseQuery(serverAndPortAndParams[1])
|
params, err := url.ParseQuery(serverAndPortAndParams[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, err
|
return model.Outbound{}, err
|
||||||
}
|
}
|
||||||
if len(serverAndPort) != 2 {
|
if len(serverAndPort) != 2 {
|
||||||
return model2.Proxy{}, errors.New("invalid vless Url")
|
return model.Outbound{}, errors.New("invalid vless Url")
|
||||||
}
|
}
|
||||||
port, err := strconv.Atoi(strings.TrimSpace(serverAndPort[1]))
|
port, err := strconv.Atoi(strings.TrimSpace(serverAndPort[1]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, err
|
return model.Outbound{}, err
|
||||||
}
|
}
|
||||||
remarks := ""
|
remarks := ""
|
||||||
if len(serverInfo) == 2 {
|
if len(serverInfo) == 2 {
|
||||||
@ -37,25 +37,27 @@ func ParseVless(proxy string) (model2.Proxy, error) {
|
|||||||
} else {
|
} else {
|
||||||
remarks, err = url.QueryUnescape(serverInfo[1])
|
remarks, err = url.QueryUnescape(serverInfo[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, err
|
return model.Outbound{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
remarks, err = url.QueryUnescape(serverAndPort[0])
|
remarks, err = url.QueryUnescape(serverAndPort[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, err
|
return model.Outbound{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
server := strings.TrimSpace(serverAndPort[0])
|
server := strings.TrimSpace(serverAndPort[0])
|
||||||
uuid := strings.TrimSpace(parts[0])
|
uuid := strings.TrimSpace(parts[0])
|
||||||
result := model2.Proxy{
|
result := model.Outbound{
|
||||||
Type: "vless",
|
Type: "vless",
|
||||||
Tag: remarks,
|
Tag: remarks,
|
||||||
VLESS: model2.VLESS{
|
VLESSOptions: model.VLESSOutboundOptions{
|
||||||
Server: server,
|
ServerOptions: model.ServerOptions{
|
||||||
ServerPort: uint16(port),
|
Server: server,
|
||||||
UUID: uuid,
|
ServerPort: uint16(port),
|
||||||
Flow: params.Get("flow"),
|
},
|
||||||
|
UUID: uuid,
|
||||||
|
Flow: params.Get("flow"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if params.Get("security") == "tls" {
|
if params.Get("security") == "tls" {
|
||||||
@ -65,10 +67,12 @@ func ParseVless(proxy string) (model2.Proxy, error) {
|
|||||||
} else {
|
} else {
|
||||||
alpn = nil
|
alpn = nil
|
||||||
}
|
}
|
||||||
result.VLESS.TLS = &model2.OutboundTLSOptions{
|
result.VLESSOptions.OutboundTLSOptionsContainer = model.OutboundTLSOptionsContainer{
|
||||||
Enabled: true,
|
TLS: &model.OutboundTLSOptions{
|
||||||
ALPN: alpn,
|
Enabled: true,
|
||||||
Insecure: params.Get("allowInsecure") == "1",
|
ALPN: alpn,
|
||||||
|
Insecure: params.Get("allowInsecure") == "1",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if params.Get("security") == "reality" {
|
if params.Get("security") == "reality" {
|
||||||
@ -78,45 +82,49 @@ func ParseVless(proxy string) (model2.Proxy, error) {
|
|||||||
} else {
|
} else {
|
||||||
alpn = nil
|
alpn = nil
|
||||||
}
|
}
|
||||||
result.VLESS.TLS = &model2.OutboundTLSOptions{
|
result.VLESSOptions.OutboundTLSOptionsContainer = model.OutboundTLSOptionsContainer{
|
||||||
Enabled: true,
|
TLS: &model.OutboundTLSOptions{
|
||||||
ServerName: params.Get("sni"),
|
Enabled: true,
|
||||||
UTLS: &model2.OutboundUTLSOptions{
|
ServerName: params.Get("sni"),
|
||||||
Enabled: params.Get("fp") != "",
|
UTLS: &model.OutboundUTLSOptions{
|
||||||
Fingerprint: params.Get("fp"),
|
Enabled: params.Get("fp") != "",
|
||||||
|
Fingerprint: params.Get("fp"),
|
||||||
|
},
|
||||||
|
Reality: &model.OutboundRealityOptions{
|
||||||
|
Enabled: true,
|
||||||
|
PublicKey: params.Get("pbk"),
|
||||||
|
ShortID: params.Get("sid"),
|
||||||
|
},
|
||||||
|
ALPN: alpn,
|
||||||
},
|
},
|
||||||
Reality: &model2.OutboundRealityOptions{
|
|
||||||
Enabled: true,
|
|
||||||
PublicKey: params.Get("pbk"),
|
|
||||||
ShortID: params.Get("sid"),
|
|
||||||
},
|
|
||||||
ALPN: alpn,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if params.Get("type") == "ws" {
|
if params.Get("type") == "ws" {
|
||||||
result.VLESS.Transport = &model2.V2RayTransportOptions{
|
result.VLESSOptions.Transport = &model.V2RayTransportOptions{
|
||||||
Type: "ws",
|
Type: "ws",
|
||||||
WebsocketOptions: model2.V2RayWebsocketOptions{
|
WebsocketOptions: model.V2RayWebsocketOptions{
|
||||||
Path: params.Get("path"),
|
Path: params.Get("path"),
|
||||||
Headers: map[string]string{
|
|
||||||
"Host": params.Get("host"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if params.Get("host") != "" {
|
||||||
|
result.VLESSOptions.Transport.WebsocketOptions.Headers["Host"] = params.Get("host")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if params.Get("type") == "quic" {
|
if params.Get("type") == "quic" {
|
||||||
result.VLESS.Transport = &model2.V2RayTransportOptions{
|
result.VLESSOptions.Transport = &model.V2RayTransportOptions{
|
||||||
Type: "quic",
|
Type: "quic",
|
||||||
QUICOptions: model2.V2RayQUICOptions{},
|
QUICOptions: model.V2RayQUICOptions{},
|
||||||
}
|
}
|
||||||
result.VLESS.TLS = &model2.OutboundTLSOptions{
|
result.VLESSOptions.OutboundTLSOptionsContainer = model.OutboundTLSOptionsContainer{
|
||||||
Enabled: true,
|
TLS: &model.OutboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if params.Get("type") == "grpc" {
|
if params.Get("type") == "grpc" {
|
||||||
result.VLESS.Transport = &model2.V2RayTransportOptions{
|
result.VLESSOptions.Transport = &model.V2RayTransportOptions{
|
||||||
Type: "grpc",
|
Type: "grpc",
|
||||||
GRPCOptions: model2.V2RayGRPCOptions{
|
GRPCOptions: model.V2RayGRPCOptions{
|
||||||
ServiceName: params.Get("serviceName"),
|
ServiceName: params.Get("serviceName"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -124,11 +132,11 @@ func ParseVless(proxy string) (model2.Proxy, error) {
|
|||||||
if params.Get("type") == "http" {
|
if params.Get("type") == "http" {
|
||||||
host, err := url.QueryUnescape(params.Get("host"))
|
host, err := url.QueryUnescape(params.Get("host"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, err
|
return model.Outbound{}, err
|
||||||
}
|
}
|
||||||
result.VLESS.Transport = &model2.V2RayTransportOptions{
|
result.VLESSOptions.Transport = &model.V2RayTransportOptions{
|
||||||
Type: "http",
|
Type: "http",
|
||||||
HTTPOptions: model2.V2RayHTTPOptions{
|
HTTPOptions: model.V2RayHTTPOptions{
|
||||||
Host: strings.Split(host, ","),
|
Host: strings.Split(host, ","),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -6,29 +6,29 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
model2 "sub2sing-box/model"
|
"sub2sing-box/model"
|
||||||
"sub2sing-box/util"
|
"sub2sing-box/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseVmess(proxy string) (model2.Proxy, error) {
|
func ParseVmess(proxy string) (model.Outbound, error) {
|
||||||
if !strings.HasPrefix(proxy, "vmess://") {
|
if !strings.HasPrefix(proxy, "vmess://") {
|
||||||
return model2.Proxy{}, errors.New("invalid vmess url")
|
return model.Outbound{}, errors.New("invalid vmess url")
|
||||||
}
|
}
|
||||||
base64, err := util.DecodeBase64(strings.TrimPrefix(proxy, "vmess://"))
|
base64, err := util.DecodeBase64(strings.TrimPrefix(proxy, "vmess://"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, errors.New("invalid vmess url" + err.Error())
|
return model.Outbound{}, errors.New("invalid vmess url" + err.Error())
|
||||||
}
|
}
|
||||||
var vmess model2.VmessJson
|
var vmess model.VmessJson
|
||||||
err = json.Unmarshal([]byte(base64), &vmess)
|
err = json.Unmarshal([]byte(base64), &vmess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, errors.New("invalid vmess url" + err.Error())
|
return model.Outbound{}, errors.New("invalid vmess url" + err.Error())
|
||||||
}
|
}
|
||||||
port := 0
|
port := 0
|
||||||
switch vmess.Port.(type) {
|
switch vmess.Port.(type) {
|
||||||
case string:
|
case string:
|
||||||
port, err = strconv.Atoi(vmess.Port.(string))
|
port, err = strconv.Atoi(vmess.Port.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, errors.New("invalid vmess url" + err.Error())
|
return model.Outbound{}, errors.New("invalid vmess url" + err.Error())
|
||||||
}
|
}
|
||||||
case float64:
|
case float64:
|
||||||
port = int(vmess.Port.(float64))
|
port = int(vmess.Port.(float64))
|
||||||
@ -38,7 +38,7 @@ func ParseVmess(proxy string) (model2.Proxy, error) {
|
|||||||
case string:
|
case string:
|
||||||
aid, err = strconv.Atoi(vmess.Aid.(string))
|
aid, err = strconv.Atoi(vmess.Aid.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model2.Proxy{}, errors.New("invalid vmess url" + err.Error())
|
return model.Outbound{}, errors.New("invalid vmess url" + err.Error())
|
||||||
}
|
}
|
||||||
case float64:
|
case float64:
|
||||||
aid = int(vmess.Aid.(float64))
|
aid = int(vmess.Aid.(float64))
|
||||||
@ -52,15 +52,17 @@ func ParseVmess(proxy string) (model2.Proxy, error) {
|
|||||||
name = vmess.Ps
|
name = vmess.Ps
|
||||||
}
|
}
|
||||||
|
|
||||||
result := model2.Proxy{
|
result := model.Outbound{
|
||||||
Type: "vmess",
|
Type: "vmess",
|
||||||
Tag: name,
|
Tag: name,
|
||||||
VMess: model2.VMess{
|
VMessOptions: model.VMessOutboundOptions{
|
||||||
Server: vmess.Add,
|
ServerOptions: model.ServerOptions{
|
||||||
ServerPort: uint16(port),
|
Server: vmess.Add,
|
||||||
UUID: vmess.Id,
|
ServerPort: uint16(port),
|
||||||
AlterId: aid,
|
},
|
||||||
Security: vmess.Scy,
|
UUID: vmess.Id,
|
||||||
|
AlterId: aid,
|
||||||
|
Security: vmess.Scy,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,12 +73,14 @@ func ParseVmess(proxy string) (model2.Proxy, error) {
|
|||||||
} else {
|
} else {
|
||||||
alpn = nil
|
alpn = nil
|
||||||
}
|
}
|
||||||
result.VMess.TLS = &model2.OutboundTLSOptions{
|
result.VMessOptions.OutboundTLSOptionsContainer = model.OutboundTLSOptionsContainer{
|
||||||
Enabled: true,
|
TLS: &model.OutboundTLSOptions{
|
||||||
UTLS: &model2.OutboundUTLSOptions{
|
Enabled: true,
|
||||||
Fingerprint: vmess.Fp,
|
UTLS: &model.OutboundUTLSOptions{
|
||||||
|
Fingerprint: vmess.Fp,
|
||||||
|
},
|
||||||
|
ALPN: alpn,
|
||||||
},
|
},
|
||||||
ALPN: alpn,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,46 +91,47 @@ func ParseVmess(proxy string) (model2.Proxy, error) {
|
|||||||
if vmess.Host == "" {
|
if vmess.Host == "" {
|
||||||
vmess.Host = vmess.Add
|
vmess.Host = vmess.Add
|
||||||
}
|
}
|
||||||
ws := model2.V2RayWebsocketOptions{
|
result.VMessOptions.Transport = &model.V2RayTransportOptions{
|
||||||
Path: vmess.Path,
|
Type: "ws",
|
||||||
Headers: map[string]string{
|
WebsocketOptions: model.V2RayWebsocketOptions{
|
||||||
"Host": vmess.Host,
|
Path: vmess.Path,
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Host": vmess.Host,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
result.VMess.Transport = &model2.V2RayTransportOptions{
|
|
||||||
Type: "ws",
|
|
||||||
WebsocketOptions: ws,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if vmess.Net == "quic" {
|
if vmess.Net == "quic" {
|
||||||
quic := model2.V2RayQUICOptions{}
|
quic := model.V2RayQUICOptions{}
|
||||||
result.VMess.Transport = &model2.V2RayTransportOptions{
|
result.VMessOptions.Transport = &model.V2RayTransportOptions{
|
||||||
Type: "quic",
|
Type: "quic",
|
||||||
QUICOptions: quic,
|
QUICOptions: quic,
|
||||||
}
|
}
|
||||||
result.VMess.TLS = &model2.OutboundTLSOptions{
|
result.VMessOptions.OutboundTLSOptionsContainer = model.OutboundTLSOptionsContainer{
|
||||||
Enabled: true,
|
TLS: &model.OutboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if vmess.Net == "grpc" {
|
if vmess.Net == "grpc" {
|
||||||
grpc := model2.V2RayGRPCOptions{
|
grpc := model.V2RayGRPCOptions{
|
||||||
ServiceName: vmess.Path,
|
ServiceName: vmess.Path,
|
||||||
PermitWithoutStream: true,
|
PermitWithoutStream: true,
|
||||||
}
|
}
|
||||||
result.VMess.Transport = &model2.V2RayTransportOptions{
|
result.VMessOptions.Transport = &model.V2RayTransportOptions{
|
||||||
Type: "grpc",
|
Type: "grpc",
|
||||||
GRPCOptions: grpc,
|
GRPCOptions: grpc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if vmess.Net == "h2" {
|
if vmess.Net == "h2" {
|
||||||
httpOps := model2.V2RayHTTPOptions{
|
httpOps := model.V2RayHTTPOptions{
|
||||||
Host: strings.Split(vmess.Host, ","),
|
Host: strings.Split(vmess.Host, ","),
|
||||||
Path: vmess.Path,
|
Path: vmess.Path,
|
||||||
}
|
}
|
||||||
result.VMess.Transport = &model2.V2RayTransportOptions{
|
result.VMessOptions.Transport = &model.V2RayTransportOptions{
|
||||||
Type: "http",
|
Type: "http",
|
||||||
HTTPOptions: httpOps,
|
HTTPOptions: httpOps,
|
||||||
}
|
}
|
||||||
|
25
util/marshal.go
Normal file
25
util/marshal.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
func MergeAndMarshal(args ...interface{}) (string, error) {
|
||||||
|
merged := make(map[string]interface{})
|
||||||
|
for _, arg := range args {
|
||||||
|
jsonBytes, err := json.Marshal(arg)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var m map[string]interface{}
|
||||||
|
if err := json.Unmarshal(jsonBytes, &m); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
for k, v := range m {
|
||||||
|
merged[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result, err := json.Marshal(merged)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(result), nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user