mirror of https://github.com/v2ray/v2ray-core
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
476 lines
13 KiB
476 lines
13 KiB
package conf |
|
|
|
import ( |
|
"encoding/json" |
|
"strings" |
|
|
|
"github.com/golang/protobuf/proto" |
|
"v2ray.com/core/common/platform/filesystem" |
|
"v2ray.com/core/common/protocol" |
|
"v2ray.com/core/common/serial" |
|
"v2ray.com/core/transport/internet" |
|
"v2ray.com/core/transport/internet/domainsocket" |
|
"v2ray.com/core/transport/internet/http" |
|
"v2ray.com/core/transport/internet/kcp" |
|
"v2ray.com/core/transport/internet/quic" |
|
"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) }, |
|
"dtls": func() interface{} { return new(DTLSAuthenticator) }, |
|
"wireguard": func() interface{} { return new(WireguardAuthenticator) }, |
|
}, "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"` |
|
} |
|
|
|
// Build implements Buildable. |
|
func (c *KCPConfig) Build() (proto.Message, error) { |
|
config := new(kcp.Config) |
|
|
|
if c.Mtu != nil { |
|
mtu := *c.Mtu |
|
if mtu < 576 || mtu > 1460 { |
|
return nil, newError("invalid mKCP MTU size: ", mtu).AtError() |
|
} |
|
config.Mtu = &kcp.MTU{Value: mtu} |
|
} |
|
if c.Tti != nil { |
|
tti := *c.Tti |
|
if tti < 10 || tti > 100 { |
|
return nil, newError("invalid mKCP TTI: ", tti).AtError() |
|
} |
|
config.Tti = &kcp.TTI{Value: tti} |
|
} |
|
if c.UpCap != nil { |
|
config.UplinkCapacity = &kcp.UplinkCapacity{Value: *c.UpCap} |
|
} |
|
if c.DownCap != nil { |
|
config.DownlinkCapacity = &kcp.DownlinkCapacity{Value: *c.DownCap} |
|
} |
|
if c.Congestion != nil { |
|
config.Congestion = *c.Congestion |
|
} |
|
if c.ReadBufferSize != nil { |
|
size := *c.ReadBufferSize |
|
if size > 0 { |
|
config.ReadBuffer = &kcp.ReadBuffer{Size: size * 1024 * 1024} |
|
} else { |
|
config.ReadBuffer = &kcp.ReadBuffer{Size: 512 * 1024} |
|
} |
|
} |
|
if c.WriteBufferSize != nil { |
|
size := *c.WriteBufferSize |
|
if size > 0 { |
|
config.WriteBuffer = &kcp.WriteBuffer{Size: size * 1024 * 1024} |
|
} else { |
|
config.WriteBuffer = &kcp.WriteBuffer{Size: 512 * 1024} |
|
} |
|
} |
|
if len(c.HeaderConfig) > 0 { |
|
headerConfig, _, err := kcpHeaderLoader.Load(c.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 = serial.ToTypedMessage(ts) |
|
} |
|
|
|
return config, nil |
|
} |
|
|
|
type TCPConfig struct { |
|
HeaderConfig json.RawMessage `json:"header"` |
|
} |
|
|
|
// Build implements Buildable. |
|
func (c *TCPConfig) Build() (proto.Message, error) { |
|
config := new(tcp.Config) |
|
if len(c.HeaderConfig) > 0 { |
|
headerConfig, _, err := tcpHeaderLoader.Load(c.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 = serial.ToTypedMessage(ts) |
|
} |
|
|
|
return config, nil |
|
} |
|
|
|
type WebSocketConfig struct { |
|
Path string `json:"path"` |
|
Path2 string `json:"Path"` // The key was misspelled. For backward compatibility, we have to keep track the old key. |
|
Headers map[string]string `json:"headers"` |
|
} |
|
|
|
// Build implements Buildable. |
|
func (c *WebSocketConfig) Build() (proto.Message, error) { |
|
path := c.Path |
|
if len(path) == 0 && len(c.Path2) > 0 { |
|
path = c.Path2 |
|
} |
|
header := make([]*websocket.Header, 0, 32) |
|
for key, value := range c.Headers { |
|
header = append(header, &websocket.Header{ |
|
Key: key, |
|
Value: value, |
|
}) |
|
} |
|
|
|
config := &websocket.Config{ |
|
Path: path, |
|
Header: header, |
|
} |
|
return config, nil |
|
} |
|
|
|
type HTTPConfig struct { |
|
Host *StringList `json:"host"` |
|
Path string `json:"path"` |
|
} |
|
|
|
func (c *HTTPConfig) Build() (proto.Message, error) { |
|
config := &http.Config{ |
|
Path: c.Path, |
|
} |
|
if c.Host != nil { |
|
config.Host = []string(*c.Host) |
|
} |
|
return config, nil |
|
} |
|
|
|
type QUICConfig struct { |
|
Header json.RawMessage `json:"header"` |
|
Security string `json:"security"` |
|
Key string `json:"key"` |
|
} |
|
|
|
func (c *QUICConfig) Build() (proto.Message, error) { |
|
config := &quic.Config{ |
|
Key: c.Key, |
|
} |
|
|
|
if len(c.Header) > 0 { |
|
headerConfig, _, err := kcpHeaderLoader.Load(c.Header) |
|
if err != nil { |
|
return nil, newError("invalid QUIC header config.").Base(err).AtError() |
|
} |
|
ts, err := headerConfig.(Buildable).Build() |
|
if err != nil { |
|
return nil, newError("invalid QUIC header config").Base(err).AtError() |
|
} |
|
config.Header = serial.ToTypedMessage(ts) |
|
} |
|
|
|
var st protocol.SecurityType |
|
switch strings.ToLower(c.Security) { |
|
case "aes-128-gcm": |
|
st = protocol.SecurityType_AES128_GCM |
|
case "chacha20-poly1305": |
|
st = protocol.SecurityType_CHACHA20_POLY1305 |
|
default: |
|
st = protocol.SecurityType_NONE |
|
} |
|
|
|
config.Security = &protocol.SecurityConfig{ |
|
Type: st, |
|
} |
|
|
|
return config, nil |
|
} |
|
|
|
type DomainSocketConfig struct { |
|
Path string `json:"path"` |
|
Abstract bool `json:"abstract"` |
|
} |
|
|
|
func (c *DomainSocketConfig) Build() (proto.Message, error) { |
|
return &domainsocket.Config{ |
|
Path: c.Path, |
|
Abstract: c.Abstract, |
|
}, nil |
|
} |
|
|
|
type TLSCertConfig struct { |
|
CertFile string `json:"certificateFile"` |
|
CertStr []string `json:"certificate"` |
|
KeyFile string `json:"keyFile"` |
|
KeyStr []string `json:"key"` |
|
Usage string `json:"usage"` |
|
} |
|
|
|
func readFileOrString(f string, s []string) ([]byte, error) { |
|
if len(f) > 0 { |
|
return filesystem.ReadFile(f) |
|
} |
|
if len(s) > 0 { |
|
return []byte(strings.Join(s, "\n")), nil |
|
} |
|
return nil, newError("both file and bytes are empty.") |
|
} |
|
|
|
func (c *TLSCertConfig) Build() (*tls.Certificate, error) { |
|
certificate := new(tls.Certificate) |
|
|
|
cert, err := readFileOrString(c.CertFile, c.CertStr) |
|
if err != nil { |
|
return nil, newError("failed to parse certificate").Base(err) |
|
} |
|
certificate.Certificate = cert |
|
|
|
if len(c.KeyFile) > 0 || len(c.KeyStr) > 0 { |
|
key, err := readFileOrString(c.KeyFile, c.KeyStr) |
|
if err != nil { |
|
return nil, newError("failed to parse key").Base(err) |
|
} |
|
certificate.Key = key |
|
} |
|
|
|
switch strings.ToLower(c.Usage) { |
|
case "encipherment": |
|
certificate.Usage = tls.Certificate_ENCIPHERMENT |
|
case "verify": |
|
certificate.Usage = tls.Certificate_AUTHORITY_VERIFY |
|
case "issue": |
|
certificate.Usage = tls.Certificate_AUTHORITY_ISSUE |
|
default: |
|
certificate.Usage = tls.Certificate_ENCIPHERMENT |
|
} |
|
|
|
return certificate, nil |
|
} |
|
|
|
type TLSConfig struct { |
|
Insecure bool `json:"allowInsecure"` |
|
InsecureCiphers bool `json:"allowInsecureCiphers"` |
|
Certs []*TLSCertConfig `json:"certificates"` |
|
ServerName string `json:"serverName"` |
|
ALPN *StringList `json:"alpn"` |
|
} |
|
|
|
// Build implements Buildable. |
|
func (c *TLSConfig) Build() (proto.Message, error) { |
|
config := new(tls.Config) |
|
config.Certificate = make([]*tls.Certificate, len(c.Certs)) |
|
for idx, certConf := range c.Certs { |
|
cert, err := certConf.Build() |
|
if err != nil { |
|
return nil, err |
|
} |
|
config.Certificate[idx] = cert |
|
} |
|
serverName := c.ServerName |
|
config.AllowInsecure = c.Insecure |
|
config.AllowInsecureCiphers = c.InsecureCiphers |
|
if len(c.ServerName) > 0 { |
|
config.ServerName = serverName |
|
} |
|
if c.ALPN != nil && len(*c.ALPN) > 0 { |
|
config.NextProtocol = []string(*c.ALPN) |
|
} |
|
return config, nil |
|
} |
|
|
|
type TransportProtocol string |
|
|
|
// Build implements Buildable. |
|
func (p TransportProtocol) Build() (string, error) { |
|
switch strings.ToLower(string(p)) { |
|
case "tcp": |
|
return "tcp", nil |
|
case "kcp", "mkcp": |
|
return "mkcp", nil |
|
case "ws", "websocket": |
|
return "websocket", nil |
|
case "h2", "http": |
|
return "http", nil |
|
case "ds", "domainsocket": |
|
return "domainsocket", nil |
|
case "quic": |
|
return "quic", nil |
|
default: |
|
return "", newError("Config: unknown transport protocol: ", p) |
|
} |
|
} |
|
|
|
type SocketConfig struct { |
|
Mark int32 `json:"mark"` |
|
TFO *bool `json:"tcpFastOpen"` |
|
TProxy string `json:"tproxy"` |
|
} |
|
|
|
func (c *SocketConfig) Build() (*internet.SocketConfig, error) { |
|
var tfoSettings internet.SocketConfig_TCPFastOpenState |
|
if c.TFO != nil { |
|
if *c.TFO { |
|
tfoSettings = internet.SocketConfig_Enable |
|
} else { |
|
tfoSettings = internet.SocketConfig_Disable |
|
} |
|
} |
|
var tproxy internet.SocketConfig_TProxyMode |
|
switch strings.ToLower(c.TProxy) { |
|
case "tproxy": |
|
tproxy = internet.SocketConfig_TProxy |
|
case "redirect": |
|
tproxy = internet.SocketConfig_Redirect |
|
default: |
|
tproxy = internet.SocketConfig_Off |
|
} |
|
|
|
return &internet.SocketConfig{ |
|
Mark: c.Mark, |
|
Tfo: tfoSettings, |
|
Tproxy: tproxy, |
|
}, nil |
|
} |
|
|
|
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"` |
|
HTTPSettings *HTTPConfig `json:"httpSettings"` |
|
DSSettings *DomainSocketConfig `json:"dsSettings"` |
|
QUICSettings *QUICConfig `json:"quicSettings"` |
|
SocketSettings *SocketConfig `json:"sockopt"` |
|
} |
|
|
|
// Build implements Buildable. |
|
func (c *StreamConfig) Build() (*internet.StreamConfig, error) { |
|
config := &internet.StreamConfig{ |
|
ProtocolName: "tcp", |
|
} |
|
if c.Network != nil { |
|
protocol, err := (*c.Network).Build() |
|
if err != nil { |
|
return nil, err |
|
} |
|
config.ProtocolName = protocol |
|
} |
|
if strings.ToLower(c.Security) == "tls" { |
|
tlsSettings := c.TLSSettings |
|
if tlsSettings == nil { |
|
tlsSettings = &TLSConfig{} |
|
} |
|
ts, err := tlsSettings.Build() |
|
if err != nil { |
|
return nil, newError("Failed to build TLS config.").Base(err) |
|
} |
|
tm := serial.ToTypedMessage(ts) |
|
config.SecuritySettings = append(config.SecuritySettings, tm) |
|
config.SecurityType = tm.Type |
|
} |
|
if c.TCPSettings != nil { |
|
ts, err := c.TCPSettings.Build() |
|
if err != nil { |
|
return nil, newError("Failed to build TCP config.").Base(err) |
|
} |
|
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ |
|
ProtocolName: "tcp", |
|
Settings: serial.ToTypedMessage(ts), |
|
}) |
|
} |
|
if c.KCPSettings != nil { |
|
ts, err := c.KCPSettings.Build() |
|
if err != nil { |
|
return nil, newError("Failed to build mKCP config.").Base(err) |
|
} |
|
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ |
|
ProtocolName: "mkcp", |
|
Settings: serial.ToTypedMessage(ts), |
|
}) |
|
} |
|
if c.WSSettings != nil { |
|
ts, err := c.WSSettings.Build() |
|
if err != nil { |
|
return nil, newError("Failed to build WebSocket config.").Base(err) |
|
} |
|
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ |
|
ProtocolName: "websocket", |
|
Settings: serial.ToTypedMessage(ts), |
|
}) |
|
} |
|
if c.HTTPSettings != nil { |
|
ts, err := c.HTTPSettings.Build() |
|
if err != nil { |
|
return nil, newError("Failed to build HTTP config.").Base(err) |
|
} |
|
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ |
|
ProtocolName: "http", |
|
Settings: serial.ToTypedMessage(ts), |
|
}) |
|
} |
|
if c.DSSettings != nil { |
|
ds, err := c.DSSettings.Build() |
|
if err != nil { |
|
return nil, newError("Failed to build DomainSocket config.").Base(err) |
|
} |
|
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ |
|
ProtocolName: "domainsocket", |
|
Settings: serial.ToTypedMessage(ds), |
|
}) |
|
} |
|
if c.QUICSettings != nil { |
|
qs, err := c.QUICSettings.Build() |
|
if err != nil { |
|
return nil, newError("failed to build QUIC config").Base(err) |
|
} |
|
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ |
|
ProtocolName: "quic", |
|
Settings: serial.ToTypedMessage(qs), |
|
}) |
|
} |
|
if c.SocketSettings != nil { |
|
ss, err := c.SocketSettings.Build() |
|
if err != nil { |
|
return nil, newError("failed to build sockopt").Base(err) |
|
} |
|
config.SocketSettings = ss |
|
} |
|
return config, nil |
|
} |
|
|
|
type ProxyConfig struct { |
|
Tag string `json:"tag"` |
|
} |
|
|
|
// Build implements Buildable. |
|
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 |
|
}
|
|
|