mirror of
https://github.com/wyx2685/V2bX.git
synced 2025-03-15 16:58:13 -04:00
commit
30c615e1f5
24
README.md
24
README.md
@ -23,18 +23,18 @@ A V2board node server based on multi core, modified from XrayR.
|
|||||||
|
|
||||||
## 功能介绍
|
## 功能介绍
|
||||||
|
|
||||||
| 功能 | v2ray | trojan | shadowsocks |
|
| 功能 | v2ray | trojan | shadowsocks | hysteria |
|
||||||
|-----------|-------|--------|-------------|
|
|-----------|-------|--------|-------------|----------|
|
||||||
| 自动申请tls证书 | √ | √ | √ |
|
| 自动申请tls证书 | √ | √ | √ | √ |
|
||||||
| 自动续签tls证书 | √ | √ | √ |
|
| 自动续签tls证书 | √ | √ | √ | √ |
|
||||||
| 在线人数统计 | √ | √ | √ |
|
| 在线人数统计 | √ | √ | √ | √ |
|
||||||
| 审计规则 | √ | √ | √ |
|
| 审计规则 | √ | √ | √ | |
|
||||||
| 自定义DNS | √ | √ | √ |
|
| 自定义DNS | √ | √ | √ | √ |
|
||||||
| 在线IP数限制 | √ | √ | √ |
|
| 在线IP数限制 | √ | √ | √ | |
|
||||||
| 连接数限制 | √ | √ | √ |
|
| 连接数限制 | √ | √ | √ | |
|
||||||
| 跨节点IP数限制 | √ | √ | √ |
|
| 跨节点IP数限制 | √ | √ | √ | |
|
||||||
| 按照用户限速 | √ | √ | √ |
|
| 按照用户限速 | √ | √ | √ | |
|
||||||
| 动态限速(未测试) | √ | √ | √ |
|
| 动态限速(未测试) | √ | √ | √ | |
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package panel
|
package panel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/goccy/go-json"
|
"github.com/goccy/go-json"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -9,21 +10,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NodeInfo struct {
|
type CommonNodeRsp struct {
|
||||||
NodeId int `json:"-"`
|
Host string `json:"host"`
|
||||||
NodeType string `json:"-"`
|
ServerPort int `json:"server_port"`
|
||||||
Rules []*regexp.Regexp `json:"-"`
|
ServerName string `json:"server_name"`
|
||||||
Host string `json:"host"`
|
Routes []Route `json:"routes"`
|
||||||
ServerPort int `json:"server_port"`
|
BaseConfig BaseConfig `json:"base_config"`
|
||||||
ServerName string `json:"server_name"`
|
|
||||||
Network string `json:"network"`
|
|
||||||
NetworkSettings json.RawMessage `json:"networkSettings"`
|
|
||||||
Cipher string `json:"cipher"`
|
|
||||||
ServerKey string `json:"server_key"`
|
|
||||||
Tls int `json:"tls"`
|
|
||||||
Routes []Route `json:"routes"`
|
|
||||||
BaseConfig *BaseConfig `json:"base_config"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Route struct {
|
type Route struct {
|
||||||
Id int `json:"id"`
|
Id int `json:"id"`
|
||||||
Match interface{} `json:"match"`
|
Match interface{} `json:"match"`
|
||||||
@ -35,37 +29,123 @@ type BaseConfig struct {
|
|||||||
PullInterval any `json:"pull_interval"`
|
PullInterval any `json:"pull_interval"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetNodeInfo() (nodeInfo *NodeInfo, err error) {
|
type V2rayNodeRsp struct {
|
||||||
|
Tls int `json:"tls"`
|
||||||
|
Network string `json:"network"`
|
||||||
|
NetworkSettings json.RawMessage `json:"networkSettings"`
|
||||||
|
ServerName string `json:"server_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShadowsocksNodeRsp struct {
|
||||||
|
Cipher string `json:"cipher"`
|
||||||
|
ServerKey string `json:"server_key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrojanNodeRsp struct {
|
||||||
|
Host string `json:"host"`
|
||||||
|
ServerName string `json:"server_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HysteriaNodeRsp struct {
|
||||||
|
UpMbps int `json:"up_mbps"`
|
||||||
|
DownMbps int `json:"down_mbps"`
|
||||||
|
Obfs string `json:"obfs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeInfo struct {
|
||||||
|
Id int
|
||||||
|
Type string
|
||||||
|
Rules []*regexp.Regexp
|
||||||
|
Host string
|
||||||
|
Port int
|
||||||
|
Network string
|
||||||
|
NetworkSettings json.RawMessage
|
||||||
|
Tls bool
|
||||||
|
ServerName string
|
||||||
|
UpMbps int
|
||||||
|
DownMbps int
|
||||||
|
ServerKey string
|
||||||
|
Cipher string
|
||||||
|
HyObfs string
|
||||||
|
PushInterval time.Duration
|
||||||
|
PullInterval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetNodeInfo() (node *NodeInfo, err error) {
|
||||||
const path = "/api/v1/server/UniProxy/config"
|
const path = "/api/v1/server/UniProxy/config"
|
||||||
r, err := c.client.R().Get(path)
|
r, err := c.client.R().Get(path)
|
||||||
if err = c.checkResponse(r, path, err); err != nil {
|
if err = c.checkResponse(r, path, err); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(r.Body(), &nodeInfo)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if c.etag == r.Header().Get("ETag") { // node info not changed
|
if c.etag == r.Header().Get("ETag") { // node info not changed
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
nodeInfo.NodeId = c.NodeId
|
// parse common params
|
||||||
nodeInfo.NodeType = c.NodeType
|
node = &NodeInfo{
|
||||||
for i := range nodeInfo.Routes { // parse rules from routes
|
Id: c.NodeId,
|
||||||
if nodeInfo.Routes[i].Action == "block" {
|
Type: c.NodeType,
|
||||||
|
}
|
||||||
|
common := CommonNodeRsp{}
|
||||||
|
err = json.Unmarshal(r.Body(), &common)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode common params error: %s", err)
|
||||||
|
}
|
||||||
|
for i := range common.Routes { // parse rules from routes
|
||||||
|
if common.Routes[i].Action == "block" {
|
||||||
var matchs []string
|
var matchs []string
|
||||||
if _, ok := nodeInfo.Routes[i].Match.(string); ok {
|
if _, ok := common.Routes[i].Match.(string); ok {
|
||||||
matchs = strings.Split(nodeInfo.Routes[i].Match.(string), ",")
|
matchs = strings.Split(common.Routes[i].Match.(string), ",")
|
||||||
} else {
|
} else {
|
||||||
matchs = nodeInfo.Routes[i].Match.([]string)
|
matchs = common.Routes[i].Match.([]string)
|
||||||
}
|
}
|
||||||
for _, v := range matchs {
|
for _, v := range matchs {
|
||||||
nodeInfo.Rules = append(nodeInfo.Rules, regexp.MustCompile(v))
|
node.Rules = append(node.Rules, regexp.MustCompile(v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nodeInfo.Routes = nil
|
node.ServerName = common.ServerName
|
||||||
nodeInfo.BaseConfig.PullInterval = intervalToTime(nodeInfo.BaseConfig.PullInterval)
|
node.Host = common.Host
|
||||||
nodeInfo.BaseConfig.PushInterval = intervalToTime(nodeInfo.BaseConfig.PushInterval)
|
node.Port = common.ServerPort
|
||||||
|
node.PullInterval = intervalToTime(common.BaseConfig.PullInterval)
|
||||||
|
node.PushInterval = intervalToTime(common.BaseConfig.PushInterval)
|
||||||
|
// parse protocol params
|
||||||
|
switch c.NodeType {
|
||||||
|
case "v2ray":
|
||||||
|
rsp := V2rayNodeRsp{}
|
||||||
|
err = json.Unmarshal(r.Body(), &rsp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode v2ray params error: %s", err)
|
||||||
|
}
|
||||||
|
node.Network = rsp.Network
|
||||||
|
node.NetworkSettings = rsp.NetworkSettings
|
||||||
|
node.ServerName = rsp.ServerName
|
||||||
|
if rsp.Tls == 1 {
|
||||||
|
node.Tls = true
|
||||||
|
}
|
||||||
|
case "shadowsocks":
|
||||||
|
rsp := ShadowsocksNodeRsp{}
|
||||||
|
err = json.Unmarshal(r.Body(), &rsp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode v2ray params error: %s", err)
|
||||||
|
}
|
||||||
|
node.ServerKey = rsp.ServerKey
|
||||||
|
node.Cipher = rsp.Cipher
|
||||||
|
case "trojan":
|
||||||
|
rsp := TrojanNodeRsp{}
|
||||||
|
err = json.Unmarshal(r.Body(), &rsp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode v2ray params error: %s", err)
|
||||||
|
}
|
||||||
|
case "hysteria":
|
||||||
|
rsp := HysteriaNodeRsp{}
|
||||||
|
err = json.Unmarshal(r.Body(), &rsp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode v2ray params error: %s", err)
|
||||||
|
}
|
||||||
|
node.DownMbps = rsp.DownMbps
|
||||||
|
node.UpMbps = rsp.UpMbps
|
||||||
|
node.HyObfs = rsp.Obfs
|
||||||
|
}
|
||||||
c.etag = r.Header().Get("Etag")
|
c.etag = r.Header().Get("Etag")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -43,9 +43,9 @@ func New(c *conf.ApiConfig) (*Client, error) {
|
|||||||
client.SetBaseURL(c.APIHost)
|
client.SetBaseURL(c.APIHost)
|
||||||
// Check node type
|
// Check node type
|
||||||
c.NodeType = strings.ToLower(c.NodeType)
|
c.NodeType = strings.ToLower(c.NodeType)
|
||||||
if c.NodeType != "v2ray" &&
|
switch c.NodeType {
|
||||||
c.NodeType != "trojan" &&
|
case "v2ray", "trojan", "shadowsocks", "hysteria":
|
||||||
c.NodeType != "shadowsocks" {
|
default:
|
||||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||||
}
|
}
|
||||||
// Create Key for each requests
|
// Create Key for each requests
|
||||||
|
@ -20,7 +20,7 @@ func (c *Client) checkResponse(res *resty.Response, path string, err error) erro
|
|||||||
}
|
}
|
||||||
if res.StatusCode() != 200 {
|
if res.StatusCode() != 200 {
|
||||||
body := res.Body()
|
body := res.Body()
|
||||||
return fmt.Errorf("request %s failed: %s, %s", c.assembleURL(path), string(body), err)
|
return fmt.Errorf("request %s failed: %s", c.assembleURL(path), string(body))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Yuzuki616/V2bX/api/panel"
|
"github.com/Yuzuki616/V2bX/api/panel"
|
||||||
"github.com/Yuzuki616/V2bX/common/file"
|
|
||||||
"github.com/Yuzuki616/V2bX/conf"
|
"github.com/Yuzuki616/V2bX/conf"
|
||||||
"github.com/Yuzuki616/V2bX/node/lego"
|
|
||||||
"github.com/goccy/go-json"
|
"github.com/goccy/go-json"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
@ -23,22 +21,22 @@ func BuildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag s
|
|||||||
t := coreConf.TransportProtocol(nodeInfo.Network)
|
t := coreConf.TransportProtocol(nodeInfo.Network)
|
||||||
in.StreamSetting = &coreConf.StreamConfig{Network: &t}
|
in.StreamSetting = &coreConf.StreamConfig{Network: &t}
|
||||||
var err error
|
var err error
|
||||||
switch nodeInfo.NodeType {
|
switch nodeInfo.Type {
|
||||||
case "v2ray":
|
case "v2ray":
|
||||||
err = buildV2ray(config, nodeInfo, in)
|
err = buildV2ray(config, nodeInfo, in)
|
||||||
case "trojan":
|
case "trojan":
|
||||||
err = buildTrojan(config, nodeInfo, in)
|
err = buildTrojan(config, in)
|
||||||
case "shadowsocks":
|
case "shadowsocks":
|
||||||
err = buildShadowsocks(config, nodeInfo, in)
|
err = buildShadowsocks(config, nodeInfo, in)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported node type: %s, Only support: V2ray, Trojan, Shadowsocks", nodeInfo.NodeType)
|
return nil, fmt.Errorf("unsupported node type: %s, Only support: V2ray, Trojan, Shadowsocks", nodeInfo.Type)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Set server port
|
// Set server port
|
||||||
in.PortList = &coreConf.PortList{
|
in.PortList = &coreConf.PortList{
|
||||||
Range: []coreConf.PortRange{{From: uint32(nodeInfo.ServerPort), To: uint32(nodeInfo.ServerPort)}},
|
Range: []coreConf.PortRange{{From: uint32(nodeInfo.Port), To: uint32(nodeInfo.Port)}},
|
||||||
}
|
}
|
||||||
// Set Listen IP address
|
// Set Listen IP address
|
||||||
ipAddress := net.ParseAddress(config.ListenIP)
|
ipAddress := net.ParseAddress(config.ListenIP)
|
||||||
@ -48,7 +46,7 @@ func BuildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag s
|
|||||||
Enabled: true,
|
Enabled: true,
|
||||||
DestOverride: &coreConf.StringList{"http", "tls"},
|
DestOverride: &coreConf.StringList{"http", "tls"},
|
||||||
}
|
}
|
||||||
if config.DisableSniffing {
|
if config.XrayOptions.DisableSniffing {
|
||||||
sniffingConfig.Enabled = false
|
sniffingConfig.Enabled = false
|
||||||
}
|
}
|
||||||
in.SniffingConfig = sniffingConfig
|
in.SniffingConfig = sniffingConfig
|
||||||
@ -66,7 +64,7 @@ func BuildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag s
|
|||||||
AcceptProxyProtocol: config.EnableProxyProtocol} //Enable proxy protocol
|
AcceptProxyProtocol: config.EnableProxyProtocol} //Enable proxy protocol
|
||||||
}
|
}
|
||||||
// Set TLS or Reality settings
|
// Set TLS or Reality settings
|
||||||
if nodeInfo.Tls != 0 {
|
if nodeInfo.Tls {
|
||||||
if config.CertConfig == nil {
|
if config.CertConfig == nil {
|
||||||
return nil, errors.New("the CertConfig is not vail")
|
return nil, errors.New("the CertConfig is not vail")
|
||||||
}
|
}
|
||||||
@ -91,16 +89,11 @@ func BuildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag s
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
// Normal tls
|
// Normal tls
|
||||||
in.StreamSetting.Security = "tls"
|
|
||||||
certFile, keyFile, err := getCertFile(config.CertConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
in.StreamSetting.TLSSettings = &coreConf.TLSConfig{
|
in.StreamSetting.TLSSettings = &coreConf.TLSConfig{
|
||||||
Certs: []*coreConf.TLSCertConfig{
|
Certs: []*coreConf.TLSCertConfig{
|
||||||
{
|
{
|
||||||
CertFile: certFile,
|
CertFile: config.CertConfig.CertFile,
|
||||||
KeyFile: keyFile,
|
KeyFile: config.CertConfig.KeyFile,
|
||||||
OcspStapling: 3600,
|
OcspStapling: 3600,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -112,23 +105,23 @@ func BuildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag s
|
|||||||
if *in.StreamSetting.Network != "tcp" &&
|
if *in.StreamSetting.Network != "tcp" &&
|
||||||
*in.StreamSetting.Network != "ws" &&
|
*in.StreamSetting.Network != "ws" &&
|
||||||
config.EnableProxyProtocol {
|
config.EnableProxyProtocol {
|
||||||
sockoptConfig := &coreConf.SocketConfig{
|
socketConfig := &coreConf.SocketConfig{
|
||||||
AcceptProxyProtocol: config.EnableProxyProtocol,
|
AcceptProxyProtocol: config.EnableProxyProtocol,
|
||||||
TFO: config.EnableTFO,
|
TFO: config.XrayOptions.EnableTFO,
|
||||||
} //Enable proxy protocol
|
} //Enable proxy protocol
|
||||||
in.StreamSetting.SocketSettings = sockoptConfig
|
in.StreamSetting.SocketSettings = socketConfig
|
||||||
}
|
}
|
||||||
in.Tag = tag
|
in.Tag = tag
|
||||||
return in.Build()
|
return in.Build()
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildV2ray(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error {
|
func buildV2ray(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error {
|
||||||
if config.EnableVless {
|
if config.XrayOptions.EnableVless {
|
||||||
//Set vless
|
//Set vless
|
||||||
inbound.Protocol = "vless"
|
inbound.Protocol = "vless"
|
||||||
if config.EnableFallback {
|
if config.XrayOptions.EnableFallback {
|
||||||
// Set fallback
|
// Set fallback
|
||||||
fallbackConfigs, err := buildVlessFallbacks(config.FallBackConfigs)
|
fallbackConfigs, err := buildVlessFallbacks(config.XrayOptions.FallBackConfigs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -183,11 +176,11 @@ func buildV2ray(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inbound
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildTrojan(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error {
|
func buildTrojan(config *conf.ControllerConfig, inbound *coreConf.InboundDetourConfig) error {
|
||||||
inbound.Protocol = "trojan"
|
inbound.Protocol = "trojan"
|
||||||
if config.EnableFallback {
|
if config.XrayOptions.EnableFallback {
|
||||||
// Set fallback
|
// Set fallback
|
||||||
fallbackConfigs, err := buildTrojanFallbacks(config.FallBackConfigs)
|
fallbackConfigs, err := buildTrojanFallbacks(config.XrayOptions.FallBackConfigs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -202,10 +195,7 @@ func buildTrojan(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inboun
|
|||||||
s := []byte("{}")
|
s := []byte("{}")
|
||||||
inbound.Settings = (*json.RawMessage)(&s)
|
inbound.Settings = (*json.RawMessage)(&s)
|
||||||
}
|
}
|
||||||
if nodeInfo.Network == "" {
|
t := coreConf.TransportProtocol("tcp")
|
||||||
nodeInfo.Network = "tcp"
|
|
||||||
}
|
|
||||||
t := coreConf.TransportProtocol(nodeInfo.Network)
|
|
||||||
inbound.StreamSetting = &coreConf.StreamConfig{Network: &t}
|
inbound.StreamSetting = &coreConf.StreamConfig{Network: &t}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -234,7 +224,7 @@ func buildShadowsocks(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, i
|
|||||||
settings.Users = append(settings.Users, defaultSSuser)
|
settings.Users = append(settings.Users, defaultSSuser)
|
||||||
settings.NetworkList = &coreConf.NetworkList{"tcp", "udp"}
|
settings.NetworkList = &coreConf.NetworkList{"tcp", "udp"}
|
||||||
settings.IVCheck = true
|
settings.IVCheck = true
|
||||||
if config.DisableIVCheck {
|
if config.XrayOptions.DisableIVCheck {
|
||||||
settings.IVCheck = false
|
settings.IVCheck = false
|
||||||
}
|
}
|
||||||
t := coreConf.TransportProtocol("tcp")
|
t := coreConf.TransportProtocol("tcp")
|
||||||
@ -247,30 +237,6 @@ func buildShadowsocks(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, i
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCertFile(certConfig *conf.CertConfig) (certFile string, keyFile string, err error) {
|
|
||||||
if certConfig.CertFile == "" || certConfig.KeyFile == "" {
|
|
||||||
return "", "", fmt.Errorf("cert file path or key file path not exist")
|
|
||||||
}
|
|
||||||
switch certConfig.CertMode {
|
|
||||||
case "file":
|
|
||||||
return certConfig.CertFile, certConfig.KeyFile, nil
|
|
||||||
case "dns", "http":
|
|
||||||
if file.IsExist(certConfig.CertFile) && file.IsExist(certConfig.KeyFile) {
|
|
||||||
return certConfig.CertFile, certConfig.KeyFile, nil
|
|
||||||
}
|
|
||||||
l, err := lego.New(certConfig)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf("create lego object error: %s", err)
|
|
||||||
}
|
|
||||||
err = l.CreateCert()
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf("create cert error: %s", err)
|
|
||||||
}
|
|
||||||
return certConfig.CertFile, certConfig.KeyFile, nil
|
|
||||||
}
|
|
||||||
return "", "", fmt.Errorf("unsupported certmode: %s", certConfig.CertMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildVlessFallbacks(fallbackConfigs []conf.FallBackConfig) ([]*coreConf.VLessInboundFallback, error) {
|
func buildVlessFallbacks(fallbackConfigs []conf.FallBackConfig) ([]*coreConf.VLessInboundFallback, error) {
|
||||||
if fallbackConfigs == nil {
|
if fallbackConfigs == nil {
|
||||||
return nil, fmt.Errorf("you must provide FallBackConfigs")
|
return nil, fmt.Errorf("you must provide FallBackConfigs")
|
||||||
|
@ -2,7 +2,6 @@ package builder
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Yuzuki616/V2bX/api/panel"
|
|
||||||
conf2 "github.com/Yuzuki616/V2bX/conf"
|
conf2 "github.com/Yuzuki616/V2bX/conf"
|
||||||
"github.com/goccy/go-json"
|
"github.com/goccy/go-json"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
@ -11,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// BuildOutbound build freedom outbund config for addoutbound
|
// BuildOutbound build freedom outbund config for addoutbound
|
||||||
func BuildOutbound(config *conf2.ControllerConfig, nodeInfo *panel.NodeInfo, tag string) (*core.OutboundHandlerConfig, error) {
|
func BuildOutbound(config *conf2.ControllerConfig, tag string) (*core.OutboundHandlerConfig, error) {
|
||||||
outboundDetourConfig := &conf.OutboundDetourConfig{}
|
outboundDetourConfig := &conf.OutboundDetourConfig{}
|
||||||
outboundDetourConfig.Protocol = "freedom"
|
outboundDetourConfig.Protocol = "freedom"
|
||||||
outboundDetourConfig.Tag = tag
|
outboundDetourConfig.Tag = tag
|
||||||
@ -24,9 +23,9 @@ func BuildOutbound(config *conf2.ControllerConfig, nodeInfo *panel.NodeInfo, tag
|
|||||||
|
|
||||||
// Freedom Protocol setting
|
// Freedom Protocol setting
|
||||||
var domainStrategy = "Asis"
|
var domainStrategy = "Asis"
|
||||||
if config.EnableDNS {
|
if config.XrayOptions.EnableDNS {
|
||||||
if config.DNSType != "" {
|
if config.XrayOptions.DNSType != "" {
|
||||||
domainStrategy = config.DNSType
|
domainStrategy = config.XrayOptions.DNSType
|
||||||
} else {
|
} else {
|
||||||
domainStrategy = "UseIP"
|
domainStrategy = "UseIP"
|
||||||
}
|
}
|
||||||
@ -37,7 +36,7 @@ func BuildOutbound(config *conf2.ControllerConfig, nodeInfo *panel.NodeInfo, tag
|
|||||||
var setting json.RawMessage
|
var setting json.RawMessage
|
||||||
setting, err := json.Marshal(proxySetting)
|
setting, err := json.Marshal(proxySetting)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("marshal proxy %s config fialed: %s", nodeInfo.NodeType, err)
|
return nil, fmt.Errorf("marshal proxy config error: %s", err)
|
||||||
}
|
}
|
||||||
outboundDetourConfig.Settings = &setting
|
outboundDetourConfig.Settings = &setting
|
||||||
return outboundDetourConfig.Build()
|
return outboundDetourConfig.Build()
|
||||||
|
@ -32,7 +32,7 @@ func BuildVmessUser(tag string, userInfo *panel.UserInfo) (user *protocol.User)
|
|||||||
}
|
}
|
||||||
return &protocol.User{
|
return &protocol.User{
|
||||||
Level: 0,
|
Level: 0,
|
||||||
Email: BuildUserTag(tag, userInfo), // Uid: InboundTag|email|uid
|
Email: BuildUserTag(tag, userInfo.Uuid), // Uid: InboundTag|email
|
||||||
Account: serial.ToTypedMessage(vmessAccount.Build()),
|
Account: serial.ToTypedMessage(vmessAccount.Build()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,7 +54,7 @@ func BuildVlessUser(tag string, userInfo *panel.UserInfo, xtls bool) (user *prot
|
|||||||
}
|
}
|
||||||
return &protocol.User{
|
return &protocol.User{
|
||||||
Level: 0,
|
Level: 0,
|
||||||
Email: BuildUserTag(tag, userInfo),
|
Email: BuildUserTag(tag, userInfo.Uuid),
|
||||||
Account: serial.ToTypedMessage(vlessAccount),
|
Account: serial.ToTypedMessage(vlessAccount),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ func BuildTrojanUser(tag string, userInfo *panel.UserInfo) (user *protocol.User)
|
|||||||
}
|
}
|
||||||
return &protocol.User{
|
return &protocol.User{
|
||||||
Level: 0,
|
Level: 0,
|
||||||
Email: BuildUserTag(tag, userInfo),
|
Email: BuildUserTag(tag, userInfo.Uuid),
|
||||||
Account: serial.ToTypedMessage(trojanAccount),
|
Account: serial.ToTypedMessage(trojanAccount),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,8 +115,8 @@ func BuildSSUser(tag string, userInfo *panel.UserInfo, cypher string, serverKey
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildUserTag(tag string, user *panel.UserInfo) string {
|
func BuildUserTag(tag string, uuid string) string {
|
||||||
return fmt.Sprintf("%s|%s|%d", tag, user.Uuid, user.Id)
|
return fmt.Sprintf("%s|%s", tag, uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCipherFromString(c string) shadowsocks.CipherType {
|
func getCipherFromString(c string) shadowsocks.CipherType {
|
||||||
|
@ -23,6 +23,7 @@ func New() *Conf {
|
|||||||
Type: "xray",
|
Type: "xray",
|
||||||
XrayConfig: &XrayConfig{
|
XrayConfig: &XrayConfig{
|
||||||
LogConfig: NewLogConfig(),
|
LogConfig: NewLogConfig(),
|
||||||
|
AssetPath: "/etc/V2bX/",
|
||||||
DnsConfigPath: "",
|
DnsConfigPath: "",
|
||||||
InboundConfigPath: "",
|
InboundConfigPath: "",
|
||||||
OutboundConfigPath: "",
|
OutboundConfigPath: "",
|
||||||
@ -35,9 +36,6 @@ func New() *Conf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Conf) LoadFromPath(filePath string) error {
|
func (p *Conf) LoadFromPath(filePath string) error {
|
||||||
confPath := path.Dir(filePath)
|
|
||||||
os.Setenv("XRAY_LOCATION_ASSET", confPath)
|
|
||||||
os.Setenv("XRAY_LOCATION_CONFIG", confPath)
|
|
||||||
f, err := os.Open(filePath)
|
f, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("open config file error: %s", err)
|
return fmt.Errorf("open config file error: %s", err)
|
||||||
|
@ -7,13 +7,13 @@ import (
|
|||||||
|
|
||||||
func TestConf_LoadFromPath(t *testing.T) {
|
func TestConf_LoadFromPath(t *testing.T) {
|
||||||
c := New()
|
c := New()
|
||||||
t.Log(c.LoadFromPath("../example/config.yml.example"), c.NodesConfig[0].ControllerConfig.EnableXtls)
|
t.Log(c.LoadFromPath("../example/config.yml.example"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConf_Watch(t *testing.T) {
|
func TestConf_Watch(t *testing.T) {
|
||||||
c := New()
|
c := New()
|
||||||
c.Watch("../example/config.yml.example", func() {
|
log.Println(c.Watch("../example/config.yml.example", func() {
|
||||||
log.Println(1)
|
log.Println(1)
|
||||||
})
|
}))
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ type CoreConfig struct {
|
|||||||
|
|
||||||
type XrayConfig struct {
|
type XrayConfig struct {
|
||||||
LogConfig *LogConfig `yaml:"Log"`
|
LogConfig *LogConfig `yaml:"Log"`
|
||||||
|
AssetPath string `yaml:"AssetPath"`
|
||||||
DnsConfigPath string `yaml:"DnsConfigPath"`
|
DnsConfigPath string `yaml:"DnsConfigPath"`
|
||||||
RouteConfigPath string `yaml:"RouteConfigPath"`
|
RouteConfigPath string `yaml:"RouteConfigPath"`
|
||||||
ConnectionConfig *ConnectionConfig `yaml:"ConnectionConfig"`
|
ConnectionConfig *ConnectionConfig `yaml:"ConnectionConfig"`
|
||||||
|
44
conf/node.go
44
conf/node.go
@ -15,22 +15,34 @@ type ApiConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ControllerConfig struct {
|
type ControllerConfig struct {
|
||||||
ListenIP string `yaml:"ListenIP"`
|
DisableUploadTraffic bool `yaml:"DisableUploadTraffic"`
|
||||||
SendIP string `yaml:"SendIP"`
|
DisableGetRule bool `yaml:"DisableGetRule"`
|
||||||
EnableDNS bool `yaml:"EnableDNS"`
|
ListenIP string `yaml:"ListenIP"`
|
||||||
DNSType string `yaml:"DNSType"`
|
SendIP string `yaml:"SendIP"`
|
||||||
EnableVless bool `yaml:"EnableVless"`
|
EnableProxyProtocol bool `yaml:"EnableProxyProtocol"`
|
||||||
EnableXtls bool `yaml:"EnableXtls"`
|
XrayOptions XrayOptions `yaml:"XrayOptions"`
|
||||||
LimitConfig LimitConfig `yaml:"LimitConfig"`
|
HyOptions HyOptions `yaml:"HyOptions"`
|
||||||
DisableUploadTraffic bool `yaml:"DisableUploadTraffic"`
|
LimitConfig LimitConfig `yaml:"LimitConfig"`
|
||||||
DisableGetRule bool `yaml:"DisableGetRule"`
|
CertConfig *CertConfig `yaml:"CertConfig"`
|
||||||
EnableProxyProtocol bool `yaml:"EnableProxyProtocol"`
|
}
|
||||||
EnableTFO bool `yaml:"EnableTFO"`
|
|
||||||
DisableIVCheck bool `yaml:"DisableIVCheck"`
|
type XrayOptions struct {
|
||||||
DisableSniffing bool `yaml:"DisableSniffing"`
|
EnableDNS bool `yaml:"EnableDNS"`
|
||||||
EnableFallback bool `yaml:"EnableFallback"`
|
DNSType string `yaml:"DNSType"`
|
||||||
FallBackConfigs []FallBackConfig `yaml:"FallBackConfigs"`
|
EnableVless bool `yaml:"EnableVless"`
|
||||||
CertConfig *CertConfig `yaml:"CertConfig"`
|
EnableXtls bool `yaml:"EnableXtls"`
|
||||||
|
EnableUot bool `yaml:"EnableUot"`
|
||||||
|
EnableTFO bool `yaml:"EnableTFO"`
|
||||||
|
DisableIVCheck bool `yaml:"DisableIVCheck"`
|
||||||
|
DisableSniffing bool `yaml:"DisableSniffing"`
|
||||||
|
EnableFallback bool `yaml:"EnableFallback"`
|
||||||
|
FallBackConfigs []FallBackConfig `yaml:"FallBackConfigs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HyOptions struct {
|
||||||
|
Resolver string `yaml:"Resolver"`
|
||||||
|
ResolvePreference string `yaml:"ResolvePreference"`
|
||||||
|
SendDevice string `yaml:"SendDevice"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LimitConfig struct {
|
type LimitConfig struct {
|
||||||
|
@ -49,7 +49,7 @@ func migrateOldConfig(c *Conf, old *OldConfig) {
|
|||||||
}
|
}
|
||||||
// node option
|
// node option
|
||||||
if old.NodesConfig[i].ApiConfig.EnableVless {
|
if old.NodesConfig[i].ApiConfig.EnableVless {
|
||||||
n.ControllerConfig.EnableVless = true
|
n.ControllerConfig.XrayOptions.EnableVless = true
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
// limit config
|
// limit config
|
||||||
|
19
core/core.go
19
core/core.go
@ -12,6 +12,25 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func NewCore(c *conf.CoreConfig) (Core, error) {
|
func NewCore(c *conf.CoreConfig) (Core, error) {
|
||||||
|
// multi core
|
||||||
|
if types := strings.Split(c.Type, " "); len(types) > 1 {
|
||||||
|
var cs []Core
|
||||||
|
for _, t := range types {
|
||||||
|
f, ok := cores[strings.ToLower(c.Type)]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("unknown core type: " + t)
|
||||||
|
}
|
||||||
|
core1, err := f(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cs = append(cs, core1)
|
||||||
|
}
|
||||||
|
return &Selecter{
|
||||||
|
cores: cs,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
// one core
|
||||||
if f, ok := cores[strings.ToLower(c.Type)]; ok {
|
if f, ok := cores[strings.ToLower(c.Type)]; ok {
|
||||||
return f(c)
|
return f(c)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
package hy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"go.uber.org/zap"
|
|
||||||
|
|
||||||
"github.com/caddyserver/certmagic"
|
|
||||||
)
|
|
||||||
|
|
||||||
func acmeTLSConfig(domains []string, email string, disableHTTP bool, disableTLSALPN bool,
|
|
||||||
altHTTPPort int, altTLSALPNPort int,
|
|
||||||
) (*tls.Config, error) {
|
|
||||||
cfg := &certmagic.Config{
|
|
||||||
RenewalWindowRatio: certmagic.DefaultRenewalWindowRatio,
|
|
||||||
KeySource: certmagic.DefaultKeyGenerator,
|
|
||||||
Storage: &certmagic.FileStorage{Path: dataDir()},
|
|
||||||
Logger: zap.NewNop(),
|
|
||||||
}
|
|
||||||
issuer := certmagic.NewACMEIssuer(cfg, certmagic.ACMEIssuer{
|
|
||||||
CA: certmagic.LetsEncryptProductionCA,
|
|
||||||
TestCA: certmagic.LetsEncryptStagingCA,
|
|
||||||
Email: email,
|
|
||||||
Agreed: true,
|
|
||||||
DisableHTTPChallenge: disableHTTP,
|
|
||||||
DisableTLSALPNChallenge: disableTLSALPN,
|
|
||||||
AltHTTPPort: altHTTPPort,
|
|
||||||
AltTLSALPNPort: altTLSALPNPort,
|
|
||||||
Logger: zap.NewNop(),
|
|
||||||
})
|
|
||||||
cfg.Issuers = []certmagic.Issuer{issuer}
|
|
||||||
|
|
||||||
cache := certmagic.NewCache(certmagic.CacheOptions{
|
|
||||||
GetConfigForCert: func(cert certmagic.Certificate) (*certmagic.Config, error) {
|
|
||||||
return cfg, nil
|
|
||||||
},
|
|
||||||
Logger: zap.NewNop(),
|
|
||||||
})
|
|
||||||
cfg = certmagic.New(cache, *cfg)
|
|
||||||
|
|
||||||
err := cfg.ManageSync(context.Background(), domains)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return cfg.TLSConfig(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func homeDir() string {
|
|
||||||
home := os.Getenv("HOME")
|
|
||||||
if home == "" && runtime.GOOS == "windows" {
|
|
||||||
drive := os.Getenv("HOMEDRIVE")
|
|
||||||
path := os.Getenv("HOMEPATH")
|
|
||||||
home = drive + path
|
|
||||||
if drive == "" || path == "" {
|
|
||||||
home = os.Getenv("USERPROFILE")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if home == "" {
|
|
||||||
home = "."
|
|
||||||
}
|
|
||||||
return home
|
|
||||||
}
|
|
||||||
|
|
||||||
func dataDir() string {
|
|
||||||
baseDir := filepath.Join(homeDir(), ".local", "share")
|
|
||||||
if xdgData := os.Getenv("XDG_DATA_HOME"); xdgData != "" {
|
|
||||||
baseDir = xdgData
|
|
||||||
}
|
|
||||||
return filepath.Join(baseDir, "certmagic")
|
|
||||||
}
|
|
@ -1,13 +1,5 @@
|
|||||||
package hy
|
package hy
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"github.com/yosuke-furukawa/json5/encoding/json5"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
mbpsToBps = 125000
|
mbpsToBps = 125000
|
||||||
minSpeedBPS = 16384
|
minSpeedBPS = 16384
|
||||||
@ -27,281 +19,8 @@ const (
|
|||||||
DefaultClientHopIntervalSec = 10
|
DefaultClientHopIntervalSec = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
var rateStringRegexp = regexp.MustCompile(`^(\d+)\s*([KMGT]?)([Bb])ps$`)
|
func SpeedTrans(upM, downM int) (uint64, uint64) {
|
||||||
|
up := uint64(upM) * mbpsToBps
|
||||||
type serverConfig struct {
|
down := uint64(downM) * mbpsToBps
|
||||||
Listen string `json:"listen"`
|
return up, down
|
||||||
Protocol string `json:"protocol"`
|
|
||||||
ACME struct {
|
|
||||||
Domains []string `json:"domains"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
DisableHTTPChallenge bool `json:"disable_http"`
|
|
||||||
DisableTLSALPNChallenge bool `json:"disable_tlsalpn"`
|
|
||||||
AltHTTPPort int `json:"alt_http_port"`
|
|
||||||
AltTLSALPNPort int `json:"alt_tlsalpn_port"`
|
|
||||||
} `json:"acme"`
|
|
||||||
CertFile string `json:"cert"`
|
|
||||||
KeyFile string `json:"key"`
|
|
||||||
// Optional below
|
|
||||||
Up string `json:"up"`
|
|
||||||
UpMbps int `json:"up_mbps"`
|
|
||||||
Down string `json:"down"`
|
|
||||||
DownMbps int `json:"down_mbps"`
|
|
||||||
DisableUDP bool `json:"disable_udp"`
|
|
||||||
ACL string `json:"acl"`
|
|
||||||
MMDB string `json:"mmdb"`
|
|
||||||
Obfs string `json:"obfs"`
|
|
||||||
Auth struct {
|
|
||||||
Mode string `json:"mode"`
|
|
||||||
Config json5.RawMessage `json:"config"`
|
|
||||||
} `json:"auth"`
|
|
||||||
ALPN string `json:"alpn"`
|
|
||||||
PrometheusListen string `json:"prometheus_listen"`
|
|
||||||
ReceiveWindowConn uint64 `json:"recv_window_conn"`
|
|
||||||
ReceiveWindowClient uint64 `json:"recv_window_client"`
|
|
||||||
MaxConnClient int `json:"max_conn_client"`
|
|
||||||
DisableMTUDiscovery bool `json:"disable_mtu_discovery"`
|
|
||||||
Resolver string `json:"resolver"`
|
|
||||||
ResolvePreference string `json:"resolve_preference"`
|
|
||||||
SOCKS5Outbound struct {
|
|
||||||
Server string `json:"server"`
|
|
||||||
User string `json:"user"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
} `json:"socks5_outbound"`
|
|
||||||
BindOutbound struct {
|
|
||||||
Address string `json:"address"`
|
|
||||||
Device string `json:"device"`
|
|
||||||
} `json:"bind_outbound"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConfig) Speed() (uint64, uint64, error) {
|
|
||||||
var up, down uint64
|
|
||||||
if len(c.Up) > 0 {
|
|
||||||
up = stringToBps(c.Up)
|
|
||||||
if up == 0 {
|
|
||||||
return 0, 0, errors.New("invalid speed format")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
up = uint64(c.UpMbps) * mbpsToBps
|
|
||||||
}
|
|
||||||
if len(c.Down) > 0 {
|
|
||||||
down = stringToBps(c.Down)
|
|
||||||
if down == 0 {
|
|
||||||
return 0, 0, errors.New("invalid speed format")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
down = uint64(c.DownMbps) * mbpsToBps
|
|
||||||
}
|
|
||||||
return up, down, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConfig) Check() error {
|
|
||||||
if len(c.Listen) == 0 {
|
|
||||||
return errors.New("missing listen address")
|
|
||||||
}
|
|
||||||
if len(c.ACME.Domains) == 0 && (len(c.CertFile) == 0 || len(c.KeyFile) == 0) {
|
|
||||||
return errors.New("need either ACME info or cert/key files")
|
|
||||||
}
|
|
||||||
if len(c.ACME.Domains) > 0 && (len(c.CertFile) > 0 || len(c.KeyFile) > 0) {
|
|
||||||
return errors.New("cannot use both ACME and cert/key files, they are mutually exclusive")
|
|
||||||
}
|
|
||||||
if up, down, err := c.Speed(); err != nil || (up != 0 && up < minSpeedBPS) || (down != 0 && down < minSpeedBPS) {
|
|
||||||
return errors.New("invalid speed")
|
|
||||||
}
|
|
||||||
if (c.ReceiveWindowConn != 0 && c.ReceiveWindowConn < 65536) ||
|
|
||||||
(c.ReceiveWindowClient != 0 && c.ReceiveWindowClient < 65536) {
|
|
||||||
return errors.New("invalid receive window size")
|
|
||||||
}
|
|
||||||
if c.MaxConnClient < 0 {
|
|
||||||
return errors.New("invalid max connections per client")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConfig) Fill() {
|
|
||||||
if len(c.ALPN) == 0 {
|
|
||||||
c.ALPN = DefaultALPN
|
|
||||||
}
|
|
||||||
if c.ReceiveWindowConn == 0 {
|
|
||||||
c.ReceiveWindowConn = DefaultStreamReceiveWindow
|
|
||||||
}
|
|
||||||
if c.ReceiveWindowClient == 0 {
|
|
||||||
c.ReceiveWindowClient = DefaultConnectionReceiveWindow
|
|
||||||
}
|
|
||||||
if c.MaxConnClient == 0 {
|
|
||||||
c.MaxConnClient = DefaultMaxIncomingStreams
|
|
||||||
}
|
|
||||||
if len(c.MMDB) == 0 {
|
|
||||||
c.MMDB = DefaultMMDBFilename
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConfig) String() string {
|
|
||||||
return fmt.Sprintf("%+v", *c)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Relay struct {
|
|
||||||
Listen string `json:"listen"`
|
|
||||||
Remote string `json:"remote"`
|
|
||||||
Timeout int `json:"timeout"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Relay) Check() error {
|
|
||||||
if len(r.Listen) == 0 {
|
|
||||||
return errors.New("missing relay listen address")
|
|
||||||
}
|
|
||||||
if len(r.Remote) == 0 {
|
|
||||||
return errors.New("missing relay remote address")
|
|
||||||
}
|
|
||||||
if r.Timeout != 0 && r.Timeout < 4 {
|
|
||||||
return errors.New("invalid relay timeout")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type clientConfig struct {
|
|
||||||
Server string `json:"server"`
|
|
||||||
Protocol string `json:"protocol"`
|
|
||||||
Up string `json:"up"`
|
|
||||||
UpMbps int `json:"up_mbps"`
|
|
||||||
Down string `json:"down"`
|
|
||||||
DownMbps int `json:"down_mbps"`
|
|
||||||
// Optional below
|
|
||||||
Retry int `json:"retry"`
|
|
||||||
RetryInterval *int `json:"retry_interval"`
|
|
||||||
QuitOnDisconnect bool `json:"quit_on_disconnect"`
|
|
||||||
HandshakeTimeout int `json:"handshake_timeout"`
|
|
||||||
IdleTimeout int `json:"idle_timeout"`
|
|
||||||
HopInterval int `json:"hop_interval"`
|
|
||||||
SOCKS5 struct {
|
|
||||||
Listen string `json:"listen"`
|
|
||||||
Timeout int `json:"timeout"`
|
|
||||||
DisableUDP bool `json:"disable_udp"`
|
|
||||||
User string `json:"user"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
} `json:"socks5"`
|
|
||||||
HTTP struct {
|
|
||||||
Listen string `json:"listen"`
|
|
||||||
Timeout int `json:"timeout"`
|
|
||||||
User string `json:"user"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
Cert string `json:"cert"`
|
|
||||||
Key string `json:"key"`
|
|
||||||
} `json:"http"`
|
|
||||||
TUN struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Timeout int `json:"timeout"`
|
|
||||||
MTU uint32 `json:"mtu"`
|
|
||||||
TCPSendBufferSize string `json:"tcp_sndbuf"`
|
|
||||||
TCPReceiveBufferSize string `json:"tcp_rcvbuf"`
|
|
||||||
TCPModerateReceiveBuffer bool `json:"tcp_autotuning"`
|
|
||||||
} `json:"tun"`
|
|
||||||
TCPRelays []Relay `json:"relay_tcps"`
|
|
||||||
TCPRelay Relay `json:"relay_tcp"` // deprecated, but we still support it for backward compatibility
|
|
||||||
UDPRelays []Relay `json:"relay_udps"`
|
|
||||||
UDPRelay Relay `json:"relay_udp"` // deprecated, but we still support it for backward compatibility
|
|
||||||
TCPTProxy struct {
|
|
||||||
Listen string `json:"listen"`
|
|
||||||
Timeout int `json:"timeout"`
|
|
||||||
} `json:"tproxy_tcp"`
|
|
||||||
UDPTProxy struct {
|
|
||||||
Listen string `json:"listen"`
|
|
||||||
Timeout int `json:"timeout"`
|
|
||||||
} `json:"tproxy_udp"`
|
|
||||||
TCPRedirect struct {
|
|
||||||
Listen string `json:"listen"`
|
|
||||||
Timeout int `json:"timeout"`
|
|
||||||
} `json:"redirect_tcp"`
|
|
||||||
ACL string `json:"acl"`
|
|
||||||
MMDB string `json:"mmdb"`
|
|
||||||
Obfs string `json:"obfs"`
|
|
||||||
Auth []byte `json:"auth"`
|
|
||||||
AuthString string `json:"auth_str"`
|
|
||||||
ALPN string `json:"alpn"`
|
|
||||||
ServerName string `json:"server_name"`
|
|
||||||
Insecure bool `json:"insecure"`
|
|
||||||
CustomCA string `json:"ca"`
|
|
||||||
ReceiveWindowConn uint64 `json:"recv_window_conn"`
|
|
||||||
ReceiveWindow uint64 `json:"recv_window"`
|
|
||||||
DisableMTUDiscovery bool `json:"disable_mtu_discovery"`
|
|
||||||
FastOpen bool `json:"fast_open"`
|
|
||||||
LazyStart bool `json:"lazy_start"`
|
|
||||||
Resolver string `json:"resolver"`
|
|
||||||
ResolvePreference string `json:"resolve_preference"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *clientConfig) Speed() (uint64, uint64, error) {
|
|
||||||
var up, down uint64
|
|
||||||
if len(c.Up) > 0 {
|
|
||||||
up = stringToBps(c.Up)
|
|
||||||
if up == 0 {
|
|
||||||
return 0, 0, errors.New("invalid speed format")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
up = uint64(c.UpMbps) * mbpsToBps
|
|
||||||
}
|
|
||||||
if len(c.Down) > 0 {
|
|
||||||
down = stringToBps(c.Down)
|
|
||||||
if down == 0 {
|
|
||||||
return 0, 0, errors.New("invalid speed format")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
down = uint64(c.DownMbps) * mbpsToBps
|
|
||||||
}
|
|
||||||
return up, down, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *clientConfig) Fill() {
|
|
||||||
if len(c.ALPN) == 0 {
|
|
||||||
c.ALPN = DefaultALPN
|
|
||||||
}
|
|
||||||
if c.ReceiveWindowConn == 0 {
|
|
||||||
c.ReceiveWindowConn = DefaultStreamReceiveWindow
|
|
||||||
}
|
|
||||||
if c.ReceiveWindow == 0 {
|
|
||||||
c.ReceiveWindow = DefaultConnectionReceiveWindow
|
|
||||||
}
|
|
||||||
if len(c.MMDB) == 0 {
|
|
||||||
c.MMDB = DefaultMMDBFilename
|
|
||||||
}
|
|
||||||
if c.IdleTimeout == 0 {
|
|
||||||
c.IdleTimeout = DefaultClientIdleTimeoutSec
|
|
||||||
}
|
|
||||||
if c.HopInterval == 0 {
|
|
||||||
c.HopInterval = DefaultClientHopIntervalSec
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *clientConfig) String() string {
|
|
||||||
return fmt.Sprintf("%+v", *c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func stringToBps(s string) uint64 {
|
|
||||||
if s == "" {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
m := rateStringRegexp.FindStringSubmatch(s)
|
|
||||||
if m == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var n uint64
|
|
||||||
switch m[2] {
|
|
||||||
case "K":
|
|
||||||
n = 1 << 10
|
|
||||||
case "M":
|
|
||||||
n = 1 << 20
|
|
||||||
case "G":
|
|
||||||
n = 1 << 30
|
|
||||||
case "T":
|
|
||||||
n = 1 << 40
|
|
||||||
default:
|
|
||||||
n = 1
|
|
||||||
}
|
|
||||||
v, _ := strconv.ParseUint(m[1], 10, 64)
|
|
||||||
n = v * n
|
|
||||||
if m[3] == "b" {
|
|
||||||
// Bits, need to convert to bytes
|
|
||||||
n = n >> 3
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
}
|
||||||
|
68
core/hy/counter.go
Normal file
68
core/hy/counter.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package hy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserTrafficCounter struct {
|
||||||
|
counters map[string]*counters
|
||||||
|
lock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type counters struct {
|
||||||
|
UpCounter atomic.Int64
|
||||||
|
DownCounter atomic.Int64
|
||||||
|
//ConnGauge atomic.Int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserTrafficCounter() *UserTrafficCounter {
|
||||||
|
return &UserTrafficCounter{
|
||||||
|
counters: map[string]*counters{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserTrafficCounter) getCounters(auth string) *counters {
|
||||||
|
c.lock.RLock()
|
||||||
|
cts, ok := c.counters[auth]
|
||||||
|
c.lock.RUnlock()
|
||||||
|
if !ok {
|
||||||
|
cts = &counters{}
|
||||||
|
c.counters[auth] = cts
|
||||||
|
}
|
||||||
|
return cts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserTrafficCounter) Rx(auth string, n int) {
|
||||||
|
cts := c.getCounters(auth)
|
||||||
|
cts.DownCounter.Add(int64(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserTrafficCounter) Tx(auth string, n int) {
|
||||||
|
cts := c.getCounters(auth)
|
||||||
|
cts.UpCounter.Add(int64(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserTrafficCounter) IncConn(_ string) {
|
||||||
|
/*cts := c.getCounters(auth)
|
||||||
|
cts.ConnGauge.Add(1)*/
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserTrafficCounter) DecConn(_ string) {
|
||||||
|
/*cts := c.getCounters(auth)
|
||||||
|
cts.ConnGauge.Add(1)*/
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserTrafficCounter) Reset(auth string) {
|
||||||
|
cts := c.getCounters(auth)
|
||||||
|
cts.UpCounter.Store(0)
|
||||||
|
cts.DownCounter.Store(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UserTrafficCounter) Delete(auth string) {
|
||||||
|
c.lock.Lock()
|
||||||
|
delete(c.counters, auth)
|
||||||
|
c.lock.Unlock()
|
||||||
|
}
|
7
core/hy/counter_test.go
Normal file
7
core/hy/counter_test.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package hy
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestUserTrafficCounter_Rx(t *testing.T) {
|
||||||
|
|
||||||
|
}
|
325
core/hy/hy.go
325
core/hy/hy.go
@ -1,306 +1,49 @@
|
|||||||
package hy
|
package hy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"fmt"
|
||||||
"io"
|
"sync"
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/oschwald/geoip2-golang"
|
"github.com/Yuzuki616/V2bX/conf"
|
||||||
"github.com/quic-go/quic-go"
|
vCore "github.com/Yuzuki616/V2bX/core"
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/apernet/hysteria/app/auth"
|
|
||||||
"github.com/apernet/hysteria/core/acl"
|
|
||||||
"github.com/apernet/hysteria/core/cs"
|
|
||||||
"github.com/apernet/hysteria/core/pktconns"
|
|
||||||
"github.com/apernet/hysteria/core/pmtud"
|
|
||||||
"github.com/apernet/hysteria/core/sockopt"
|
|
||||||
"github.com/apernet/hysteria/core/transport"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/yosuke-furukawa/json5/encoding/json5"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var serverPacketConnFuncFactoryMap = map[string]pktconns.ServerPacketConnFuncFactory{
|
func init() {
|
||||||
"": pktconns.NewServerUDPConnFunc,
|
vCore.RegisterCore("hy", NewHy)
|
||||||
"udp": pktconns.NewServerUDPConnFunc,
|
|
||||||
"wechat": pktconns.NewServerWeChatConnFunc,
|
|
||||||
"wechat-video": pktconns.NewServerWeChatConnFunc,
|
|
||||||
"faketcp": pktconns.NewServerFakeTCPConnFunc,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func server(config *serverConfig) {
|
type Hy struct {
|
||||||
logrus.WithField("config", config.String()).Info("Server configuration loaded")
|
servers sync.Map
|
||||||
config.Fill() // Fill default values
|
}
|
||||||
// Resolver
|
|
||||||
if len(config.Resolver) > 0 {
|
func NewHy(_ *conf.CoreConfig) (vCore.Core, error) {
|
||||||
err := setResolver(config.Resolver)
|
return &Hy{
|
||||||
|
servers: sync.Map{},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hy) Start() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hy) Close() error {
|
||||||
|
var errs error
|
||||||
|
h.servers.Range(func(tag, s any) bool {
|
||||||
|
err := s.(*Server).Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithFields(logrus.Fields{
|
errs = multierror.Append(errs, fmt.Errorf("close %s error: %s", tag, err))
|
||||||
"error": err,
|
|
||||||
}).Fatal("Failed to set resolver")
|
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if errs != nil {
|
||||||
|
return errs
|
||||||
}
|
}
|
||||||
// Load TLS config
|
return nil
|
||||||
var tlsConfig *tls.Config
|
|
||||||
if len(config.ACME.Domains) > 0 {
|
|
||||||
// ACME mode
|
|
||||||
tc, err := acmeTLSConfig(config.ACME.Domains, config.ACME.Email,
|
|
||||||
config.ACME.DisableHTTPChallenge, config.ACME.DisableTLSALPNChallenge,
|
|
||||||
config.ACME.AltHTTPPort, config.ACME.AltTLSALPNPort)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"error": err,
|
|
||||||
}).Fatal("Failed to get a certificate with ACME")
|
|
||||||
}
|
|
||||||
tc.NextProtos = []string{config.ALPN}
|
|
||||||
tc.MinVersion = tls.VersionTLS13
|
|
||||||
tlsConfig = tc
|
|
||||||
} else {
|
|
||||||
// Local cert mode
|
|
||||||
kpl, err := newKeypairLoader(config.CertFile, config.KeyFile)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"error": err,
|
|
||||||
"cert": config.CertFile,
|
|
||||||
"key": config.KeyFile,
|
|
||||||
}).Fatal("Failed to load the certificate")
|
|
||||||
}
|
|
||||||
tlsConfig = &tls.Config{
|
|
||||||
GetCertificate: kpl.GetCertificateFunc(),
|
|
||||||
NextProtos: []string{config.ALPN},
|
|
||||||
MinVersion: tls.VersionTLS13,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// QUIC config
|
|
||||||
quicConfig := &quic.Config{
|
|
||||||
InitialStreamReceiveWindow: config.ReceiveWindowConn,
|
|
||||||
MaxStreamReceiveWindow: config.ReceiveWindowConn,
|
|
||||||
InitialConnectionReceiveWindow: config.ReceiveWindowClient,
|
|
||||||
MaxConnectionReceiveWindow: config.ReceiveWindowClient,
|
|
||||||
MaxIncomingStreams: int64(config.MaxConnClient),
|
|
||||||
MaxIdleTimeout: ServerMaxIdleTimeoutSec * time.Second,
|
|
||||||
KeepAlivePeriod: 0, // Keep alive should solely be client's responsibility
|
|
||||||
DisablePathMTUDiscovery: config.DisableMTUDiscovery,
|
|
||||||
EnableDatagrams: true,
|
|
||||||
}
|
|
||||||
if !quicConfig.DisablePathMTUDiscovery && pmtud.DisablePathMTUDiscovery {
|
|
||||||
logrus.Info("Path MTU Discovery is not yet supported on this platform")
|
|
||||||
}
|
|
||||||
// Auth
|
|
||||||
var authFunc cs.ConnectFunc
|
|
||||||
var err error
|
|
||||||
switch authMode := config.Auth.Mode; authMode {
|
|
||||||
case "", "none":
|
|
||||||
if len(config.Obfs) == 0 {
|
|
||||||
logrus.Warn("Neither authentication nor obfuscation is turned on. " +
|
|
||||||
"Your server could be used by anyone! Are you sure this is what you want?")
|
|
||||||
}
|
|
||||||
authFunc = func(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) {
|
|
||||||
return true, "Welcome"
|
|
||||||
}
|
|
||||||
case "password", "passwords":
|
|
||||||
authFunc, err = auth.PasswordAuthFunc(config.Auth.Config)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"error": err,
|
|
||||||
}).Fatal("Failed to enable password authentication")
|
|
||||||
} else {
|
|
||||||
logrus.Info("Password authentication enabled")
|
|
||||||
}
|
|
||||||
case "external":
|
|
||||||
authFunc, err = auth.ExternalAuthFunc(config.Auth.Config)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"error": err,
|
|
||||||
}).Fatal("Failed to enable external authentication")
|
|
||||||
} else {
|
|
||||||
logrus.Info("External authentication enabled")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
logrus.WithField("mode", config.Auth.Mode).Fatal("Unsupported authentication mode")
|
|
||||||
}
|
|
||||||
connectFunc := func(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) {
|
|
||||||
ok, msg := authFunc(addr, auth, sSend, sRecv)
|
|
||||||
if !ok {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"src": defaultIPMasker.Mask(addr.String()),
|
|
||||||
"msg": msg,
|
|
||||||
}).Info("Authentication failed, client rejected")
|
|
||||||
} else {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"src": defaultIPMasker.Mask(addr.String()),
|
|
||||||
}).Info("Client connected")
|
|
||||||
}
|
|
||||||
return ok, msg
|
|
||||||
}
|
|
||||||
// Resolve preference
|
|
||||||
if len(config.ResolvePreference) > 0 {
|
|
||||||
pref, err := transport.ResolvePreferenceFromString(config.ResolvePreference)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"error": err,
|
|
||||||
}).Fatal("Failed to parse the resolve preference")
|
|
||||||
}
|
|
||||||
transport.DefaultServerTransport.ResolvePreference = pref
|
|
||||||
}
|
|
||||||
// SOCKS5 outbound
|
|
||||||
if config.SOCKS5Outbound.Server != "" {
|
|
||||||
transport.DefaultServerTransport.SOCKS5Client = transport.NewSOCKS5Client(config.SOCKS5Outbound.Server,
|
|
||||||
config.SOCKS5Outbound.User, config.SOCKS5Outbound.Password)
|
|
||||||
}
|
|
||||||
// Bind outbound
|
|
||||||
if config.BindOutbound.Device != "" {
|
|
||||||
iface, err := net.InterfaceByName(config.BindOutbound.Device)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"error": err,
|
|
||||||
}).Fatal("Failed to find the interface")
|
|
||||||
}
|
|
||||||
transport.DefaultServerTransport.LocalUDPIntf = iface
|
|
||||||
sockopt.BindDialer(transport.DefaultServerTransport.Dialer, iface)
|
|
||||||
}
|
|
||||||
if config.BindOutbound.Address != "" {
|
|
||||||
ip := net.ParseIP(config.BindOutbound.Address)
|
|
||||||
if ip == nil {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"error": err,
|
|
||||||
}).Fatal("Failed to parse the address")
|
|
||||||
}
|
|
||||||
transport.DefaultServerTransport.Dialer.LocalAddr = &net.TCPAddr{IP: ip}
|
|
||||||
transport.DefaultServerTransport.LocalUDPAddr = &net.UDPAddr{IP: ip}
|
|
||||||
}
|
|
||||||
// ACL
|
|
||||||
var aclEngine *acl.Engine
|
|
||||||
if len(config.ACL) > 0 {
|
|
||||||
aclEngine, err = acl.LoadFromFile(config.ACL, func(addr string) (*net.IPAddr, error) {
|
|
||||||
ipAddr, _, err := transport.DefaultServerTransport.ResolveIPAddr(addr)
|
|
||||||
return ipAddr, err
|
|
||||||
},
|
|
||||||
func() (*geoip2.Reader, error) {
|
|
||||||
return loadMMDBReader(config.MMDB)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"error": err,
|
|
||||||
"file": config.ACL,
|
|
||||||
}).Fatal("Failed to parse ACL")
|
|
||||||
}
|
|
||||||
aclEngine.DefaultAction = acl.ActionDirect
|
|
||||||
}
|
|
||||||
// Prometheus
|
|
||||||
var trafficCounter cs.TrafficCounter
|
|
||||||
if len(config.PrometheusListen) > 0 {
|
|
||||||
promReg := prometheus.NewRegistry()
|
|
||||||
trafficCounter = NewPrometheusTrafficCounter(promReg)
|
|
||||||
go func() {
|
|
||||||
http.Handle("/metrics", promhttp.HandlerFor(promReg, promhttp.HandlerOpts{}))
|
|
||||||
err := http.ListenAndServe(config.PrometheusListen, nil)
|
|
||||||
logrus.WithField("error", err).Fatal("Prometheus HTTP server error")
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
// Packet conn
|
|
||||||
pktConnFuncFactory := serverPacketConnFuncFactoryMap[config.Protocol]
|
|
||||||
if pktConnFuncFactory == nil {
|
|
||||||
logrus.WithField("protocol", config.Protocol).Fatal("Unsupported protocol")
|
|
||||||
}
|
|
||||||
pktConnFunc := pktConnFuncFactory(config.Obfs)
|
|
||||||
pktConn, err := pktConnFunc(config.Listen)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"error": err,
|
|
||||||
"addr": config.Listen,
|
|
||||||
}).Fatal("Failed to listen on the UDP address")
|
|
||||||
}
|
|
||||||
// Server
|
|
||||||
up, down, _ := config.Speed()
|
|
||||||
server, err := cs.NewServer(tlsConfig, quicConfig, pktConn,
|
|
||||||
transport.DefaultServerTransport, up, down, config.DisableUDP, aclEngine,
|
|
||||||
connectFunc, disconnectFunc, tcpRequestFunc, tcpErrorFunc, udpRequestFunc, udpErrorFunc, trafficCounter)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithField("error", err).Fatal("Failed to initialize server")
|
|
||||||
}
|
|
||||||
defer server.Close()
|
|
||||||
logrus.WithField("addr", config.Listen).Info("Server up and running")
|
|
||||||
|
|
||||||
err = server.Serve()
|
|
||||||
logrus.WithField("error", err).Fatal("Server shutdown")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func disconnectFunc(addr net.Addr, auth []byte, err error) {
|
func (h *Hy) Protocols() []string {
|
||||||
logrus.WithFields(logrus.Fields{
|
return []string{
|
||||||
"src": defaultIPMasker.Mask(addr.String()),
|
"hysteria",
|
||||||
"error": err,
|
|
||||||
}).Info("Client disconnected")
|
|
||||||
}
|
|
||||||
|
|
||||||
func tcpRequestFunc(addr net.Addr, auth []byte, reqAddr string, action acl.Action, arg string) {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"src": defaultIPMasker.Mask(addr.String()),
|
|
||||||
"dst": defaultIPMasker.Mask(reqAddr),
|
|
||||||
"action": actionToString(action, arg),
|
|
||||||
}).Debug("TCP request")
|
|
||||||
}
|
|
||||||
|
|
||||||
func tcpErrorFunc(addr net.Addr, auth []byte, reqAddr string, err error) {
|
|
||||||
if err != io.EOF {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"src": defaultIPMasker.Mask(addr.String()),
|
|
||||||
"dst": defaultIPMasker.Mask(reqAddr),
|
|
||||||
"error": err,
|
|
||||||
}).Info("TCP error")
|
|
||||||
} else {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"src": defaultIPMasker.Mask(addr.String()),
|
|
||||||
"dst": defaultIPMasker.Mask(reqAddr),
|
|
||||||
}).Debug("TCP EOF")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func udpRequestFunc(addr net.Addr, auth []byte, sessionID uint32) {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"src": defaultIPMasker.Mask(addr.String()),
|
|
||||||
"session": sessionID,
|
|
||||||
}).Debug("UDP request")
|
|
||||||
}
|
|
||||||
|
|
||||||
func udpErrorFunc(addr net.Addr, auth []byte, sessionID uint32, err error) {
|
|
||||||
if err != io.EOF {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"src": defaultIPMasker.Mask(addr.String()),
|
|
||||||
"session": sessionID,
|
|
||||||
"error": err,
|
|
||||||
}).Info("UDP error")
|
|
||||||
} else {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"src": defaultIPMasker.Mask(addr.String()),
|
|
||||||
"session": sessionID,
|
|
||||||
}).Debug("UDP EOF")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func actionToString(action acl.Action, arg string) string {
|
|
||||||
switch action {
|
|
||||||
case acl.ActionDirect:
|
|
||||||
return "Direct"
|
|
||||||
case acl.ActionProxy:
|
|
||||||
return "Proxy"
|
|
||||||
case acl.ActionBlock:
|
|
||||||
return "Block"
|
|
||||||
case acl.ActionHijack:
|
|
||||||
return "Hijack to " + arg
|
|
||||||
default:
|
|
||||||
return "Unknown"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseServerConfig(cb []byte) (*serverConfig, error) {
|
|
||||||
var c serverConfig
|
|
||||||
err := json5.Unmarshal(cb, &c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &c, c.Check()
|
|
||||||
}
|
|
||||||
|
38
core/hy/node.go
Normal file
38
core/hy/node.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package hy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Yuzuki616/V2bX/api/panel"
|
||||||
|
"github.com/Yuzuki616/V2bX/conf"
|
||||||
|
"github.com/apernet/hysteria/core/cs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Hy) AddNode(tag string, info *panel.NodeInfo, c *conf.ControllerConfig) error {
|
||||||
|
if info.Type != "hysteria" {
|
||||||
|
return errors.New("the core not support " + info.Type)
|
||||||
|
}
|
||||||
|
switch c.CertConfig.CertMode {
|
||||||
|
case "reality", "none", "":
|
||||||
|
return errors.New("hysteria need normal tls cert")
|
||||||
|
}
|
||||||
|
s := NewServer(tag)
|
||||||
|
err := s.runServer(info, c)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("run hy server error: %s", err)
|
||||||
|
}
|
||||||
|
h.servers.Store(tag, s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hy) DelNode(tag string) error {
|
||||||
|
if s, e := h.servers.Load(tag); e {
|
||||||
|
err := s.(*cs.Server).Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
h.servers.Delete(tag)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("the node is not have")
|
||||||
|
}
|
@ -1,71 +0,0 @@
|
|||||||
package hy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/apernet/hysteria/core/cs"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
)
|
|
||||||
|
|
||||||
type prometheusTrafficCounter struct {
|
|
||||||
reg *prometheus.Registry
|
|
||||||
upCounterVec *prometheus.CounterVec
|
|
||||||
downCounterVec *prometheus.CounterVec
|
|
||||||
connGaugeVec *prometheus.GaugeVec
|
|
||||||
counterMap map[string]counters
|
|
||||||
}
|
|
||||||
|
|
||||||
type counters struct {
|
|
||||||
UpCounter prometheus.Counter
|
|
||||||
DownCounter prometheus.Counter
|
|
||||||
ConnGauge prometheus.Gauge
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPrometheusTrafficCounter(reg *prometheus.Registry) cs.TrafficCounter {
|
|
||||||
c := &prometheusTrafficCounter{
|
|
||||||
reg: reg,
|
|
||||||
upCounterVec: prometheus.NewCounterVec(prometheus.CounterOpts{
|
|
||||||
Name: "hysteria_traffic_uplink_bytes_total",
|
|
||||||
}, []string{"auth"}),
|
|
||||||
downCounterVec: prometheus.NewCounterVec(prometheus.CounterOpts{
|
|
||||||
Name: "hysteria_traffic_downlink_bytes_total",
|
|
||||||
}, []string{"auth"}),
|
|
||||||
connGaugeVec: prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
|
||||||
Name: "hysteria_active_conn",
|
|
||||||
}, []string{"auth"}),
|
|
||||||
counterMap: make(map[string]counters),
|
|
||||||
}
|
|
||||||
reg.MustRegister(c.upCounterVec, c.downCounterVec, c.connGaugeVec)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *prometheusTrafficCounter) getCounters(auth string) counters {
|
|
||||||
cts, ok := c.counterMap[auth]
|
|
||||||
if !ok {
|
|
||||||
cts = counters{
|
|
||||||
UpCounter: c.upCounterVec.WithLabelValues(auth),
|
|
||||||
DownCounter: c.downCounterVec.WithLabelValues(auth),
|
|
||||||
ConnGauge: c.connGaugeVec.WithLabelValues(auth),
|
|
||||||
}
|
|
||||||
c.counterMap[auth] = cts
|
|
||||||
}
|
|
||||||
return cts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *prometheusTrafficCounter) Rx(auth string, n int) {
|
|
||||||
cts := c.getCounters(auth)
|
|
||||||
cts.DownCounter.Add(float64(n))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *prometheusTrafficCounter) Tx(auth string, n int) {
|
|
||||||
cts := c.getCounters(auth)
|
|
||||||
cts.UpCounter.Add(float64(n))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *prometheusTrafficCounter) IncConn(auth string) {
|
|
||||||
cts := c.getCounters(auth)
|
|
||||||
cts.ConnGauge.Inc()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *prometheusTrafficCounter) DecConn(auth string) {
|
|
||||||
cts := c.getCounters(auth)
|
|
||||||
cts.ConnGauge.Dec()
|
|
||||||
}
|
|
263
core/hy/server.go
Normal file
263
core/hy/server.go
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
package hy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Yuzuki616/V2bX/api/panel"
|
||||||
|
"github.com/Yuzuki616/V2bX/conf"
|
||||||
|
"github.com/apernet/hysteria/core/sockopt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/quic-go/quic-go"
|
||||||
|
|
||||||
|
"github.com/apernet/hysteria/core/acl"
|
||||||
|
"github.com/apernet/hysteria/core/cs"
|
||||||
|
"github.com/apernet/hysteria/core/pktconns"
|
||||||
|
"github.com/apernet/hysteria/core/pmtud"
|
||||||
|
"github.com/apernet/hysteria/core/transport"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var serverPacketConnFuncFactoryMap = map[string]pktconns.ServerPacketConnFuncFactory{
|
||||||
|
"": pktconns.NewServerUDPConnFunc,
|
||||||
|
"udp": pktconns.NewServerUDPConnFunc,
|
||||||
|
"wechat": pktconns.NewServerWeChatConnFunc,
|
||||||
|
"wechat-video": pktconns.NewServerWeChatConnFunc,
|
||||||
|
"faketcp": pktconns.NewServerFakeTCPConnFunc,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
tag string
|
||||||
|
counter *UserTrafficCounter
|
||||||
|
users sync.Map
|
||||||
|
running atomic.Bool
|
||||||
|
*cs.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(tag string) *Server {
|
||||||
|
return &Server{
|
||||||
|
tag: tag,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) runServer(node *panel.NodeInfo, c *conf.ControllerConfig) error {
|
||||||
|
/*if c.HyOptions == nil {
|
||||||
|
return errors.New("hy options is not vail")
|
||||||
|
}*/
|
||||||
|
// Resolver
|
||||||
|
if len(c.HyOptions.Resolver) > 0 {
|
||||||
|
err := setResolver(c.HyOptions.Resolver)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("set resolver error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// tls config
|
||||||
|
kpl, err := newKeypairLoader(c.CertConfig.CertFile, c.CertConfig.KeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("load cert error: %s", err)
|
||||||
|
}
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
GetCertificate: kpl.GetCertificateFunc(),
|
||||||
|
NextProtos: []string{DefaultALPN},
|
||||||
|
MinVersion: tls.VersionTLS13,
|
||||||
|
}
|
||||||
|
// QUIC config
|
||||||
|
quicConfig := &quic.Config{
|
||||||
|
InitialStreamReceiveWindow: DefaultStreamReceiveWindow,
|
||||||
|
MaxStreamReceiveWindow: DefaultStreamReceiveWindow,
|
||||||
|
InitialConnectionReceiveWindow: DefaultConnectionReceiveWindow,
|
||||||
|
MaxConnectionReceiveWindow: DefaultConnectionReceiveWindow,
|
||||||
|
MaxIncomingStreams: int64(DefaultMaxIncomingStreams),
|
||||||
|
MaxIdleTimeout: ServerMaxIdleTimeoutSec * time.Second,
|
||||||
|
KeepAlivePeriod: 0, // Keep alive should solely be client's responsibility
|
||||||
|
DisablePathMTUDiscovery: false,
|
||||||
|
EnableDatagrams: true,
|
||||||
|
}
|
||||||
|
if !quicConfig.DisablePathMTUDiscovery && pmtud.DisablePathMTUDiscovery {
|
||||||
|
logrus.Info("Path MTU Discovery is not yet supported on this platform")
|
||||||
|
}
|
||||||
|
// Resolve preference
|
||||||
|
if len(c.HyOptions.ResolvePreference) > 0 {
|
||||||
|
pref, err := transport.ResolvePreferenceFromString(c.HyOptions.Resolver)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"error": err,
|
||||||
|
}).Fatal("Failed to parse the resolve preference")
|
||||||
|
}
|
||||||
|
transport.DefaultServerTransport.ResolvePreference = pref
|
||||||
|
}
|
||||||
|
/*// SOCKS5 outbound
|
||||||
|
if config.SOCKS5Outbound.Server != "" {
|
||||||
|
transport.DefaultServerTransport.SOCKS5Client = transport.NewSOCKS5Client(config.SOCKS5Outbound.Server,
|
||||||
|
config.SOCKS5Outbound.User, config.SOCKS5Outbound.Password)
|
||||||
|
}*/
|
||||||
|
// Bind outbound
|
||||||
|
if c.HyOptions.SendDevice != "" {
|
||||||
|
iface, err := net.InterfaceByName(c.HyOptions.SendDevice)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"error": err,
|
||||||
|
}).Fatal("Failed to find the interface")
|
||||||
|
}
|
||||||
|
transport.DefaultServerTransport.LocalUDPIntf = iface
|
||||||
|
sockopt.BindDialer(transport.DefaultServerTransport.Dialer, iface)
|
||||||
|
}
|
||||||
|
if c.SendIP != "" {
|
||||||
|
ip := net.ParseIP(c.SendIP)
|
||||||
|
if ip == nil {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"error": err,
|
||||||
|
}).Fatal("Failed to parse the address")
|
||||||
|
}
|
||||||
|
transport.DefaultServerTransport.Dialer.LocalAddr = &net.TCPAddr{IP: ip}
|
||||||
|
transport.DefaultServerTransport.LocalUDPAddr = &net.UDPAddr{IP: ip}
|
||||||
|
}
|
||||||
|
// ACL
|
||||||
|
var aclEngine *acl.Engine
|
||||||
|
/*if len(config.ACL) > 0 {
|
||||||
|
aclEngine, err = acl.LoadFromFile(config.ACL, func(addr string) (*net.IPAddr, error) {
|
||||||
|
ipAddr, _, err := transport.DefaultServerTransport.ResolveIPAddr(addr)
|
||||||
|
return ipAddr, err
|
||||||
|
},
|
||||||
|
func() (*geoip2.Reader, error) {
|
||||||
|
return loadMMDBReader(config.MMDB)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"error": err,
|
||||||
|
"file": config.ACL,
|
||||||
|
}).Fatal("Failed to parse ACL")
|
||||||
|
}
|
||||||
|
aclEngine.DefaultAction = acl.ActionDirect
|
||||||
|
}*/
|
||||||
|
// Prometheus
|
||||||
|
s.counter = NewUserTrafficCounter()
|
||||||
|
// Packet conn
|
||||||
|
pktConnFuncFactory := serverPacketConnFuncFactoryMap[""]
|
||||||
|
if pktConnFuncFactory == nil {
|
||||||
|
return fmt.Errorf("unsopport protocol")
|
||||||
|
}
|
||||||
|
pktConnFunc := pktConnFuncFactory(node.HyObfs)
|
||||||
|
addr := fmt.Sprintf("%s:%d", c.ListenIP, node.Port)
|
||||||
|
pktConn, err := pktConnFunc(addr)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"error": err,
|
||||||
|
"addr": addr,
|
||||||
|
}).Fatal("Failed to listen on the UDP address")
|
||||||
|
}
|
||||||
|
// Server
|
||||||
|
up, down := SpeedTrans(node.UpMbps, node.DownMbps)
|
||||||
|
s.Server, err = cs.NewServer(tlsConfig, quicConfig, pktConn,
|
||||||
|
transport.DefaultServerTransport, up, down, false, aclEngine,
|
||||||
|
s.connectFunc, s.disconnectFunc, tcpRequestFunc, tcpErrorFunc, udpRequestFunc, udpErrorFunc, s.counter)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("new server error: %s", err)
|
||||||
|
}
|
||||||
|
logrus.WithField("addr", addr).Info("Server up and running")
|
||||||
|
go func() {
|
||||||
|
s.running.Store(true)
|
||||||
|
defer func() {
|
||||||
|
s.running.Store(false)
|
||||||
|
}()
|
||||||
|
err = s.Server.Serve()
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithField("addr", addr).Errorf("serve error: %s", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) authByUser(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) {
|
||||||
|
if _, ok := s.users.Load(string(auth)); ok {
|
||||||
|
return true, "Done"
|
||||||
|
}
|
||||||
|
return false, "Failed"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) connectFunc(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) {
|
||||||
|
ok, msg := s.authByUser(addr, auth, sSend, sRecv)
|
||||||
|
if !ok {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"src": defaultIPMasker.Mask(addr.String()),
|
||||||
|
}).Info("Authentication failed, client rejected")
|
||||||
|
return false, msg
|
||||||
|
}
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"src": defaultIPMasker.Mask(addr.String()),
|
||||||
|
"Uuid": string(auth),
|
||||||
|
"Tag": s.tag,
|
||||||
|
}).Info("Client connected")
|
||||||
|
return ok, msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) disconnectFunc(addr net.Addr, auth []byte, err error) {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"src": defaultIPMasker.Mask(addr.String()),
|
||||||
|
"error": err,
|
||||||
|
}).Info("Client disconnected")
|
||||||
|
}
|
||||||
|
|
||||||
|
func tcpRequestFunc(addr net.Addr, auth []byte, reqAddr string, action acl.Action, arg string) {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"src": defaultIPMasker.Mask(addr.String()),
|
||||||
|
"dst": defaultIPMasker.Mask(reqAddr),
|
||||||
|
"action": actionToString(action, arg),
|
||||||
|
}).Debug("TCP request")
|
||||||
|
}
|
||||||
|
|
||||||
|
func tcpErrorFunc(addr net.Addr, auth []byte, reqAddr string, err error) {
|
||||||
|
if err != io.EOF {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"src": defaultIPMasker.Mask(addr.String()),
|
||||||
|
"dst": defaultIPMasker.Mask(reqAddr),
|
||||||
|
"error": err,
|
||||||
|
}).Info("TCP error")
|
||||||
|
} else {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"src": defaultIPMasker.Mask(addr.String()),
|
||||||
|
"dst": defaultIPMasker.Mask(reqAddr),
|
||||||
|
}).Debug("TCP EOF")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func udpRequestFunc(addr net.Addr, auth []byte, sessionID uint32) {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"src": defaultIPMasker.Mask(addr.String()),
|
||||||
|
"session": sessionID,
|
||||||
|
}).Debug("UDP request")
|
||||||
|
}
|
||||||
|
|
||||||
|
func udpErrorFunc(addr net.Addr, auth []byte, sessionID uint32, err error) {
|
||||||
|
if err != io.EOF {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"src": defaultIPMasker.Mask(addr.String()),
|
||||||
|
"session": sessionID,
|
||||||
|
"error": err,
|
||||||
|
}).Info("UDP error")
|
||||||
|
} else {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"src": defaultIPMasker.Mask(addr.String()),
|
||||||
|
"session": sessionID,
|
||||||
|
}).Debug("UDP EOF")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func actionToString(action acl.Action, arg string) string {
|
||||||
|
switch action {
|
||||||
|
case acl.ActionDirect:
|
||||||
|
return "Direct"
|
||||||
|
case acl.ActionProxy:
|
||||||
|
return "Proxy"
|
||||||
|
case acl.ActionBlock:
|
||||||
|
return "Block"
|
||||||
|
case acl.ActionHijack:
|
||||||
|
return "Hijack to " + arg
|
||||||
|
default:
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
}
|
28
core/hy/server_test.go
Normal file
28
core/hy/server_test.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package hy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Yuzuki616/V2bX/api/panel"
|
||||||
|
"github.com/Yuzuki616/V2bX/conf"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServer(t *testing.T) {
|
||||||
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
|
s := NewServer("test")
|
||||||
|
t.Log(s.runServer(&panel.NodeInfo{
|
||||||
|
Port: 11415,
|
||||||
|
UpMbps: 100,
|
||||||
|
DownMbps: 100,
|
||||||
|
HyObfs: "atresssdaaaadd",
|
||||||
|
}, &conf.ControllerConfig{
|
||||||
|
ListenIP: "127.0.0.1",
|
||||||
|
HyOptions: conf.HyOptions{},
|
||||||
|
CertConfig: &conf.CertConfig{
|
||||||
|
CertFile: "../../test_data/1.pem",
|
||||||
|
KeyFile: "../../test_data/1.key",
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
s.users.Store("test1111", struct{}{})
|
||||||
|
select {}
|
||||||
|
}
|
44
core/hy/user.go
Normal file
44
core/hy/user.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package hy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"github.com/Yuzuki616/V2bX/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Hy) AddUsers(p *core.AddUsersParams) (int, error) {
|
||||||
|
s, ok := h.servers.Load(p.Tag)
|
||||||
|
if !ok {
|
||||||
|
return 0, errors.New("the node not have")
|
||||||
|
}
|
||||||
|
u := &s.(*Server).users
|
||||||
|
for i := range p.UserInfo {
|
||||||
|
u.Store(p.UserInfo[i].Uuid, struct{}{})
|
||||||
|
}
|
||||||
|
return len(p.UserInfo), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hy) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) {
|
||||||
|
v, _ := h.servers.Load(tag)
|
||||||
|
s := v.(*Server)
|
||||||
|
auth := base64.StdEncoding.EncodeToString([]byte(uuid))
|
||||||
|
up = s.counter.getCounters(auth).UpCounter.Load()
|
||||||
|
down = s.counter.getCounters(uuid).DownCounter.Load()
|
||||||
|
if reset {
|
||||||
|
s.counter.Reset(uuid)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hy) DelUsers(users []string, tag string) error {
|
||||||
|
v, e := h.servers.Load(tag)
|
||||||
|
if !e {
|
||||||
|
return errors.New("the node is not have")
|
||||||
|
}
|
||||||
|
s := v.(*Server)
|
||||||
|
for i := range users {
|
||||||
|
s.users.Delete(users[i])
|
||||||
|
s.counter.Delete(users[i])
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
package imports
|
package imports
|
||||||
|
|
||||||
// not works for now
|
// not yet tested
|
||||||
//import _ "github.com/Yuzuki616/V2bX/core/hy"
|
import _ "github.com/Yuzuki616/V2bX/core/hy"
|
||||||
|
@ -17,6 +17,7 @@ type Core interface {
|
|||||||
AddNode(tag string, info *panel.NodeInfo, config *conf.ControllerConfig) error
|
AddNode(tag string, info *panel.NodeInfo, config *conf.ControllerConfig) error
|
||||||
DelNode(tag string) error
|
DelNode(tag string) error
|
||||||
AddUsers(p *AddUsersParams) (added int, err error)
|
AddUsers(p *AddUsersParams) (added int, err error)
|
||||||
GetUserTraffic(email string, reset bool) (up int64, down int64)
|
GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64)
|
||||||
DelUsers(users []string, tag string) error
|
DelUsers(users []string, tag string) error
|
||||||
|
Protocols() []string
|
||||||
}
|
}
|
||||||
|
98
core/selecter.go
Normal file
98
core/selecter.go
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Yuzuki616/V2bX/api/panel"
|
||||||
|
"github.com/Yuzuki616/V2bX/conf"
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Selecter struct {
|
||||||
|
cores []Core
|
||||||
|
nodes sync.Map
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Selecter) Start() error {
|
||||||
|
for i := range s.cores {
|
||||||
|
err := s.cores[i].Start()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Selecter) Close() error {
|
||||||
|
var errs error
|
||||||
|
for i := range s.cores {
|
||||||
|
errs = multierror.Append(errs, s.cores[i].Close())
|
||||||
|
}
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSupported(protocol string, protocols []string) bool {
|
||||||
|
for i := range protocols {
|
||||||
|
if protocol == protocols[i] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Selecter) AddNode(tag string, info *panel.NodeInfo, config *conf.ControllerConfig) error {
|
||||||
|
for i := range s.cores {
|
||||||
|
if !isSupported(info.Type, s.cores[i].Protocols()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := s.cores[i].AddNode(tag, info, config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.nodes.Store(tag, i)
|
||||||
|
}
|
||||||
|
return errors.New("the node type is not support")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Selecter) DelNode(tag string) error {
|
||||||
|
if t, e := s.nodes.Load(tag); e {
|
||||||
|
err := s.cores[t.(int)].DelNode(tag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.nodes.Delete(tag)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("the node is not have")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Selecter) AddUsers(p *AddUsersParams) (added int, err error) {
|
||||||
|
t, e := s.nodes.Load(p.Tag)
|
||||||
|
if !e {
|
||||||
|
return 0, errors.New("the node is not have")
|
||||||
|
}
|
||||||
|
return s.cores[t.(int)].AddUsers(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Selecter) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) {
|
||||||
|
t, e := s.nodes.Load(tag)
|
||||||
|
if !e {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
return s.cores[t.(int)].GetUserTraffic(tag, uuid, reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Selecter) DelUsers(users []string, tag string) error {
|
||||||
|
t, e := s.nodes.Load(tag)
|
||||||
|
if !e {
|
||||||
|
return errors.New("the node is not have")
|
||||||
|
}
|
||||||
|
return s.cores[t.(int)].DelUsers(users, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Selecter) Protocols() []string {
|
||||||
|
protocols := make([]string, 0)
|
||||||
|
for i := range s.cores {
|
||||||
|
protocols = append(protocols, s.cores[i].Protocols()...)
|
||||||
|
}
|
||||||
|
return protocols
|
||||||
|
}
|
@ -20,7 +20,7 @@ func (c *Core) AddNode(tag string, info *panel.NodeInfo, config *conf.Controller
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("add inbound error: %s", err)
|
return fmt.Errorf("add inbound error: %s", err)
|
||||||
}
|
}
|
||||||
outBoundConfig, err := builder.BuildOutbound(config, info, tag)
|
outBoundConfig, err := builder.BuildOutbound(config, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("build outbound error: %s", err)
|
return fmt.Errorf("build outbound error: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -40,9 +40,9 @@ func (c *Core) DelUsers(users []string, tag string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) GetUserTraffic(email string, reset bool) (up int64, down int64) {
|
func (c *Core) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) {
|
||||||
upName := "user>>>" + email + ">>>traffic>>>uplink"
|
upName := "user>>>" + builder.BuildUserTag(tag, uuid) + ">>>traffic>>>uplink"
|
||||||
downName := "user>>>" + email + ">>>traffic>>>downlink"
|
downName := "user>>>" + builder.BuildUserTag(tag, uuid) + ">>>traffic>>>downlink"
|
||||||
upCounter := c.shm.GetCounter(upName)
|
upCounter := c.shm.GetCounter(upName)
|
||||||
downCounter := c.shm.GetCounter(downName)
|
downCounter := c.shm.GetCounter(downName)
|
||||||
if reset {
|
if reset {
|
||||||
@ -65,10 +65,10 @@ func (c *Core) GetUserTraffic(email string, reset bool) (up int64, down int64) {
|
|||||||
|
|
||||||
func (c *Core) AddUsers(p *vCore.AddUsersParams) (added int, err error) {
|
func (c *Core) AddUsers(p *vCore.AddUsersParams) (added int, err error) {
|
||||||
users := make([]*protocol.User, 0, len(p.UserInfo))
|
users := make([]*protocol.User, 0, len(p.UserInfo))
|
||||||
switch p.NodeInfo.NodeType {
|
switch p.NodeInfo.Type {
|
||||||
case "v2ray":
|
case "v2ray":
|
||||||
if p.Config.EnableVless {
|
if p.Config.XrayOptions.EnableXtls {
|
||||||
users = builder.BuildVlessUsers(p.Tag, p.UserInfo, p.Config.EnableXtls)
|
users = builder.BuildVlessUsers(p.Tag, p.UserInfo, true)
|
||||||
} else {
|
} else {
|
||||||
users = builder.BuildVmessUsers(p.Tag, p.UserInfo)
|
users = builder.BuildVmessUsers(p.Tag, p.UserInfo)
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ func (c *Core) AddUsers(p *vCore.AddUsersParams) (added int, err error) {
|
|||||||
p.NodeInfo.Cipher,
|
p.NodeInfo.Cipher,
|
||||||
p.NodeInfo.ServerKey)
|
p.NodeInfo.ServerKey)
|
||||||
default:
|
default:
|
||||||
return 0, fmt.Errorf("unsupported node type: %s", p.NodeInfo.NodeType)
|
return 0, fmt.Errorf("unsupported node type: %s", p.NodeInfo.Type)
|
||||||
}
|
}
|
||||||
man, err := c.GetUserManager(p.Tag)
|
man, err := c.GetUserManager(p.Tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -53,6 +53,7 @@ func parseConnectionConfig(c *conf.ConnectionConfig) (policy *coreConf.Policy) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getCore(c *conf.XrayConfig) *core.Instance {
|
func getCore(c *conf.XrayConfig) *core.Instance {
|
||||||
|
os.Setenv("XRAY_LOCATION_ASSET", c.AssetPath)
|
||||||
// Log Config
|
// Log Config
|
||||||
coreLogConfig := &coreConf.LogConfig{}
|
coreLogConfig := &coreConf.LogConfig{}
|
||||||
coreLogConfig.LogLevel = c.LogConfig.Level
|
coreLogConfig.LogLevel = c.LogConfig.Level
|
||||||
@ -183,3 +184,11 @@ func (c *Core) Close() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Core) Protocols() []string {
|
||||||
|
return []string{
|
||||||
|
"v2ray",
|
||||||
|
"shadowsocks",
|
||||||
|
"trojan",
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
CoreConfig:
|
CoreConfig:
|
||||||
Type: "xray" # Core type
|
Type: "xray" # Core type. if you need many cores, use " " to split
|
||||||
XrayConfig:
|
XrayConfig:
|
||||||
Log:
|
Log:
|
||||||
Level: warning # Log level: none, error, warning, info, debug
|
Level: warning # Log level: none, error, warning, info, debug
|
||||||
@ -26,20 +26,24 @@ Nodes:
|
|||||||
ControllerConfig:
|
ControllerConfig:
|
||||||
ListenIP: 0.0.0.0 # IP address you want to listen
|
ListenIP: 0.0.0.0 # IP address you want to listen
|
||||||
SendIP: 0.0.0.0 # IP address you want to send pacakage
|
SendIP: 0.0.0.0 # IP address you want to send pacakage
|
||||||
EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well
|
XrayOptions:
|
||||||
DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy
|
EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well
|
||||||
EnableTFO: false # Enable TCP Fast Open
|
DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy
|
||||||
EnableVless: false # Enable Vless for V2ray Type
|
EnableTFO: false # Enable TCP Fast Open
|
||||||
EnableXtls: false # Enable xtls-rprx-vision, only vless
|
EnableVless: false # Enable Vless for V2ray Type
|
||||||
EnableProxyProtocol: false # Only works for WebSocket and TCP
|
EnableXtls: false # Enable xtls-rprx-vision, only vless
|
||||||
EnableFallback: false # Only support for Trojan and Vless
|
EnableProxyProtocol: false # Only works for WebSocket and TCP
|
||||||
FallBackConfigs: # Support multiple fallbacks
|
EnableFallback: false # Only support for Trojan and Vless
|
||||||
- SNI: # TLS SNI(Server Name Indication), Empty for any
|
FallBackConfigs: # Support multiple fallbacks
|
||||||
Alpn: # Alpn, Empty for any
|
- SNI: # TLS SNI(Server Name Indication), Empty for any
|
||||||
Path: # HTTP PATH, Empty for any
|
Alpn: # Alpn, Empty for any
|
||||||
Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details.
|
Path: # HTTP PATH, Empty for any
|
||||||
ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for dsable
|
Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details.
|
||||||
|
ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for dsable
|
||||||
|
HyOptions:
|
||||||
|
Resolver: "udp://1.1.1.1:53" # DNS resolver address
|
||||||
|
ResolvePreference: 64 # DNS IPv4/IPv6 preference. Available options: "64" (IPv6 first, fallback to IPv4), "46" (IPv4 first, fallback to IPv6), "6" (IPv6 only), "4" (IPv4 only)
|
||||||
|
SendDevice: "eth0" # Bind device for outbound connections (usually requires root)
|
||||||
LimitConfig:
|
LimitConfig:
|
||||||
EnableRealtime: false # Check device limit on real time
|
EnableRealtime: false # Check device limit on real time
|
||||||
SpeedLimit: 0 # Mbps, Local settings will replace remote settings, 0 means disable
|
SpeedLimit: 0 # Mbps, Local settings will replace remote settings, 0 means disable
|
||||||
|
16
go.mod
16
go.mod
@ -3,27 +3,23 @@ module github.com/Yuzuki616/V2bX
|
|||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/apernet/hysteria/app v1.3.4
|
|
||||||
github.com/apernet/hysteria/core v1.3.4
|
github.com/apernet/hysteria/core v1.3.4
|
||||||
github.com/beevik/ntp v1.0.0
|
github.com/beevik/ntp v1.0.0
|
||||||
github.com/caddyserver/certmagic v0.17.2
|
|
||||||
github.com/folbricht/routedns v0.1.21-0.20230220022436-4ae86ce30d53
|
github.com/folbricht/routedns v0.1.21-0.20230220022436-4ae86ce30d53
|
||||||
github.com/fsnotify/fsnotify v1.6.0
|
github.com/fsnotify/fsnotify v1.6.0
|
||||||
github.com/go-acme/lego/v4 v4.12.1
|
github.com/go-acme/lego/v4 v4.12.1
|
||||||
github.com/go-redis/redis/v8 v8.11.5
|
github.com/go-redis/redis/v8 v8.11.5
|
||||||
github.com/go-resty/resty/v2 v2.7.0
|
github.com/go-resty/resty/v2 v2.7.0
|
||||||
github.com/goccy/go-json v0.10.2
|
github.com/goccy/go-json v0.10.2
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1
|
||||||
github.com/juju/ratelimit v1.0.2
|
github.com/juju/ratelimit v1.0.2
|
||||||
github.com/oschwald/geoip2-golang v1.8.0
|
github.com/oschwald/geoip2-golang v1.8.0
|
||||||
github.com/prometheus/client_golang v1.14.0
|
|
||||||
github.com/quic-go/quic-go v0.33.0
|
github.com/quic-go/quic-go v0.33.0
|
||||||
github.com/sagernet/sing-box v1.2.7
|
github.com/sagernet/sing-box v1.2.7
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.0
|
||||||
github.com/spf13/cobra v1.7.0
|
github.com/spf13/cobra v1.7.0
|
||||||
github.com/spf13/viper v1.15.0
|
github.com/spf13/viper v1.15.0
|
||||||
github.com/xtls/xray-core v1.8.1
|
github.com/xtls/xray-core v1.8.1
|
||||||
github.com/yosuke-furukawa/json5 v0.1.1
|
|
||||||
go.uber.org/zap v1.24.0
|
|
||||||
golang.org/x/crypto v0.9.0
|
golang.org/x/crypto v0.9.0
|
||||||
google.golang.org/protobuf v1.30.0
|
google.golang.org/protobuf v1.30.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
@ -50,7 +46,6 @@ require (
|
|||||||
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
|
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
|
||||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||||
github.com/aws/aws-sdk-go v1.39.0 // indirect
|
github.com/aws/aws-sdk-go v1.39.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
@ -89,7 +84,6 @@ require (
|
|||||||
github.com/gorilla/websocket v1.5.0 // indirect
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect
|
github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
@ -105,7 +99,6 @@ require (
|
|||||||
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b // indirect
|
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b // indirect
|
||||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
||||||
github.com/labbsr0x/goh v1.0.1 // indirect
|
github.com/labbsr0x/goh v1.0.1 // indirect
|
||||||
github.com/libdns/libdns v0.2.1 // indirect
|
|
||||||
github.com/linode/linodego v1.9.1 // indirect
|
github.com/linode/linodego v1.9.1 // indirect
|
||||||
github.com/liquidweb/go-lwApi v0.0.5 // indirect
|
github.com/liquidweb/go-lwApi v0.0.5 // indirect
|
||||||
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
|
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
|
||||||
@ -114,8 +107,6 @@ require (
|
|||||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect
|
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
|
||||||
github.com/mholt/acmez v1.1.1 // indirect
|
|
||||||
github.com/miekg/dns v1.1.54 // indirect
|
github.com/miekg/dns v1.1.54 // indirect
|
||||||
github.com/mimuret/golang-iij-dpf v0.7.1 // indirect
|
github.com/mimuret/golang-iij-dpf v0.7.1 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
@ -145,14 +136,12 @@ require (
|
|||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/pquerna/otp v1.3.0 // indirect
|
github.com/pquerna/otp v1.3.0 // indirect
|
||||||
github.com/prometheus/client_model v0.3.0 // indirect
|
|
||||||
github.com/prometheus/common v0.37.0 // indirect
|
|
||||||
github.com/prometheus/procfs v0.8.0 // indirect
|
|
||||||
github.com/quic-go/qpack v0.4.0 // indirect
|
github.com/quic-go/qpack v0.4.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
|
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
|
||||||
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
|
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
|
||||||
github.com/refraction-networking/utls v1.3.2 // indirect
|
github.com/refraction-networking/utls v1.3.2 // indirect
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||||
github.com/sacloud/api-client-go v0.2.1 // indirect
|
github.com/sacloud/api-client-go v0.2.1 // indirect
|
||||||
github.com/sacloud/go-http v0.1.2 // indirect
|
github.com/sacloud/go-http v0.1.2 // indirect
|
||||||
github.com/sacloud/iaas-api-go v1.3.2 // indirect
|
github.com/sacloud/iaas-api-go v1.3.2 // indirect
|
||||||
@ -194,7 +183,6 @@ require (
|
|||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 // indirect
|
go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 // indirect
|
||||||
go.uber.org/atomic v1.10.0 // indirect
|
go.uber.org/atomic v1.10.0 // indirect
|
||||||
go.uber.org/multierr v1.10.0 // indirect
|
|
||||||
go.uber.org/ratelimit v0.2.0 // indirect
|
go.uber.org/ratelimit v0.2.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect
|
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect
|
||||||
golang.org/x/mod v0.10.0 // indirect
|
golang.org/x/mod v0.10.0 // indirect
|
||||||
|
64
go.sum
64
go.sum
@ -84,10 +84,7 @@ github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91/go.mod h1:cDLGBht23
|
|||||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.1 h1:5BIsppVPdWJA29Yb5cYawQYeh5geN413WxAgBZvEtdA=
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.1 h1:5BIsppVPdWJA29Yb5cYawQYeh5geN413WxAgBZvEtdA=
|
||||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.1/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8=
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.1/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
|
||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 h1:J45/QHgrzUdqe/Vco/Vxk0wRvdS2nKUxmf/zLgvfass=
|
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 h1:J45/QHgrzUdqe/Vco/Vxk0wRvdS2nKUxmf/zLgvfass=
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
|
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
|
||||||
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI=
|
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI=
|
||||||
@ -96,8 +93,6 @@ github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/
|
|||||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
github.com/apernet/hysteria/app v1.3.4 h1:xoePo8NUtQ1KsQCqO/MWPqpbt7lbkE5K6RRX5W5Ea3Q=
|
|
||||||
github.com/apernet/hysteria/app v1.3.4/go.mod h1:hRzfCSWiT6eyPMXIqYyKRYvz+n+MHrbwTErHsodMReo=
|
|
||||||
github.com/apernet/hysteria/core v1.3.4 h1:i5EqGIf7FZqwsOrT7PagLQeh+P5gHfLkDZWn7/pXUjo=
|
github.com/apernet/hysteria/core v1.3.4 h1:i5EqGIf7FZqwsOrT7PagLQeh+P5gHfLkDZWn7/pXUjo=
|
||||||
github.com/apernet/hysteria/core v1.3.4/go.mod h1:WxFihvoDBhatefn4w8Lipzy97CWjK6NF1/duT0kGUG0=
|
github.com/apernet/hysteria/core v1.3.4/go.mod h1:WxFihvoDBhatefn4w8Lipzy97CWjK6NF1/duT0kGUG0=
|
||||||
github.com/apernet/quic-go v0.34.1-0.20230507231629-ec008b7e8473 h1:3KFetJ/lUFn0m9xTFg+rMmz2nyHg+D2boJX0Rp4OF6c=
|
github.com/apernet/quic-go v0.34.1-0.20230507231629-ec008b7e8473 h1:3KFetJ/lUFn0m9xTFg+rMmz2nyHg+D2boJX0Rp4OF6c=
|
||||||
@ -109,10 +104,8 @@ github.com/aws/aws-sdk-go v1.39.0 h1:74BBwkEmiqBbi2CGflEh34l0YNtIibTjZsibGarkNjo
|
|||||||
github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||||
github.com/beevik/ntp v1.0.0 h1:d0Lgy1xbNNqVyGfvg2Z96ItKcfyn3lzgus/oRoj9vnk=
|
github.com/beevik/ntp v1.0.0 h1:d0Lgy1xbNNqVyGfvg2Z96ItKcfyn3lzgus/oRoj9vnk=
|
||||||
github.com/beevik/ntp v1.0.0/go.mod h1:JN7/74B0Z4GUGO/1aUeRI2adARlfJGUeaJb0y0Wvnf4=
|
github.com/beevik/ntp v1.0.0/go.mod h1:JN7/74B0Z4GUGO/1aUeRI2adARlfJGUeaJb0y0Wvnf4=
|
||||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||||
@ -122,14 +115,11 @@ github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBT
|
|||||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||||
github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw=
|
github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw=
|
||||||
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
||||||
github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE=
|
|
||||||
github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE=
|
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
|
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
@ -226,13 +216,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
|
|||||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
|
||||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
|
||||||
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
|
||||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
|
||||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
@ -428,13 +413,10 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW
|
|||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
|
||||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
@ -445,7 +427,6 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
|||||||
github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
|
github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
|
||||||
github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
|
github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
|
||||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg=
|
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg=
|
||||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
@ -458,7 +439,6 @@ github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8t
|
|||||||
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b h1:DzHy0GlWeF0KAglaTMY7Q+khIFoG8toHP+wLFBVBQJc=
|
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b h1:DzHy0GlWeF0KAglaTMY7Q+khIFoG8toHP+wLFBVBQJc=
|
||||||
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
|
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
@ -485,8 +465,6 @@ github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++
|
|||||||
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
|
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
|
||||||
github.com/lestrrat-go/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA=
|
github.com/lestrrat-go/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA=
|
||||||
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||||
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
|
|
||||||
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
|
||||||
github.com/linode/linodego v1.9.1 h1:29UpEPpYcGFnbwiJW8mbk/bjBZpgd/pv68io2IKTo34=
|
github.com/linode/linodego v1.9.1 h1:29UpEPpYcGFnbwiJW8mbk/bjBZpgd/pv68io2IKTo34=
|
||||||
github.com/linode/linodego v1.9.1/go.mod h1:h6AuFR/JpqwwM/vkj7s8KV3iGN8/jxn+zc437F8SZ8w=
|
github.com/linode/linodego v1.9.1/go.mod h1:h6AuFR/JpqwwM/vkj7s8KV3iGN8/jxn+zc437F8SZ8w=
|
||||||
github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
||||||
@ -525,10 +503,7 @@ github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
|
|||||||
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=
|
github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/mholt/acmez v1.1.1 h1:sYeeYd/EHVm9cSmLdWey5oW/fXFVAq5pNLjSczN2ZUg=
|
|
||||||
github.com/mholt/acmez v1.1.1/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
|
|
||||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||||
@ -557,7 +532,6 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
|||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
|
||||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAAxrQxRHEu7FkapwTuI2WmL1rw4g=
|
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAAxrQxRHEu7FkapwTuI2WmL1rw4g=
|
||||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
|
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
|
||||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||||
@ -643,37 +617,19 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
|
|||||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
|
||||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
|
||||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
|
||||||
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
|
|
||||||
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
|
||||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
|
||||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
|
||||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
|
||||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
|
||||||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
|
|
||||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
|
||||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
|
||||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
|
||||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
|
||||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||||
@ -691,6 +647,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
|||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
@ -753,7 +710,6 @@ github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 h1:ZTzdx88+
|
|||||||
github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04/go.mod h1:5KS21fpch8TIMyAUv/qQqTa3GZfBDYgjaZbd2KXKYfg=
|
github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04/go.mod h1:5KS21fpch8TIMyAUv/qQqTa3GZfBDYgjaZbd2KXKYfg=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
|
||||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
@ -855,8 +811,6 @@ github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f h1:cG+ehP
|
|||||||
github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=
|
github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=
|
||||||
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 h1:2wzke3JH7OtN20WsNDZx2VH/TCmsbqtDEbXzjF+i05E=
|
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 h1:2wzke3JH7OtN20WsNDZx2VH/TCmsbqtDEbXzjF+i05E=
|
||||||
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997/go.mod h1:2CHKs/YGbCcNn/BPaCkEBwKz/FNCELi+MLILjR9RaTA=
|
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997/go.mod h1:2CHKs/YGbCcNn/BPaCkEBwKz/FNCELi+MLILjR9RaTA=
|
||||||
github.com/yosuke-furukawa/json5 v0.1.1 h1:0F9mNwTvOuDNH243hoPqvf+dxa5QsKnZzU20uNsh3ZI=
|
|
||||||
github.com/yosuke-furukawa/json5 v0.1.1/go.mod h1:sw49aWDqNdRJ6DYUtIQiaA3xyj2IL9tjeNYmX2ixwcU=
|
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
@ -880,15 +834,10 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
|||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
|
||||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
|
||||||
go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA=
|
go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA=
|
||||||
go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
|
go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
|
||||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
|
||||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
@ -1000,12 +949,10 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||||
@ -1025,8 +972,6 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
|
|||||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
|
||||||
golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
|
golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
|
||||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||||
@ -1072,7 +1017,6 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -1087,8 +1031,6 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -1100,20 +1042,17 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@ -1373,7 +1312,6 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
@ -60,7 +60,7 @@ func AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo) *Limite
|
|||||||
SpeedLimit: users[i].SpeedLimit,
|
SpeedLimit: users[i].SpeedLimit,
|
||||||
ExpireTime: 0,
|
ExpireTime: 0,
|
||||||
}
|
}
|
||||||
info.UserLimitInfo.Store(builder.BuildUserTag(tag, &users[i]), userLimit)
|
info.UserLimitInfo.Store(builder.BuildUserTag(tag, users[i].Uuid), userLimit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
limitLock.Lock()
|
limitLock.Lock()
|
||||||
|
31
node/cert.go
Normal file
31
node/cert.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Yuzuki616/V2bX/common/file"
|
||||||
|
"github.com/Yuzuki616/V2bX/node/lego"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Controller) requestCert() error {
|
||||||
|
if c.CertConfig.CertFile == "" || c.CertConfig.KeyFile == "" {
|
||||||
|
return fmt.Errorf("cert file path or key file path not exist")
|
||||||
|
}
|
||||||
|
switch c.CertConfig.CertMode {
|
||||||
|
case "reality", "none", "":
|
||||||
|
return nil
|
||||||
|
case "dns", "http":
|
||||||
|
if file.IsExist(c.CertConfig.CertFile) && file.IsExist(c.CertConfig.KeyFile) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
l, err := lego.New(c.CertConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create lego object error: %s", err)
|
||||||
|
}
|
||||||
|
err = l.CreateCert()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create cert error: %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unsupported certmode: %s", c.CertConfig.CertMode)
|
||||||
|
}
|
@ -44,15 +44,15 @@ func (c *Controller) Start() error {
|
|||||||
var err error
|
var err error
|
||||||
c.nodeInfo, err = c.apiClient.GetNodeInfo()
|
c.nodeInfo, err = c.apiClient.GetNodeInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get node info failed: %s", err)
|
return fmt.Errorf("get node info error: %s", err)
|
||||||
}
|
}
|
||||||
// Update user
|
// Update user
|
||||||
c.userList, err = c.apiClient.GetUserList()
|
c.userList, err = c.apiClient.GetUserList()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get user list failed: %s", err)
|
return fmt.Errorf("get user list error: %s", err)
|
||||||
}
|
}
|
||||||
if len(c.userList) == 0 {
|
if len(c.userList) == 0 {
|
||||||
return errors.New("add users failed: not have any user")
|
return errors.New("add users error: not have any user")
|
||||||
}
|
}
|
||||||
c.Tag = c.buildNodeTag()
|
c.Tag = c.buildNodeTag()
|
||||||
|
|
||||||
@ -61,13 +61,19 @@ func (c *Controller) Start() error {
|
|||||||
// add rule limiter
|
// add rule limiter
|
||||||
if !c.DisableGetRule {
|
if !c.DisableGetRule {
|
||||||
if err = l.UpdateRule(c.nodeInfo.Rules); err != nil {
|
if err = l.UpdateRule(c.nodeInfo.Rules); err != nil {
|
||||||
log.Printf("Update rule filed: %s", err)
|
return fmt.Errorf("update rule error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.nodeInfo.Tls {
|
||||||
|
err = c.requestCert()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("request cert error: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add new tag
|
// Add new tag
|
||||||
err = c.server.AddNode(c.Tag, c.nodeInfo, c.ControllerConfig)
|
err = c.server.AddNode(c.Tag, c.nodeInfo, c.ControllerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("add new tag failed: %s", err)
|
return fmt.Errorf("add new node error: %s", err)
|
||||||
}
|
}
|
||||||
added, err := c.server.AddUsers(&vCore.AddUsersParams{
|
added, err := c.server.AddUsers(&vCore.AddUsersParams{
|
||||||
Tag: c.Tag,
|
Tag: c.Tag,
|
||||||
@ -75,10 +81,10 @@ func (c *Controller) Start() error {
|
|||||||
UserInfo: c.userList,
|
UserInfo: c.userList,
|
||||||
NodeInfo: c.nodeInfo,
|
NodeInfo: c.nodeInfo,
|
||||||
})
|
})
|
||||||
log.Printf("[%s: %d] Added %d new users", c.nodeInfo.NodeType, c.nodeInfo.NodeId, added)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("add users error: %s", err)
|
||||||
}
|
}
|
||||||
|
log.Printf("[%s: %d] Added %d new users", c.nodeInfo.Type, c.nodeInfo.Id, added)
|
||||||
c.initTask()
|
c.initTask()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -89,36 +95,36 @@ func (c *Controller) Close() error {
|
|||||||
if c.nodeInfoMonitorPeriodic != nil {
|
if c.nodeInfoMonitorPeriodic != nil {
|
||||||
err := c.nodeInfoMonitorPeriodic.Close()
|
err := c.nodeInfoMonitorPeriodic.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("node info periodic close failed: %s", err)
|
return fmt.Errorf("node info periodic close error: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.nodeInfoMonitorPeriodic != nil {
|
if c.nodeInfoMonitorPeriodic != nil {
|
||||||
err := c.userReportPeriodic.Close()
|
err := c.userReportPeriodic.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("user report periodic close failed: %s", err)
|
return fmt.Errorf("user report periodic close error: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.renewCertPeriodic != nil {
|
if c.renewCertPeriodic != nil {
|
||||||
err := c.renewCertPeriodic.Close()
|
err := c.renewCertPeriodic.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("renew cert periodic close failed: %s", err)
|
return fmt.Errorf("renew cert periodic close error: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.dynamicSpeedLimitPeriodic != nil {
|
if c.dynamicSpeedLimitPeriodic != nil {
|
||||||
err := c.dynamicSpeedLimitPeriodic.Close()
|
err := c.dynamicSpeedLimitPeriodic.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("dynamic speed limit periodic close failed: %s", err)
|
return fmt.Errorf("dynamic speed limit periodic close error: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.onlineIpReportPeriodic != nil {
|
if c.onlineIpReportPeriodic != nil {
|
||||||
err := c.onlineIpReportPeriodic.Close()
|
err := c.onlineIpReportPeriodic.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("online ip report periodic close failed: %s", err)
|
return fmt.Errorf("online ip report periodic close error: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) buildNodeTag() string {
|
func (c *Controller) buildNodeTag() string {
|
||||||
return fmt.Sprintf("%s_%s_%d", c.nodeInfo.NodeType, c.ListenIP, c.nodeInfo.NodeId)
|
return fmt.Sprintf("%s_%s_%d", c.nodeInfo.Type, c.ListenIP, c.nodeInfo.Id)
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ func (l *Lego) CheckCert(file []byte) (bool, error) {
|
|||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
func (l *Lego) parseParmas(path string) string {
|
func (l *Lego) parseParams(path string) string {
|
||||||
r := strings.NewReplacer("{domain}", l.config.CertDomain,
|
r := strings.NewReplacer("{domain}", l.config.CertDomain,
|
||||||
"{email}", l.config.Email)
|
"{email}", l.config.Email)
|
||||||
return r.Replace(path)
|
return r.Replace(path)
|
||||||
@ -88,7 +88,7 @@ func (l *Lego) writeCert(certificates *certificate.Resource) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("check path error: %s", err)
|
return fmt.Errorf("check path error: %s", err)
|
||||||
}
|
}
|
||||||
err = os.WriteFile(l.parseParmas(l.config.CertFile), certificates.Certificate, 0644)
|
err = os.WriteFile(l.parseParams(l.config.CertFile), certificates.Certificate, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -96,7 +96,7 @@ func (l *Lego) writeCert(certificates *certificate.Resource) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("check path error: %s", err)
|
return fmt.Errorf("check path error: %s", err)
|
||||||
}
|
}
|
||||||
err = os.WriteFile(l.parseParmas(l.config.KeyFile), certificates.PrivateKey, 0644)
|
err = os.WriteFile(l.parseParams(l.config.KeyFile), certificates.PrivateKey, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package node
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/Yuzuki616/V2bX/api/panel"
|
"github.com/Yuzuki616/V2bX/api/panel"
|
||||||
"github.com/Yuzuki616/V2bX/conf"
|
"github.com/Yuzuki616/V2bX/conf"
|
||||||
vCore "github.com/Yuzuki616/V2bX/core"
|
vCore "github.com/Yuzuki616/V2bX/core"
|
||||||
@ -25,7 +26,11 @@ func (n *Node) Start(nodes []*conf.NodeConfig, core vCore.Core) error {
|
|||||||
n.controllers[i] = NewController(core, p, c.ControllerConfig)
|
n.controllers[i] = NewController(core, p, c.ControllerConfig)
|
||||||
err = n.controllers[i].Start()
|
err = n.controllers[i].Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("start node controller [%s-%s-%d] error: %s",
|
||||||
|
c.ApiConfig.NodeType,
|
||||||
|
c.ApiConfig.APIHost,
|
||||||
|
c.ApiConfig.NodeID,
|
||||||
|
err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
80
node/task.go
80
node/task.go
@ -4,13 +4,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
vCore "github.com/Yuzuki616/V2bX/core"
|
vCore "github.com/Yuzuki616/V2bX/core"
|
||||||
|
|
||||||
"github.com/Yuzuki616/V2bX/api/panel"
|
"github.com/Yuzuki616/V2bX/api/panel"
|
||||||
"github.com/Yuzuki616/V2bX/common/builder"
|
|
||||||
"github.com/Yuzuki616/V2bX/limiter"
|
"github.com/Yuzuki616/V2bX/limiter"
|
||||||
"github.com/Yuzuki616/V2bX/node/lego"
|
"github.com/Yuzuki616/V2bX/node/lego"
|
||||||
"github.com/xtls/xray-core/common/task"
|
"github.com/xtls/xray-core/common/task"
|
||||||
@ -19,33 +17,33 @@ import (
|
|||||||
func (c *Controller) initTask() {
|
func (c *Controller) initTask() {
|
||||||
// fetch node info task
|
// fetch node info task
|
||||||
c.nodeInfoMonitorPeriodic = &task.Periodic{
|
c.nodeInfoMonitorPeriodic = &task.Periodic{
|
||||||
Interval: c.nodeInfo.BaseConfig.PullInterval.(time.Duration),
|
Interval: c.nodeInfo.PullInterval,
|
||||||
Execute: c.nodeInfoMonitor,
|
Execute: c.nodeInfoMonitor,
|
||||||
}
|
}
|
||||||
// fetch user list task
|
// fetch user list task
|
||||||
c.userReportPeriodic = &task.Periodic{
|
c.userReportPeriodic = &task.Periodic{
|
||||||
Interval: c.nodeInfo.BaseConfig.PushInterval.(time.Duration),
|
Interval: c.nodeInfo.PushInterval,
|
||||||
Execute: c.reportUserTraffic,
|
Execute: c.reportUserTraffic,
|
||||||
}
|
}
|
||||||
log.Printf("[%s: %d] Start monitor node status", c.nodeInfo.NodeType, c.nodeInfo.NodeId)
|
log.Printf("[%s: %d] Start monitor node status", c.nodeInfo.Type, c.nodeInfo.Id)
|
||||||
// delay to start nodeInfoMonitor
|
// delay to start nodeInfoMonitor
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(c.nodeInfo.BaseConfig.PullInterval.(time.Duration))
|
time.Sleep(c.nodeInfo.PullInterval)
|
||||||
_ = c.nodeInfoMonitorPeriodic.Start()
|
_ = c.nodeInfoMonitorPeriodic.Start()
|
||||||
}()
|
}()
|
||||||
log.Printf("[%s: %d] Start report node status", c.nodeInfo.NodeType, c.nodeInfo.NodeId)
|
log.Printf("[%s: %d] Start report node status", c.nodeInfo.Type, c.nodeInfo.Id)
|
||||||
// delay to start userReport
|
// delay to start userReport
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(c.nodeInfo.BaseConfig.PushInterval.(time.Duration))
|
time.Sleep(c.nodeInfo.PullInterval)
|
||||||
_ = c.userReportPeriodic.Start()
|
_ = c.userReportPeriodic.Start()
|
||||||
}()
|
}()
|
||||||
if c.nodeInfo.Tls != 0 && c.CertConfig.CertMode != "none" &&
|
if c.nodeInfo.Tls && c.CertConfig.CertMode != "none" &&
|
||||||
(c.CertConfig.CertMode == "dns" || c.CertConfig.CertMode == "http") {
|
(c.CertConfig.CertMode == "dns" || c.CertConfig.CertMode == "http") {
|
||||||
c.renewCertPeriodic = &task.Periodic{
|
c.renewCertPeriodic = &task.Periodic{
|
||||||
Interval: time.Hour * 24,
|
Interval: time.Hour * 24,
|
||||||
Execute: c.reportUserTraffic,
|
Execute: c.reportUserTraffic,
|
||||||
}
|
}
|
||||||
log.Printf("[%s: %d] Start renew cert", c.nodeInfo.NodeType, c.nodeInfo.NodeId)
|
log.Printf("[%s: %d] Start renew cert", c.nodeInfo.Type, c.nodeInfo.Id)
|
||||||
// delay to start renewCert
|
// delay to start renewCert
|
||||||
go func() {
|
go func() {
|
||||||
_ = c.renewCertPeriodic.Start()
|
_ = c.renewCertPeriodic.Start()
|
||||||
@ -80,6 +78,12 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
|||||||
log.Print(err)
|
log.Print(err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if newNodeInfo.Tls {
|
||||||
|
err = c.requestCert()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("request cert error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
nodeInfoChanged = true
|
nodeInfoChanged = true
|
||||||
}
|
}
|
||||||
// Update User
|
// Update User
|
||||||
@ -93,8 +97,10 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
|||||||
// Add new Limiter
|
// Add new Limiter
|
||||||
l := limiter.AddLimiter(c.Tag, &c.LimitConfig, newUserInfo)
|
l := limiter.AddLimiter(c.Tag, &c.LimitConfig, newUserInfo)
|
||||||
_, err = c.server.AddUsers(&vCore.AddUsersParams{
|
_, err = c.server.AddUsers(&vCore.AddUsersParams{
|
||||||
Tag: c.Tag,
|
Tag: c.Tag,
|
||||||
Config: c.ControllerConfig,
|
Config: c.ControllerConfig,
|
||||||
|
UserInfo: newUserInfo,
|
||||||
|
NodeInfo: newNodeInfo,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
log.Print(err)
|
||||||
@ -105,18 +111,18 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
|||||||
log.Printf("Update Rule error: %s", err)
|
log.Printf("Update Rule error: %s", err)
|
||||||
}
|
}
|
||||||
// Check interval
|
// Check interval
|
||||||
if c.nodeInfoMonitorPeriodic.Interval != newNodeInfo.BaseConfig.PullInterval.(time.Duration) &&
|
if c.nodeInfoMonitorPeriodic.Interval != newNodeInfo.PullInterval &&
|
||||||
newNodeInfo.BaseConfig.PullInterval.(time.Duration) != 0 {
|
newNodeInfo.PullInterval != 0 {
|
||||||
c.nodeInfoMonitorPeriodic.Interval = newNodeInfo.BaseConfig.PullInterval.(time.Duration)
|
c.nodeInfoMonitorPeriodic.Interval = newNodeInfo.PullInterval
|
||||||
_ = c.nodeInfoMonitorPeriodic.Close()
|
_ = c.nodeInfoMonitorPeriodic.Close()
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(c.nodeInfoMonitorPeriodic.Interval)
|
time.Sleep(c.nodeInfoMonitorPeriodic.Interval)
|
||||||
_ = c.nodeInfoMonitorPeriodic.Start()
|
_ = c.nodeInfoMonitorPeriodic.Start()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
if c.userReportPeriodic.Interval != newNodeInfo.BaseConfig.PushInterval.(time.Duration) &&
|
if c.userReportPeriodic.Interval != newNodeInfo.PushInterval &&
|
||||||
newNodeInfo.BaseConfig.PushInterval.(time.Duration) != 0 {
|
newNodeInfo.PushInterval != 0 {
|
||||||
c.userReportPeriodic.Interval = newNodeInfo.BaseConfig.PullInterval.(time.Duration)
|
c.userReportPeriodic.Interval = newNodeInfo.PullInterval
|
||||||
_ = c.userReportPeriodic.Close()
|
_ = c.userReportPeriodic.Close()
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(c.userReportPeriodic.Interval)
|
time.Sleep(c.userReportPeriodic.Interval)
|
||||||
@ -156,46 +162,18 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
|||||||
log.Print("update limiter:", err)
|
log.Print("update limiter:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Printf("[%s: %d] %d user deleted, %d user added", c.nodeInfo.NodeType, c.nodeInfo.NodeId,
|
log.Printf("[%s: %d] %d user deleted, %d user added", c.nodeInfo.Type, c.nodeInfo.Id,
|
||||||
len(deleted), len(added))
|
len(deleted), len(added))
|
||||||
c.userList = newUserInfo
|
c.userList = newUserInfo
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareUserList(old, new []panel.UserInfo) (deleted, added []panel.UserInfo) {
|
|
||||||
tmp := map[string]struct{}{}
|
|
||||||
tmp2 := map[string]struct{}{}
|
|
||||||
for i := range old {
|
|
||||||
tmp[old[i].Uuid+strconv.Itoa(old[i].SpeedLimit)] = struct{}{}
|
|
||||||
}
|
|
||||||
l := len(tmp)
|
|
||||||
for i := range new {
|
|
||||||
e := new[i].Uuid + strconv.Itoa(new[i].SpeedLimit)
|
|
||||||
tmp[e] = struct{}{}
|
|
||||||
tmp2[e] = struct{}{}
|
|
||||||
if l != len(tmp) {
|
|
||||||
added = append(added, new[i])
|
|
||||||
l++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmp = nil
|
|
||||||
l = len(tmp2)
|
|
||||||
for i := range old {
|
|
||||||
tmp2[old[i].Uuid+strconv.Itoa(old[i].SpeedLimit)] = struct{}{}
|
|
||||||
if l != len(tmp2) {
|
|
||||||
deleted = append(deleted, old[i])
|
|
||||||
l++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return deleted, added
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Controller) reportUserTraffic() (err error) {
|
func (c *Controller) reportUserTraffic() (err error) {
|
||||||
// Get User traffic
|
// Get User traffic
|
||||||
userTraffic := make([]panel.UserTraffic, 0)
|
userTraffic := make([]panel.UserTraffic, 0)
|
||||||
for i := range c.userList {
|
for i := range c.userList {
|
||||||
up, down := c.server.GetUserTraffic(builder.BuildUserTag(c.Tag, &c.userList[i]), true)
|
up, down := c.server.GetUserTraffic(c.Tag, c.userList[i].Uuid, true)
|
||||||
if up > 0 || down > 0 {
|
if up > 0 || down > 0 {
|
||||||
if c.LimitConfig.EnableDynamicSpeedLimit {
|
if c.LimitConfig.EnableDynamicSpeedLimit {
|
||||||
c.userList[i].Traffic += up + down
|
c.userList[i].Traffic += up + down
|
||||||
@ -211,7 +189,7 @@ func (c *Controller) reportUserTraffic() (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Report user traffic faild: %s", err)
|
log.Printf("Report user traffic faild: %s", err)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("[%s: %d] Report %d online users", c.nodeInfo.NodeType, c.nodeInfo.NodeId, len(userTraffic))
|
log.Printf("[%s: %d] Report %d online users", c.nodeInfo.Type, c.nodeInfo.Id, len(userTraffic))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
userTraffic = nil
|
userTraffic = nil
|
||||||
@ -222,12 +200,12 @@ func (c *Controller) reportUserTraffic() (err error) {
|
|||||||
func (c *Controller) RenewCert() {
|
func (c *Controller) RenewCert() {
|
||||||
l, err := lego.New(c.CertConfig)
|
l, err := lego.New(c.CertConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
log.Print("new lego error: ", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = l.RenewCert()
|
err = l.RenewCert()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
log.Print("renew cert error: ", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
34
node/user.go
Normal file
34
node/user.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Yuzuki616/V2bX/api/panel"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func compareUserList(old, new []panel.UserInfo) (deleted, added []panel.UserInfo) {
|
||||||
|
tmp := map[string]struct{}{}
|
||||||
|
tmp2 := map[string]struct{}{}
|
||||||
|
for i := range old {
|
||||||
|
tmp[old[i].Uuid+strconv.Itoa(old[i].SpeedLimit)] = struct{}{}
|
||||||
|
}
|
||||||
|
l := len(tmp)
|
||||||
|
for i := range new {
|
||||||
|
e := new[i].Uuid + strconv.Itoa(new[i].SpeedLimit)
|
||||||
|
tmp[e] = struct{}{}
|
||||||
|
tmp2[e] = struct{}{}
|
||||||
|
if l != len(tmp) {
|
||||||
|
added = append(added, new[i])
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmp = nil
|
||||||
|
l = len(tmp2)
|
||||||
|
for i := range old {
|
||||||
|
tmp2[old[i].Uuid+strconv.Itoa(old[i].SpeedLimit)] = struct{}{}
|
||||||
|
if l != len(tmp2) {
|
||||||
|
deleted = append(deleted, old[i])
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deleted, added
|
||||||
|
}
|
27
test_data/1.key
Normal file
27
test_data/1.key
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEAyryUpSI01T4jgPywpt4CIaf+MNgPn9DxHtJmuo1VB7Ysk13Z
|
||||||
|
uOBVfS9HJoikbrcVeqENkR8Bq1vdgMLbHmlxrSTwe/pynmRvWx+L1hKscxEmR6uV
|
||||||
|
jgqWsPkoUAKVJlHBfjw6oFEQzmgwDOfyS90TVr4gwDo/GCLX1iX5jC+KrteuAMOp
|
||||||
|
V6Yiu1FAhd0TeUDPMKs4wbyE8qx3+Mn6FqGorIwyefCX9m9QCz1N/5Ut6I5uT5Vr
|
||||||
|
28Ox1Q++mZ0s5ZuFpMlzKvrfav7fxt31rgSUro7/NTNuIydHvwMcZYeVm+DT+Vfp
|
||||||
|
wRZW9uoB4jiwLDxnfMOMLd72yGId+9EJ6HpOIQIDAQABAoIBAHhV/xUVfK6mN4S0
|
||||||
|
eFZTqIg5otNzK7L83mIhGQDaKwJsy4CdUEJARf4MNftVV+Svn3wuZFMjSGZiHNP0
|
||||||
|
1QL0K5lON8AfJDGIA+DelK34X4vdPg+EdTzeZBufiKIVJlqcZHF9Zn8KHyOlDABd
|
||||||
|
HKCTFIuERwRSjmjRJbPizoC7J2Ina15pi+35T3x3oye4V5kQlJH3WxlMOhp0Z+iO
|
||||||
|
qGlqJITWeOqxXaLfPIoOYcJ2LBPzvgv4IRVbmhi4mxphfPTEAlGnvITauDa9fx7S
|
||||||
|
REBAFyt5NyU9M7dusmlLiTIHryOensBzCQOwbMseeUS0Z1rIfbusqwBM3X5oPcx8
|
||||||
|
jOP1QxECgYEA+qCU4Q84hDBhC2SLfH4D1QyOixeho5re6E0NchSposIhrxLdRDDk
|
||||||
|
04GGNTKUflgfoskaI9loe84MkTqqrjxguRWVXf5qPMAtfpXr2GYiPq+VhiEeo7ZG
|
||||||
|
f1rHYqK7Ity9jzy7z/CpTLekveUT93I7/yF5iEOUYQgnIZhDg+Hr0ysCgYEAzxUu
|
||||||
|
DmL7GceRauoD9w3tT2nsa23hXc+jo1QWV5I7veZyosXnz8gxpax4SgwZ/iCH8pDJ
|
||||||
|
RagWTHQHO3X6DdkovzPEWYiLtLCE2APlfClzkZif5tv7kO4B3BuyuDlNleuCH894
|
||||||
|
5WlieyCHcPtXqG/60tXxVrlNrevI+ccsYRb+reMCgYBgu8AaybQnmUCrlAgeacjy
|
||||||
|
3yDZYKqbqfflM3BAGueKkWFM4HwUiMaZOAHj4Hzd8wdq3jG/qncgadwB5eHg1B8E
|
||||||
|
8Oaw27SHdClbFWRtJqaLCVwt4/SefYjiONiCIosWHprvgSKAVMQTf0IPpS46sJWl
|
||||||
|
mHb++A56ERqBZfKRIY7S9wKBgCKf+flx12ZyFgB4bH1MmNdkcKFt1/bllwjiMHIo
|
||||||
|
A1E3TQema6I0aQi4k8xdxaLWMaT/TIgXGNNjuynYCh1yp/uAXl5SFHn74dp0nFRs
|
||||||
|
YeSATow9UAzlnu38u59OBYkBvdovyJkjS9ImmD7t57RENP43w4iqpzBjclFBWkxJ
|
||||||
|
mf/dAoGBAOZa8+FmFyx2VmgwR4qPrNKg6EuhO6OgDJ8NvRnx4Mu5/0L1JoYoM0Yc
|
||||||
|
2NMKh14fqmDB/CvCbMGxCj86WF8534t+eBqhY6/AUkSolh8139KNmYHNB0ynqDYn
|
||||||
|
QDm/HJ4cux2rkLUCKlbGBnS9M+PSJA/MJeh0JTIrSOu4bH7aanVl
|
||||||
|
-----END RSA PRIVATE KEY-----
|
23
test_data/1.pem
Normal file
23
test_data/1.pem
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIID2zCCAsOgAwIBAgIRAJP0pRlp9k2eiBLK2a2B7LYwDQYJKoZIhvcNAQELBQAw
|
||||||
|
XjELMAkGA1UEBhMCQ04xDjAMBgNVBAoTBU15U1NMMSswKQYDVQQLEyJNeVNTTCBU
|
||||||
|
ZXN0IFJTQSAtIEZvciB0ZXN0IHVzZSBvbmx5MRIwEAYDVQQDEwlNeVNTTC5jb20w
|
||||||
|
HhcNMjMwNjA4MTQxOTQzWhcNMjgwNjA2MTQxOTQzWjAiMQswCQYDVQQGEwJDTjET
|
||||||
|
MBEGA1UEAxMKMTE0NTE0LmdheTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
||||||
|
ggEBAMq8lKUiNNU+I4D8sKbeAiGn/jDYD5/Q8R7SZrqNVQe2LJNd2bjgVX0vRyaI
|
||||||
|
pG63FXqhDZEfAatb3YDC2x5pca0k8Hv6cp5kb1sfi9YSrHMRJkerlY4KlrD5KFAC
|
||||||
|
lSZRwX48OqBREM5oMAzn8kvdE1a+IMA6Pxgi19Yl+Ywviq7XrgDDqVemIrtRQIXd
|
||||||
|
E3lAzzCrOMG8hPKsd/jJ+hahqKyMMnnwl/ZvUAs9Tf+VLeiObk+Va9vDsdUPvpmd
|
||||||
|
LOWbhaTJcyr632r+38bd9a4ElK6O/zUzbiMnR78DHGWHlZvg0/lX6cEWVvbqAeI4
|
||||||
|
sCw8Z3zDjC3e9shiHfvRCeh6TiECAwEAAaOBzzCBzDAOBgNVHQ8BAf8EBAMCBaAw
|
||||||
|
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB8GA1UdIwQYMBaAFCiBJgXR
|
||||||
|
NBo/wXMPu5PPFRw/A79/MGMGCCsGAQUFBwEBBFcwVTAhBggrBgEFBQcwAYYVaHR0
|
||||||
|
cDovL29jc3AubXlzc2wuY29tMDAGCCsGAQUFBzAChiRodHRwOi8vY2EubXlzc2wu
|
||||||
|
Y29tL215c3NsdGVzdHJzYS5jcnQwFQYDVR0RBA4wDIIKMTE0NTE0LmdheTANBgkq
|
||||||
|
hkiG9w0BAQsFAAOCAQEAdlvVKnB1OHojoHfgPKUVTk5+OXk9X/q++wkkrGa1rQs0
|
||||||
|
bPikKdc1TQoW/ylpX9wN3rwLLYGf/Hs9SqHr4RkAhAb6v+K1O5HUMqJARONdB7j0
|
||||||
|
/1BuKd0wx5pIqJhs6qRf+grsOGj9EdTfKXqElCljAES0t+2ZbZ2666XftwSjybtF
|
||||||
|
yKg+9iS9PX5VA1SIsa7XSVTlJ8oXy91KuCm07UxnSEKovpzV4TIlxPgKOfWBUYh9
|
||||||
|
JfOhwh1rpUy6tqNjFyJdGHxBJsf7HLcO9VJe/RD55c54ovZaTT24Cy/II5DKiQ26
|
||||||
|
TUeMj1BMedu5Ou0YCH7W9QhH40fvwi/hSQrjysQAoA==
|
||||||
|
-----END CERTIFICATE-----
|
Loading…
Reference in New Issue
Block a user