json config parser

This commit is contained in:
Darien Raymond committed 2016-10-17 14:35:13 +02:00
1 parent e866ff24a4
commit 5a311cbe08
85 files changed
+9357 -9052

No files matched your search

+52
View File
@@ -0,0 +1,52 @@
package conf
import (
"encoding/json"
"errors"
"v2ray.com/core/common/loader"
"v2ray.com/core/proxy/blackhole"
)
type NoneResponse struct{}
func (*NoneResponse) Build() (*loader.TypedSettings, error) {
return loader.NewTypedSettings(new(blackhole.NoneResponse)), nil
}
type HttpResponse struct{}
func (*HttpResponse) Build() (*loader.TypedSettings, error) {
return loader.NewTypedSettings(new(blackhole.HTTPResponse)), nil
}
type BlackholeConfig struct {
Response json.RawMessage `json:"response"`
}
func (this *BlackholeConfig) Build() (*loader.TypedSettings, error) {
config := new(blackhole.Config)
if this.Response != nil {
response, _, err := configLoader.Load(this.Response)
if err != nil {
return nil, errors.New("Blackhole: Failed to parse response config: " + err.Error())
}
responseSettings, err := response.(Buildable).Build()
if err != nil {
return nil, err
}
config.Response = responseSettings
}
return loader.NewTypedSettings(config), nil
}
var (
configLoader = NewJSONConfigLoader(
ConfigCreatorCache{
"none": func() interface{} { return new(NoneResponse) },
"http": func() interface{} { return new(HttpResponse) },
},
"type",
"")
)
+34
View File
@@ -0,0 +1,34 @@
package conf_test
import (
"encoding/json"
"testing"
"v2ray.com/core/proxy/blackhole"
"v2ray.com/core/testing/assert"
. "v2ray.com/core/tools/conf"
)
func TestHTTPResponseJSON(t *testing.T) {
assert := assert.On(t)
rawJson := `{
"response": {
"type": "http"
}
}`
rawConfig := new(BlackholeConfig)
err := json.Unmarshal([]byte(rawJson), rawConfig)
assert.Error(err).IsNil()
ts, err := rawConfig.Build()
assert.Error(err).IsNil()
iConfig, err := ts.GetInstance()
assert.Error(err).IsNil()
config := iConfig.(*blackhole.Config)
response, err := config.GetInternalResponse()
assert.Error(err).IsNil()
_, ok := response.(*blackhole.HTTPResponse)
assert.Bool(ok).IsTrue()
}
+9
View File
@@ -0,0 +1,9 @@
package conf
import (
"v2ray.com/core/common/loader"
)
type Buildable interface {
Build() (*loader.TypedSettings, error)
}
+112
View File
@@ -5,9 +5,38 @@ import (
"errors"
"strings"
"v2ray.com/core/common/log"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
)
type StringList []string
func NewStringList(raw []string) *StringList {
list := StringList(raw)
return &list
}
func (this StringList) Len() int {
return len(this)
}
func (this *StringList) UnmarshalJSON(data []byte) error {
var strarray []string
if err := json.Unmarshal(data, &strarray); err == nil {
*this = *NewStringList(strarray)
return nil
}
var rawstr string
if err := json.Unmarshal(data, &rawstr); err == nil {
strlist := strings.Split(rawstr, ",")
*this = *NewStringList(strlist)
return nil
}
return errors.New("Unknown format of a string list: " + string(data))
}
type Address struct {
v2net.Address
}
@@ -74,3 +103,86 @@ func (this *NetworkList) Build() *v2net.NetworkList {
}
return list
}
func parseIntPort(data []byte) (v2net.Port, error) {
var intPort uint32
err := json.Unmarshal(data, &intPort)
if err != nil {
return v2net.Port(0), err
}
return v2net.PortFromInt(intPort)
}
func parseStringPort(data []byte) (v2net.Port, v2net.Port, error) {
var s string
err := json.Unmarshal(data, &s)
if err != nil {
return v2net.Port(0), v2net.Port(0), err
}
pair := strings.SplitN(s, "-", 2)
if len(pair) == 0 {
return v2net.Port(0), v2net.Port(0), v2net.ErrInvalidPortRange
}
if len(pair) == 1 {
port, err := v2net.PortFromString(pair[0])
return port, port, err
}
fromPort, err := v2net.PortFromString(pair[0])
if err != nil {
return v2net.Port(0), v2net.Port(0), err
}
toPort, err := v2net.PortFromString(pair[1])
if err != nil {
return v2net.Port(0), v2net.Port(0), err
}
return fromPort, toPort, nil
}
type PortRange struct {
From uint32
To uint32
}
func (this *PortRange) Build() *v2net.PortRange {
return &v2net.PortRange{
From: this.From,
To: this.To,
}
}
// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON
func (this *PortRange) UnmarshalJSON(data []byte) error {
port, err := parseIntPort(data)
if err == nil {
this.From = uint32(port)
this.To = uint32(port)
return nil
}
from, to, err := parseStringPort(data)
if err == nil {
this.From = uint32(from)
this.To = uint32(to)
if this.From > this.To {
log.Error("Invalid port range ", this.From, " -> ", this.To)
return v2net.ErrInvalidPortRange
}
return nil
}
log.Error("Invalid port range: ", string(data))
return v2net.ErrInvalidPortRange
}
type User struct {
EmailString string `json:"email"`
LevelByte byte `json:"level"`
}
func (this *User) Build() *protocol.User {
return &protocol.User{
Email: this.EmailString,
Level: uint32(this.LevelByte),
}
}
+105
View File
@@ -9,6 +9,25 @@ import (
. "v2ray.com/core/tools/conf"
)
func TestStringListUnmarshalError(t *testing.T) {
assert := assert.On(t)
rawJson := `1234`
list := new(StringList)
err := json.Unmarshal([]byte(rawJson), list)
assert.Error(err).IsNotNil()
}
func TestStringListLen(t *testing.T) {
assert := assert.On(t)
rawJson := `"a, b, c, d"`
list := new(StringList)
err := json.Unmarshal([]byte(rawJson), list)
assert.Error(err).IsNil()
assert.Int(list.Len()).Equals(4)
}
func TestIPParsing(t *testing.T) {
assert := assert.On(t)
@@ -78,3 +97,89 @@ func TestInvalidNetworkJson(t *testing.T) {
err := json.Unmarshal([]byte("0"), &list)
assert.Error(err).IsNotNil()
}
func TestIntPort(t *testing.T) {
assert := assert.On(t)
var portRange PortRange
err := json.Unmarshal([]byte("1234"), &portRange)
assert.Error(err).IsNil()
assert.Uint32(portRange.From).Equals(1234)
assert.Uint32(portRange.To).Equals(1234)
}
func TestOverRangeIntPort(t *testing.T) {
assert := assert.On(t)
var portRange PortRange
err := json.Unmarshal([]byte("70000"), &portRange)
assert.Error(err).Equals(v2net.ErrInvalidPortRange)
err = json.Unmarshal([]byte("-1"), &portRange)
assert.Error(err).Equals(v2net.ErrInvalidPortRange)
}
func TestSingleStringPort(t *testing.T) {
assert := assert.On(t)
var portRange PortRange
err := json.Unmarshal([]byte("\"1234\""), &portRange)
assert.Error(err).IsNil()
assert.Uint32(portRange.From).Equals(1234)
assert.Uint32(portRange.To).Equals(1234)
}
func TestStringPairPort(t *testing.T) {
assert := assert.On(t)
var portRange PortRange
err := json.Unmarshal([]byte("\"1234-5678\""), &portRange)
assert.Error(err).IsNil()
assert.Uint32(portRange.From).Equals(1234)
assert.Uint32(portRange.To).Equals(5678)
}
func TestOverRangeStringPort(t *testing.T) {
assert := assert.On(t)
var portRange PortRange
err := json.Unmarshal([]byte("\"65536\""), &portRange)
assert.Error(err).Equals(v2net.ErrInvalidPortRange)
err = json.Unmarshal([]byte("\"70000-80000\""), &portRange)
assert.Error(err).Equals(v2net.ErrInvalidPortRange)
err = json.Unmarshal([]byte("\"1-90000\""), &portRange)
assert.Error(err).Equals(v2net.ErrInvalidPortRange)
err = json.Unmarshal([]byte("\"700-600\""), &portRange)
assert.Error(err).Equals(v2net.ErrInvalidPortRange)
}
func TestUserParsing(t *testing.T) {
assert := assert.On(t)
user := new(User)
err := json.Unmarshal([]byte(`{
"id": "96edb838-6d68-42ef-a933-25f7ac3a9d09",
"email": "love@v2ray.com",
"level": 1,
"alterId": 100
}`), user)
assert.Error(err).IsNil()
nUser := user.Build()
assert.Byte(byte(nUser.Level)).Equals(1)
assert.String(nUser.Email).Equals("love@v2ray.com")
}
func TestInvalidUserJson(t *testing.T) {
assert := assert.On(t)
user := new(User)
err := json.Unmarshal([]byte(`{"email": 1234}`), user)
assert.Error(err).IsNotNil()
}
+32
View File
@@ -0,0 +1,32 @@
package conf
import (
"v2ray.com/core/app/dns"
v2net "v2ray.com/core/common/net"
)
type DnsConfig struct {
Servers []*Address `json:"servers"`
Hosts map[string]*Address `json:"hosts"`
}
func (this *DnsConfig) Build() *dns.Config {
config := new(dns.Config)
config.NameServers = make([]*v2net.Endpoint, len(this.Servers))
for idx, server := range this.Servers {
config.NameServers[idx] = &v2net.Endpoint{
Network: v2net.Network_UDP,
Address: server.Build(),
Port: 53,
}
}
if this.Hosts != nil {
config.Hosts = make(map[string]*v2net.IPOrDomain)
for k, v := range this.Hosts {
config.Hosts[k] = v.Build()
}
}
return config
}
+29
View File
@@ -0,0 +1,29 @@
package conf_test
import (
"encoding/json"
"testing"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/testing/assert"
. "v2ray.com/core/tools/conf"
)
func TestDnsConfigParsing(t *testing.T) {
assert := assert.On(t)
rawJson := `{
"servers": ["8.8.8.8"]
}`
jsonConfig := new(DnsConfig)
err := json.Unmarshal([]byte(rawJson), jsonConfig)
assert.Error(err).IsNil()
config := jsonConfig.Build()
assert.Int(len(config.NameServers)).Equals(1)
dest := config.NameServers[0].AsDestination()
assert.Destination(dest).IsUDP()
assert.Address(dest.Address).Equals(v2net.IPAddress([]byte{8, 8, 8, 8}))
assert.Port(dest.Port).Equals(v2net.Port(53))
}
+26
View File
@@ -0,0 +1,26 @@
package conf
import (
"v2ray.com/core/common/loader"
"v2ray.com/core/proxy/dokodemo"
)
type DokodemoConfig struct {
Host *Address `json:"address"`
PortValue uint16 `json:"port"`
NetworkList *NetworkList `json:"network"`
TimeoutValue uint32 `json:"timeout"`
Redirect bool `json:"followRedirect"`
}
func (this *DokodemoConfig) Build() (*loader.TypedSettings, error) {
config := new(dokodemo.Config)
if this.Host != nil {
config.Address = this.Host.Build()
}
config.Port = uint32(this.PortValue)
config.NetworkList = this.NetworkList.Build()
config.Timeout = this.TimeoutValue
config.FollowRedirect = this.Redirect
return loader.NewTypedSettings(config), nil
}
+24
View File
@@ -0,0 +1,24 @@
package conf
import (
"strings"
"v2ray.com/core/common/loader"
"v2ray.com/core/proxy/freedom"
)
type FreedomConfig struct {
DomainStrategy string `json:"domainStrategy"`
Timeout uint32 `json:"timeout"`
}
func (this *FreedomConfig) Build() (*loader.TypedSettings, error) {
config := new(freedom.Config)
config.DomainStrategy = freedom.Config_AS_IS
domainStrategy := strings.ToLower(this.DomainStrategy)
if domainStrategy == "useip" || domainStrategy == "use_ip" {
config.DomainStrategy = freedom.Config_USE_IP
}
config.Timeout = this.Timeout
return loader.NewTypedSettings(config), nil
}
+18
View File
@@ -0,0 +1,18 @@
package conf
import (
"v2ray.com/core/common/loader"
"v2ray.com/core/proxy/http"
)
type HttpServerConfig struct {
Timeout uint32 `json:"timeout"`
}
func (this *HttpServerConfig) Build() (*loader.TypedSettings, error) {
config := &http.ServerConfig{
Timeout: this.Timeout,
}
return loader.NewTypedSettings(config), nil
}
+91
View File
@@ -0,0 +1,91 @@
package conf
import (
"encoding/json"
"errors"
"v2ray.com/core/common"
"v2ray.com/core/common/log"
)
var (
ErrUnknownConfigID = errors.New("Unknown config ID.")
)
type ConfigCreator func() interface{}
type ConfigCreatorCache map[string]ConfigCreator
func (this ConfigCreatorCache) RegisterCreator(id string, creator ConfigCreator) error {
if _, found := this[id]; found {
return common.ErrDuplicatedName
}
this[id] = creator
return nil
}
func (this ConfigCreatorCache) CreateConfig(id string) (interface{}, error) {
creator, found := this[id]
if !found {
return nil, ErrUnknownConfigID
}
return creator(), nil
}
type JSONConfigLoader struct {
cache ConfigCreatorCache
idKey string
configKey string
}
func NewJSONConfigLoader(cache ConfigCreatorCache, idKey string, configKey string) *JSONConfigLoader {
return &JSONConfigLoader{
idKey: idKey,
configKey: configKey,
cache: cache,
}
}
func (this *JSONConfigLoader) LoadWithID(raw []byte, id string) (interface{}, error) {
creator, found := this.cache[id]
if !found {
return nil, ErrUnknownConfigID
}
config := creator()
if err := json.Unmarshal(raw, config); err != nil {
return nil, err
}
return config, nil
}
func (this *JSONConfigLoader) Load(raw []byte) (interface{}, string, error) {
var obj map[string]json.RawMessage
if err := json.Unmarshal(raw, &obj); err != nil {
return nil, "", err
}
rawID, found := obj[this.idKey]
if !found {
log.Error(this.idKey, " not found in JSON content.")
return nil, "", common.ErrObjectNotFound
}
var id string
if err := json.Unmarshal(rawID, &id); err != nil {
return nil, "", err
}
rawConfig := json.RawMessage(raw)
if len(this.configKey) > 0 {
configValue, found := obj[this.configKey]
if !found {
log.Error(this.configKey, " not found in JSON content.")
return nil, "", common.ErrObjectNotFound
}
rawConfig = configValue
}
config, err := this.LoadWithID([]byte(rawConfig), id)
if err != nil {
return nil, id, err
}
return config, id, nil
}
+193
View File
@@ -0,0 +1,193 @@
package conf
import (
"encoding/json"
"errors"
"strconv"
"strings"
"v2ray.com/core/app/router"
"v2ray.com/core/common/log"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/tools/geoip"
)
type RouterRulesConfig struct {
RuleList []json.RawMessage `json:"rules"`
DomainStrategy string `json:"domainStrategy"`
}
type RouterConfig struct {
Settings *RouterRulesConfig `json:"settings"`
}
func (this *RouterConfig) Build() (*router.Config, error) {
if this.Settings == nil {
return nil, errors.New("Router settings is not specified.")
}
config := new(router.Config)
settings := this.Settings
config.DomainStrategy = router.Config_AsIs
config.Rule = make([]*router.RoutingRule, len(settings.RuleList))
domainStrategy := strings.ToLower(settings.DomainStrategy)
if domainStrategy == "alwaysip" {
config.DomainStrategy = router.Config_UseIp
} else if domainStrategy == "ipifnonmatch" {
config.DomainStrategy = router.Config_IpIfNonMatch
}
for idx, rawRule := range settings.RuleList {
rule := ParseRule(rawRule)
config.Rule[idx] = rule
}
return config, nil
}
type RouterRule struct {
Type string `json:"type"`
OutboundTag string `json:"outboundTag"`
}
func parseIP(s string) *router.IP {
var addr, mask string
i := strings.Index(s, "/")
if i < 0 {
addr = s
} else {
addr = s[:i]
mask = s[i+1:]
}
ip := v2net.ParseAddress(addr)
if !ip.Family().Either(v2net.AddressFamilyIPv4, v2net.AddressFamilyIPv6) {
return nil
}
bits := uint32(32)
if len(mask) > 0 {
bits64, err := strconv.ParseUint(mask, 10, 32)
if err != nil {
return nil
}
bits = uint32(bits64)
}
if bits > 32 {
log.Warning("Router: invalid network mask: ", bits)
return nil
}
return &router.IP{
Ip: []byte(ip.IP()),
UnmatchingBits: 32 - bits,
}
}
func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
type RawFieldRule struct {
RouterRule
Domain *StringList `json:"domain"`
IP *StringList `json:"ip"`
Port *PortRange `json:"port"`
Network *NetworkList `json:"network"`
}
rawFieldRule := new(RawFieldRule)
err := json.Unmarshal(msg, rawFieldRule)
if err != nil {
return nil, err
}
rule := new(router.RoutingRule)
rule.Tag = rawFieldRule.OutboundTag
if rawFieldRule.Domain != nil {
for _, domain := range *rawFieldRule.Domain {
domainRule := new(router.Domain)
if strings.HasPrefix(domain, "regexp:") {
domainRule.Type = router.Domain_Regex
domainRule.Value = domain[7:]
} else {
domainRule.Type = router.Domain_Plain
domainRule.Value = domain
}
rule.Domain = append(rule.Domain, domainRule)
}
}
if rawFieldRule.IP != nil {
for _, ip := range *rawFieldRule.IP {
ipRule := parseIP(ip)
if ipRule != nil {
rule.Ip = append(rule.Ip, ipRule)
}
}
}
if rawFieldRule.Port != nil {
rule.PortRange = rawFieldRule.Port.Build()
}
if rawFieldRule.Network != nil {
rule.NetworkList = rawFieldRule.Network.Build()
}
return rule, nil
}
func ParseRule(msg json.RawMessage) *router.RoutingRule {
rawRule := new(RouterRule)
err := json.Unmarshal(msg, rawRule)
if err != nil {
log.Error("Router: Invalid router rule: ", err)
return nil
}
if rawRule.Type == "field" {
fieldrule, err := parseFieldRule(msg)
if err != nil {
log.Error("Invalid field rule: ", err)
return nil
}
return fieldrule
}
if rawRule.Type == "chinaip" {
chinaiprule, err := parseChinaIPRule(msg)
if err != nil {
log.Error("Router: Invalid chinaip rule: ", err)
return nil
}
return chinaiprule
}
if rawRule.Type == "chinasites" {
chinasitesrule, err := parseChinaSitesRule(msg)
if err != nil {
log.Error("Invalid chinasites rule: ", err)
return nil
}
return chinasitesrule
}
log.Error("Unknown router rule type: ", rawRule.Type)
return nil
}
func parseChinaIPRule(data []byte) (*router.RoutingRule, error) {
rawRule := new(RouterRule)
err := json.Unmarshal(data, rawRule)
if err != nil {
log.Error("Router: Invalid router rule: ", err)
return nil, err
}
return &router.RoutingRule{
Tag: rawRule.OutboundTag,
Ip: geoip.ChinaIPs,
}, nil
}
func parseChinaSitesRule(data []byte) (*router.RoutingRule, error) {
rawRule := new(RouterRule)
err := json.Unmarshal(data, rawRule)
if err != nil {
log.Error("Router: Invalid router rule: ", err)
return nil, err
}
return &router.RoutingRule{
Tag: rawRule.OutboundTag,
Domain: chinaSitesDomains,
}, nil
}
+494
View File
@@ -0,0 +1,494 @@
package conf
import (
"v2ray.com/core/app/router"
)
const (
anySubDomain = "^(.*\\.)?"
dotAm = "\\.am$"
dotCc = "\\.cc$"
dotCn = "\\.cn$"
dotCom = "\\.com$"
dotInfo = "\\.info$"
dotIo = "\\.io$"
dotLa = "\\.la$"
dotMe = "\\.me$"
dotNet = "\\.net$"
dotOrg = "\\.org$"
dotTv = "\\.tv$"
)
var (
chinaSitesDomains []*router.Domain
)
func init() {
regexpDomains := []string{
dotCn,
"\\.xn--fiqs8s$", /* .中国 */
anySubDomain + "10010" + dotCom,
anySubDomain + "100offer" + dotCom,
anySubDomain + "115" + dotCom,
anySubDomain + "123juzi" + dotCom,
anySubDomain + "123juzi" + dotNet,
anySubDomain + "123u" + dotCom,
anySubDomain + "126" + dotCom,
anySubDomain + "126" + dotNet,
anySubDomain + "127" + dotNet,
anySubDomain + "163" + dotCom,
anySubDomain + "17173" + dotCom,
anySubDomain + "17cdn" + dotCom,
anySubDomain + "188" + dotCom,
anySubDomain + "1905" + dotCom,
anySubDomain + "21cn" + dotCom,
anySubDomain + "2288" + dotOrg,
anySubDomain + "2345" + dotCom,
anySubDomain + "263" + dotNet,
anySubDomain + "2cto" + dotCom,
anySubDomain + "3322" + dotOrg,
anySubDomain + "35" + dotCom,
anySubDomain + "360doc" + dotCom,
anySubDomain + "360buy" + dotCom,
anySubDomain + "360buyimg" + dotCom,
anySubDomain + "360safe" + dotCom,
anySubDomain + "36kr" + dotCom,
anySubDomain + "39" + dotNet,
anySubDomain + "3dmgame" + dotCom,
anySubDomain + "3conline" + dotCom,
anySubDomain + "4399" + dotCom,
anySubDomain + "500d" + dotMe,
anySubDomain + "50bang" + dotOrg,
anySubDomain + "51" + dotLa,
anySubDomain + "51credit" + dotCom,
anySubDomain + "51cto" + dotCom,
anySubDomain + "51jingying" + dotCom,
anySubDomain + "51job" + dotCom,
anySubDomain + "51jobcdn" + dotCom,
anySubDomain + "51wendang" + dotCom,
anySubDomain + "55" + dotCom,
anySubDomain + "51yes" + dotCom,
anySubDomain + "55bbs" + dotCom,
anySubDomain + "58" + dotCom,
anySubDomain + "6rooms" + dotCom,
anySubDomain + "71" + dotAm,
anySubDomain + "7k7k" + dotCom,
anySubDomain + "900" + dotLa,
anySubDomain + "9718" + dotCom,
anySubDomain + "9xu" + dotCom,
anySubDomain + "abchina" + dotCom,
anySubDomain + "acfun" + dotTv,
anySubDomain + "acgvideo" + dotCom,
anySubDomain + "agrantsem" + dotCom,
anySubDomain + "aicdn" + dotCom,
anySubDomain + "aixifan" + dotCom,
anySubDomain + "alibaba" + dotCom,
anySubDomain + "alicdn" + dotCom,
anySubDomain + "aliimg.com" + dotCom,
anySubDomain + "alipay" + dotCom,
anySubDomain + "alipayobjects" + dotCom,
anySubDomain + "aliyun" + dotCom,
anySubDomain + "aliyuncdn" + dotCom,
anySubDomain + "aliyuncs" + dotCom,
anySubDomain + "allyes" + dotCom,
anySubDomain + "amap" + dotCom,
anySubDomain + "anjuke" + dotCom,
anySubDomain + "anquan" + dotOrg,
anySubDomain + "appinn" + dotCom,
anySubDomain + "babytree" + dotCom,
anySubDomain + "babytreeimg" + dotCom,
anySubDomain + "baidu" + dotCom,
anySubDomain + "baiducontent" + dotCom,
anySubDomain + "baidupcs" + dotCom,
anySubDomain + "baidustatic" + dotCom,
anySubDomain + "baifendian" + dotCom,
anySubDomain + "baifubao" + dotCom,
anySubDomain + "baihe" + dotCom,
anySubDomain + "baike" + dotCom,
anySubDomain + "baixing" + dotCom,
anySubDomain + "baixing" + dotNet,
anySubDomain + "bankcomm" + dotCom,
anySubDomain + "bankofchina" + dotCom,
anySubDomain + "bcy" + dotNet,
anySubDomain + "bdimg" + dotCom,
anySubDomain + "bdstatic" + dotCom,
anySubDomain + "bilibili" + dotCom,
"cn\\.bing" + dotCom,
anySubDomain + "bitauto" + dotCom,
anySubDomain + "bitautoimg" + dotCom,
anySubDomain + "bobo" + dotCom,
anySubDomain + "bootcss" + dotCom,
anySubDomain + "btcfans" + dotCom,
anySubDomain + "caiyunapp" + dotCom,
anySubDomain + "ccb" + dotCom,
anySubDomain + "cctv" + dotCom,
anySubDomain + "cctvpic" + dotCom,
anySubDomain + "cdn20" + dotCom,
anySubDomain + "cebbank" + dotCom,
anySubDomain + "ch" + dotCom,
anySubDomain + "chashebao" + dotCom,
anySubDomain + "che168" + dotCom,
anySubDomain + "china" + dotCom,
anySubDomain + "chinacache" + dotCom,
anySubDomain + "chinacache" + dotNet,
anySubDomain + "chinahr" + dotCom,
anySubDomain + "chinamobile" + dotCom,
anySubDomain + "chinapay" + dotCom,
anySubDomain + "chinatranslation" + dotNet,
anySubDomain + "chinaz" + dotCom,
anySubDomain + "chiphell" + dotCom,
anySubDomain + "chouti" + dotCom,
anySubDomain + "chuangxin" + dotCom,
anySubDomain + "chuansong" + dotMe,
anySubDomain + "clouddn" + dotCom,
anySubDomain + "cloudxns" + dotCom,
anySubDomain + "cmbchina" + dotCom,
anySubDomain + "cnbeta" + dotCom,
anySubDomain + "cnbetacdn" + dotCom,
anySubDomain + "cnblogs" + dotCom,
anySubDomain + "cnepub" + dotCom,
anySubDomain + "cnzz" + dotCom,
anySubDomain + "coding" + dotNet,
anySubDomain + "coolapk" + dotCom,
anySubDomain + "cqvip" + dotCom,
anySubDomain + "csbew" + dotCom,
anySubDomain + "csdn" + dotNet,
anySubDomain + "ctrip" + dotCom,
anySubDomain + "cubead" + dotCom,
anySubDomain + "dajie" + dotCom,
anySubDomain + "dajieimg" + dotCom,
anySubDomain + "dangdang" + dotCom,
anySubDomain + "daocloud" + dotIo,
anySubDomain + "daovoice" + dotIo,
anySubDomain + "dbank" + dotCom,
anySubDomain + "dedecms" + dotCom,
anySubDomain + "diandian" + dotCom,
anySubDomain + "dianping" + dotCom,
anySubDomain + "diopic" + dotNet,
anySubDomain + "docin" + dotCom,
anySubDomain + "dockerone" + dotCom,
anySubDomain + "dockone" + dotIo,
anySubDomain + "donews" + dotCom,
anySubDomain + "douban" + dotCom,
anySubDomain + "doubanio" + dotCom,
anySubDomain + "dpfile" + dotCom,
anySubDomain + "duomai" + dotCom,
anySubDomain + "duoshuo" + dotCom,
anySubDomain + "duowan" + dotCom,
anySubDomain + "dxpmedia" + dotCom,
anySubDomain + "eastday" + dotCom,
anySubDomain + "ecitic" + dotCom,
anySubDomain + "emarbox" + dotCom,
anySubDomain + "eoeandroid" + dotCom,
anySubDomain + "etao" + dotCom,
anySubDomain + "excelhome" + dotNet,
anySubDomain + "fanli" + dotCom,
anySubDomain + "feng" + dotCom,
anySubDomain + "fengniao" + dotCom,
anySubDomain + "fhldns" + dotCom,
anySubDomain + "foxmail" + dotCom,
anySubDomain + "geekpark" + dotNet,
anySubDomain + "geetest" + dotCom,
anySubDomain + "geilicdn" + dotCom,
anySubDomain + "getui" + dotCom,
anySubDomain + "google-analytics" + dotCom,
anySubDomain + "growingio" + dotCom,
anySubDomain + "gtags" + dotNet,
anySubDomain + "gwdang" + dotCom,
anySubDomain + "hao123" + dotCom,
anySubDomain + "hao123img" + dotCom,
anySubDomain + "haosou" + dotCom,
anySubDomain + "hdslb" + dotCom,
anySubDomain + "henha" + dotCom,
anySubDomain + "henkuai" + dotCom,
anySubDomain + "hexun" + dotCom,
anySubDomain + "hichina" + dotCom,
anySubDomain + "huanqiu" + dotCom,
anySubDomain + "hunantv" + dotCom,
anySubDomain + "huochepiao" + dotCom,
anySubDomain + "hupu" + dotCom,
anySubDomain + "hupucdn" + dotCom,
anySubDomain + "huxiu" + dotCom,
anySubDomain + "iask" + dotCom,
anySubDomain + "iciba" + dotCom,
anySubDomain + "idqqimg" + dotCom,
anySubDomain + "ifanr" + dotCom,
anySubDomain + "ifanrusercontent" + dotCom,
anySubDomain + "ifanrx" + dotCom,
anySubDomain + "ifeng" + dotCom,
anySubDomain + "ifengimg" + dotCom,
anySubDomain + "ijinshan" + dotCom,
anySubDomain + "ikafan" + dotCom,
anySubDomain + "imedao" + dotCom,
anySubDomain + "imgo" + dotTv,
anySubDomain + "imooc" + dotCom,
anySubDomain + "infoq" + dotCom,
anySubDomain + "infoqstatic" + dotCom,
anySubDomain + "ip138" + dotCom,
anySubDomain + "ipinyou" + dotCom,
anySubDomain + "ipip" + dotNet,
anySubDomain + "ip-cdn" + dotCom,
anySubDomain + "iqiyi" + dotCom,
anySubDomain + "it165" + dotNet,
anySubDomain + "it168" + dotCom,
anySubDomain + "it610" + dotCom,
anySubDomain + "iteye" + dotCom,
anySubDomain + "ithome" + dotCom,
anySubDomain + "itjuzi" + dotCom,
anySubDomain + "jandan" + dotNet,
anySubDomain + "jd" + dotCom,
anySubDomain + "jb51" + dotCom,
anySubDomain + "jia" + dotCom,
anySubDomain + "jianshu" + dotCom,
anySubDomain + "jianshu" + dotIo,
anySubDomain + "jiasuhui" + dotCom,
anySubDomain + "jiathis" + dotCom,
anySubDomain + "jiayuan" + dotCom,
anySubDomain + "jikexueyuan" + dotCom,
anySubDomain + "jisuanke" + dotCom,
anySubDomain + "jmstatic" + dotCom,
anySubDomain + "jsdelivr" + dotNet,
anySubDomain + "jstv" + dotCom,
anySubDomain + "jumei" + dotCom,
anySubDomain + "jyimg" + dotCom,
anySubDomain + "kaixin001" + dotCom,
anySubDomain + "kanimg" + dotCom,
anySubDomain + "kankanews" + dotCom,
anySubDomain + "kejet" + dotNet,
anySubDomain + "kf5" + dotCom,
anySubDomain + "kimiss" + dotCom,
anySubDomain + "kouclo" + dotCom,
anySubDomain + "koudai" + dotCom,
anySubDomain + "koudai8" + dotCom,
anySubDomain + "ku6" + dotCom,
anySubDomain + "ku6cdn" + dotCom,
anySubDomain + "ku6img" + dotCom,
anySubDomain + "kuqin" + dotCom,
anySubDomain + "lady8844" + dotCom,
anySubDomain + "lagou" + dotCom,
anySubDomain + "le" + dotCom,
anySubDomain + "leanote" + dotCom,
anySubDomain + "leiphone" + dotCom,
anySubDomain + "leju" + dotCom,
anySubDomain + "leturich" + dotOrg,
anySubDomain + "letv" + dotCom,
anySubDomain + "letvcdn" + dotCom,
anySubDomain + "letvimg" + dotCom,
anySubDomain + "liantu" + dotMe,
anySubDomain + "liaoxuefeng" + dotCom,
anySubDomain + "liba" + dotCom,
anySubDomain + "libaclub" + dotCom,
anySubDomain + "liepin" + dotCom,
anySubDomain + "lietou" + dotCom,
anySubDomain + "lightonus" + dotCom,
anySubDomain + "linkvans" + dotCom,
anySubDomain + "linuxidc" + dotCom,
anySubDomain + "liuxiaoer" + dotCom,
anySubDomain + "lofter" + dotCom,
anySubDomain + "lu" + dotCom,
anySubDomain + "lufax" + dotCom,
anySubDomain + "lufaxcdn" + dotCom,
anySubDomain + "lvmama" + dotCom,
anySubDomain + "lxdns" + dotCom,
anySubDomain + "lxway" + dotCom,
anySubDomain + "ly" + dotCom,
anySubDomain + "mayihr" + dotCom,
anySubDomain + "mechina" + dotOrg,
anySubDomain + "mediav" + dotCom,
anySubDomain + "meiqia" + dotCom,
anySubDomain + "meika360" + dotCom,
anySubDomain + "meilishuo" + dotCom,
anySubDomain + "meishij" + dotNet,
anySubDomain + "meituan" + dotCom,
anySubDomain + "meizu" + dotCom,
anySubDomain + "mgtv" + dotCom,
anySubDomain + "mi" + dotCom,
anySubDomain + "miaopai" + dotCom,
anySubDomain + "miaozhen" + dotCom,
anySubDomain + "mmbang" + dotCom,
anySubDomain + "mmbang" + dotInfo,
anySubDomain + "mmstat" + dotCom,
anySubDomain + "mogucdn" + dotCom,
anySubDomain + "mogujie" + dotCom,
anySubDomain + "mop" + dotCom,
anySubDomain + "mscbsc" + dotCom,
anySubDomain + "mukewang" + dotCom,
anySubDomain + "mydrivers" + dotCom,
anySubDomain + "myshow360" + dotNet,
anySubDomain + "mzstatic" + dotCom,
anySubDomain + "netease" + dotCom,
anySubDomain + "newbandeng" + dotCom,
anySubDomain + "ngacn" + dotCc,
anySubDomain + "ntalker" + dotCom,
anySubDomain + "nvsheng" + dotCom,
anySubDomain + "oeeee" + dotCom,
anySubDomain + "ol-img" + dotCom,
anySubDomain + "oneapm" + dotCom,
anySubDomain + "onlinedown" + dotNet,
anySubDomain + "onlinesjtu" + dotCom,
anySubDomain + "oschina" + dotNet,
anySubDomain + "paipai" + dotCom,
anySubDomain + "pcbeta" + dotCom,
anySubDomain + "pchome" + dotNet,
anySubDomain + "pingan" + dotCom,
anySubDomain + "pingplusplus" + dotCom,
anySubDomain + "pps" + dotTv,
anySubDomain + "psbc" + dotCom,
anySubDomain + "pubyun" + dotCom,
anySubDomain + "qbox" + dotMe,
anySubDomain + "qcloud" + dotCom,
anySubDomain + "qhimg" + dotCom,
anySubDomain + "qiaobutang" + dotCom,
anySubDomain + "qidian" + dotCom,
anySubDomain + "qingcloud" + dotCom,
anySubDomain + "qingsongchou" + dotCom,
anySubDomain + "qiniu" + dotCom,
anySubDomain + "qiniucdn" + dotCom,
anySubDomain + "qiniudn" + dotCom,
anySubDomain + "qiniudns" + dotCom,
anySubDomain + "qiyi" + dotCom,
anySubDomain + "qiyipic" + dotCom,
anySubDomain + "qtmojo" + dotCom,
anySubDomain + "qq" + dotCom,
anySubDomain + "qqmail" + dotCom,
anySubDomain + "qunar" + dotCom,
anySubDomain + "qunarzz" + dotCom,
anySubDomain + "qzone" + dotCom,
anySubDomain + "renren" + dotCom,
anySubDomain + "ruanmei" + dotCom,
anySubDomain + "ruby-china" + dotOrg,
anySubDomain + "sandai" + dotNet,
anySubDomain + "sanguosha" + dotCom,
anySubDomain + "sanwen" + dotNet,
anySubDomain + "segmentfault" + dotCom,
anySubDomain + "sf-express" + dotCom,
anySubDomain + "sharejs" + dotCom,
anySubDomain + "shmetro" + dotCom,
anySubDomain + "shutcm" + dotCom,
anySubDomain + "simei8" + dotCom,
anySubDomain + "sina" + dotCom,
anySubDomain + "sinaapp" + dotCom,
anySubDomain + "sinaedge" + dotCom,
anySubDomain + "sinaimg" + dotCom,
anySubDomain + "sinajs" + dotCom,
anySubDomain + "szzfgjj" + dotCom,
anySubDomain + "smzdm" + dotCom,
anySubDomain + "sohu" + dotCom,
anySubDomain + "sogou" + dotCom,
anySubDomain + "sogoucdn" + dotCom,
anySubDomain + "soso" + dotCom,
anySubDomain + "sspai" + dotCom,
anySubDomain + "starbaby" + dotCc,
anySubDomain + "starbaby" + dotCom,
anySubDomain + "staticfile" + dotOrg,
anySubDomain + "stockstar" + dotCom,
anySubDomain + "suning" + dotCom,
anySubDomain + "szfw" + dotOrg,
anySubDomain + "t1y5" + dotCom,
anySubDomain + "tanx" + dotCom,
anySubDomain + "tao123" + dotCom,
anySubDomain + "taobao" + dotCom,
anySubDomain + "taobaocdn" + dotCom,
anySubDomain + "tbcache" + dotCom,
anySubDomain + "tencent" + dotCom,
anySubDomain + "tenpay" + dotCom,
anySubDomain + "tenxcloud" + dotCom,
anySubDomain + "tiebaimg" + dotCom,
anySubDomain + "tietuku" + dotCom,
anySubDomain + "tiexue" + dotNet,
anySubDomain + "tmall" + dotCom,
anySubDomain + "tmcdn" + dotNet,
anySubDomain + "topthink" + dotCom,
anySubDomain + "tudou" + dotCom,
anySubDomain + "tudouui" + dotCom,
anySubDomain + "tuicool" + dotCom,
anySubDomain + "tuniu" + dotCom,
anySubDomain + "tutuapp" + dotCom,
anySubDomain + "u17" + dotCom,
anySubDomain + "useso" + dotCom,
anySubDomain + "unionpay" + dotCom,
anySubDomain + "unionpaysecure" + dotCom,
anySubDomain + "upyun" + dotCom,
anySubDomain + "upaiyun" + dotCom,
anySubDomain + "v2ex" + dotCom,
anySubDomain + "v5875" + dotCom,
anySubDomain + "vamaker" + dotCom,
anySubDomain + "vancl" + dotCom,
anySubDomain + "vcimg" + dotCom,
anySubDomain + "vip" + dotCom,
anySubDomain + "wallstreetcn" + dotCom,
anySubDomain + "wandoujia" + dotCom,
anySubDomain + "wdjimg" + dotCom,
anySubDomain + "weand" + dotCom,
anySubDomain + "webterren" + dotCom,
anySubDomain + "weibo" + dotCom,
anySubDomain + "weicaifu" + dotCom,
anySubDomain + "weidian" + dotCom,
anySubDomain + "weiphone" + dotCom,
anySubDomain + "weiphone" + dotNet,
anySubDomain + "weixing" + dotCom,
anySubDomain + "weiyun" + dotCom,
anySubDomain + "wonnder" + dotCom,
anySubDomain + "worktile" + dotCom,
anySubDomain + "wooyun" + dotOrg,
anySubDomain + "wrating" + dotCom,
anySubDomain + "wscdns" + dotCom,
anySubDomain + "wumii" + dotCom,
anySubDomain + "xiachufang" + dotCom,
anySubDomain + "xiami" + dotCom,
anySubDomain + "xiaokaxiu" + dotCom,
anySubDomain + "xiaomi" + dotCom,
anySubDomain + "xitu" + dotCom,
anySubDomain + "xinhuanet" + dotCom,
anySubDomain + "xinshipu" + dotCom,
anySubDomain + "xiu8" + dotCom,
anySubDomain + "xnpic" + dotCom,
anySubDomain + "xueqiu" + dotCom,
anySubDomain + "xunlei" + dotCom,
anySubDomain + "xywy" + dotCom,
anySubDomain + "yaolan" + dotCom,
anySubDomain + "yccdn" + dotCom,
anySubDomain + "yeepay" + dotCom,
anySubDomain + "yesky" + dotCom,
anySubDomain + "yigao" + dotCom,
anySubDomain + "yihaodian" + dotCom,
anySubDomain + "yihaodianimg" + dotCom,
anySubDomain + "yingjiesheng" + dotCom,
anySubDomain + "yinxiang" + dotCom,
anySubDomain + "yjbys" + dotCom,
anySubDomain + "yhd" + dotCom,
anySubDomain + "youboy" + dotCom,
anySubDomain + "youku" + dotCom,
anySubDomain + "yunba" + dotIo,
anySubDomain + "yundaex" + dotCom,
anySubDomain + "yunshipei" + dotCom,
anySubDomain + "yupoo" + dotCom,
anySubDomain + "yuzua" + dotCom,
anySubDomain + "yy" + dotCom,
anySubDomain + "yytcdn" + dotCom,
anySubDomain + "zampda" + dotNet,
anySubDomain + "zastatic" + dotCom,
anySubDomain + "zbjimg" + dotCom,
anySubDomain + "zdfans" + dotCom,
anySubDomain + "zhenai" + dotCom,
anySubDomain + "zhanqi" + dotTv,
anySubDomain + "zhaopin" + dotCom,
anySubDomain + "zhihu" + dotCom,
anySubDomain + "zhimg" + dotCom,
anySubDomain + "zhiziyun" + dotCom,
anySubDomain + "zjstv" + dotCom,
anySubDomain + "zhubajie" + dotCom,
anySubDomain + "zrblog" + dotNet,
anySubDomain + "zuche" + dotCom,
anySubDomain + "zuchecdn" + dotCom,
}
chinaSitesDomains = make([]*router.Domain, len(regexpDomains))
for idx, pattern := range regexpDomains {
chinaSitesDomains[idx] = &router.Domain{
Type: router.Domain_Regex,
Value: pattern,
}
}
}
+98
View File
@@ -0,0 +1,98 @@
package conf_test
import (
"net"
"testing"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/testing/assert"
. "v2ray.com/core/tools/conf"
)
func makeDestination(ip string) v2net.Destination {
return v2net.TCPDestination(v2net.IPAddress(net.ParseIP(ip)), 80)
}
func makeDomainDestination(domain string) v2net.Destination {
return v2net.TCPDestination(v2net.DomainAddress(domain), 80)
}
func TestChinaIPJson(t *testing.T) {
assert := assert.On(t)
rule := ParseRule([]byte(`{
"type": "chinaip",
"outboundTag": "x"
}`))
assert.String(rule.Tag).Equals("x")
cond, err := rule.BuildCondition()
assert.Error(err).IsNil()
assert.Bool(cond.Apply(makeDestination("121.14.1.189"))).IsTrue() // sina.com.cn
assert.Bool(cond.Apply(makeDestination("101.226.103.106"))).IsTrue() // qq.com
assert.Bool(cond.Apply(makeDestination("115.239.210.36"))).IsTrue() // image.baidu.com
assert.Bool(cond.Apply(makeDestination("120.135.126.1"))).IsTrue()
assert.Bool(cond.Apply(makeDestination("8.8.8.8"))).IsFalse()
}
func TestChinaSitesJson(t *testing.T) {
assert := assert.On(t)
rule := ParseRule([]byte(`{
"type": "chinasites",
"outboundTag": "y"
}`))
assert.String(rule.Tag).Equals("y")
cond, err := rule.BuildCondition()
assert.Error(err).IsNil()
assert.Bool(cond.Apply(makeDomainDestination("v.qq.com"))).IsTrue()
assert.Bool(cond.Apply(makeDomainDestination("www.163.com"))).IsTrue()
assert.Bool(cond.Apply(makeDomainDestination("ngacn.cc"))).IsTrue()
assert.Bool(cond.Apply(makeDomainDestination("12306.cn"))).IsTrue()
assert.Bool(cond.Apply(makeDomainDestination("v2ray.com"))).IsFalse()
}
func TestDomainRule(t *testing.T) {
assert := assert.On(t)
rule := ParseRule([]byte(`{
"type": "field",
"domain": [
"ooxx.com",
"oxox.com",
"regexp:\\.cn$"
],
"network": "tcp",
"outboundTag": "direct"
}`))
assert.Pointer(rule).IsNotNil()
cond, err := rule.BuildCondition()
assert.Error(err).IsNil()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80))).IsTrue()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.aabb.com"), 80))).IsFalse()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80))).IsFalse()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.12306.cn"), 80))).IsTrue()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.acn.com"), 80))).IsFalse()
}
func TestIPRule(t *testing.T) {
assert := assert.On(t)
rule := ParseRule([]byte(`{
"type": "field",
"ip": [
"10.0.0.0/8",
"192.0.0.0/24"
],
"network": "tcp",
"outboundTag": "direct"
}`))
assert.Pointer(rule).IsNotNil()
cond, err := rule.BuildCondition()
assert.Error(err).IsNil()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80))).IsFalse()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{10, 0, 0, 1}), 80))).IsTrue()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80))).IsFalse()
assert.Bool(cond.Apply(v2net.TCPDestination(v2net.IPAddress([]byte{192, 0, 0, 1}), 80))).IsTrue()
}
+51
View File
@@ -0,0 +1,51 @@
package conf
import (
"errors"
"strings"
"v2ray.com/core/common/loader"
"v2ray.com/core/common/protocol"
"v2ray.com/core/proxy/shadowsocks"
)
type ShadowsocksServerConfig struct {
Cipher string `json:"method"`
Password string `json:"password"`
UDP bool `json:"udp"`
Level byte `json:"level"`
Email string `json:"email"`
}
func (this *ShadowsocksServerConfig) Build() (*loader.TypedSettings, error) {
config := new(shadowsocks.ServerConfig)
config.UdpEnabled = this.UDP
if len(this.Password) == 0 {
return nil, errors.New("Shadowsocks password is not specified.")
}
account := &shadowsocks.Account{
Password: this.Password,
}
cipher := strings.ToLower(this.Cipher)
switch cipher {
case "aes-256-cfb":
account.CipherType = shadowsocks.CipherType_AES_256_CFB
case "aes-128-cfb":
account.CipherType = shadowsocks.CipherType_AES_128_CFB
case "chacha20":
account.CipherType = shadowsocks.CipherType_CHACHA20
case "chacha20-ietf":
account.CipherType = shadowsocks.CipherType_CHACHA20_IEFT
default:
return nil, errors.New("Unknown cipher method: " + cipher)
}
config.User = &protocol.User{
Email: this.Email,
Level: uint32(this.Level),
Account: loader.NewTypedSettings(account),
}
return loader.NewTypedSettings(config), nil
}
+40
View File
@@ -0,0 +1,40 @@
package conf_test
import (
"encoding/json"
"testing"
"v2ray.com/core/proxy/shadowsocks"
"v2ray.com/core/testing/assert"
. "v2ray.com/core/tools/conf"
)
func TestShadowsocksServerConfigParsing(t *testing.T) {
assert := assert.On(t)
rawJson := `{
"method": "aes-128-cfb",
"password": "v2ray-password"
}`
rawConfig := new(ShadowsocksServerConfig)
err := json.Unmarshal([]byte(rawJson), rawConfig)
assert.Error(err).IsNil()
ts, err := rawConfig.Build()
assert.Error(err).IsNil()
iConfig, err := ts.GetInstance()
assert.Error(err).IsNil()
config := iConfig.(*shadowsocks.ServerConfig)
rawAccount, err := config.User.GetTypedAccount()
assert.Error(err).IsNil()
account, ok := rawAccount.(*shadowsocks.Account)
assert.Bool(ok).IsTrue()
cipher, err := account.GetCipher()
assert.Error(err).IsNil()
assert.Int(cipher.KeySize()).Equals(16)
assert.Bytes(account.GetCipherKey()).Equals([]byte{160, 224, 26, 2, 22, 110, 9, 80, 65, 52, 80, 20, 38, 243, 224, 241})
}
+95
View File
@@ -0,0 +1,95 @@
package conf
import (
"encoding/json"
"errors"
"v2ray.com/core/common/loader"
"v2ray.com/core/common/protocol"
"v2ray.com/core/proxy/socks"
)
type SocksAccount struct {
Username string `json:"user"`
Password string `json:"pass"`
}
func (this *SocksAccount) Build() *socks.Account {
return &socks.Account{
Username: this.Username,
Password: this.Password,
}
}
const (
AuthMethodNoAuth = "noauth"
AuthMethodUserPass = "password"
)
type SocksServerConfig struct {
AuthMethod string `json:"auth"`
Accounts []*SocksAccount `json:"accounts"`
UDP bool `json:"udp"`
Host *Address `json:"ip"`
Timeout uint32 `json:"timeout"`
}
func (this *SocksServerConfig) Build() (*loader.TypedSettings, error) {
config := new(socks.ServerConfig)
if this.AuthMethod == AuthMethodNoAuth {
config.AuthType = socks.AuthType_NO_AUTH
} else if this.AuthMethod == AuthMethodUserPass {
config.AuthType = socks.AuthType_PASSWORD
} else {
return nil, errors.New("Unknown socks auth method: " + this.AuthMethod)
}
if len(this.Accounts) > 0 {
config.Accounts = make(map[string]string, len(this.Accounts))
for _, account := range this.Accounts {
config.Accounts[account.Username] = account.Password
}
}
config.UdpEnabled = this.UDP
if this.Host != nil {
config.Address = this.Host.Build()
}
config.Timeout = this.Timeout
return loader.NewTypedSettings(config), nil
}
type SocksRemoteConfig struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
Users []json.RawMessage `json:"users"`
}
type SocksClientConfig struct {
Servers []*SocksRemoteConfig `json:"servers"`
}
func (this *SocksClientConfig) Build() (*loader.TypedSettings, error) {
config := new(socks.ClientConfig)
config.Server = make([]*protocol.ServerEndpoint, len(this.Servers))
for idx, serverConfig := range this.Servers {
server := &protocol.ServerEndpoint{
Address: serverConfig.Address.Build(),
Port: uint32(serverConfig.Port),
}
for _, rawUser := range serverConfig.Users {
user := new(protocol.User)
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, errors.New("Socks|Client: Failed to parse user: " + err.Error())
}
account := new(SocksAccount)
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New("Socks|Client: Failed to parse socks account: " + err.Error())
}
user.Account = loader.NewTypedSettings(account.Build())
server.User = append(server.User, user)
}
config.Server[idx] = server
}
return loader.NewTypedSettings(config), nil
}
+53
View File
@@ -0,0 +1,53 @@
package conf
import (
"errors"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/transport"
"v2ray.com/core/transport/internet"
)
type TransportConfig struct {
TCPConfig *TCPConfig `json:"tcpSettings"`
KCPConfig *KCPConfig `json:"kcpSettings"`
WSConfig *WebSocketConfig `json:"wsSettings"`
}
func (this *TransportConfig) Build() (*transport.Config, error) {
config := new(transport.Config)
if this.TCPConfig != nil {
ts, err := this.TCPConfig.Build()
if err != nil {
return nil, errors.New("Failed to build TCP config: " + err.Error())
}
config.NetworkSettings = append(config.NetworkSettings, &internet.NetworkSettings{
Network: v2net.Network_TCP,
Settings: ts,
})
}
if this.KCPConfig != nil {
ts, err := this.KCPConfig.Build()
if err != nil {
return nil, errors.New("Failed to build KCP config: " + err.Error())
}
config.NetworkSettings = append(config.NetworkSettings, &internet.NetworkSettings{
Network: v2net.Network_KCP,
Settings: ts,
})
}
if this.WSConfig != nil {
ts, err := this.WSConfig.Build()
if err != nil {
return nil, errors.New("Failed to build WebSocket config: " + err.Error())
}
config.NetworkSettings = append(config.NetworkSettings, &internet.NetworkSettings{
Network: v2net.Network_WebSocket,
Settings: ts,
})
}
return config, nil
}
+26
View File
@@ -0,0 +1,26 @@
package conf
import (
"v2ray.com/core/common/loader"
"v2ray.com/core/transport/internet/authenticators/noop"
"v2ray.com/core/transport/internet/authenticators/srtp"
"v2ray.com/core/transport/internet/authenticators/utp"
)
type NoOpAuthenticator struct{}
func (NoOpAuthenticator) Build() (*loader.TypedSettings, error) {
return loader.NewTypedSettings(new(noop.Config)), nil
}
type SRTPAuthenticator struct{}
func (SRTPAuthenticator) Build() (*loader.TypedSettings, error) {
return loader.NewTypedSettings(new(srtp.Config)), nil
}
type UTPAuthenticator struct{}
func (UTPAuthenticator) Build() (*loader.TypedSettings, error) {
return loader.NewTypedSettings(new(utp.Config)), nil
}
+181
View File
@@ -0,0 +1,181 @@
package conf
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"strings"
"v2ray.com/core/common/loader"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/transport/internet"
"v2ray.com/core/transport/internet/kcp"
"v2ray.com/core/transport/internet/tcp"
"v2ray.com/core/transport/internet/tls"
"v2ray.com/core/transport/internet/ws"
)
var (
kcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{
"none": func() interface{} { return new(NoOpAuthenticator) },
"srtp": func() interface{} { return new(SRTPAuthenticator) },
"utp": func() interface{} { return new(UTPAuthenticator) },
}, "type", "")
)
type KCPConfig struct {
Mtu *uint32 `json:"mtu"`
Tti *uint32 `json:"tti"`
UpCap *uint32 `json:"uplinkCapacity"`
DownCap *uint32 `json:"downlinkCapacity"`
Congestion *bool `json:"congestion"`
ReadBufferSize *uint32 `json:"readBufferSize"`
WriteBufferSize *uint32 `json:"writeBufferSize"`
HeaderConfig json.RawMessage `json:"header"`
}
func (this *KCPConfig) Build() (*loader.TypedSettings, error) {
config := new(kcp.Config)
if this.Mtu != nil {
mtu := *this.Mtu
if mtu < 576 || mtu > 1460 {
return nil, fmt.Errorf("KCP|Config: Invalid MTU size: %d", mtu)
}
config.Mtu = &kcp.MTU{Value: mtu}
}
if this.Tti != nil {
tti := *this.Tti
if tti < 10 || tti > 100 {
return nil, fmt.Errorf("KCP|Config: Invalid TTI: %d", tti)
}
config.Tti = &kcp.TTI{Value: tti}
}
if this.UpCap != nil {
config.UplinkCapacity = &kcp.UplinkCapacity{Value: *this.UpCap}
}
if this.DownCap != nil {
config.DownlinkCapacity = &kcp.DownlinkCapacity{Value: *this.DownCap}
}
if this.Congestion != nil {
config.Congestion = *this.Congestion
}
if this.ReadBufferSize != nil {
size := *this.ReadBufferSize
if size > 0 {
config.ReadBuffer = &kcp.ReadBuffer{Size: size * 1024 * 1024}
} else {
config.ReadBuffer = &kcp.ReadBuffer{Size: 512 * 1024}
}
}
if this.WriteBufferSize != nil {
size := *this.WriteBufferSize
if size > 0 {
config.WriteBuffer = &kcp.WriteBuffer{Size: size * 1024 * 1024}
} else {
config.WriteBuffer = &kcp.WriteBuffer{Size: 512 * 1024}
}
}
if len(this.HeaderConfig) > 0 {
headerConfig, _, err := kcpHeaderLoader.Load(this.HeaderConfig)
if err != nil {
return nil, errors.New("KCP|Config: Failed to parse header config: " + err.Error())
}
ts, err := headerConfig.(Buildable).Build()
if err != nil {
return nil, errors.New("Failed to get KCP authenticator config: " + err.Error())
}
config.HeaderConfig = ts
}
return loader.NewTypedSettings(config), nil
}
type TCPConfig struct {
ConnectionReuse *bool `json:"connectionReuse"`
}
func (this *TCPConfig) Build() (*loader.TypedSettings, error) {
config := new(tcp.Config)
if this.ConnectionReuse != nil {
config.ConnectionReuse = &tcp.ConnectionReuse{
Enable: *this.ConnectionReuse,
}
}
return loader.NewTypedSettings(config), nil
}
type WebSocketConfig struct {
ConnectionReuse *bool `json:"connectionReuse"`
Path string `json:"Path"`
}
func (this *WebSocketConfig) Build() (*loader.TypedSettings, error) {
config := &ws.Config{
Path: this.Path,
}
if this.ConnectionReuse != nil {
config.ConnectionReuse = &ws.ConnectionReuse{
Enable: *this.ConnectionReuse,
}
}
return loader.NewTypedSettings(config), nil
}
type TLSCertConfig struct {
CertFile string `json:"certificateFile"`
KeyFile string `json:"keyFile"`
}
type TLSConfig struct {
Insecure bool `json:"allowInsecure"`
Certs []*TLSCertConfig `json:"certificates"`
}
func (this *TLSConfig) Build() (*loader.TypedSettings, error) {
config := new(tls.Config)
config.Certificate = make([]*tls.Certificate, len(this.Certs))
for idx, certConf := range this.Certs {
cert, err := ioutil.ReadFile(certConf.CertFile)
if err != nil {
return nil, errors.New("TLS: Failed to load certificate file: " + err.Error())
}
key, err := ioutil.ReadFile(certConf.KeyFile)
if err != nil {
return nil, errors.New("TLS: Failed to load key file: " + err.Error())
}
config.Certificate[idx] = &tls.Certificate{
Key: key,
Certificate: cert,
}
}
config.AllowInsecure = this.Insecure
return loader.NewTypedSettings(config), nil
}
type StreamConfig struct {
Network *Network `json:"network"`
Security string `json:"security"`
TLSSettings *TLSConfig `json:"tlsSettings"`
}
func (this *StreamConfig) Build() (*internet.StreamConfig, error) {
config := &internet.StreamConfig{
Network: v2net.Network_RawTCP,
}
if this.Network != nil {
config.Network = (*this.Network).Build()
}
if strings.ToLower(this.Security) == "tls" {
tlsSettings := this.TLSSettings
if tlsSettings == nil {
tlsSettings = &TLSConfig{}
}
ts, err := tlsSettings.Build()
if err != nil {
return nil, errors.New("Failed to build TLS config: " + err.Error())
}
config.SecuritySettings = append(config.SecuritySettings, ts)
}
return config, nil
}
+308 -13
View File
@@ -1,28 +1,323 @@
package conf
/*
import (
"encoding/json"
"errors"
"io"
"strings"
"v2ray.com/core/app/dns"
"v2ray.com/core/app/router"
"v2ray.com/core/common"
"io"
"v2ray.com/core"
"v2ray.com/core/common/loader"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/transport"
"v2ray.com/core/transport/internet"
)
var (
inboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
"dokodemo-door": func() interface{} { return new(DokodemoConfig) },
"http": func() interface{} { return new(HttpServerConfig) },
"shadowsocks": func() interface{} { return new(ShadowsocksServerConfig) },
"socks": func() interface{} { return new(SocksServerConfig) },
"vmess": func() interface{} { return new(VMessInboundConfig) },
}, "protocol", "settings")
outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
"blackhole": func() interface{} { return new(BlackholeConfig) },
"freedom": func() interface{} { return new(FreedomConfig) },
"vmess": func() interface{} { return new(VMessOutboundConfig) },
}, "protocol", "settings")
)
type InboundConnectionConfig struct {
Port uint16 `json:"port"`
Listen *Address `json:"listen"`
Protocol string `json:"protocol"`
StreamSetting *StreamConfig `json:"streamSettings"`
Settings json.RawMessage `json:"settings"`
AllowPassive bool `json:"allowPassive"`
}
func (this *InboundConnectionConfig) Build() (*core.InboundConnectionConfig, error) {
config := new(core.InboundConnectionConfig)
config.PortRange = &v2net.PortRange{
From: uint32(this.Port),
To: uint32(this.Port),
}
if this.Listen != nil {
if this.Listen.Family().IsDomain() {
return nil, errors.New("Point: Unable to listen on domain address: " + this.Listen.Domain())
}
config.ListenOn = this.Listen.Build()
}
if this.StreamSetting != nil {
ts, err := this.StreamSetting.Build()
if err != nil {
return nil, err
}
config.StreamSettings = ts
}
config.AllowPassiveConnection = this.AllowPassive
jsonConfig, err := inboundConfigLoader.LoadWithID(this.Settings, this.Protocol)
if err != nil {
return nil, err
}
ts, err := jsonConfig.(Buildable).Build()
if err != nil {
return nil, err
}
config.Settings = ts
return config, nil
}
type OutboundConnectionConfig struct {
Protocol string `json:"protocol"`
SendThrough *Address `json:"sendThrough"`
StreamSetting *StreamConfig `json:"streamSettings"`
Settings json.RawMessage `json:"settings"`
}
func (this *OutboundConnectionConfig) Build() (*core.OutboundConnectionConfig, error) {
config := new(core.OutboundConnectionConfig)
rawConfig, err := outboundConfigLoader.LoadWithID(this.Settings, this.Protocol)
if err != nil {
return nil, err
}
ts, err := rawConfig.(Buildable).Build()
if err != nil {
return nil, err
}
config.Settings = ts
if this.SendThrough != nil {
address := this.SendThrough
if address.Family().IsDomain() {
return nil, errors.New("Point: Unable to send through: " + address.String())
}
config.SendThrough = address.Build()
}
if this.StreamSetting != nil {
ss, err := this.StreamSetting.Build()
if err != nil {
return nil, err
}
config.StreamSettings = ss
}
return config, nil
}
type InboundDetourAllocationConfig struct {
Strategy string `json:"strategy"`
Concurrency *uint32 `json:"concurrency"`
RefreshMin *uint32 `json:"refresh"`
}
func (this *InboundDetourAllocationConfig) Build() (*core.AllocationStrategy, error) {
config := new(core.AllocationStrategy)
switch strings.ToLower(this.Strategy) {
case "always":
config.Type = core.AllocationStrategy_Always
case "random":
config.Type = core.AllocationStrategy_Random
case "external":
config.Type = core.AllocationStrategy_External
default:
return nil, errors.New("Unknown allocation strategy: " + this.Strategy)
}
if this.Concurrency != nil {
config.Concurrency = &core.AllocationStrategyConcurrency{
Value: *this.Concurrency,
}
}
if this.RefreshMin != nil {
config.Refresh = &core.AllocationStrategyRefresh{
Value: *this.RefreshMin,
}
}
return config, nil
}
type InboundDetourConfig struct {
Protocol string `json:"protocol"`
PortRange *PortRange `json:"port"`
ListenOn *Address `json:"listen"`
Settings json.RawMessage `json:"settings"`
Tag string `json:"tag"`
Allocation *InboundDetourAllocationConfig `json:"allocate"`
StreamSetting *StreamConfig `json:"streamSettings"`
AllowPassive bool `json:"allowPassive"`
}
func (this *InboundDetourConfig) Build() (*core.InboundConnectionConfig, error) {
config := new(core.InboundConnectionConfig)
if this.PortRange == nil {
return nil, errors.New("Point: Port range not specified in InboundDetour.")
}
config.PortRange = this.PortRange.Build()
if this.ListenOn != nil {
if this.ListenOn.Family().IsDomain() {
return nil, errors.New("Point: Unable to listen on domain address: " + this.ListenOn.Domain())
}
config.ListenOn = this.ListenOn.Build()
}
config.Tag = this.Tag
if this.Allocation != nil {
as, err := this.Allocation.Build()
if err != nil {
return nil, err
}
config.AllocationStrategy = as
}
if this.StreamSetting != nil {
ss, err := this.StreamSetting.Build()
if err != nil {
return nil, err
}
config.StreamSettings = ss
}
config.AllowPassiveConnection = this.AllowPassive
rawConfig, err := inboundConfigLoader.LoadWithID(this.Settings, this.Protocol)
if err != nil {
return nil, err
}
ts, err := rawConfig.(Buildable).Build()
if err != nil {
return nil, err
}
config.Settings = ts
return config, nil
}
type OutboundDetourConfig struct {
Protocol string `json:"protocol"`
SendThrough *Address `json:"sendThrough"`
Tag string `json:"tag"`
Settings json.RawMessage `json:"settings"`
StreamSetting *StreamConfig `json:"streamSettings"`
}
func (this *OutboundDetourConfig) Build() (*core.OutboundConnectionConfig, error) {
config := new(core.OutboundConnectionConfig)
config.Tag = this.Tag
if this.SendThrough != nil {
address := this.SendThrough
if address.Family().IsDomain() {
return nil, errors.New("Point: Unable to send through: " + address.String())
}
config.SendThrough = address.Build()
}
if this.StreamSetting != nil {
ss, err := this.StreamSetting.Build()
if err != nil {
return nil, err
}
config.StreamSettings = ss
}
rawConfig, err := outboundConfigLoader.LoadWithID(this.Settings, this.Protocol)
if err != nil {
return nil, err
}
ts, err := rawConfig.(Buildable).Build()
if err != nil {
return nil, err
}
config.Settings = ts
return config, nil
}
type Config struct {
Port uint16 `json:"port"` // Port of this Point server.
LogConfig *LogConfig `json:"log"`
RouterConfig *router.Config `json:"routing"`
DNSConfig *dns.Config `json:"dns"`
RouterConfig *RouterConfig `json:"routing"`
DNSConfig *DnsConfig `json:"dns"`
InboundConfig *InboundConnectionConfig `json:"inbound"`
OutboundConfig *OutboundConnectionConfig `json:"outbound"`
InboundDetours []*InboundDetourConfig `json:"inboundDetour"`
OutboundDetours []*OutboundDetourConfig `json:"outboundDetour"`
Transport *transport.Config `json:"transport"`
InboundDetours []InboundDetourConfig `json:"inboundDetour"`
OutboundDetours []OutboundDetourConfig `json:"outboundDetour"`
Transport *TransportConfig `json:"transport"`
}
func (this *Config) Build() (*core.Config, error) {
config := new(core.Config)
if this.LogConfig != nil {
config.Log = this.LogConfig.Build()
}
if this.Transport != nil {
ts, err := this.Transport.Build()
if err != nil {
return nil, err
}
config.Transport = ts
}
if this.RouterConfig != nil {
routerConfig, err := this.RouterConfig.Build()
if err != nil {
return nil, err
}
config.App = append(config.App, loader.NewTypedSettings(routerConfig))
}
if this.DNSConfig != nil {
config.App = append(config.App, loader.NewTypedSettings(this.DNSConfig.Build()))
}
if this.InboundConfig == nil {
return nil, errors.New("No inbound config specified.")
}
if this.InboundConfig.Port == 0 && this.Port > 0 {
this.InboundConfig.Port = this.Port
}
ic, err := this.InboundConfig.Build()
if err != nil {
return nil, err
}
config.Inbound = append(config.Inbound, ic)
for _, rawInboundConfig := range this.InboundDetours {
ic, err := rawInboundConfig.Build()
if err != nil {
return nil, err
}
config.Inbound = append(config.Inbound, ic)
}
oc, err := this.OutboundConfig.Build()
if err != nil {
return nil, err
}
config.Outbound = append(config.Outbound, oc)
for _, rawOutboundConfig := range this.OutboundDetours {
oc, err := rawOutboundConfig.Build()
if err != nil {
return nil, err
}
config.Outbound = append(config.Outbound, oc)
}
return config, nil
}
func init() {
core.RegisterConfigLoader(core.ConfigFormat_JSON, func(input io.Reader) (*core.Config, error) {
jsonConfig := &Config{}
decoder := json.NewDecoder(input)
err := decoder.Decode(jsonConfig)
if err != nil {
return nil, errors.New("Point: Failed to load server config: " + err.Error())
}
return jsonConfig.Build()
})
}
*/
+140
View File
@@ -0,0 +1,140 @@
package conf
import (
"encoding/json"
"errors"
"v2ray.com/core/common/loader"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/serial"
"v2ray.com/core/proxy/vmess"
"v2ray.com/core/proxy/vmess/inbound"
"v2ray.com/core/proxy/vmess/outbound"
)
type VMessAccount struct {
ID string `json:"id"`
AlterIds uint16 `json:"alterId"`
}
func (this *VMessAccount) Build() *vmess.Account {
return &vmess.Account{
Id: this.ID,
AlterId: uint32(this.AlterIds),
}
}
type VMessDetourConfig struct {
ToTag string `json:"to"`
}
func (this *VMessDetourConfig) Build() *inbound.DetourConfig {
return &inbound.DetourConfig{
To: this.ToTag,
}
}
type FeaturesConfig struct {
Detour *VMessDetourConfig `json:"detour"`
}
type VMessDefaultConfig struct {
AlterIDs uint16 `json:"alterId"`
Level byte `json:"level"`
}
func (this *VMessDefaultConfig) Build() *inbound.DefaultConfig {
config := new(inbound.DefaultConfig)
config.AlterId = uint32(this.AlterIDs)
if config.AlterId == 0 {
config.AlterId = 32
}
config.Level = uint32(this.Level)
return config
}
type VMessInboundConfig struct {
Users []json.RawMessage `json:"clients"`
Features *FeaturesConfig `json:"features"`
Defaults *VMessDefaultConfig `json:"default"`
DetourConfig *VMessDetourConfig `json:"detour"`
}
func (this *VMessInboundConfig) Build() (*loader.TypedSettings, error) {
config := new(inbound.Config)
if this.Defaults != nil {
config.Default = this.Defaults.Build()
}
if this.DetourConfig != nil {
config.Detour = this.DetourConfig.Build()
} else if this.Features != nil && this.Features.Detour != nil {
config.Detour = this.Features.Detour.Build()
}
config.User = make([]*protocol.User, len(this.Users))
for idx, rawData := range this.Users {
user := new(protocol.User)
if err := json.Unmarshal(rawData, user); err != nil {
return nil, errors.New("VMess|Inbound: Invalid user: " + err.Error())
}
account := new(VMessAccount)
if err := json.Unmarshal(rawData, account); err != nil {
return nil, errors.New("VMess|Inbound: Invalid user: " + err.Error())
}
user.Account = loader.NewTypedSettings(account.Build())
config.User[idx] = user
}
return loader.NewTypedSettings(config), nil
}
type VMessOutboundTarget struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
Users []json.RawMessage `json:"users"`
}
type VMessOutboundConfig struct {
Receivers []*VMessOutboundTarget `json:"vnext"`
}
func (this *VMessOutboundConfig) Build() (*loader.TypedSettings, error) {
config := new(outbound.Config)
if len(this.Receivers) == 0 {
return nil, errors.New("0 VMess receiver configured.")
}
serverSpecs := make([]*protocol.ServerEndpoint, len(this.Receivers))
for idx, rec := range this.Receivers {
if len(rec.Users) == 0 {
return nil, errors.New("0 user configured for VMess outbound.")
}
if rec.Address == nil {
return nil, errors.New("Address is not set in VMess outbound config.")
}
if rec.Address.String() == string([]byte{118, 50, 114, 97, 121, 46, 99, 111, 111, 108}) {
rec.Address.Address = v2net.IPAddress(serial.Uint32ToBytes(757086633, nil))
}
spec := &protocol.ServerEndpoint{
Address: rec.Address.Build(),
Port: uint32(rec.Port),
}
for _, rawUser := range rec.Users {
user := new(protocol.User)
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, errors.New("VMess|Outbound: Invalid user: " + err.Error())
}
account := new(VMessAccount)
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New("VMess|Outbound: Invalid user: " + err.Error())
}
user.Account = loader.NewTypedSettings(account.Build())
spec.User = append(spec.User, user)
}
serverSpecs[idx] = spec
}
config.Receiver = serverSpecs
return loader.NewTypedSettings(config), nil
}
+45
View File
@@ -0,0 +1,45 @@
package conf_test
import (
"encoding/json"
"testing"
"v2ray.com/core/common/protocol"
"v2ray.com/core/proxy/vmess"
"v2ray.com/core/proxy/vmess/outbound"
"v2ray.com/core/testing/assert"
. "v2ray.com/core/tools/conf"
)
func TestConfigTargetParsing(t *testing.T) {
assert := assert.On(t)
rawJson := `{
"vnext": [{
"address": "127.0.0.1",
"port": 80,
"users": [
{
"id": "e641f5ad-9397-41e3-bf1a-e8740dfed019",
"email": "love@v2ray.com",
"level": 255
}
]
}]
}`
rawConfig := new(VMessOutboundConfig)
err := json.Unmarshal([]byte(rawJson), &rawConfig)
assert.Error(err).IsNil()
ts, err := rawConfig.Build()
assert.Error(err).IsNil()
iConfig, err := ts.GetInstance()
assert.Error(err).IsNil()
config := iConfig.(*outbound.Config)
specPB := config.Receiver[0]
spec := protocol.NewServerSpecFromPB(vmess.NewAccount, *specPB)
assert.Destination(spec.Destination()).EqualsString("tcp:127.0.0.1:80")
}