mirror of
https://github.com/wyx2685/V2bX.git
synced 2025-02-02 06:48:14 -05: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 |
|
||||
|-----------|-------|--------|-------------|
|
||||
| 自动申请tls证书 | √ | √ | √ |
|
||||
| 自动续签tls证书 | √ | √ | √ |
|
||||
| 在线人数统计 | √ | √ | √ |
|
||||
| 审计规则 | √ | √ | √ |
|
||||
| 自定义DNS | √ | √ | √ |
|
||||
| 在线IP数限制 | √ | √ | √ |
|
||||
| 连接数限制 | √ | √ | √ |
|
||||
| 跨节点IP数限制 | √ | √ | √ |
|
||||
| 按照用户限速 | √ | √ | √ |
|
||||
| 动态限速(未测试) | √ | √ | √ |
|
||||
| 功能 | v2ray | trojan | shadowsocks | hysteria |
|
||||
|-----------|-------|--------|-------------|----------|
|
||||
| 自动申请tls证书 | √ | √ | √ | √ |
|
||||
| 自动续签tls证书 | √ | √ | √ | √ |
|
||||
| 在线人数统计 | √ | √ | √ | √ |
|
||||
| 审计规则 | √ | √ | √ | |
|
||||
| 自定义DNS | √ | √ | √ | √ |
|
||||
| 在线IP数限制 | √ | √ | √ | |
|
||||
| 连接数限制 | √ | √ | √ | |
|
||||
| 跨节点IP数限制 | √ | √ | √ | |
|
||||
| 按照用户限速 | √ | √ | √ | |
|
||||
| 动态限速(未测试) | √ | √ | √ | |
|
||||
|
||||
## TODO
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package panel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/goccy/go-json"
|
||||
"reflect"
|
||||
"regexp"
|
||||
@ -9,21 +10,14 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type NodeInfo struct {
|
||||
NodeId int `json:"-"`
|
||||
NodeType string `json:"-"`
|
||||
Rules []*regexp.Regexp `json:"-"`
|
||||
Host string `json:"host"`
|
||||
ServerPort int `json:"server_port"`
|
||||
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 CommonNodeRsp struct {
|
||||
Host string `json:"host"`
|
||||
ServerPort int `json:"server_port"`
|
||||
ServerName string `json:"server_name"`
|
||||
Routes []Route `json:"routes"`
|
||||
BaseConfig BaseConfig `json:"base_config"`
|
||||
}
|
||||
|
||||
type Route struct {
|
||||
Id int `json:"id"`
|
||||
Match interface{} `json:"match"`
|
||||
@ -35,37 +29,123 @@ type BaseConfig struct {
|
||||
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"
|
||||
r, err := c.client.R().Get(path)
|
||||
if err = c.checkResponse(r, path, err); err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(r.Body(), &nodeInfo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if c.etag == r.Header().Get("ETag") { // node info not changed
|
||||
return nil, nil
|
||||
}
|
||||
nodeInfo.NodeId = c.NodeId
|
||||
nodeInfo.NodeType = c.NodeType
|
||||
for i := range nodeInfo.Routes { // parse rules from routes
|
||||
if nodeInfo.Routes[i].Action == "block" {
|
||||
// parse common params
|
||||
node = &NodeInfo{
|
||||
Id: c.NodeId,
|
||||
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
|
||||
if _, ok := nodeInfo.Routes[i].Match.(string); ok {
|
||||
matchs = strings.Split(nodeInfo.Routes[i].Match.(string), ",")
|
||||
if _, ok := common.Routes[i].Match.(string); ok {
|
||||
matchs = strings.Split(common.Routes[i].Match.(string), ",")
|
||||
} else {
|
||||
matchs = nodeInfo.Routes[i].Match.([]string)
|
||||
matchs = common.Routes[i].Match.([]string)
|
||||
}
|
||||
for _, v := range matchs {
|
||||
nodeInfo.Rules = append(nodeInfo.Rules, regexp.MustCompile(v))
|
||||
node.Rules = append(node.Rules, regexp.MustCompile(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
nodeInfo.Routes = nil
|
||||
nodeInfo.BaseConfig.PullInterval = intervalToTime(nodeInfo.BaseConfig.PullInterval)
|
||||
nodeInfo.BaseConfig.PushInterval = intervalToTime(nodeInfo.BaseConfig.PushInterval)
|
||||
node.ServerName = common.ServerName
|
||||
node.Host = common.Host
|
||||
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")
|
||||
return
|
||||
}
|
||||
|
@ -43,9 +43,9 @@ func New(c *conf.ApiConfig) (*Client, error) {
|
||||
client.SetBaseURL(c.APIHost)
|
||||
// Check node type
|
||||
c.NodeType = strings.ToLower(c.NodeType)
|
||||
if c.NodeType != "v2ray" &&
|
||||
c.NodeType != "trojan" &&
|
||||
c.NodeType != "shadowsocks" {
|
||||
switch c.NodeType {
|
||||
case "v2ray", "trojan", "shadowsocks", "hysteria":
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||
}
|
||||
// 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 {
|
||||
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
|
||||
}
|
||||
|
@ -7,9 +7,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/Yuzuki616/V2bX/api/panel"
|
||||
"github.com/Yuzuki616/V2bX/common/file"
|
||||
"github.com/Yuzuki616/V2bX/conf"
|
||||
"github.com/Yuzuki616/V2bX/node/lego"
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"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)
|
||||
in.StreamSetting = &coreConf.StreamConfig{Network: &t}
|
||||
var err error
|
||||
switch nodeInfo.NodeType {
|
||||
switch nodeInfo.Type {
|
||||
case "v2ray":
|
||||
err = buildV2ray(config, nodeInfo, in)
|
||||
case "trojan":
|
||||
err = buildTrojan(config, nodeInfo, in)
|
||||
err = buildTrojan(config, in)
|
||||
case "shadowsocks":
|
||||
err = buildShadowsocks(config, nodeInfo, in)
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
// Set server port
|
||||
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
|
||||
ipAddress := net.ParseAddress(config.ListenIP)
|
||||
@ -48,7 +46,7 @@ func BuildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag s
|
||||
Enabled: true,
|
||||
DestOverride: &coreConf.StringList{"http", "tls"},
|
||||
}
|
||||
if config.DisableSniffing {
|
||||
if config.XrayOptions.DisableSniffing {
|
||||
sniffingConfig.Enabled = false
|
||||
}
|
||||
in.SniffingConfig = sniffingConfig
|
||||
@ -66,7 +64,7 @@ func BuildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag s
|
||||
AcceptProxyProtocol: config.EnableProxyProtocol} //Enable proxy protocol
|
||||
}
|
||||
// Set TLS or Reality settings
|
||||
if nodeInfo.Tls != 0 {
|
||||
if nodeInfo.Tls {
|
||||
if config.CertConfig == nil {
|
||||
return nil, errors.New("the CertConfig is not vail")
|
||||
}
|
||||
@ -91,16 +89,11 @@ func BuildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag s
|
||||
}
|
||||
default:
|
||||
// Normal tls
|
||||
in.StreamSetting.Security = "tls"
|
||||
certFile, keyFile, err := getCertFile(config.CertConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
in.StreamSetting.TLSSettings = &coreConf.TLSConfig{
|
||||
Certs: []*coreConf.TLSCertConfig{
|
||||
{
|
||||
CertFile: certFile,
|
||||
KeyFile: keyFile,
|
||||
CertFile: config.CertConfig.CertFile,
|
||||
KeyFile: config.CertConfig.KeyFile,
|
||||
OcspStapling: 3600,
|
||||
},
|
||||
},
|
||||
@ -112,23 +105,23 @@ func BuildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag s
|
||||
if *in.StreamSetting.Network != "tcp" &&
|
||||
*in.StreamSetting.Network != "ws" &&
|
||||
config.EnableProxyProtocol {
|
||||
sockoptConfig := &coreConf.SocketConfig{
|
||||
socketConfig := &coreConf.SocketConfig{
|
||||
AcceptProxyProtocol: config.EnableProxyProtocol,
|
||||
TFO: config.EnableTFO,
|
||||
TFO: config.XrayOptions.EnableTFO,
|
||||
} //Enable proxy protocol
|
||||
in.StreamSetting.SocketSettings = sockoptConfig
|
||||
in.StreamSetting.SocketSettings = socketConfig
|
||||
}
|
||||
in.Tag = tag
|
||||
return in.Build()
|
||||
}
|
||||
|
||||
func buildV2ray(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error {
|
||||
if config.EnableVless {
|
||||
if config.XrayOptions.EnableVless {
|
||||
//Set vless
|
||||
inbound.Protocol = "vless"
|
||||
if config.EnableFallback {
|
||||
if config.XrayOptions.EnableFallback {
|
||||
// Set fallback
|
||||
fallbackConfigs, err := buildVlessFallbacks(config.FallBackConfigs)
|
||||
fallbackConfigs, err := buildVlessFallbacks(config.XrayOptions.FallBackConfigs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -183,11 +176,11 @@ func buildV2ray(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inbound
|
||||
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"
|
||||
if config.EnableFallback {
|
||||
if config.XrayOptions.EnableFallback {
|
||||
// Set fallback
|
||||
fallbackConfigs, err := buildTrojanFallbacks(config.FallBackConfigs)
|
||||
fallbackConfigs, err := buildTrojanFallbacks(config.XrayOptions.FallBackConfigs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -202,10 +195,7 @@ func buildTrojan(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inboun
|
||||
s := []byte("{}")
|
||||
inbound.Settings = (*json.RawMessage)(&s)
|
||||
}
|
||||
if nodeInfo.Network == "" {
|
||||
nodeInfo.Network = "tcp"
|
||||
}
|
||||
t := coreConf.TransportProtocol(nodeInfo.Network)
|
||||
t := coreConf.TransportProtocol("tcp")
|
||||
inbound.StreamSetting = &coreConf.StreamConfig{Network: &t}
|
||||
return nil
|
||||
}
|
||||
@ -234,7 +224,7 @@ func buildShadowsocks(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, i
|
||||
settings.Users = append(settings.Users, defaultSSuser)
|
||||
settings.NetworkList = &coreConf.NetworkList{"tcp", "udp"}
|
||||
settings.IVCheck = true
|
||||
if config.DisableIVCheck {
|
||||
if config.XrayOptions.DisableIVCheck {
|
||||
settings.IVCheck = false
|
||||
}
|
||||
t := coreConf.TransportProtocol("tcp")
|
||||
@ -247,30 +237,6 @@ func buildShadowsocks(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, i
|
||||
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) {
|
||||
if fallbackConfigs == nil {
|
||||
return nil, fmt.Errorf("you must provide FallBackConfigs")
|
||||
|
@ -2,7 +2,6 @@ package builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Yuzuki616/V2bX/api/panel"
|
||||
conf2 "github.com/Yuzuki616/V2bX/conf"
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
@ -11,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
// 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.Protocol = "freedom"
|
||||
outboundDetourConfig.Tag = tag
|
||||
@ -24,9 +23,9 @@ func BuildOutbound(config *conf2.ControllerConfig, nodeInfo *panel.NodeInfo, tag
|
||||
|
||||
// Freedom Protocol setting
|
||||
var domainStrategy = "Asis"
|
||||
if config.EnableDNS {
|
||||
if config.DNSType != "" {
|
||||
domainStrategy = config.DNSType
|
||||
if config.XrayOptions.EnableDNS {
|
||||
if config.XrayOptions.DNSType != "" {
|
||||
domainStrategy = config.XrayOptions.DNSType
|
||||
} else {
|
||||
domainStrategy = "UseIP"
|
||||
}
|
||||
@ -37,7 +36,7 @@ func BuildOutbound(config *conf2.ControllerConfig, nodeInfo *panel.NodeInfo, tag
|
||||
var setting json.RawMessage
|
||||
setting, err := json.Marshal(proxySetting)
|
||||
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
|
||||
return outboundDetourConfig.Build()
|
||||
|
@ -32,7 +32,7 @@ func BuildVmessUser(tag string, userInfo *panel.UserInfo) (user *protocol.User)
|
||||
}
|
||||
return &protocol.User{
|
||||
Level: 0,
|
||||
Email: BuildUserTag(tag, userInfo), // Uid: InboundTag|email|uid
|
||||
Email: BuildUserTag(tag, userInfo.Uuid), // Uid: InboundTag|email
|
||||
Account: serial.ToTypedMessage(vmessAccount.Build()),
|
||||
}
|
||||
}
|
||||
@ -54,7 +54,7 @@ func BuildVlessUser(tag string, userInfo *panel.UserInfo, xtls bool) (user *prot
|
||||
}
|
||||
return &protocol.User{
|
||||
Level: 0,
|
||||
Email: BuildUserTag(tag, userInfo),
|
||||
Email: BuildUserTag(tag, userInfo.Uuid),
|
||||
Account: serial.ToTypedMessage(vlessAccount),
|
||||
}
|
||||
}
|
||||
@ -73,7 +73,7 @@ func BuildTrojanUser(tag string, userInfo *panel.UserInfo) (user *protocol.User)
|
||||
}
|
||||
return &protocol.User{
|
||||
Level: 0,
|
||||
Email: BuildUserTag(tag, userInfo),
|
||||
Email: BuildUserTag(tag, userInfo.Uuid),
|
||||
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 {
|
||||
return fmt.Sprintf("%s|%s|%d", tag, user.Uuid, user.Id)
|
||||
func BuildUserTag(tag string, uuid string) string {
|
||||
return fmt.Sprintf("%s|%s", tag, uuid)
|
||||
}
|
||||
|
||||
func getCipherFromString(c string) shadowsocks.CipherType {
|
||||
|
@ -23,6 +23,7 @@ func New() *Conf {
|
||||
Type: "xray",
|
||||
XrayConfig: &XrayConfig{
|
||||
LogConfig: NewLogConfig(),
|
||||
AssetPath: "/etc/V2bX/",
|
||||
DnsConfigPath: "",
|
||||
InboundConfigPath: "",
|
||||
OutboundConfigPath: "",
|
||||
@ -35,9 +36,6 @@ func New() *Conf {
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open config file error: %s", err)
|
||||
|
@ -7,13 +7,13 @@ import (
|
||||
|
||||
func TestConf_LoadFromPath(t *testing.T) {
|
||||
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) {
|
||||
c := New()
|
||||
c.Watch("../example/config.yml.example", func() {
|
||||
log.Println(c.Watch("../example/config.yml.example", func() {
|
||||
log.Println(1)
|
||||
})
|
||||
}))
|
||||
select {}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ type CoreConfig struct {
|
||||
|
||||
type XrayConfig struct {
|
||||
LogConfig *LogConfig `yaml:"Log"`
|
||||
AssetPath string `yaml:"AssetPath"`
|
||||
DnsConfigPath string `yaml:"DnsConfigPath"`
|
||||
RouteConfigPath string `yaml:"RouteConfigPath"`
|
||||
ConnectionConfig *ConnectionConfig `yaml:"ConnectionConfig"`
|
||||
|
44
conf/node.go
44
conf/node.go
@ -15,22 +15,34 @@ type ApiConfig struct {
|
||||
}
|
||||
|
||||
type ControllerConfig struct {
|
||||
ListenIP string `yaml:"ListenIP"`
|
||||
SendIP string `yaml:"SendIP"`
|
||||
EnableDNS bool `yaml:"EnableDNS"`
|
||||
DNSType string `yaml:"DNSType"`
|
||||
EnableVless bool `yaml:"EnableVless"`
|
||||
EnableXtls bool `yaml:"EnableXtls"`
|
||||
LimitConfig LimitConfig `yaml:"LimitConfig"`
|
||||
DisableUploadTraffic bool `yaml:"DisableUploadTraffic"`
|
||||
DisableGetRule bool `yaml:"DisableGetRule"`
|
||||
EnableProxyProtocol bool `yaml:"EnableProxyProtocol"`
|
||||
EnableTFO bool `yaml:"EnableTFO"`
|
||||
DisableIVCheck bool `yaml:"DisableIVCheck"`
|
||||
DisableSniffing bool `yaml:"DisableSniffing"`
|
||||
EnableFallback bool `yaml:"EnableFallback"`
|
||||
FallBackConfigs []FallBackConfig `yaml:"FallBackConfigs"`
|
||||
CertConfig *CertConfig `yaml:"CertConfig"`
|
||||
DisableUploadTraffic bool `yaml:"DisableUploadTraffic"`
|
||||
DisableGetRule bool `yaml:"DisableGetRule"`
|
||||
ListenIP string `yaml:"ListenIP"`
|
||||
SendIP string `yaml:"SendIP"`
|
||||
EnableProxyProtocol bool `yaml:"EnableProxyProtocol"`
|
||||
XrayOptions XrayOptions `yaml:"XrayOptions"`
|
||||
HyOptions HyOptions `yaml:"HyOptions"`
|
||||
LimitConfig LimitConfig `yaml:"LimitConfig"`
|
||||
CertConfig *CertConfig `yaml:"CertConfig"`
|
||||
}
|
||||
|
||||
type XrayOptions struct {
|
||||
EnableDNS bool `yaml:"EnableDNS"`
|
||||
DNSType string `yaml:"DNSType"`
|
||||
EnableVless bool `yaml:"EnableVless"`
|
||||
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 {
|
||||
|
@ -49,7 +49,7 @@ func migrateOldConfig(c *Conf, old *OldConfig) {
|
||||
}
|
||||
// node option
|
||||
if old.NodesConfig[i].ApiConfig.EnableVless {
|
||||
n.ControllerConfig.EnableVless = true
|
||||
n.ControllerConfig.XrayOptions.EnableVless = true
|
||||
changed = true
|
||||
}
|
||||
// limit config
|
||||
|
19
core/core.go
19
core/core.go
@ -12,6 +12,25 @@ var (
|
||||
)
|
||||
|
||||
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 {
|
||||
return f(c)
|
||||
} 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
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/yosuke-furukawa/json5/encoding/json5"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
mbpsToBps = 125000
|
||||
minSpeedBPS = 16384
|
||||
@ -27,281 +19,8 @@ const (
|
||||
DefaultClientHopIntervalSec = 10
|
||||
)
|
||||
|
||||
var rateStringRegexp = regexp.MustCompile(`^(\d+)\s*([KMGT]?)([Bb])ps$`)
|
||||
|
||||
type serverConfig struct {
|
||||
Listen string `json:"listen"`
|
||||
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
|
||||
func SpeedTrans(upM, downM int) (uint64, uint64) {
|
||||
up := uint64(upM) * mbpsToBps
|
||||
down := uint64(downM) * mbpsToBps
|
||||
return up, down
|
||||
}
|
||||
|
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
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/oschwald/geoip2-golang"
|
||||
"github.com/quic-go/quic-go"
|
||||
|
||||
"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"
|
||||
"github.com/Yuzuki616/V2bX/conf"
|
||||
vCore "github.com/Yuzuki616/V2bX/core"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
||||
var serverPacketConnFuncFactoryMap = map[string]pktconns.ServerPacketConnFuncFactory{
|
||||
"": pktconns.NewServerUDPConnFunc,
|
||||
"udp": pktconns.NewServerUDPConnFunc,
|
||||
"wechat": pktconns.NewServerWeChatConnFunc,
|
||||
"wechat-video": pktconns.NewServerWeChatConnFunc,
|
||||
"faketcp": pktconns.NewServerFakeTCPConnFunc,
|
||||
func init() {
|
||||
vCore.RegisterCore("hy", NewHy)
|
||||
}
|
||||
|
||||
func server(config *serverConfig) {
|
||||
logrus.WithField("config", config.String()).Info("Server configuration loaded")
|
||||
config.Fill() // Fill default values
|
||||
// Resolver
|
||||
if len(config.Resolver) > 0 {
|
||||
err := setResolver(config.Resolver)
|
||||
type Hy struct {
|
||||
servers sync.Map
|
||||
}
|
||||
|
||||
func NewHy(_ *conf.CoreConfig) (vCore.Core, error) {
|
||||
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 {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"error": err,
|
||||
}).Fatal("Failed to set resolver")
|
||||
errs = multierror.Append(errs, fmt.Errorf("close %s error: %s", tag, err))
|
||||
}
|
||||
return true
|
||||
})
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
// Load TLS config
|
||||
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")
|
||||
return nil
|
||||
}
|
||||
|
||||
func 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 (h *Hy) Protocols() []string {
|
||||
return []string{
|
||||
"hysteria",
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// not works for now
|
||||
//import _ "github.com/Yuzuki616/V2bX/core/hy"
|
||||
// not yet tested
|
||||
import _ "github.com/Yuzuki616/V2bX/core/hy"
|
||||
|
@ -17,6 +17,7 @@ type Core interface {
|
||||
AddNode(tag string, info *panel.NodeInfo, config *conf.ControllerConfig) error
|
||||
DelNode(tag string) 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
|
||||
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 {
|
||||
return fmt.Errorf("add inbound error: %s", err)
|
||||
}
|
||||
outBoundConfig, err := builder.BuildOutbound(config, info, tag)
|
||||
outBoundConfig, err := builder.BuildOutbound(config, tag)
|
||||
if err != nil {
|
||||
return fmt.Errorf("build outbound error: %s", err)
|
||||
}
|
||||
|
@ -40,9 +40,9 @@ func (c *Core) DelUsers(users []string, tag string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Core) GetUserTraffic(email string, reset bool) (up int64, down int64) {
|
||||
upName := "user>>>" + email + ">>>traffic>>>uplink"
|
||||
downName := "user>>>" + email + ">>>traffic>>>downlink"
|
||||
func (c *Core) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) {
|
||||
upName := "user>>>" + builder.BuildUserTag(tag, uuid) + ">>>traffic>>>uplink"
|
||||
downName := "user>>>" + builder.BuildUserTag(tag, uuid) + ">>>traffic>>>downlink"
|
||||
upCounter := c.shm.GetCounter(upName)
|
||||
downCounter := c.shm.GetCounter(downName)
|
||||
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) {
|
||||
users := make([]*protocol.User, 0, len(p.UserInfo))
|
||||
switch p.NodeInfo.NodeType {
|
||||
switch p.NodeInfo.Type {
|
||||
case "v2ray":
|
||||
if p.Config.EnableVless {
|
||||
users = builder.BuildVlessUsers(p.Tag, p.UserInfo, p.Config.EnableXtls)
|
||||
if p.Config.XrayOptions.EnableXtls {
|
||||
users = builder.BuildVlessUsers(p.Tag, p.UserInfo, true)
|
||||
} else {
|
||||
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.ServerKey)
|
||||
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)
|
||||
if err != nil {
|
||||
|
@ -53,6 +53,7 @@ func parseConnectionConfig(c *conf.ConnectionConfig) (policy *coreConf.Policy) {
|
||||
}
|
||||
|
||||
func getCore(c *conf.XrayConfig) *core.Instance {
|
||||
os.Setenv("XRAY_LOCATION_ASSET", c.AssetPath)
|
||||
// Log Config
|
||||
coreLogConfig := &coreConf.LogConfig{}
|
||||
coreLogConfig.LogLevel = c.LogConfig.Level
|
||||
@ -183,3 +184,11 @@ func (c *Core) Close() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Core) Protocols() []string {
|
||||
return []string{
|
||||
"v2ray",
|
||||
"shadowsocks",
|
||||
"trojan",
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
CoreConfig:
|
||||
Type: "xray" # Core type
|
||||
Type: "xray" # Core type. if you need many cores, use " " to split
|
||||
XrayConfig:
|
||||
Log:
|
||||
Level: warning # Log level: none, error, warning, info, debug
|
||||
@ -26,20 +26,24 @@ Nodes:
|
||||
ControllerConfig:
|
||||
ListenIP: 0.0.0.0 # IP address you want to listen
|
||||
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
|
||||
DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy
|
||||
EnableTFO: false # Enable TCP Fast Open
|
||||
EnableVless: false # Enable Vless for V2ray Type
|
||||
EnableXtls: false # Enable xtls-rprx-vision, only vless
|
||||
EnableProxyProtocol: false # Only works for WebSocket and TCP
|
||||
EnableFallback: false # Only support for Trojan and Vless
|
||||
FallBackConfigs: # Support multiple fallbacks
|
||||
- SNI: # TLS SNI(Server Name Indication), Empty for any
|
||||
Alpn: # Alpn, Empty for any
|
||||
Path: # HTTP PATH, Empty for any
|
||||
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
|
||||
|
||||
XrayOptions:
|
||||
EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well
|
||||
DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy
|
||||
EnableTFO: false # Enable TCP Fast Open
|
||||
EnableVless: false # Enable Vless for V2ray Type
|
||||
EnableXtls: false # Enable xtls-rprx-vision, only vless
|
||||
EnableProxyProtocol: false # Only works for WebSocket and TCP
|
||||
EnableFallback: false # Only support for Trojan and Vless
|
||||
FallBackConfigs: # Support multiple fallbacks
|
||||
- SNI: # TLS SNI(Server Name Indication), Empty for any
|
||||
Alpn: # Alpn, Empty for any
|
||||
Path: # HTTP PATH, Empty for any
|
||||
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:
|
||||
EnableRealtime: false # Check device limit on real time
|
||||
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
|
||||
|
||||
require (
|
||||
github.com/apernet/hysteria/app v1.3.4
|
||||
github.com/apernet/hysteria/core v1.3.4
|
||||
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/fsnotify/fsnotify v1.6.0
|
||||
github.com/go-acme/lego/v4 v4.12.1
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/go-resty/resty/v2 v2.7.0
|
||||
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/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/sagernet/sing-box v1.2.7
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/spf13/viper v1.15.0
|
||||
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
|
||||
google.golang.org/protobuf v1.30.0
|
||||
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/andybalholm/brotli v1.0.5 // 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/cenkalti/backoff/v4 v4.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/hashicorp/errwrap v1.0.0 // 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/golang-lru/v2 v2.0.1 // 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/labbsr0x/bindman-dns-webhook v1.0.2 // 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/liquidweb/go-lwApi v0.0.5 // 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/magiconair/properties v1.8.7 // 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/mimuret/golang-iij-dpf v0.7.1 // 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/pmezard/go-difflib v1.0.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/qtls-go1-19 v0.3.2 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
|
||||
github.com/refraction-networking/utls v1.3.2 // 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/go-http v0.1.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.starlark.net v0.0.0-20230302034142-4b1e35fe2254 // 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
|
||||
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // 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/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-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-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/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
|
||||
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/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/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/go.mod h1:WxFihvoDBhatefn4w8Lipzy97CWjK6NF1/duT0kGUG0=
|
||||
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/beevik/ntp v1.0.0 h1:d0Lgy1xbNNqVyGfvg2Z96ItKcfyn3lzgus/oRoj9vnk=
|
||||
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 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
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/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/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/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
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/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/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
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/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.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.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-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
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/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
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.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.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/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
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/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.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/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||
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/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.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
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/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/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA=
|
||||
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/go.mod h1:h6AuFR/JpqwwM/vkj7s8KV3iGN8/jxn+zc437F8SZ8w=
|
||||
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.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
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/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/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
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/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-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/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
|
||||
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 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.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-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.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-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
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.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-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.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
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/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
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.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/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
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.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/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.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
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-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/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.27/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.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
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.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/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
|
||||
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=
|
||||
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=
|
||||
@ -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-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-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-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-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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
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-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-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/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||
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-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-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-20200116001909-b77594299b42/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-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-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-20200905004654-be1d3432aa8f/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-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-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-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-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-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-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-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-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-20220520151302-bc2c85ada10a/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.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.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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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,
|
||||
ExpireTime: 0,
|
||||
}
|
||||
info.UserLimitInfo.Store(builder.BuildUserTag(tag, &users[i]), userLimit)
|
||||
info.UserLimitInfo.Store(builder.BuildUserTag(tag, users[i].Uuid), userLimit)
|
||||
}
|
||||
}
|
||||
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
|
||||
c.nodeInfo, err = c.apiClient.GetNodeInfo()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get node info failed: %s", err)
|
||||
return fmt.Errorf("get node info error: %s", err)
|
||||
}
|
||||
// Update user
|
||||
c.userList, err = c.apiClient.GetUserList()
|
||||
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 {
|
||||
return errors.New("add users failed: not have any user")
|
||||
return errors.New("add users error: not have any user")
|
||||
}
|
||||
c.Tag = c.buildNodeTag()
|
||||
|
||||
@ -61,13 +61,19 @@ func (c *Controller) Start() error {
|
||||
// add rule limiter
|
||||
if !c.DisableGetRule {
|
||||
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
|
||||
err = c.server.AddNode(c.Tag, c.nodeInfo, c.ControllerConfig)
|
||||
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{
|
||||
Tag: c.Tag,
|
||||
@ -75,10 +81,10 @@ func (c *Controller) Start() error {
|
||||
UserInfo: c.userList,
|
||||
NodeInfo: c.nodeInfo,
|
||||
})
|
||||
log.Printf("[%s: %d] Added %d new users", c.nodeInfo.NodeType, c.nodeInfo.NodeId, added)
|
||||
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()
|
||||
return nil
|
||||
}
|
||||
@ -89,36 +95,36 @@ func (c *Controller) Close() error {
|
||||
if c.nodeInfoMonitorPeriodic != nil {
|
||||
err := c.nodeInfoMonitorPeriodic.Close()
|
||||
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 {
|
||||
err := c.userReportPeriodic.Close()
|
||||
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 {
|
||||
err := c.renewCertPeriodic.Close()
|
||||
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 {
|
||||
err := c.dynamicSpeedLimitPeriodic.Close()
|
||||
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 {
|
||||
err := c.onlineIpReportPeriodic.Close()
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
func (l *Lego) parseParmas(path string) string {
|
||||
func (l *Lego) parseParams(path string) string {
|
||||
r := strings.NewReplacer("{domain}", l.config.CertDomain,
|
||||
"{email}", l.config.Email)
|
||||
return r.Replace(path)
|
||||
@ -88,7 +88,7 @@ func (l *Lego) writeCert(certificates *certificate.Resource) error {
|
||||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@ -96,7 +96,7 @@ func (l *Lego) writeCert(certificates *certificate.Resource) error {
|
||||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Yuzuki616/V2bX/api/panel"
|
||||
"github.com/Yuzuki616/V2bX/conf"
|
||||
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)
|
||||
err = n.controllers[i].Start()
|
||||
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
|
||||
|
80
node/task.go
80
node/task.go
@ -4,13 +4,11 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
vCore "github.com/Yuzuki616/V2bX/core"
|
||||
|
||||
"github.com/Yuzuki616/V2bX/api/panel"
|
||||
"github.com/Yuzuki616/V2bX/common/builder"
|
||||
"github.com/Yuzuki616/V2bX/limiter"
|
||||
"github.com/Yuzuki616/V2bX/node/lego"
|
||||
"github.com/xtls/xray-core/common/task"
|
||||
@ -19,33 +17,33 @@ import (
|
||||
func (c *Controller) initTask() {
|
||||
// fetch node info task
|
||||
c.nodeInfoMonitorPeriodic = &task.Periodic{
|
||||
Interval: c.nodeInfo.BaseConfig.PullInterval.(time.Duration),
|
||||
Interval: c.nodeInfo.PullInterval,
|
||||
Execute: c.nodeInfoMonitor,
|
||||
}
|
||||
// fetch user list task
|
||||
c.userReportPeriodic = &task.Periodic{
|
||||
Interval: c.nodeInfo.BaseConfig.PushInterval.(time.Duration),
|
||||
Interval: c.nodeInfo.PushInterval,
|
||||
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
|
||||
go func() {
|
||||
time.Sleep(c.nodeInfo.BaseConfig.PullInterval.(time.Duration))
|
||||
time.Sleep(c.nodeInfo.PullInterval)
|
||||
_ = 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
|
||||
go func() {
|
||||
time.Sleep(c.nodeInfo.BaseConfig.PushInterval.(time.Duration))
|
||||
time.Sleep(c.nodeInfo.PullInterval)
|
||||
_ = 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.renewCertPeriodic = &task.Periodic{
|
||||
Interval: time.Hour * 24,
|
||||
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
|
||||
go func() {
|
||||
_ = c.renewCertPeriodic.Start()
|
||||
@ -80,6 +78,12 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
if newNodeInfo.Tls {
|
||||
err = c.requestCert()
|
||||
if err != nil {
|
||||
return fmt.Errorf("request cert error: %s", err)
|
||||
}
|
||||
}
|
||||
nodeInfoChanged = true
|
||||
}
|
||||
// Update User
|
||||
@ -93,8 +97,10 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
// Add new Limiter
|
||||
l := limiter.AddLimiter(c.Tag, &c.LimitConfig, newUserInfo)
|
||||
_, err = c.server.AddUsers(&vCore.AddUsersParams{
|
||||
Tag: c.Tag,
|
||||
Config: c.ControllerConfig,
|
||||
Tag: c.Tag,
|
||||
Config: c.ControllerConfig,
|
||||
UserInfo: newUserInfo,
|
||||
NodeInfo: newNodeInfo,
|
||||
})
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
@ -105,18 +111,18 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
log.Printf("Update Rule error: %s", err)
|
||||
}
|
||||
// Check interval
|
||||
if c.nodeInfoMonitorPeriodic.Interval != newNodeInfo.BaseConfig.PullInterval.(time.Duration) &&
|
||||
newNodeInfo.BaseConfig.PullInterval.(time.Duration) != 0 {
|
||||
c.nodeInfoMonitorPeriodic.Interval = newNodeInfo.BaseConfig.PullInterval.(time.Duration)
|
||||
if c.nodeInfoMonitorPeriodic.Interval != newNodeInfo.PullInterval &&
|
||||
newNodeInfo.PullInterval != 0 {
|
||||
c.nodeInfoMonitorPeriodic.Interval = newNodeInfo.PullInterval
|
||||
_ = c.nodeInfoMonitorPeriodic.Close()
|
||||
go func() {
|
||||
time.Sleep(c.nodeInfoMonitorPeriodic.Interval)
|
||||
_ = c.nodeInfoMonitorPeriodic.Start()
|
||||
}()
|
||||
}
|
||||
if c.userReportPeriodic.Interval != newNodeInfo.BaseConfig.PushInterval.(time.Duration) &&
|
||||
newNodeInfo.BaseConfig.PushInterval.(time.Duration) != 0 {
|
||||
c.userReportPeriodic.Interval = newNodeInfo.BaseConfig.PullInterval.(time.Duration)
|
||||
if c.userReportPeriodic.Interval != newNodeInfo.PushInterval &&
|
||||
newNodeInfo.PushInterval != 0 {
|
||||
c.userReportPeriodic.Interval = newNodeInfo.PullInterval
|
||||
_ = c.userReportPeriodic.Close()
|
||||
go func() {
|
||||
time.Sleep(c.userReportPeriodic.Interval)
|
||||
@ -156,46 +162,18 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
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))
|
||||
c.userList = newUserInfo
|
||||
}
|
||||
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) {
|
||||
// Get User traffic
|
||||
userTraffic := make([]panel.UserTraffic, 0)
|
||||
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 c.LimitConfig.EnableDynamicSpeedLimit {
|
||||
c.userList[i].Traffic += up + down
|
||||
@ -211,7 +189,7 @@ func (c *Controller) reportUserTraffic() (err error) {
|
||||
if err != nil {
|
||||
log.Printf("Report user traffic faild: %s", err)
|
||||
} 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
|
||||
@ -222,12 +200,12 @@ func (c *Controller) reportUserTraffic() (err error) {
|
||||
func (c *Controller) RenewCert() {
|
||||
l, err := lego.New(c.CertConfig)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
log.Print("new lego error: ", err)
|
||||
return
|
||||
}
|
||||
err = l.RenewCert()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
log.Print("renew cert error: ", err)
|
||||
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