mirror of
https://github.com/v2ray/v2ray-core.git
synced 2026-11-12 22:42:18 +00:00
json config parser
This commit is contained in:
85 files changed
+9357
-9052
No files matched your search
@@ -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",
|
||||
"")
|
||||
)
|
||||
@@ -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()
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"v2ray.com/core/common/loader"
|
||||
)
|
||||
|
||||
type Buildable interface {
|
||||
Build() (*loader.TypedSettings, error)
|
||||
}
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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()
|
||||
})
|
||||
}
|
||||
*/
|
||||
@@ -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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
Reference in new issue
Block a user