mirror of https://github.com/v2ray/v2ray-core
remove json conf from core
parent
a8da85eca5
commit
c287df631d
|
@ -1,51 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"v2ray.com/core/common/serial"
|
|
||||||
"v2ray.com/core/proxy/blackhole"
|
|
||||||
)
|
|
||||||
|
|
||||||
type NoneResponse struct{}
|
|
||||||
|
|
||||||
func (*NoneResponse) Build() (*serial.TypedMessage, error) {
|
|
||||||
return serial.ToTypedMessage(new(blackhole.NoneResponse)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type HttpResponse struct{}
|
|
||||||
|
|
||||||
func (*HttpResponse) Build() (*serial.TypedMessage, error) {
|
|
||||||
return serial.ToTypedMessage(new(blackhole.HTTPResponse)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type BlackholeConfig struct {
|
|
||||||
Response json.RawMessage `json:"response"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *BlackholeConfig) Build() (*serial.TypedMessage, error) {
|
|
||||||
config := new(blackhole.Config)
|
|
||||||
if v.Response != nil {
|
|
||||||
response, _, err := configLoader.Load(v.Response)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("Config: Failed to parse Blackhole response config.").Base(err)
|
|
||||||
}
|
|
||||||
responseSettings, err := response.(Buildable).Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config.Response = responseSettings
|
|
||||||
}
|
|
||||||
|
|
||||||
return serial.ToTypedMessage(config), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
configLoader = NewJSONConfigLoader(
|
|
||||||
ConfigCreatorCache{
|
|
||||||
"none": func() interface{} { return new(NoneResponse) },
|
|
||||||
"http": func() interface{} { return new(HttpResponse) },
|
|
||||||
},
|
|
||||||
"type",
|
|
||||||
"")
|
|
||||||
)
|
|
|
@ -1,34 +0,0 @@
|
||||||
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()
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"v2ray.com/core/common/serial"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Buildable interface {
|
|
||||||
Build() (*serial.TypedMessage, error)
|
|
||||||
}
|
|
|
@ -1,178 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
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 (v StringList) Len() int {
|
|
||||||
return len(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *StringList) UnmarshalJSON(data []byte) error {
|
|
||||||
var strarray []string
|
|
||||||
if err := json.Unmarshal(data, &strarray); err == nil {
|
|
||||||
*v = *NewStringList(strarray)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var rawstr string
|
|
||||||
if err := json.Unmarshal(data, &rawstr); err == nil {
|
|
||||||
strlist := strings.Split(rawstr, ",")
|
|
||||||
*v = *NewStringList(strlist)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return newError("unknown format of a string list: " + string(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
type Address struct {
|
|
||||||
v2net.Address
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Address) UnmarshalJSON(data []byte) error {
|
|
||||||
var rawStr string
|
|
||||||
if err := json.Unmarshal(data, &rawStr); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v.Address = v2net.ParseAddress(rawStr)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Address) Build() *v2net.IPOrDomain {
|
|
||||||
return v2net.NewIPOrDomain(v.Address)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Network string
|
|
||||||
|
|
||||||
func (v Network) Build() v2net.Network {
|
|
||||||
return v2net.ParseNetwork(string(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
type NetworkList []Network
|
|
||||||
|
|
||||||
func (v *NetworkList) UnmarshalJSON(data []byte) error {
|
|
||||||
var strarray []Network
|
|
||||||
if err := json.Unmarshal(data, &strarray); err == nil {
|
|
||||||
nl := NetworkList(strarray)
|
|
||||||
*v = nl
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var rawstr Network
|
|
||||||
if err := json.Unmarshal(data, &rawstr); err == nil {
|
|
||||||
strlist := strings.Split(string(rawstr), ",")
|
|
||||||
nl := make([]Network, len(strlist))
|
|
||||||
for idx, network := range strlist {
|
|
||||||
nl[idx] = Network(network)
|
|
||||||
}
|
|
||||||
*v = nl
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return newError("unknown format of a string list: " + string(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *NetworkList) Build() *v2net.NetworkList {
|
|
||||||
if v == nil {
|
|
||||||
return &v2net.NetworkList{
|
|
||||||
Network: []v2net.Network{v2net.Network_TCP},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
list := new(v2net.NetworkList)
|
|
||||||
for _, network := range *v {
|
|
||||||
list.Network = append(list.Network, network.Build())
|
|
||||||
}
|
|
||||||
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), newError("Config: Invalid port range: ", s)
|
|
||||||
}
|
|
||||||
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 (v *PortRange) Build() *v2net.PortRange {
|
|
||||||
return &v2net.PortRange{
|
|
||||||
From: v.From,
|
|
||||||
To: v.To,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON
|
|
||||||
func (v *PortRange) UnmarshalJSON(data []byte) error {
|
|
||||||
port, err := parseIntPort(data)
|
|
||||||
if err == nil {
|
|
||||||
v.From = uint32(port)
|
|
||||||
v.To = uint32(port)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
from, to, err := parseStringPort(data)
|
|
||||||
if err == nil {
|
|
||||||
v.From = uint32(from)
|
|
||||||
v.To = uint32(to)
|
|
||||||
if v.From > v.To {
|
|
||||||
return newError("invalid port range ", v.From, " -> ", v.To)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return newError("invalid port range: ", string(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
type User struct {
|
|
||||||
EmailString string `json:"email"`
|
|
||||||
LevelByte byte `json:"level"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *User) Build() *protocol.User {
|
|
||||||
return &protocol.User{
|
|
||||||
Email: v.EmailString,
|
|
||||||
Level: uint32(v.LevelByte),
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,185 +0,0 @@
|
||||||
package conf_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
v2net "v2ray.com/core/common/net"
|
|
||||||
"v2ray.com/core/testing/assert"
|
|
||||||
. "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)
|
|
||||||
|
|
||||||
rawJson := "\"8.8.8.8\""
|
|
||||||
var address Address
|
|
||||||
err := json.Unmarshal([]byte(rawJson), &address)
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.Bytes([]byte(address.IP())).Equals([]byte{8, 8, 8, 8})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDomainParsing(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
rawJson := "\"v2ray.com\""
|
|
||||||
var address Address
|
|
||||||
err := json.Unmarshal([]byte(rawJson), &address)
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.String(address.Domain()).Equals("v2ray.com")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInvalidAddressJson(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
rawJson := "1234"
|
|
||||||
var address Address
|
|
||||||
err := json.Unmarshal([]byte(rawJson), &address)
|
|
||||||
assert.Error(err).IsNotNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringNetwork(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
var network Network
|
|
||||||
err := json.Unmarshal([]byte(`"tcp"`), &network)
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.Bool(network.Build() == v2net.Network_TCP).IsTrue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArrayNetworkList(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
var list NetworkList
|
|
||||||
err := json.Unmarshal([]byte("[\"Tcp\"]"), &list)
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
|
|
||||||
nlist := list.Build()
|
|
||||||
assert.Bool(nlist.HasNetwork(v2net.ParseNetwork("tcp"))).IsTrue()
|
|
||||||
assert.Bool(nlist.HasNetwork(v2net.ParseNetwork("udp"))).IsFalse()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringNetworkList(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
var list NetworkList
|
|
||||||
err := json.Unmarshal([]byte("\"TCP, ip\""), &list)
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
|
|
||||||
nlist := list.Build()
|
|
||||||
assert.Bool(nlist.HasNetwork(v2net.ParseNetwork("tcp"))).IsTrue()
|
|
||||||
assert.Bool(nlist.HasNetwork(v2net.ParseNetwork("udp"))).IsFalse()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInvalidNetworkJson(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
var list NetworkList
|
|
||||||
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).IsNotNil()
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte("-1"), &portRange)
|
|
||||||
assert.Error(err).IsNotNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
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).IsNotNil()
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte("\"70000-80000\""), &portRange)
|
|
||||||
assert.Error(err).IsNotNil()
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte("\"1-90000\""), &portRange)
|
|
||||||
assert.Error(err).IsNotNil()
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte("\"700-600\""), &portRange)
|
|
||||||
assert.Error(err).IsNotNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
|
@ -1,3 +1,16 @@
|
||||||
package conf
|
package conf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"v2ray.com/core"
|
||||||
|
jsonconf "v2ray.com/ext/tools/conf/serial"
|
||||||
|
)
|
||||||
|
|
||||||
//go:generate go run $GOPATH/src/v2ray.com/core/tools/generrorgen/main.go -pkg conf -path Tools,Conf
|
//go:generate go run $GOPATH/src/v2ray.com/core/tools/generrorgen/main.go -pkg conf -path Tools,Conf
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
core.RegisterConfigLoader(core.ConfigFormat_JSON, func(input io.Reader) (*core.Config, error) {
|
||||||
|
return jsonconf.LoadJSONConfig(input)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
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 (v *DnsConfig) Build() *dns.Config {
|
|
||||||
config := new(dns.Config)
|
|
||||||
config.NameServers = make([]*v2net.Endpoint, len(v.Servers))
|
|
||||||
for idx, server := range v.Servers {
|
|
||||||
config.NameServers[idx] = &v2net.Endpoint{
|
|
||||||
Network: v2net.Network_UDP,
|
|
||||||
Address: server.Build(),
|
|
||||||
Port: 53,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.Hosts != nil {
|
|
||||||
config.Hosts = make(map[string]*v2net.IPOrDomain)
|
|
||||||
for domain, ip := range v.Hosts {
|
|
||||||
config.Hosts[domain] = ip.Build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return config
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
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))
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"v2ray.com/core/common/serial"
|
|
||||||
"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 (v *DokodemoConfig) Build() (*serial.TypedMessage, error) {
|
|
||||||
config := new(dokodemo.Config)
|
|
||||||
if v.Host != nil {
|
|
||||||
config.Address = v.Host.Build()
|
|
||||||
}
|
|
||||||
config.Port = uint32(v.PortValue)
|
|
||||||
config.NetworkList = v.NetworkList.Build()
|
|
||||||
config.Timeout = v.TimeoutValue
|
|
||||||
config.FollowRedirect = v.Redirect
|
|
||||||
return serial.ToTypedMessage(config), nil
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
v2net "v2ray.com/core/common/net"
|
|
||||||
"v2ray.com/core/common/protocol"
|
|
||||||
"v2ray.com/core/common/serial"
|
|
||||||
"v2ray.com/core/proxy/freedom"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FreedomConfig struct {
|
|
||||||
DomainStrategy string `json:"domainStrategy"`
|
|
||||||
Timeout *uint32 `json:"timeout"`
|
|
||||||
Redirect string `json:"redirect"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *FreedomConfig) Build() (*serial.TypedMessage, error) {
|
|
||||||
config := new(freedom.Config)
|
|
||||||
config.DomainStrategy = freedom.Config_AS_IS
|
|
||||||
domainStrategy := strings.ToLower(v.DomainStrategy)
|
|
||||||
if domainStrategy == "useip" || domainStrategy == "use_ip" {
|
|
||||||
config.DomainStrategy = freedom.Config_USE_IP
|
|
||||||
}
|
|
||||||
config.Timeout = 600
|
|
||||||
if v.Timeout != nil {
|
|
||||||
config.Timeout = *v.Timeout
|
|
||||||
}
|
|
||||||
if len(v.Redirect) > 0 {
|
|
||||||
host, portStr, err := net.SplitHostPort(v.Redirect)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid redirect address: ", v.Redirect, ": ", err).Base(err)
|
|
||||||
}
|
|
||||||
port, err := v2net.PortFromString(portStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid redirect port: ", v.Redirect, ": ", err).Base(err)
|
|
||||||
}
|
|
||||||
if len(host) == 0 {
|
|
||||||
host = "127.0.0.1"
|
|
||||||
}
|
|
||||||
config.DestinationOverride = &freedom.DestinationOverride{
|
|
||||||
Server: &protocol.ServerEndpoint{
|
|
||||||
Address: v2net.NewIPOrDomain(v2net.ParseAddress(host)),
|
|
||||||
Port: uint32(port),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return serial.ToTypedMessage(config), nil
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"v2ray.com/core/common/serial"
|
|
||||||
"v2ray.com/core/proxy/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HttpServerConfig struct {
|
|
||||||
Timeout uint32 `json:"timeout"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *HttpServerConfig) Build() (*serial.TypedMessage, error) {
|
|
||||||
config := &http.ServerConfig{
|
|
||||||
Timeout: v.Timeout,
|
|
||||||
}
|
|
||||||
|
|
||||||
return serial.ToTypedMessage(config), nil
|
|
||||||
}
|
|
|
@ -1,115 +0,0 @@
|
||||||
package json
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// State is the internal state of parser.
|
|
||||||
type State byte
|
|
||||||
|
|
||||||
const (
|
|
||||||
StateContent State = iota
|
|
||||||
StateEscape
|
|
||||||
StateDoubleQuote
|
|
||||||
StateDoubleQuoteEscape
|
|
||||||
StateSingleQuote
|
|
||||||
StateSingleQuoteEscape
|
|
||||||
StateComment
|
|
||||||
StateSlash
|
|
||||||
StateMultilineComment
|
|
||||||
StateMultilineCommentStar
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reader is a JSON reader which allows comments.
|
|
||||||
type Reader struct {
|
|
||||||
io.Reader
|
|
||||||
state State
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read implements io.Reader.Read().
|
|
||||||
func (v *Reader) Read(b []byte) (int, error) {
|
|
||||||
n, err := v.Reader.Read(b)
|
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
p := b[:0]
|
|
||||||
for _, x := range b[:n] {
|
|
||||||
switch v.state {
|
|
||||||
case StateContent:
|
|
||||||
switch x {
|
|
||||||
case '"':
|
|
||||||
v.state = StateDoubleQuote
|
|
||||||
p = append(p, x)
|
|
||||||
case '\'':
|
|
||||||
v.state = StateSingleQuote
|
|
||||||
p = append(p, x)
|
|
||||||
case '\\':
|
|
||||||
v.state = StateEscape
|
|
||||||
case '#':
|
|
||||||
v.state = StateComment
|
|
||||||
case '/':
|
|
||||||
v.state = StateSlash
|
|
||||||
default:
|
|
||||||
p = append(p, x)
|
|
||||||
}
|
|
||||||
case StateEscape:
|
|
||||||
p = append(p, '\\', x)
|
|
||||||
v.state = StateContent
|
|
||||||
case StateDoubleQuote:
|
|
||||||
switch x {
|
|
||||||
case '"':
|
|
||||||
v.state = StateContent
|
|
||||||
p = append(p, x)
|
|
||||||
case '\\':
|
|
||||||
v.state = StateDoubleQuoteEscape
|
|
||||||
default:
|
|
||||||
p = append(p, x)
|
|
||||||
}
|
|
||||||
case StateDoubleQuoteEscape:
|
|
||||||
p = append(p, '\\', x)
|
|
||||||
v.state = StateDoubleQuote
|
|
||||||
case StateSingleQuote:
|
|
||||||
switch x {
|
|
||||||
case '\'':
|
|
||||||
v.state = StateContent
|
|
||||||
p = append(p, x)
|
|
||||||
case '\\':
|
|
||||||
v.state = StateSingleQuoteEscape
|
|
||||||
default:
|
|
||||||
p = append(p, x)
|
|
||||||
}
|
|
||||||
case StateSingleQuoteEscape:
|
|
||||||
p = append(p, '\\', x)
|
|
||||||
v.state = StateSingleQuote
|
|
||||||
case StateComment:
|
|
||||||
if x == '\n' {
|
|
||||||
v.state = StateContent
|
|
||||||
}
|
|
||||||
case StateSlash:
|
|
||||||
switch x {
|
|
||||||
case '/':
|
|
||||||
v.state = StateComment
|
|
||||||
case '*':
|
|
||||||
v.state = StateMultilineComment
|
|
||||||
default:
|
|
||||||
p = append(p, '/', x)
|
|
||||||
}
|
|
||||||
case StateMultilineComment:
|
|
||||||
if x == '*' {
|
|
||||||
v.state = StateMultilineCommentStar
|
|
||||||
}
|
|
||||||
case StateMultilineCommentStar:
|
|
||||||
switch x {
|
|
||||||
case '/':
|
|
||||||
v.state = StateContent
|
|
||||||
case '*':
|
|
||||||
// Stay
|
|
||||||
default:
|
|
||||||
v.state = StateMultilineComment
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("Unknown state.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return len(p), nil
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
package json_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"bytes"
|
|
||||||
|
|
||||||
"v2ray.com/core/testing/assert"
|
|
||||||
. "v2ray.com/core/tools/conf/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestReader(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
data := []struct {
|
|
||||||
input string
|
|
||||||
output string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
`
|
|
||||||
content #comment 1
|
|
||||||
#comment 2
|
|
||||||
content 2`,
|
|
||||||
`
|
|
||||||
content content 2`},
|
|
||||||
{`content`, `content`},
|
|
||||||
{" ", " "},
|
|
||||||
{`con/*abcd*/tent`, "content"},
|
|
||||||
{`
|
|
||||||
text // adlkhdf /*
|
|
||||||
//comment adfkj
|
|
||||||
text 2*/`, `
|
|
||||||
text text 2*`},
|
|
||||||
{`"//"content`, `"//"content`},
|
|
||||||
{`abcd'//'abcd`, `abcd'//'abcd`},
|
|
||||||
{`"\""`, `"\""`},
|
|
||||||
{`\"/*abcd*/\"`, `\"\"`},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, testCase := range data {
|
|
||||||
reader := &Reader{
|
|
||||||
Reader: bytes.NewReader([]byte(testCase.input)),
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := make([]byte, 1024)
|
|
||||||
n, err := reader.Read(actual)
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.String(string(actual[:n])).Equals(testCase.output)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import "encoding/json"
|
|
||||||
|
|
||||||
type ConfigCreator func() interface{}
|
|
||||||
|
|
||||||
type ConfigCreatorCache map[string]ConfigCreator
|
|
||||||
|
|
||||||
func (v ConfigCreatorCache) RegisterCreator(id string, creator ConfigCreator) error {
|
|
||||||
if _, found := v[id]; found {
|
|
||||||
return newError(id, " already registered.").AtError()
|
|
||||||
}
|
|
||||||
|
|
||||||
v[id] = creator
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v ConfigCreatorCache) CreateConfig(id string) (interface{}, error) {
|
|
||||||
creator, found := v[id]
|
|
||||||
if !found {
|
|
||||||
return nil, newError("unknown config id: ", id)
|
|
||||||
}
|
|
||||||
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 (v *JSONConfigLoader) LoadWithID(raw []byte, id string) (interface{}, error) {
|
|
||||||
creator, found := v.cache[id]
|
|
||||||
if !found {
|
|
||||||
return nil, newError("unknown config id: ", id).AtError()
|
|
||||||
}
|
|
||||||
|
|
||||||
config := creator()
|
|
||||||
if err := json.Unmarshal(raw, config); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *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[v.idKey]
|
|
||||||
if !found {
|
|
||||||
return nil, "", newError(v.idKey, " not found in JSON context").AtError()
|
|
||||||
}
|
|
||||||
var id string
|
|
||||||
if err := json.Unmarshal(rawID, &id); err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
rawConfig := json.RawMessage(raw)
|
|
||||||
if len(v.configKey) > 0 {
|
|
||||||
configValue, found := obj[v.configKey]
|
|
||||||
if !found {
|
|
||||||
return nil, "", newError(v.configKey, " not found in JSON content").AtError()
|
|
||||||
}
|
|
||||||
rawConfig = configValue
|
|
||||||
}
|
|
||||||
config, err := v.LoadWithID([]byte(rawConfig), id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, id, err
|
|
||||||
}
|
|
||||||
return config, id, nil
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"v2ray.com/core/app/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LogConfig struct {
|
|
||||||
AccessLog string `json:"access"`
|
|
||||||
ErrorLog string `json:"error"`
|
|
||||||
LogLevel string `json:"loglevel"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *LogConfig) Build() *log.Config {
|
|
||||||
if v == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
config := &log.Config{
|
|
||||||
ErrorLogType: log.LogType_Console,
|
|
||||||
AccessLogType: log.LogType_Console,
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(v.AccessLog) > 0 {
|
|
||||||
config.AccessLogPath = v.AccessLog
|
|
||||||
config.AccessLogType = log.LogType_File
|
|
||||||
}
|
|
||||||
if len(v.ErrorLog) > 0 {
|
|
||||||
config.ErrorLogPath = v.ErrorLog
|
|
||||||
config.ErrorLogType = log.LogType_File
|
|
||||||
}
|
|
||||||
|
|
||||||
level := strings.ToLower(v.LogLevel)
|
|
||||||
switch level {
|
|
||||||
case "debug":
|
|
||||||
config.ErrorLogLevel = log.LogLevel_Debug
|
|
||||||
case "info":
|
|
||||||
config.ErrorLogLevel = log.LogLevel_Info
|
|
||||||
case "error":
|
|
||||||
config.ErrorLogLevel = log.LogLevel_Error
|
|
||||||
case "none":
|
|
||||||
config.ErrorLogType = log.LogType_None
|
|
||||||
config.AccessLogType = log.LogType_None
|
|
||||||
default:
|
|
||||||
config.ErrorLogLevel = log.LogLevel_Warning
|
|
||||||
}
|
|
||||||
return config
|
|
||||||
}
|
|
|
@ -1,241 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"v2ray.com/core/app/log"
|
|
||||||
"v2ray.com/core/app/router"
|
|
||||||
v2net "v2ray.com/core/common/net"
|
|
||||||
"v2ray.com/core/tools/geoip"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RouterRulesConfig struct {
|
|
||||||
RuleList []json.RawMessage `json:"rules"`
|
|
||||||
DomainStrategy string `json:"domainStrategy"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RouterConfig struct {
|
|
||||||
Settings *RouterRulesConfig `json:"settings"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RouterConfig) Build() (*router.Config, error) {
|
|
||||||
if c.Settings == nil {
|
|
||||||
return nil, newError("Router settings is not specified.")
|
|
||||||
}
|
|
||||||
config := new(router.Config)
|
|
||||||
|
|
||||||
settings := c.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, err := ParseRule(rawRule)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config.Rule[idx] = rule
|
|
||||||
}
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type RouterRule struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
OutboundTag string `json:"outboundTag"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseIP(s string) (*router.CIDR, error) {
|
|
||||||
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)
|
|
||||||
switch ip.Family() {
|
|
||||||
case v2net.AddressFamilyIPv4:
|
|
||||||
bits := uint32(32)
|
|
||||||
if len(mask) > 0 {
|
|
||||||
bits64, err := strconv.ParseUint(mask, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid network mask for router: ", mask).Base(err)
|
|
||||||
}
|
|
||||||
bits = uint32(bits64)
|
|
||||||
}
|
|
||||||
if bits > 32 {
|
|
||||||
return nil, newError("invalid network mask for router: ", bits)
|
|
||||||
}
|
|
||||||
return &router.CIDR{
|
|
||||||
Ip: []byte(ip.IP()),
|
|
||||||
Prefix: bits,
|
|
||||||
}, nil
|
|
||||||
case v2net.AddressFamilyIPv6:
|
|
||||||
bits := uint32(128)
|
|
||||||
if len(mask) > 0 {
|
|
||||||
bits64, err := strconv.ParseUint(mask, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid network mask for router: ", mask).Base(err)
|
|
||||||
}
|
|
||||||
bits = uint32(bits64)
|
|
||||||
}
|
|
||||||
if bits > 128 {
|
|
||||||
return nil, newError("invalid network mask for router: ", bits)
|
|
||||||
}
|
|
||||||
return &router.CIDR{
|
|
||||||
Ip: []byte(ip.IP()),
|
|
||||||
Prefix: bits,
|
|
||||||
}, nil
|
|
||||||
default:
|
|
||||||
return nil, newError("unsupported address for router: ", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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"`
|
|
||||||
SourceIP *StringList `json:"source"`
|
|
||||||
User *StringList `json:"user"`
|
|
||||||
InboundTag *StringList `json:"inboundTag"`
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
switch {
|
|
||||||
case strings.HasPrefix(domain, "regexp:"):
|
|
||||||
domainRule.Type = router.Domain_Regex
|
|
||||||
domainRule.Value = domain[7:]
|
|
||||||
case strings.HasPrefix(domain, "domain:"):
|
|
||||||
domainRule.Type = router.Domain_Domain
|
|
||||||
domainRule.Value = domain[7:]
|
|
||||||
default:
|
|
||||||
domainRule.Type = router.Domain_Plain
|
|
||||||
domainRule.Value = domain
|
|
||||||
}
|
|
||||||
rule.Domain = append(rule.Domain, domainRule)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rawFieldRule.IP != nil {
|
|
||||||
for _, ip := range *rawFieldRule.IP {
|
|
||||||
ipRule, err := parseIP(ip)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid IP: ", ip).Base(err)
|
|
||||||
}
|
|
||||||
rule.Cidr = append(rule.Cidr, ipRule)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rawFieldRule.Port != nil {
|
|
||||||
rule.PortRange = rawFieldRule.Port.Build()
|
|
||||||
}
|
|
||||||
|
|
||||||
if rawFieldRule.Network != nil {
|
|
||||||
rule.NetworkList = rawFieldRule.Network.Build()
|
|
||||||
}
|
|
||||||
|
|
||||||
if rawFieldRule.SourceIP != nil {
|
|
||||||
for _, ip := range *rawFieldRule.SourceIP {
|
|
||||||
ipRule, err := parseIP(ip)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid IP: ", ip).Base(err)
|
|
||||||
}
|
|
||||||
rule.SourceCidr = append(rule.SourceCidr, ipRule)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rawFieldRule.User != nil {
|
|
||||||
for _, s := range *rawFieldRule.User {
|
|
||||||
rule.UserEmail = append(rule.UserEmail, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rawFieldRule.InboundTag != nil {
|
|
||||||
for _, s := range *rawFieldRule.InboundTag {
|
|
||||||
rule.InboundTag = append(rule.InboundTag, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rule, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseRule(msg json.RawMessage) (*router.RoutingRule, error) {
|
|
||||||
rawRule := new(RouterRule)
|
|
||||||
err := json.Unmarshal(msg, rawRule)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid router rule").Base(err)
|
|
||||||
}
|
|
||||||
if rawRule.Type == "field" {
|
|
||||||
fieldrule, err := parseFieldRule(msg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid field rule").Base(err)
|
|
||||||
}
|
|
||||||
return fieldrule, nil
|
|
||||||
}
|
|
||||||
if rawRule.Type == "chinaip" {
|
|
||||||
chinaiprule, err := parseChinaIPRule(msg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid chinaip rule").Base(err)
|
|
||||||
}
|
|
||||||
return chinaiprule, nil
|
|
||||||
}
|
|
||||||
if rawRule.Type == "chinasites" {
|
|
||||||
chinasitesrule, err := parseChinaSitesRule(msg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid chinasites rule").Base(err)
|
|
||||||
}
|
|
||||||
return chinasitesrule, nil
|
|
||||||
}
|
|
||||||
return nil, newError("unknown router rule type: ", rawRule.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseChinaIPRule(data []byte) (*router.RoutingRule, error) {
|
|
||||||
rawRule := new(RouterRule)
|
|
||||||
err := json.Unmarshal(data, rawRule)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid router rule").Base(err)
|
|
||||||
}
|
|
||||||
var chinaIPs geoip.CountryIPRange
|
|
||||||
if err := proto.Unmarshal(geoip.ChinaIPs, &chinaIPs); err != nil {
|
|
||||||
return nil, newError("invalid china ips").Base(err)
|
|
||||||
}
|
|
||||||
return &router.RoutingRule{
|
|
||||||
Tag: rawRule.OutboundTag,
|
|
||||||
Cidr: chinaIPs.Ips,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseChinaSitesRule(data []byte) (*router.RoutingRule, error) {
|
|
||||||
rawRule := new(RouterRule)
|
|
||||||
err := json.Unmarshal(data, rawRule)
|
|
||||||
if err != nil {
|
|
||||||
log.Trace(newError("invalid router rule: ", err).AtError())
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &router.RoutingRule{
|
|
||||||
Tag: rawRule.OutboundTag,
|
|
||||||
Domain: chinaSitesDomains,
|
|
||||||
}, nil
|
|
||||||
}
|
|
|
@ -1,483 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"v2ray.com/core/app/router"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
chinaSitesDomains []*router.Domain
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
domains := []string{
|
|
||||||
"cn",
|
|
||||||
"xn--fiqs8s", /* .中国 */
|
|
||||||
|
|
||||||
"10010.com",
|
|
||||||
"100offer.com",
|
|
||||||
"115.com",
|
|
||||||
"123juzi.com",
|
|
||||||
"123juzi.net",
|
|
||||||
"123u.com",
|
|
||||||
"126.com",
|
|
||||||
"126.net",
|
|
||||||
"127.net",
|
|
||||||
"163.com",
|
|
||||||
"17173.com",
|
|
||||||
"17cdn.com",
|
|
||||||
"188.com",
|
|
||||||
"1905.com",
|
|
||||||
"21cn.com",
|
|
||||||
"2288.org",
|
|
||||||
"2345.com",
|
|
||||||
"263.net",
|
|
||||||
"2cto.com",
|
|
||||||
"3322.org",
|
|
||||||
"35.com",
|
|
||||||
"360doc.com",
|
|
||||||
"360buy.com",
|
|
||||||
"360buyimg.com",
|
|
||||||
"360safe.com",
|
|
||||||
"36kr.com",
|
|
||||||
"39.net",
|
|
||||||
"3dmgame.com",
|
|
||||||
"3conline.com",
|
|
||||||
"4399.com",
|
|
||||||
"500d.me",
|
|
||||||
"50bang.org",
|
|
||||||
"51.la",
|
|
||||||
"51credit.com",
|
|
||||||
"51cto.com",
|
|
||||||
"51jingying.com",
|
|
||||||
"51job.com",
|
|
||||||
"51jobcdn.com",
|
|
||||||
"51wendang.com",
|
|
||||||
"55.com",
|
|
||||||
"51yes.com",
|
|
||||||
"55bbs.com",
|
|
||||||
"58.com",
|
|
||||||
"6rooms.com",
|
|
||||||
"71.am",
|
|
||||||
"7k7k.com",
|
|
||||||
"900.la",
|
|
||||||
"9718.com",
|
|
||||||
"9xu.com",
|
|
||||||
"abchina.com",
|
|
||||||
"acfun.tv",
|
|
||||||
"acgvideo.com",
|
|
||||||
"agrantsem.com",
|
|
||||||
"aicdn.com",
|
|
||||||
"aixifan.com",
|
|
||||||
"alibaba.com",
|
|
||||||
"alicdn.com",
|
|
||||||
"aliimg.com.com",
|
|
||||||
"alipay.com",
|
|
||||||
"alipayobjects.com",
|
|
||||||
"aliyun.com",
|
|
||||||
"aliyuncdn.com",
|
|
||||||
"aliyuncs.com",
|
|
||||||
"allyes.com",
|
|
||||||
"amap.com",
|
|
||||||
"anjuke.com",
|
|
||||||
"anquan.org",
|
|
||||||
"appinn.com",
|
|
||||||
"babytree.com",
|
|
||||||
"babytreeimg.com",
|
|
||||||
"baidu.com",
|
|
||||||
"baiducontent.com",
|
|
||||||
"baidupcs.com",
|
|
||||||
"baidustatic.com",
|
|
||||||
"baifendian.com",
|
|
||||||
"baifubao.com",
|
|
||||||
"baihe.com",
|
|
||||||
"baike.com",
|
|
||||||
"baixing.com",
|
|
||||||
"baixing.net",
|
|
||||||
"bankcomm.com",
|
|
||||||
"bankofchina.com",
|
|
||||||
"bcy.net",
|
|
||||||
"bdimg.com",
|
|
||||||
"bdstatic.com",
|
|
||||||
"bilibili.com",
|
|
||||||
"cn.bing.com",
|
|
||||||
"bitauto.com",
|
|
||||||
"bitautoimg.com",
|
|
||||||
"bobo.com",
|
|
||||||
"bootcss.com",
|
|
||||||
"btcfans.com",
|
|
||||||
"caiyunapp.com",
|
|
||||||
"ccb.com",
|
|
||||||
"cctv.com",
|
|
||||||
"cctvpic.com",
|
|
||||||
"cdn20.com",
|
|
||||||
"cebbank.com",
|
|
||||||
"ch.com",
|
|
||||||
"chashebao.com",
|
|
||||||
"che168.com",
|
|
||||||
"china.com",
|
|
||||||
"chinacache.com",
|
|
||||||
"chinacache.net",
|
|
||||||
"chinahr.com",
|
|
||||||
"chinamobile.com",
|
|
||||||
"chinapay.com",
|
|
||||||
"chinatranslation.net",
|
|
||||||
"chinaz.com",
|
|
||||||
"chiphell.com",
|
|
||||||
"chouti.com",
|
|
||||||
"chuangxin.com",
|
|
||||||
"chuansong.me",
|
|
||||||
"clouddn.com",
|
|
||||||
"cloudxns.com",
|
|
||||||
"cmbchina.com",
|
|
||||||
"cnbeta.com",
|
|
||||||
"cnbetacdn.com",
|
|
||||||
"cnblogs.com",
|
|
||||||
"cnepub.com",
|
|
||||||
"cnzz.com",
|
|
||||||
"coding.net",
|
|
||||||
"coolapk.com",
|
|
||||||
"cqvip.com",
|
|
||||||
"csbew.com",
|
|
||||||
"csdn.net",
|
|
||||||
"ctrip.com",
|
|
||||||
"cubead.com",
|
|
||||||
"dajie.com",
|
|
||||||
"dajieimg.com",
|
|
||||||
"dangdang.com",
|
|
||||||
"daocloud.io",
|
|
||||||
"daovoice.io",
|
|
||||||
"dbank.com",
|
|
||||||
"dedecms.com",
|
|
||||||
"dgtle.com",
|
|
||||||
"diandian.com",
|
|
||||||
"dianping.com",
|
|
||||||
"diopic.net",
|
|
||||||
"docin.com",
|
|
||||||
"dockerone.com",
|
|
||||||
"dockone.io",
|
|
||||||
"donews.com",
|
|
||||||
"douban.com",
|
|
||||||
"doubanio.com",
|
|
||||||
"dpfile.com",
|
|
||||||
"duomai.com",
|
|
||||||
"duoshuo.com",
|
|
||||||
"duowan.com",
|
|
||||||
"dxpmedia.com",
|
|
||||||
"eastday.com",
|
|
||||||
"ecitic.com",
|
|
||||||
"emarbox.com",
|
|
||||||
"eoeandroid.com",
|
|
||||||
"etao.com",
|
|
||||||
"excelhome.net",
|
|
||||||
"fanli.com",
|
|
||||||
"feng.com",
|
|
||||||
"fengniao.com",
|
|
||||||
"fhldns.com",
|
|
||||||
"foxmail.com",
|
|
||||||
"geekpark.net",
|
|
||||||
"geetest.com",
|
|
||||||
"geilicdn.com",
|
|
||||||
"getui.com",
|
|
||||||
"google-analytics.com",
|
|
||||||
"growingio.com",
|
|
||||||
"gtags.net",
|
|
||||||
"gwdang.com",
|
|
||||||
"hao123.com",
|
|
||||||
"hao123img.com",
|
|
||||||
"haosou.com",
|
|
||||||
"hdslb.com",
|
|
||||||
"henha.com",
|
|
||||||
"henkuai.com",
|
|
||||||
"hexun.com",
|
|
||||||
"hichina.com",
|
|
||||||
"huanqiu.com",
|
|
||||||
"hunantv.com",
|
|
||||||
"huochepiao.com",
|
|
||||||
"hupu.com",
|
|
||||||
"hupucdn.com",
|
|
||||||
"huxiu.com",
|
|
||||||
"iask.com",
|
|
||||||
"iciba.com",
|
|
||||||
"idqqimg.com",
|
|
||||||
"ifanr.com",
|
|
||||||
"ifanrusercontent.com",
|
|
||||||
"ifanrx.com",
|
|
||||||
"ifeng.com",
|
|
||||||
"ifengimg.com",
|
|
||||||
"ijinshan.com",
|
|
||||||
"ikafan.com",
|
|
||||||
"imedao.com",
|
|
||||||
"imgo.tv",
|
|
||||||
"imooc.com",
|
|
||||||
"infoq.com",
|
|
||||||
"infoqstatic.com",
|
|
||||||
"ip138.com",
|
|
||||||
"ipinyou.com",
|
|
||||||
"ipip.net",
|
|
||||||
"ip-cdn.com",
|
|
||||||
"iqiyi.com",
|
|
||||||
"it165.net",
|
|
||||||
"it168.com",
|
|
||||||
"it610.com",
|
|
||||||
"iteye.com",
|
|
||||||
"ithome.com",
|
|
||||||
"itjuzi.com",
|
|
||||||
"jandan.net",
|
|
||||||
"jd.com",
|
|
||||||
"jb51.com",
|
|
||||||
"jia.com",
|
|
||||||
"jianshu.com",
|
|
||||||
"jianshu.io",
|
|
||||||
"jiasuhui.com",
|
|
||||||
"jiathis.com",
|
|
||||||
"jiayuan.com",
|
|
||||||
"jikexueyuan.com",
|
|
||||||
"jisuanke.com",
|
|
||||||
"jmstatic.com",
|
|
||||||
"jsdelivr.net",
|
|
||||||
"jstv.com",
|
|
||||||
"jumei.com",
|
|
||||||
"jyimg.com",
|
|
||||||
"kaixin001.com",
|
|
||||||
"kanimg.com",
|
|
||||||
"kankanews.com",
|
|
||||||
"kejet.net",
|
|
||||||
"kf5.com",
|
|
||||||
"kimiss.com",
|
|
||||||
"kouclo.com",
|
|
||||||
"koudai.com",
|
|
||||||
"koudai8.com",
|
|
||||||
"ku6.com",
|
|
||||||
"ku6cdn.com",
|
|
||||||
"ku6img.com",
|
|
||||||
"kuqin.com",
|
|
||||||
"lady8844.com",
|
|
||||||
"lagou.com",
|
|
||||||
"le.com",
|
|
||||||
"leanote.com",
|
|
||||||
"leiphone.com",
|
|
||||||
"leju.com",
|
|
||||||
"leturich.org",
|
|
||||||
"letv.com",
|
|
||||||
"letvcdn.com",
|
|
||||||
"letvimg.com",
|
|
||||||
"liantu.me",
|
|
||||||
"liaoxuefeng.com",
|
|
||||||
"liba.com",
|
|
||||||
"libaclub.com",
|
|
||||||
"liepin.com",
|
|
||||||
"lietou.com",
|
|
||||||
"lightonus.com",
|
|
||||||
"linkvans.com",
|
|
||||||
"linuxidc.com",
|
|
||||||
"liuxiaoer.com",
|
|
||||||
"lofter.com",
|
|
||||||
"lu.com",
|
|
||||||
"lufax.com",
|
|
||||||
"lufaxcdn.com",
|
|
||||||
"lvmama.com",
|
|
||||||
"lxdns.com",
|
|
||||||
"lxway.com",
|
|
||||||
"ly.com",
|
|
||||||
"mayihr.com",
|
|
||||||
"mechina.org",
|
|
||||||
"mediav.com",
|
|
||||||
"meiqia.com",
|
|
||||||
"meika360.com",
|
|
||||||
"meilishuo.com",
|
|
||||||
"meishij.net",
|
|
||||||
"meituan.com",
|
|
||||||
"meizu.com",
|
|
||||||
"mgtv.com",
|
|
||||||
"mi.com",
|
|
||||||
"miaopai.com",
|
|
||||||
"miaozhen.com",
|
|
||||||
"miui.com",
|
|
||||||
"mmbang.com",
|
|
||||||
"mmbang.info",
|
|
||||||
"mmstat.com",
|
|
||||||
"mogucdn.com",
|
|
||||||
"mogujie.com",
|
|
||||||
"mop.com",
|
|
||||||
"mscbsc.com",
|
|
||||||
"mukewang.com",
|
|
||||||
"mydrivers.com",
|
|
||||||
"myshow360.net",
|
|
||||||
"mzstatic.com",
|
|
||||||
"netease.com",
|
|
||||||
"newbandeng.com",
|
|
||||||
"ngacn.cc",
|
|
||||||
"ntalker.com",
|
|
||||||
"nvsheng.com",
|
|
||||||
"oeeee.com",
|
|
||||||
"ol-img.com",
|
|
||||||
"oneapm.com",
|
|
||||||
"onlinedown.net",
|
|
||||||
"onlinesjtu.com",
|
|
||||||
"oschina.net",
|
|
||||||
"paipai.com",
|
|
||||||
"pcbeta.com",
|
|
||||||
"pchome.net",
|
|
||||||
"pingan.com",
|
|
||||||
"pingplusplus.com",
|
|
||||||
"pps.tv",
|
|
||||||
"psbc.com",
|
|
||||||
"pubyun.com",
|
|
||||||
"qbox.me",
|
|
||||||
"qcloud.com",
|
|
||||||
"qhimg.com",
|
|
||||||
"qiaobutang.com",
|
|
||||||
"qidian.com",
|
|
||||||
"qingcloud.com",
|
|
||||||
"qingsongchou.com",
|
|
||||||
"qiniu.com",
|
|
||||||
"qiniucdn.com",
|
|
||||||
"qiniudn.com",
|
|
||||||
"qiniudns.com",
|
|
||||||
"qiyi.com",
|
|
||||||
"qiyipic.com",
|
|
||||||
"qtmojo.com",
|
|
||||||
"qq.com",
|
|
||||||
"qqmail.com",
|
|
||||||
"qunar.com",
|
|
||||||
"qunarzz.com",
|
|
||||||
"qzone.com",
|
|
||||||
"renren.com",
|
|
||||||
"runoob.com",
|
|
||||||
"ruanmei.com",
|
|
||||||
"ruby-china.org",
|
|
||||||
"sandai.net",
|
|
||||||
"sanguosha.com",
|
|
||||||
"sanwen.net",
|
|
||||||
"segmentfault.com",
|
|
||||||
"sf-express.com",
|
|
||||||
"sharejs.com",
|
|
||||||
"shmetro.com",
|
|
||||||
"shutcm.com",
|
|
||||||
"simei8.com",
|
|
||||||
"sina.com",
|
|
||||||
"sinaapp.com",
|
|
||||||
"sinaedge.com",
|
|
||||||
"sinaimg.com",
|
|
||||||
"sinajs.com",
|
|
||||||
"szzfgjj.com",
|
|
||||||
"smzdm.com",
|
|
||||||
"sohu.com",
|
|
||||||
"sogou.com",
|
|
||||||
"sogoucdn.com",
|
|
||||||
"soso.com",
|
|
||||||
"sspai.com",
|
|
||||||
"starbaby.cc",
|
|
||||||
"starbaby.com",
|
|
||||||
"staticfile.org",
|
|
||||||
"stockstar.com",
|
|
||||||
"suning.com",
|
|
||||||
"szfw.org",
|
|
||||||
"t1y5.com",
|
|
||||||
"tanx.com",
|
|
||||||
"tao123.com",
|
|
||||||
"taobao.com",
|
|
||||||
"taobaocdn.com",
|
|
||||||
"tbcache.com",
|
|
||||||
"tencent.com",
|
|
||||||
"tenpay.com",
|
|
||||||
"tenxcloud.com",
|
|
||||||
"tiebaimg.com",
|
|
||||||
"tietuku.com",
|
|
||||||
"tiexue.net",
|
|
||||||
"tmall.com",
|
|
||||||
"tmcdn.net",
|
|
||||||
"topthink.com",
|
|
||||||
"tudou.com",
|
|
||||||
"tudouui.com",
|
|
||||||
"tuicool.com",
|
|
||||||
"tuniu.com",
|
|
||||||
"tutuapp.com",
|
|
||||||
"u17.com",
|
|
||||||
"useso.com",
|
|
||||||
"unionpay.com",
|
|
||||||
"unionpaysecure.com",
|
|
||||||
"upyun.com",
|
|
||||||
"upaiyun.com",
|
|
||||||
"v2ex.com",
|
|
||||||
"v5875.com",
|
|
||||||
"vamaker.com",
|
|
||||||
"vancl.com",
|
|
||||||
"vcimg.com",
|
|
||||||
"vip.com",
|
|
||||||
"wallstreetcn.com",
|
|
||||||
"wandoujia.com",
|
|
||||||
"wdjimg.com",
|
|
||||||
"weand.com",
|
|
||||||
"webterren.com",
|
|
||||||
"weibo.com",
|
|
||||||
"weicaifu.com",
|
|
||||||
"weidian.com",
|
|
||||||
"weiphone.com",
|
|
||||||
"weiphone.net",
|
|
||||||
"weixing.com",
|
|
||||||
"weiyun.com",
|
|
||||||
"wonnder.com",
|
|
||||||
"worktile.com",
|
|
||||||
"wooyun.org",
|
|
||||||
"wrating.com",
|
|
||||||
"wscdns.com",
|
|
||||||
"wumii.com",
|
|
||||||
"xiachufang.com",
|
|
||||||
"xiami.com",
|
|
||||||
"xiaokaxiu.com",
|
|
||||||
"xiaomi.com",
|
|
||||||
"xitu.com",
|
|
||||||
"xinhuanet.com",
|
|
||||||
"xinshipu.com",
|
|
||||||
"xiu8.com",
|
|
||||||
"xnpic.com",
|
|
||||||
"xueqiu.com",
|
|
||||||
"xunlei.com",
|
|
||||||
"xywy.com",
|
|
||||||
"yaolan.com",
|
|
||||||
"yccdn.com",
|
|
||||||
"yeepay.com",
|
|
||||||
"yesky.com",
|
|
||||||
"yigao.com",
|
|
||||||
"yihaodian.com",
|
|
||||||
"yihaodianimg.com",
|
|
||||||
"yingjiesheng.com",
|
|
||||||
"yinxiang.com",
|
|
||||||
"yixi.tv",
|
|
||||||
"yjbys.com",
|
|
||||||
"yhd.com",
|
|
||||||
"youboy.com",
|
|
||||||
"youku.com",
|
|
||||||
"yunba.io",
|
|
||||||
"yundaex.com",
|
|
||||||
"yunshipei.com",
|
|
||||||
"yupoo.com",
|
|
||||||
"yuzua.com",
|
|
||||||
"yy.com",
|
|
||||||
"yytcdn.com",
|
|
||||||
"zampda.net",
|
|
||||||
"zastatic.com",
|
|
||||||
"zbjimg.com",
|
|
||||||
"zdfans.com",
|
|
||||||
"zhenai.com",
|
|
||||||
"zhanqi.tv",
|
|
||||||
"zhaopin.com",
|
|
||||||
"zhihu.com",
|
|
||||||
"zhimg.com",
|
|
||||||
"zhiziyun.com",
|
|
||||||
"zjstv.com",
|
|
||||||
"zhubajie.com",
|
|
||||||
"zrblog.net",
|
|
||||||
"zuche.com",
|
|
||||||
"zuchecdn.com",
|
|
||||||
}
|
|
||||||
|
|
||||||
chinaSitesDomains = make([]*router.Domain, len(domains))
|
|
||||||
for idx, pattern := range domains {
|
|
||||||
chinaSitesDomains[idx] = &router.Domain{
|
|
||||||
Type: router.Domain_Domain,
|
|
||||||
Value: pattern,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,125 +0,0 @@
|
||||||
package conf_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
v2net "v2ray.com/core/common/net"
|
|
||||||
"v2ray.com/core/proxy"
|
|
||||||
"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, err := ParseRule([]byte(`{
|
|
||||||
"type": "chinaip",
|
|
||||||
"outboundTag": "x"
|
|
||||||
}`))
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.String(rule.Tag).Equals("x")
|
|
||||||
cond, err := rule.BuildCondition()
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.ParseAddress("121.14.1.189"), 80)))).IsTrue() // sina.com.cn
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.ParseAddress("101.226.103.106"), 80)))).IsTrue() // qq.com
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.ParseAddress("115.239.210.36"), 80)))).IsTrue() // image.baidu.com
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.ParseAddress("120.135.126.1"), 80)))).IsTrue()
|
|
||||||
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.ParseAddress("8.8.8.8"), 80)))).IsFalse()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChinaSitesJson(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
rule, err := ParseRule([]byte(`{
|
|
||||||
"type": "chinasites",
|
|
||||||
"outboundTag": "y"
|
|
||||||
}`))
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.String(rule.Tag).Equals("y")
|
|
||||||
cond, err := rule.BuildCondition()
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.ParseAddress("v.qq.com"), 80)))).IsTrue()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.ParseAddress("www.163.com"), 80)))).IsTrue()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.ParseAddress("ngacn.cc"), 80)))).IsTrue()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.ParseAddress("12306.cn"), 80)))).IsTrue()
|
|
||||||
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.ParseAddress("v2ray.com"), 80)))).IsFalse()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDomainRule(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
rule, err := ParseRule([]byte(`{
|
|
||||||
"type": "field",
|
|
||||||
"domain": [
|
|
||||||
"ooxx.com",
|
|
||||||
"oxox.com",
|
|
||||||
"regexp:\\.cn$"
|
|
||||||
],
|
|
||||||
"network": "tcp",
|
|
||||||
"outboundTag": "direct"
|
|
||||||
}`))
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.Pointer(rule).IsNotNil()
|
|
||||||
cond, err := rule.BuildCondition()
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.ParseAddress("www.ooxx.com"), 80)))).IsTrue()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.ParseAddress("www.aabb.com"), 80)))).IsFalse()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80)))).IsFalse()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.ParseAddress("www.12306.cn"), 80)))).IsTrue()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.ParseAddress("www.acn.com"), 80)))).IsFalse()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIPRule(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
rule, err := ParseRule([]byte(`{
|
|
||||||
"type": "field",
|
|
||||||
"ip": [
|
|
||||||
"10.0.0.0/8",
|
|
||||||
"192.0.0.0/24"
|
|
||||||
],
|
|
||||||
"network": "tcp",
|
|
||||||
"outboundTag": "direct"
|
|
||||||
}`))
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.Pointer(rule).IsNotNil()
|
|
||||||
cond, err := rule.BuildCondition()
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80)))).IsFalse()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.IPAddress([]byte{10, 0, 0, 1}), 80)))).IsTrue()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80)))).IsFalse()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithTarget(context.Background(), v2net.TCPDestination(v2net.IPAddress([]byte{192, 0, 0, 1}), 80)))).IsTrue()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSourceIPRule(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
rule, err := ParseRule([]byte(`{
|
|
||||||
"type": "field",
|
|
||||||
"source": [
|
|
||||||
"10.0.0.0/8",
|
|
||||||
"192.0.0.0/24"
|
|
||||||
],
|
|
||||||
"outboundTag": "direct"
|
|
||||||
}`))
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.Pointer(rule).IsNotNil()
|
|
||||||
cond, err := rule.BuildCondition()
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithSource(context.Background(), v2net.TCPDestination(v2net.DomainAddress("www.ooxx.com"), 80)))).IsFalse()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithSource(context.Background(), v2net.TCPDestination(v2net.IPAddress([]byte{10, 0, 0, 1}), 80)))).IsTrue()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithSource(context.Background(), v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 80)))).IsFalse()
|
|
||||||
assert.Bool(cond.Apply(proxy.ContextWithSource(context.Background(), v2net.TCPDestination(v2net.IPAddress([]byte{192, 0, 0, 1}), 80)))).IsTrue()
|
|
||||||
}
|
|
|
@ -1,130 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"v2ray.com/core/common/protocol"
|
|
||||||
"v2ray.com/core/common/serial"
|
|
||||||
"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"`
|
|
||||||
OTA *bool `json:"ota"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *ShadowsocksServerConfig) Build() (*serial.TypedMessage, error) {
|
|
||||||
config := new(shadowsocks.ServerConfig)
|
|
||||||
config.UdpEnabled = v.UDP
|
|
||||||
|
|
||||||
if len(v.Password) == 0 {
|
|
||||||
return nil, newError("Shadowsocks password is not specified.")
|
|
||||||
}
|
|
||||||
account := &shadowsocks.Account{
|
|
||||||
Password: v.Password,
|
|
||||||
Ota: shadowsocks.Account_Auto,
|
|
||||||
}
|
|
||||||
if v.OTA != nil {
|
|
||||||
if *v.OTA {
|
|
||||||
account.Ota = shadowsocks.Account_Enabled
|
|
||||||
} else {
|
|
||||||
account.Ota = shadowsocks.Account_Disabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cipher := strings.ToLower(v.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_IETF
|
|
||||||
default:
|
|
||||||
return nil, newError("unknown cipher method: " + cipher)
|
|
||||||
}
|
|
||||||
|
|
||||||
config.User = &protocol.User{
|
|
||||||
Email: v.Email,
|
|
||||||
Level: uint32(v.Level),
|
|
||||||
Account: serial.ToTypedMessage(account),
|
|
||||||
}
|
|
||||||
|
|
||||||
return serial.ToTypedMessage(config), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ShadowsocksServerTarget struct {
|
|
||||||
Address *Address `json:"address"`
|
|
||||||
Port uint16 `json:"port"`
|
|
||||||
Cipher string `json:"method"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
Ota bool `json:"ota"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ShadowsocksClientConfig struct {
|
|
||||||
Servers []*ShadowsocksServerTarget `json:"servers"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *ShadowsocksClientConfig) Build() (*serial.TypedMessage, error) {
|
|
||||||
config := new(shadowsocks.ClientConfig)
|
|
||||||
|
|
||||||
if len(v.Servers) == 0 {
|
|
||||||
return nil, newError("0 Shadowsocks server configured.")
|
|
||||||
}
|
|
||||||
|
|
||||||
serverSpecs := make([]*protocol.ServerEndpoint, len(v.Servers))
|
|
||||||
for idx, server := range v.Servers {
|
|
||||||
if server.Address == nil {
|
|
||||||
return nil, newError("Shadowsocks server address is not set.")
|
|
||||||
}
|
|
||||||
if server.Port == 0 {
|
|
||||||
return nil, newError("Invalid Shadowsocks port.")
|
|
||||||
}
|
|
||||||
if len(server.Password) == 0 {
|
|
||||||
return nil, newError("Shadowsocks password is not specified.")
|
|
||||||
}
|
|
||||||
account := &shadowsocks.Account{
|
|
||||||
Password: server.Password,
|
|
||||||
Ota: shadowsocks.Account_Enabled,
|
|
||||||
}
|
|
||||||
if !server.Ota {
|
|
||||||
account.Ota = shadowsocks.Account_Disabled
|
|
||||||
}
|
|
||||||
cipher := strings.ToLower(server.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_IETF
|
|
||||||
default:
|
|
||||||
return nil, newError("unknown cipher method: " + cipher)
|
|
||||||
}
|
|
||||||
|
|
||||||
ss := &protocol.ServerEndpoint{
|
|
||||||
Address: server.Address.Build(),
|
|
||||||
Port: uint32(server.Port),
|
|
||||||
User: []*protocol.User{
|
|
||||||
{
|
|
||||||
Email: server.Email,
|
|
||||||
Account: serial.ToTypedMessage(account),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
serverSpecs[idx] = ss
|
|
||||||
}
|
|
||||||
|
|
||||||
config.Server = serverSpecs
|
|
||||||
|
|
||||||
return serial.ToTypedMessage(config), nil
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
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.ShadowsocksAccount)
|
|
||||||
assert.Bool(ok).IsTrue()
|
|
||||||
|
|
||||||
assert.Int(account.Cipher.KeySize()).Equals(16)
|
|
||||||
assert.Bytes(account.Key).Equals([]byte{160, 224, 26, 2, 22, 110, 9, 80, 65, 52, 80, 20, 38, 243, 224, 241})
|
|
||||||
}
|
|
|
@ -1,94 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"v2ray.com/core/common/protocol"
|
|
||||||
"v2ray.com/core/common/serial"
|
|
||||||
"v2ray.com/core/proxy/socks"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SocksAccount struct {
|
|
||||||
Username string `json:"user"`
|
|
||||||
Password string `json:"pass"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *SocksAccount) Build() *socks.Account {
|
|
||||||
return &socks.Account{
|
|
||||||
Username: v.Username,
|
|
||||||
Password: v.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 (v *SocksServerConfig) Build() (*serial.TypedMessage, error) {
|
|
||||||
config := new(socks.ServerConfig)
|
|
||||||
if v.AuthMethod == AuthMethodNoAuth {
|
|
||||||
config.AuthType = socks.AuthType_NO_AUTH
|
|
||||||
} else if v.AuthMethod == AuthMethodUserPass {
|
|
||||||
config.AuthType = socks.AuthType_PASSWORD
|
|
||||||
} else {
|
|
||||||
return nil, newError("unknown socks auth method: " + v.AuthMethod).AtError()
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(v.Accounts) > 0 {
|
|
||||||
config.Accounts = make(map[string]string, len(v.Accounts))
|
|
||||||
for _, account := range v.Accounts {
|
|
||||||
config.Accounts[account.Username] = account.Password
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config.UdpEnabled = v.UDP
|
|
||||||
if v.Host != nil {
|
|
||||||
config.Address = v.Host.Build()
|
|
||||||
}
|
|
||||||
|
|
||||||
config.Timeout = v.Timeout
|
|
||||||
return serial.ToTypedMessage(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 (v *SocksClientConfig) Build() (*serial.TypedMessage, error) {
|
|
||||||
config := new(socks.ClientConfig)
|
|
||||||
config.Server = make([]*protocol.ServerEndpoint, len(v.Servers))
|
|
||||||
for idx, serverConfig := range v.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, newError("failed to parse Socks user").Base(err).AtError()
|
|
||||||
}
|
|
||||||
account := new(SocksAccount)
|
|
||||||
if err := json.Unmarshal(rawUser, account); err != nil {
|
|
||||||
return nil, newError("failed to parse socks account").Base(err).AtError()
|
|
||||||
}
|
|
||||||
user.Account = serial.ToTypedMessage(account.Build())
|
|
||||||
server.User = append(server.User, user)
|
|
||||||
}
|
|
||||||
config.Server[idx] = server
|
|
||||||
}
|
|
||||||
return serial.ToTypedMessage(config), nil
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
package conf_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"v2ray.com/core/common/net"
|
|
||||||
"v2ray.com/core/common/protocol"
|
|
||||||
"v2ray.com/core/proxy/socks"
|
|
||||||
"v2ray.com/core/testing/assert"
|
|
||||||
. "v2ray.com/core/tools/conf"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSocksInboundConfig(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
rawJson := `{
|
|
||||||
"auth": "password",
|
|
||||||
"accounts": [
|
|
||||||
{
|
|
||||||
"user": "my-username",
|
|
||||||
"pass": "my-password"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"udp": false,
|
|
||||||
"ip": "127.0.0.1",
|
|
||||||
"timeout": 5
|
|
||||||
}`
|
|
||||||
|
|
||||||
config := new(SocksServerConfig)
|
|
||||||
err := json.Unmarshal([]byte(rawJson), &config)
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
|
|
||||||
message, err := config.Build()
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
|
|
||||||
iConfig, err := message.GetInstance()
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
|
|
||||||
socksConfig := iConfig.(*socks.ServerConfig)
|
|
||||||
assert.Bool(socksConfig.AuthType == socks.AuthType_PASSWORD).IsTrue()
|
|
||||||
assert.Int(len(socksConfig.Accounts)).Equals(1)
|
|
||||||
assert.String(socksConfig.Accounts["my-username"]).Equals("my-password")
|
|
||||||
assert.Bool(socksConfig.UdpEnabled).IsFalse()
|
|
||||||
assert.Address(socksConfig.Address.AsAddress()).Equals(net.LocalHostIP)
|
|
||||||
assert.Uint32(socksConfig.Timeout).Equals(5)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSocksOutboundConfig(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
rawJson := `{
|
|
||||||
"servers": [{
|
|
||||||
"address": "127.0.0.1",
|
|
||||||
"port": 1234,
|
|
||||||
"users": [
|
|
||||||
{"user": "test user", "pass": "test pass", "email": "test@email.com"}
|
|
||||||
]
|
|
||||||
}]
|
|
||||||
}`
|
|
||||||
|
|
||||||
config := new(SocksClientConfig)
|
|
||||||
err := json.Unmarshal([]byte(rawJson), &config)
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
|
|
||||||
message, err := config.Build()
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
|
|
||||||
iConfig, err := message.GetInstance()
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
|
|
||||||
socksConfig := iConfig.(*socks.ClientConfig)
|
|
||||||
assert.Int(len(socksConfig.Server)).Equals(1)
|
|
||||||
|
|
||||||
ss := protocol.NewServerSpecFromPB(*socksConfig.Server[0])
|
|
||||||
assert.Destination(ss.Destination()).EqualsString("tcp:127.0.0.1:1234")
|
|
||||||
|
|
||||||
user := ss.PickUser()
|
|
||||||
assert.String(user.Email).Equals("test@email.com")
|
|
||||||
|
|
||||||
account, err := user.GetTypedAccount()
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
|
|
||||||
socksAccount := account.(*socks.Account)
|
|
||||||
assert.String(socksAccount.Username).Equals("test user")
|
|
||||||
assert.String(socksAccount.Password).Equals("test pass")
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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 (v *TransportConfig) Build() (*transport.Config, error) {
|
|
||||||
config := new(transport.Config)
|
|
||||||
|
|
||||||
if v.TCPConfig != nil {
|
|
||||||
ts, err := v.TCPConfig.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to build TCP config").Base(err).AtError()
|
|
||||||
}
|
|
||||||
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
|
|
||||||
Protocol: internet.TransportProtocol_TCP,
|
|
||||||
Settings: ts,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.KCPConfig != nil {
|
|
||||||
ts, err := v.KCPConfig.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to build mKCP config").Base(err).AtError()
|
|
||||||
}
|
|
||||||
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
|
|
||||||
Protocol: internet.TransportProtocol_MKCP,
|
|
||||||
Settings: ts,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.WSConfig != nil {
|
|
||||||
ts, err := v.WSConfig.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to build WebSocket config").Base(err)
|
|
||||||
}
|
|
||||||
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
|
|
||||||
Protocol: internet.TransportProtocol_WebSocket,
|
|
||||||
Settings: ts,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return config, nil
|
|
||||||
}
|
|
|
@ -1,193 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"v2ray.com/core/common/serial"
|
|
||||||
"v2ray.com/core/transport/internet/headers/http"
|
|
||||||
"v2ray.com/core/transport/internet/headers/noop"
|
|
||||||
"v2ray.com/core/transport/internet/headers/srtp"
|
|
||||||
"v2ray.com/core/transport/internet/headers/utp"
|
|
||||||
"v2ray.com/core/transport/internet/headers/wechat"
|
|
||||||
)
|
|
||||||
|
|
||||||
type NoOpAuthenticator struct{}
|
|
||||||
|
|
||||||
func (NoOpAuthenticator) Build() (*serial.TypedMessage, error) {
|
|
||||||
return serial.ToTypedMessage(new(noop.Config)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type NoOpConnectionAuthenticator struct{}
|
|
||||||
|
|
||||||
func (NoOpConnectionAuthenticator) Build() (*serial.TypedMessage, error) {
|
|
||||||
return serial.ToTypedMessage(new(noop.ConnectionConfig)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type SRTPAuthenticator struct{}
|
|
||||||
|
|
||||||
func (SRTPAuthenticator) Build() (*serial.TypedMessage, error) {
|
|
||||||
return serial.ToTypedMessage(new(srtp.Config)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type UTPAuthenticator struct{}
|
|
||||||
|
|
||||||
func (UTPAuthenticator) Build() (*serial.TypedMessage, error) {
|
|
||||||
return serial.ToTypedMessage(new(utp.Config)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type WechatVideoAuthenticator struct{}
|
|
||||||
|
|
||||||
func (WechatVideoAuthenticator) Build() (*serial.TypedMessage, error) {
|
|
||||||
return serial.ToTypedMessage(new(wechat.VideoConfig)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type HTTPAuthenticatorRequest struct {
|
|
||||||
Version string `json:"version"`
|
|
||||||
Method string `json:"method"`
|
|
||||||
Path StringList `json:"path"`
|
|
||||||
Headers map[string]*StringList `json:"headers"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *HTTPAuthenticatorRequest) Build() (*http.RequestConfig, error) {
|
|
||||||
config := &http.RequestConfig{
|
|
||||||
Uri: []string{"/"},
|
|
||||||
Header: []*http.Header{
|
|
||||||
{
|
|
||||||
Name: "Host",
|
|
||||||
Value: []string{"www.baidu.com", "www.bing.com"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "User-Agent",
|
|
||||||
Value: []string{
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Accept-Encoding",
|
|
||||||
Value: []string{"gzip, deflate"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Connection",
|
|
||||||
Value: []string{"keep-alive"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Pragma",
|
|
||||||
Value: []string{"no-cache"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(v.Version) > 0 {
|
|
||||||
config.Version = &http.Version{Value: v.Version}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(v.Method) > 0 {
|
|
||||||
config.Method = &http.Method{Value: v.Method}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(v.Path) > 0 {
|
|
||||||
config.Uri = append([]string(nil), (v.Path)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(v.Headers) > 0 {
|
|
||||||
config.Header = make([]*http.Header, 0, len(v.Headers))
|
|
||||||
for key, value := range v.Headers {
|
|
||||||
if value == nil {
|
|
||||||
return nil, newError("empty HTTP header value: " + key).AtError()
|
|
||||||
}
|
|
||||||
config.Header = append(config.Header, &http.Header{
|
|
||||||
Name: key,
|
|
||||||
Value: append([]string(nil), (*value)...),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type HTTPAuthenticatorResponse struct {
|
|
||||||
Version string `json:"version"`
|
|
||||||
Status string `json:"status"`
|
|
||||||
Reason string `json:"reason"`
|
|
||||||
Headers map[string]*StringList `json:"headers"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *HTTPAuthenticatorResponse) Build() (*http.ResponseConfig, error) {
|
|
||||||
config := &http.ResponseConfig{
|
|
||||||
Header: []*http.Header{
|
|
||||||
{
|
|
||||||
Name: "Content-Type",
|
|
||||||
Value: []string{"application/octet-stream", "video/mpeg"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Transfer-Encoding",
|
|
||||||
Value: []string{"chunked"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Connection",
|
|
||||||
Value: []string{"keep-alive"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Pragma",
|
|
||||||
Value: []string{"no-cache"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Cache-Control",
|
|
||||||
Value: []string{"private", "no-cache"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(v.Version) > 0 {
|
|
||||||
config.Version = &http.Version{Value: v.Version}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(v.Status) > 0 || len(v.Reason) > 0 {
|
|
||||||
config.Status = &http.Status{
|
|
||||||
Code: "200",
|
|
||||||
Reason: "OK",
|
|
||||||
}
|
|
||||||
if len(v.Status) > 0 {
|
|
||||||
config.Status.Code = v.Status
|
|
||||||
}
|
|
||||||
if len(v.Reason) > 0 {
|
|
||||||
config.Status.Reason = v.Reason
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(v.Headers) > 0 {
|
|
||||||
config.Header = make([]*http.Header, 0, len(v.Headers))
|
|
||||||
for key, value := range v.Headers {
|
|
||||||
if value == nil {
|
|
||||||
return nil, newError("empty HTTP header value: " + key).AtError()
|
|
||||||
}
|
|
||||||
config.Header = append(config.Header, &http.Header{
|
|
||||||
Name: key,
|
|
||||||
Value: append([]string(nil), (*value)...),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type HTTPAuthenticator struct {
|
|
||||||
Request HTTPAuthenticatorRequest `json:"request"`
|
|
||||||
Response HTTPAuthenticatorResponse `json:"response"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *HTTPAuthenticator) Build() (*serial.TypedMessage, error) {
|
|
||||||
config := new(http.Config)
|
|
||||||
requestConfig, err := v.Request.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config.Request = requestConfig
|
|
||||||
|
|
||||||
responseConfig, err := v.Response.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config.Response = responseConfig
|
|
||||||
|
|
||||||
return serial.ToTypedMessage(config), nil
|
|
||||||
}
|
|
|
@ -1,255 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"v2ray.com/core/common/serial"
|
|
||||||
"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/websocket"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
kcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
|
||||||
"none": func() interface{} { return new(NoOpAuthenticator) },
|
|
||||||
"srtp": func() interface{} { return new(SRTPAuthenticator) },
|
|
||||||
"utp": func() interface{} { return new(UTPAuthenticator) },
|
|
||||||
"wechat-video": func() interface{} { return new(WechatVideoAuthenticator) },
|
|
||||||
}, "type", "")
|
|
||||||
|
|
||||||
tcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
|
||||||
"none": func() interface{} { return new(NoOpConnectionAuthenticator) },
|
|
||||||
"http": func() interface{} { return new(HTTPAuthenticator) },
|
|
||||||
}, "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 (v *KCPConfig) Build() (*serial.TypedMessage, error) {
|
|
||||||
config := new(kcp.Config)
|
|
||||||
|
|
||||||
if v.Mtu != nil {
|
|
||||||
mtu := *v.Mtu
|
|
||||||
if mtu < 576 || mtu > 1460 {
|
|
||||||
return nil, newError("invalid mKCP MTU size: ", mtu).AtError()
|
|
||||||
}
|
|
||||||
config.Mtu = &kcp.MTU{Value: mtu}
|
|
||||||
}
|
|
||||||
if v.Tti != nil {
|
|
||||||
tti := *v.Tti
|
|
||||||
if tti < 10 || tti > 100 {
|
|
||||||
return nil, newError("invalid mKCP TTI: ", tti).AtError()
|
|
||||||
}
|
|
||||||
config.Tti = &kcp.TTI{Value: tti}
|
|
||||||
}
|
|
||||||
if v.UpCap != nil {
|
|
||||||
config.UplinkCapacity = &kcp.UplinkCapacity{Value: *v.UpCap}
|
|
||||||
}
|
|
||||||
if v.DownCap != nil {
|
|
||||||
config.DownlinkCapacity = &kcp.DownlinkCapacity{Value: *v.DownCap}
|
|
||||||
}
|
|
||||||
if v.Congestion != nil {
|
|
||||||
config.Congestion = *v.Congestion
|
|
||||||
}
|
|
||||||
if v.ReadBufferSize != nil {
|
|
||||||
size := *v.ReadBufferSize
|
|
||||||
if size > 0 {
|
|
||||||
config.ReadBuffer = &kcp.ReadBuffer{Size: size * 1024 * 1024}
|
|
||||||
} else {
|
|
||||||
config.ReadBuffer = &kcp.ReadBuffer{Size: 512 * 1024}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if v.WriteBufferSize != nil {
|
|
||||||
size := *v.WriteBufferSize
|
|
||||||
if size > 0 {
|
|
||||||
config.WriteBuffer = &kcp.WriteBuffer{Size: size * 1024 * 1024}
|
|
||||||
} else {
|
|
||||||
config.WriteBuffer = &kcp.WriteBuffer{Size: 512 * 1024}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(v.HeaderConfig) > 0 {
|
|
||||||
headerConfig, _, err := kcpHeaderLoader.Load(v.HeaderConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid mKCP header config.").Base(err).AtError()
|
|
||||||
}
|
|
||||||
ts, err := headerConfig.(Buildable).Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid mKCP header config").Base(err).AtError()
|
|
||||||
}
|
|
||||||
config.HeaderConfig = ts
|
|
||||||
}
|
|
||||||
|
|
||||||
return serial.ToTypedMessage(config), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type TCPConfig struct {
|
|
||||||
HeaderConfig json.RawMessage `json:"header"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *TCPConfig) Build() (*serial.TypedMessage, error) {
|
|
||||||
config := new(tcp.Config)
|
|
||||||
if len(v.HeaderConfig) > 0 {
|
|
||||||
headerConfig, _, err := tcpHeaderLoader.Load(v.HeaderConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid TCP header config").Base(err).AtError()
|
|
||||||
}
|
|
||||||
ts, err := headerConfig.(Buildable).Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid TCP header config").Base(err).AtError()
|
|
||||||
}
|
|
||||||
config.HeaderSettings = ts
|
|
||||||
}
|
|
||||||
|
|
||||||
return serial.ToTypedMessage(config), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type WebSocketConfig struct {
|
|
||||||
Path string `json:"Path"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *WebSocketConfig) Build() (*serial.TypedMessage, error) {
|
|
||||||
config := &websocket.Config{
|
|
||||||
Path: v.Path,
|
|
||||||
}
|
|
||||||
return serial.ToTypedMessage(config), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type TLSCertConfig struct {
|
|
||||||
CertFile string `json:"certificateFile"`
|
|
||||||
KeyFile string `json:"keyFile"`
|
|
||||||
}
|
|
||||||
type TLSConfig struct {
|
|
||||||
Insecure bool `json:"allowInsecure"`
|
|
||||||
Certs []*TLSCertConfig `json:"certificates"`
|
|
||||||
ServerName string `json:"serverName"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *TLSConfig) Build() (*serial.TypedMessage, error) {
|
|
||||||
config := new(tls.Config)
|
|
||||||
config.Certificate = make([]*tls.Certificate, len(v.Certs))
|
|
||||||
for idx, certConf := range v.Certs {
|
|
||||||
cert, err := ioutil.ReadFile(certConf.CertFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to load TLS certificate file: ", certConf.CertFile).Base(err).AtError()
|
|
||||||
}
|
|
||||||
key, err := ioutil.ReadFile(certConf.KeyFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to load TLS key file: ", certConf.KeyFile).Base(err).AtError()
|
|
||||||
}
|
|
||||||
config.Certificate[idx] = &tls.Certificate{
|
|
||||||
Key: key,
|
|
||||||
Certificate: cert,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config.AllowInsecure = v.Insecure
|
|
||||||
if len(v.ServerName) > 0 {
|
|
||||||
config.ServerName = v.ServerName
|
|
||||||
}
|
|
||||||
return serial.ToTypedMessage(config), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type TransportProtocol string
|
|
||||||
|
|
||||||
func (p TransportProtocol) Build() (internet.TransportProtocol, error) {
|
|
||||||
switch strings.ToLower(string(p)) {
|
|
||||||
case "tcp":
|
|
||||||
return internet.TransportProtocol_TCP, nil
|
|
||||||
case "kcp", "mkcp":
|
|
||||||
return internet.TransportProtocol_MKCP, nil
|
|
||||||
case "ws", "websocket":
|
|
||||||
return internet.TransportProtocol_WebSocket, nil
|
|
||||||
default:
|
|
||||||
return internet.TransportProtocol_TCP, newError("Config: unknown transport protocol: ", p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type StreamConfig struct {
|
|
||||||
Network *TransportProtocol `json:"network"`
|
|
||||||
Security string `json:"security"`
|
|
||||||
TLSSettings *TLSConfig `json:"tlsSettings"`
|
|
||||||
TCPSettings *TCPConfig `json:"tcpSettings"`
|
|
||||||
KCPSettings *KCPConfig `json:"kcpSettings"`
|
|
||||||
WSSettings *WebSocketConfig `json:"wsSettings"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *StreamConfig) Build() (*internet.StreamConfig, error) {
|
|
||||||
config := &internet.StreamConfig{
|
|
||||||
Protocol: internet.TransportProtocol_TCP,
|
|
||||||
}
|
|
||||||
if v.Network != nil {
|
|
||||||
protocol, err := (*v.Network).Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config.Protocol = protocol
|
|
||||||
}
|
|
||||||
if strings.ToLower(v.Security) == "tls" {
|
|
||||||
tlsSettings := v.TLSSettings
|
|
||||||
if tlsSettings == nil {
|
|
||||||
tlsSettings = &TLSConfig{}
|
|
||||||
}
|
|
||||||
ts, err := tlsSettings.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("Failed to build TLS config.").Base(err)
|
|
||||||
}
|
|
||||||
config.SecuritySettings = append(config.SecuritySettings, ts)
|
|
||||||
config.SecurityType = ts.Type
|
|
||||||
}
|
|
||||||
if v.TCPSettings != nil {
|
|
||||||
ts, err := v.TCPSettings.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("Failed to build TCP config.").Base(err)
|
|
||||||
}
|
|
||||||
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
|
|
||||||
Protocol: internet.TransportProtocol_TCP,
|
|
||||||
Settings: ts,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if v.KCPSettings != nil {
|
|
||||||
ts, err := v.KCPSettings.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("Failed to build mKCP config.").Base(err)
|
|
||||||
}
|
|
||||||
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
|
|
||||||
Protocol: internet.TransportProtocol_MKCP,
|
|
||||||
Settings: ts,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if v.WSSettings != nil {
|
|
||||||
ts, err := v.WSSettings.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("Failed to build WebSocket config.").Base(err)
|
|
||||||
}
|
|
||||||
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
|
|
||||||
Protocol: internet.TransportProtocol_WebSocket,
|
|
||||||
Settings: ts,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProxyConfig struct {
|
|
||||||
Tag string `json:"tag"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *ProxyConfig) Build() (*internet.ProxyConfig, error) {
|
|
||||||
if len(v.Tag) == 0 {
|
|
||||||
return nil, newError("Proxy tag is not set.")
|
|
||||||
}
|
|
||||||
return &internet.ProxyConfig{
|
|
||||||
Tag: v.Tag,
|
|
||||||
}, nil
|
|
||||||
}
|
|
|
@ -1,105 +0,0 @@
|
||||||
package conf_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"v2ray.com/core/testing/assert"
|
|
||||||
. "v2ray.com/core/tools/conf"
|
|
||||||
"v2ray.com/core/transport/internet"
|
|
||||||
"v2ray.com/core/transport/internet/headers/http"
|
|
||||||
"v2ray.com/core/transport/internet/headers/noop"
|
|
||||||
"v2ray.com/core/transport/internet/kcp"
|
|
||||||
"v2ray.com/core/transport/internet/tcp"
|
|
||||||
"v2ray.com/core/transport/internet/websocket"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTransportConfig(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
rawJson := `{
|
|
||||||
"tcpSettings": {
|
|
||||||
"header": {
|
|
||||||
"type": "http",
|
|
||||||
"request": {
|
|
||||||
"version": "1.1",
|
|
||||||
"method": "GET",
|
|
||||||
"path": "/b",
|
|
||||||
"headers": {
|
|
||||||
"a": "b",
|
|
||||||
"c": "d"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": {
|
|
||||||
"version": "1.0",
|
|
||||||
"status": "404",
|
|
||||||
"reason": "Not Found"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"kcpSettings": {
|
|
||||||
"mtu": 1200,
|
|
||||||
"header": {
|
|
||||||
"type": "none"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"wsSettings": {
|
|
||||||
"path": "/t"
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
|
|
||||||
var transportSettingsConf TransportConfig
|
|
||||||
assert.Error(json.Unmarshal([]byte(rawJson), &transportSettingsConf)).IsNil()
|
|
||||||
|
|
||||||
ts, err := transportSettingsConf.Build()
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
|
|
||||||
assert.Int(len(ts.TransportSettings)).Equals(3)
|
|
||||||
var settingsCount uint32
|
|
||||||
for _, settingsWithProtocol := range ts.TransportSettings {
|
|
||||||
rawSettings, err := settingsWithProtocol.Settings.GetInstance()
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
switch settings := rawSettings.(type) {
|
|
||||||
case *tcp.Config:
|
|
||||||
settingsCount++
|
|
||||||
assert.Bool(settingsWithProtocol.Protocol == internet.TransportProtocol_TCP).IsTrue()
|
|
||||||
rawHeader, err := settings.HeaderSettings.GetInstance()
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
header := rawHeader.(*http.Config)
|
|
||||||
assert.String(header.Request.GetVersionValue()).Equals("1.1")
|
|
||||||
assert.String(header.Request.Uri[0]).Equals("/b")
|
|
||||||
assert.String(header.Request.Method.Value).Equals("GET")
|
|
||||||
var va, vc string
|
|
||||||
for _, h := range header.Request.Header {
|
|
||||||
switch h.Name {
|
|
||||||
case "a":
|
|
||||||
va = h.Value[0]
|
|
||||||
case "c":
|
|
||||||
vc = h.Value[0]
|
|
||||||
default:
|
|
||||||
t.Error("Unknown header ", h.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert.String(va).Equals("b")
|
|
||||||
assert.String(vc).Equals("d")
|
|
||||||
assert.String(header.Response.Version.Value).Equals("1.0")
|
|
||||||
assert.String(header.Response.Status.Code).Equals("404")
|
|
||||||
assert.String(header.Response.Status.Reason).Equals("Not Found")
|
|
||||||
case *kcp.Config:
|
|
||||||
settingsCount++
|
|
||||||
assert.Bool(settingsWithProtocol.Protocol == internet.TransportProtocol_MKCP).IsTrue()
|
|
||||||
assert.Uint32(settings.GetMTUValue()).Equals(1200)
|
|
||||||
rawHeader, err := settings.HeaderConfig.GetInstance()
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
header := rawHeader.(*noop.Config)
|
|
||||||
assert.Pointer(header).IsNotNil()
|
|
||||||
case *websocket.Config:
|
|
||||||
settingsCount++
|
|
||||||
assert.Bool(settingsWithProtocol.Protocol == internet.TransportProtocol_WebSocket).IsTrue()
|
|
||||||
assert.String(settings.Path).Equals("/t")
|
|
||||||
default:
|
|
||||||
t.Error("Unknown type of settings.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert.Uint32(settingsCount).Equals(3)
|
|
||||||
}
|
|
|
@ -1,428 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"v2ray.com/core"
|
|
||||||
"v2ray.com/core/app/proxyman"
|
|
||||||
v2net "v2ray.com/core/common/net"
|
|
||||||
"v2ray.com/core/common/serial"
|
|
||||||
json_reader "v2ray.com/core/tools/conf/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
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) },
|
|
||||||
"shadowsocks": func() interface{} { return new(ShadowsocksClientConfig) },
|
|
||||||
"vmess": func() interface{} { return new(VMessOutboundConfig) },
|
|
||||||
"socks": func() interface{} { return new(SocksClientConfig) },
|
|
||||||
}, "protocol", "settings")
|
|
||||||
)
|
|
||||||
|
|
||||||
func toProtocolList(s []string) ([]proxyman.KnownProtocols, error) {
|
|
||||||
kp := make([]proxyman.KnownProtocols, 0, 8)
|
|
||||||
for _, p := range s {
|
|
||||||
switch strings.ToLower(p) {
|
|
||||||
case "http":
|
|
||||||
kp = append(kp, proxyman.KnownProtocols_HTTP)
|
|
||||||
case "https", "tls", "ssl":
|
|
||||||
kp = append(kp, proxyman.KnownProtocols_TLS)
|
|
||||||
default:
|
|
||||||
return nil, newError("Unknown protocol: ", p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return kp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type InboundConnectionConfig struct {
|
|
||||||
Port uint16 `json:"port"`
|
|
||||||
Listen *Address `json:"listen"`
|
|
||||||
Protocol string `json:"protocol"`
|
|
||||||
StreamSetting *StreamConfig `json:"streamSettings"`
|
|
||||||
Settings json.RawMessage `json:"settings"`
|
|
||||||
Tag string `json:"tag"`
|
|
||||||
DomainOverride *StringList `json:"domainOverride"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *InboundConnectionConfig) Build() (*proxyman.InboundHandlerConfig, error) {
|
|
||||||
receiverConfig := &proxyman.ReceiverConfig{
|
|
||||||
PortRange: &v2net.PortRange{
|
|
||||||
From: uint32(v.Port),
|
|
||||||
To: uint32(v.Port),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if v.Listen != nil {
|
|
||||||
if v.Listen.Family().IsDomain() {
|
|
||||||
return nil, newError("unable to listen on domain address: " + v.Listen.Domain())
|
|
||||||
}
|
|
||||||
receiverConfig.Listen = v.Listen.Build()
|
|
||||||
}
|
|
||||||
if v.StreamSetting != nil {
|
|
||||||
ts, err := v.StreamSetting.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
receiverConfig.StreamSettings = ts
|
|
||||||
}
|
|
||||||
if v.DomainOverride != nil {
|
|
||||||
kp, err := toProtocolList(*v.DomainOverride)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to parse inbound config").Base(err)
|
|
||||||
}
|
|
||||||
receiverConfig.DomainOverride = kp
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonConfig, err := inboundConfigLoader.LoadWithID(v.Settings, v.Protocol)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to load inbound config.").Base(err)
|
|
||||||
}
|
|
||||||
if dokodemoConfig, ok := jsonConfig.(*DokodemoConfig); ok {
|
|
||||||
receiverConfig.ReceiveOriginalDestination = dokodemoConfig.Redirect
|
|
||||||
}
|
|
||||||
ts, err := jsonConfig.(Buildable).Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &proxyman.InboundHandlerConfig{
|
|
||||||
Tag: v.Tag,
|
|
||||||
ReceiverSettings: serial.ToTypedMessage(receiverConfig),
|
|
||||||
ProxySettings: ts,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type MuxConfig struct {
|
|
||||||
Enabled bool `json:"enabled"`
|
|
||||||
Concurrency uint16 `json:"concurrency"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *MuxConfig) GetConcurrency() uint16 {
|
|
||||||
if c.Concurrency == 0 {
|
|
||||||
return 8
|
|
||||||
}
|
|
||||||
return c.Concurrency
|
|
||||||
}
|
|
||||||
|
|
||||||
type OutboundConnectionConfig struct {
|
|
||||||
Protocol string `json:"protocol"`
|
|
||||||
SendThrough *Address `json:"sendThrough"`
|
|
||||||
StreamSetting *StreamConfig `json:"streamSettings"`
|
|
||||||
ProxySettings *ProxyConfig `json:"proxySettings"`
|
|
||||||
Settings json.RawMessage `json:"settings"`
|
|
||||||
Tag string `json:"tag"`
|
|
||||||
MuxSettings *MuxConfig `json:"mux"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *OutboundConnectionConfig) Build() (*proxyman.OutboundHandlerConfig, error) {
|
|
||||||
senderSettings := &proxyman.SenderConfig{}
|
|
||||||
|
|
||||||
if v.SendThrough != nil {
|
|
||||||
address := v.SendThrough
|
|
||||||
if address.Family().IsDomain() {
|
|
||||||
return nil, newError("invalid sendThrough address: " + address.String())
|
|
||||||
}
|
|
||||||
senderSettings.Via = address.Build()
|
|
||||||
}
|
|
||||||
if v.StreamSetting != nil {
|
|
||||||
ss, err := v.StreamSetting.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
senderSettings.StreamSettings = ss
|
|
||||||
}
|
|
||||||
if v.ProxySettings != nil {
|
|
||||||
ps, err := v.ProxySettings.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid outbound proxy settings").Base(err)
|
|
||||||
}
|
|
||||||
senderSettings.ProxySettings = ps
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.MuxSettings != nil && v.MuxSettings.Enabled {
|
|
||||||
senderSettings.MultiplexSettings = &proxyman.MultiplexingConfig{
|
|
||||||
Enabled: true,
|
|
||||||
Concurrency: uint32(v.MuxSettings.GetConcurrency()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rawConfig, err := outboundConfigLoader.LoadWithID(v.Settings, v.Protocol)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to parse outbound config").Base(err)
|
|
||||||
}
|
|
||||||
ts, err := rawConfig.(Buildable).Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &proxyman.OutboundHandlerConfig{
|
|
||||||
SenderSettings: serial.ToTypedMessage(senderSettings),
|
|
||||||
ProxySettings: ts,
|
|
||||||
Tag: v.Tag,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type InboundDetourAllocationConfig struct {
|
|
||||||
Strategy string `json:"strategy"`
|
|
||||||
Concurrency *uint32 `json:"concurrency"`
|
|
||||||
RefreshMin *uint32 `json:"refresh"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *InboundDetourAllocationConfig) Build() (*proxyman.AllocationStrategy, error) {
|
|
||||||
config := new(proxyman.AllocationStrategy)
|
|
||||||
switch strings.ToLower(v.Strategy) {
|
|
||||||
case "always":
|
|
||||||
config.Type = proxyman.AllocationStrategy_Always
|
|
||||||
case "random":
|
|
||||||
config.Type = proxyman.AllocationStrategy_Random
|
|
||||||
case "external":
|
|
||||||
config.Type = proxyman.AllocationStrategy_External
|
|
||||||
default:
|
|
||||||
return nil, newError("unknown allocation strategy: ", v.Strategy)
|
|
||||||
}
|
|
||||||
if v.Concurrency != nil {
|
|
||||||
config.Concurrency = &proxyman.AllocationStrategy_AllocationStrategyConcurrency{
|
|
||||||
Value: *v.Concurrency,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.RefreshMin != nil {
|
|
||||||
config.Refresh = &proxyman.AllocationStrategy_AllocationStrategyRefresh{
|
|
||||||
Value: *v.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"`
|
|
||||||
DomainOverride *StringList `json:"domainOverride"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *InboundDetourConfig) Build() (*proxyman.InboundHandlerConfig, error) {
|
|
||||||
receiverSettings := &proxyman.ReceiverConfig{}
|
|
||||||
|
|
||||||
if v.PortRange == nil {
|
|
||||||
return nil, newError("port range not specified in InboundDetour.")
|
|
||||||
}
|
|
||||||
receiverSettings.PortRange = v.PortRange.Build()
|
|
||||||
|
|
||||||
if v.ListenOn != nil {
|
|
||||||
if v.ListenOn.Family().IsDomain() {
|
|
||||||
return nil, newError("unable to listen on domain address: ", v.ListenOn.Domain())
|
|
||||||
}
|
|
||||||
receiverSettings.Listen = v.ListenOn.Build()
|
|
||||||
}
|
|
||||||
if v.Allocation != nil {
|
|
||||||
as, err := v.Allocation.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
receiverSettings.AllocationStrategy = as
|
|
||||||
}
|
|
||||||
if v.StreamSetting != nil {
|
|
||||||
ss, err := v.StreamSetting.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
receiverSettings.StreamSettings = ss
|
|
||||||
}
|
|
||||||
if v.DomainOverride != nil {
|
|
||||||
kp, err := toProtocolList(*v.DomainOverride)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to parse inbound detour config").Base(err)
|
|
||||||
}
|
|
||||||
receiverSettings.DomainOverride = kp
|
|
||||||
}
|
|
||||||
|
|
||||||
rawConfig, err := inboundConfigLoader.LoadWithID(v.Settings, v.Protocol)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to load inbound detour config.").Base(err)
|
|
||||||
}
|
|
||||||
if dokodemoConfig, ok := rawConfig.(*DokodemoConfig); ok {
|
|
||||||
receiverSettings.ReceiveOriginalDestination = dokodemoConfig.Redirect
|
|
||||||
}
|
|
||||||
ts, err := rawConfig.(Buildable).Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &proxyman.InboundHandlerConfig{
|
|
||||||
Tag: v.Tag,
|
|
||||||
ReceiverSettings: serial.ToTypedMessage(receiverSettings),
|
|
||||||
ProxySettings: ts,
|
|
||||||
}, 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"`
|
|
||||||
ProxySettings *ProxyConfig `json:"proxySettings"`
|
|
||||||
MuxSettings *MuxConfig `json:"mux"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *OutboundDetourConfig) Build() (*proxyman.OutboundHandlerConfig, error) {
|
|
||||||
senderSettings := &proxyman.SenderConfig{}
|
|
||||||
|
|
||||||
if v.SendThrough != nil {
|
|
||||||
address := v.SendThrough
|
|
||||||
if address.Family().IsDomain() {
|
|
||||||
return nil, newError("unable to send through: " + address.String())
|
|
||||||
}
|
|
||||||
senderSettings.Via = address.Build()
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.StreamSetting != nil {
|
|
||||||
ss, err := v.StreamSetting.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
senderSettings.StreamSettings = ss
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.ProxySettings != nil {
|
|
||||||
ps, err := v.ProxySettings.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid outbound detour proxy settings.").Base(err)
|
|
||||||
}
|
|
||||||
senderSettings.ProxySettings = ps
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.MuxSettings != nil && v.MuxSettings.Enabled {
|
|
||||||
senderSettings.MultiplexSettings = &proxyman.MultiplexingConfig{
|
|
||||||
Enabled: true,
|
|
||||||
Concurrency: uint32(v.MuxSettings.GetConcurrency()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rawConfig, err := outboundConfigLoader.LoadWithID(v.Settings, v.Protocol)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to parse to outbound detour config.").Base(err)
|
|
||||||
}
|
|
||||||
ts, err := rawConfig.(Buildable).Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &proxyman.OutboundHandlerConfig{
|
|
||||||
SenderSettings: serial.ToTypedMessage(senderSettings),
|
|
||||||
Tag: v.Tag,
|
|
||||||
ProxySettings: ts,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Port uint16 `json:"port"` // Port of this Point server.
|
|
||||||
LogConfig *LogConfig `json:"log"`
|
|
||||||
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 *TransportConfig `json:"transport"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Config) Build() (*core.Config, error) {
|
|
||||||
config := new(core.Config)
|
|
||||||
|
|
||||||
if v.LogConfig != nil {
|
|
||||||
config.App = append(config.App, serial.ToTypedMessage(v.LogConfig.Build()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.Transport != nil {
|
|
||||||
ts, err := v.Transport.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config.Transport = ts
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.RouterConfig != nil {
|
|
||||||
routerConfig, err := v.RouterConfig.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config.App = append(config.App, serial.ToTypedMessage(routerConfig))
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.DNSConfig != nil {
|
|
||||||
config.App = append(config.App, serial.ToTypedMessage(v.DNSConfig.Build()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.InboundConfig == nil {
|
|
||||||
return nil, newError("no inbound config specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.InboundConfig.Port == 0 && v.Port > 0 {
|
|
||||||
v.InboundConfig.Port = v.Port
|
|
||||||
}
|
|
||||||
|
|
||||||
ic, err := v.InboundConfig.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config.Inbound = append(config.Inbound, ic)
|
|
||||||
|
|
||||||
for _, rawInboundConfig := range v.InboundDetours {
|
|
||||||
ic, err := rawInboundConfig.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config.Inbound = append(config.Inbound, ic)
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.OutboundConfig == nil {
|
|
||||||
return nil, newError("no outbound config specified")
|
|
||||||
}
|
|
||||||
oc, err := v.OutboundConfig.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config.Outbound = append(config.Outbound, oc)
|
|
||||||
|
|
||||||
for _, rawOutboundConfig := range v.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(&json_reader.Reader{
|
|
||||||
Reader: input,
|
|
||||||
})
|
|
||||||
err := decoder.Decode(jsonConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("invalid V2Ray config").Base(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonConfig.Build()
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,159 +0,0 @@
|
||||||
package conf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
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"`
|
|
||||||
Security string `json:"security"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *VMessAccount) Build() *vmess.Account {
|
|
||||||
var st protocol.SecurityType
|
|
||||||
switch strings.ToLower(v.Security) {
|
|
||||||
case "aes-128-gcm":
|
|
||||||
st = protocol.SecurityType_AES128_GCM
|
|
||||||
case "chacha20-poly1305":
|
|
||||||
st = protocol.SecurityType_CHACHA20_POLY1305
|
|
||||||
case "auto":
|
|
||||||
st = protocol.SecurityType_AUTO
|
|
||||||
case "none":
|
|
||||||
st = protocol.SecurityType_NONE
|
|
||||||
default:
|
|
||||||
st = protocol.SecurityType_LEGACY
|
|
||||||
}
|
|
||||||
return &vmess.Account{
|
|
||||||
Id: v.ID,
|
|
||||||
AlterId: uint32(v.AlterIds),
|
|
||||||
SecuritySettings: &protocol.SecurityConfig{
|
|
||||||
Type: st,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type VMessDetourConfig struct {
|
|
||||||
ToTag string `json:"to"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *VMessDetourConfig) Build() *inbound.DetourConfig {
|
|
||||||
return &inbound.DetourConfig{
|
|
||||||
To: v.ToTag,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type FeaturesConfig struct {
|
|
||||||
Detour *VMessDetourConfig `json:"detour"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type VMessDefaultConfig struct {
|
|
||||||
AlterIDs uint16 `json:"alterId"`
|
|
||||||
Level byte `json:"level"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *VMessDefaultConfig) Build() *inbound.DefaultConfig {
|
|
||||||
config := new(inbound.DefaultConfig)
|
|
||||||
config.AlterId = uint32(v.AlterIDs)
|
|
||||||
if config.AlterId == 0 {
|
|
||||||
config.AlterId = 32
|
|
||||||
}
|
|
||||||
config.Level = uint32(v.Level)
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
|
|
||||||
type VMessInboundConfig struct {
|
|
||||||
Users []json.RawMessage `json:"clients"`
|
|
||||||
Features *FeaturesConfig `json:"features"`
|
|
||||||
Defaults *VMessDefaultConfig `json:"default"`
|
|
||||||
DetourConfig *VMessDetourConfig `json:"detour"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *VMessInboundConfig) Build() (*serial.TypedMessage, error) {
|
|
||||||
config := new(inbound.Config)
|
|
||||||
|
|
||||||
if v.Defaults != nil {
|
|
||||||
config.Default = v.Defaults.Build()
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.DetourConfig != nil {
|
|
||||||
config.Detour = v.DetourConfig.Build()
|
|
||||||
} else if v.Features != nil && v.Features.Detour != nil {
|
|
||||||
config.Detour = v.Features.Detour.Build()
|
|
||||||
}
|
|
||||||
|
|
||||||
config.User = make([]*protocol.User, len(v.Users))
|
|
||||||
for idx, rawData := range v.Users {
|
|
||||||
user := new(protocol.User)
|
|
||||||
if err := json.Unmarshal(rawData, user); err != nil {
|
|
||||||
return nil, newError("invalid VMess user").Base(err)
|
|
||||||
}
|
|
||||||
account := new(VMessAccount)
|
|
||||||
if err := json.Unmarshal(rawData, account); err != nil {
|
|
||||||
return nil, newError("invalid VMess user").Base(err)
|
|
||||||
}
|
|
||||||
user.Account = serial.ToTypedMessage(account.Build())
|
|
||||||
config.User[idx] = user
|
|
||||||
}
|
|
||||||
|
|
||||||
return serial.ToTypedMessage(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"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var bUser = "cd70f07b-1da8-4815-b9f3-0327d30b0e1e"
|
|
||||||
|
|
||||||
func (v *VMessOutboundConfig) Build() (*serial.TypedMessage, error) {
|
|
||||||
config := new(outbound.Config)
|
|
||||||
|
|
||||||
if len(v.Receivers) == 0 {
|
|
||||||
return nil, newError("0 VMess receiver configured")
|
|
||||||
}
|
|
||||||
serverSpecs := make([]*protocol.ServerEndpoint, len(v.Receivers))
|
|
||||||
for idx, rec := range v.Receivers {
|
|
||||||
if len(rec.Users) == 0 {
|
|
||||||
return nil, newError("0 user configured for VMess outbound")
|
|
||||||
}
|
|
||||||
if rec.Address == nil {
|
|
||||||
return nil, newError("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))
|
|
||||||
rec.Users = []json.RawMessage{[]byte(`{"id":"` + bUser + `", "alterId": 64, "security": "auto"}`)}
|
|
||||||
}
|
|
||||||
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, newError("invalid VMess user").Base(err)
|
|
||||||
}
|
|
||||||
account := new(VMessAccount)
|
|
||||||
if err := json.Unmarshal(rawUser, account); err != nil {
|
|
||||||
return nil, newError("invalid VMess user").Base(err)
|
|
||||||
}
|
|
||||||
user.Account = serial.ToTypedMessage(account.Build())
|
|
||||||
spec.User = append(spec.User, user)
|
|
||||||
}
|
|
||||||
serverSpecs[idx] = spec
|
|
||||||
}
|
|
||||||
config.Receiver = serverSpecs
|
|
||||||
return serial.ToTypedMessage(config), nil
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package conf_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"v2ray.com/core/common/protocol"
|
|
||||||
"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(*specPB)
|
|
||||||
assert.Destination(spec.Destination()).EqualsString("tcp:127.0.0.1:80")
|
|
||||||
}
|
|
Loading…
Reference in New Issue