新增调试信息
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
//go:build !windows
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ExecuteCmd(executablePath string, background bool, args ...string) (string, error) {
|
||||
cwd := filepath.Dir(executablePath)
|
||||
if appimage := os.Getenv("APPIMAGE"); appimage != "" {
|
||||
executablePath = appimage
|
||||
if !background {
|
||||
return "Fail", fmt.Errorf("Appimage cannot have service")
|
||||
}
|
||||
}
|
||||
|
||||
commands := [][]string{
|
||||
{"cocoasudo", "--prompt=Hiddify needs root for tunneling.", executablePath},
|
||||
{"gksu", executablePath},
|
||||
{"pkexec", executablePath},
|
||||
{"xterm", "-e", "sudo", executablePath, strings.Join(args, " ")},
|
||||
{"sudo", executablePath},
|
||||
}
|
||||
|
||||
var err error
|
||||
var cmd *exec.Cmd
|
||||
for _, command := range commands {
|
||||
cmd = exec.Command(command[0], command[1:]...)
|
||||
cmd.Dir = cwd
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
fmt.Printf("Running command: %v\n", command)
|
||||
if background {
|
||||
err = cmd.Start()
|
||||
} else {
|
||||
err = cmd.Run()
|
||||
}
|
||||
if err == nil {
|
||||
return "Ok", nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("Error executing run as root shell command")
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//go:build windows
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func ExecuteCmd(exe string, background bool, args ...string) (string, error) {
|
||||
verb := "runas"
|
||||
cwd, err := os.Getwd() // Error handling added
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
verbPtr, _ := syscall.UTF16PtrFromString(verb)
|
||||
exePtr, _ := syscall.UTF16PtrFromString(exe)
|
||||
cwdPtr, _ := syscall.UTF16PtrFromString(cwd)
|
||||
argPtr, _ := syscall.UTF16PtrFromString(strings.Join(args, " "))
|
||||
|
||||
var showCmd int32 = 0 // SW_NORMAL
|
||||
|
||||
err = windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
context "context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
pb "github.com/hiddify/hiddify-core/hiddifyrpc"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
dns "github.com/sagernet/sing-dns"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
const (
|
||||
serviceURL = "http://localhost:18020"
|
||||
startEndpoint = "/start"
|
||||
stopEndpoint = "/stop"
|
||||
)
|
||||
|
||||
var tunnelServiceRunning = false
|
||||
|
||||
func isSupportedOS() bool {
|
||||
return runtime.GOOS == "windows" || runtime.GOOS == "linux"
|
||||
}
|
||||
|
||||
func ActivateTunnelService(opt HiddifyOptions) (bool, error) {
|
||||
tunnelServiceRunning = true
|
||||
// if !isSupportedOS() {
|
||||
// return false, E.New("Unsupported OS: " + runtime.GOOS)
|
||||
// }
|
||||
|
||||
go startTunnelRequestWithFailover(opt, true)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func DeactivateTunnelServiceForce() (bool, error) {
|
||||
return stopTunnelRequest()
|
||||
}
|
||||
|
||||
func DeactivateTunnelService() (bool, error) {
|
||||
// if !isSupportedOS() {
|
||||
// return true, nil
|
||||
// }
|
||||
|
||||
if tunnelServiceRunning {
|
||||
res, err := stopTunnelRequest()
|
||||
if err != nil {
|
||||
tunnelServiceRunning = false
|
||||
}
|
||||
return res, err
|
||||
} else {
|
||||
go stopTunnelRequest()
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func startTunnelRequestWithFailover(opt HiddifyOptions, installService bool) {
|
||||
res, err := startTunnelRequest(opt, installService)
|
||||
fmt.Printf("Start Tunnel Result: %v\n", res)
|
||||
if err != nil {
|
||||
fmt.Printf("Start Tunnel Failed! Stopping core... err=%v\n", err)
|
||||
// StopAndAlert(pb.MessageType.MessageType_UNEXPECTED_ERROR, "Start Tunnel Failed! Stopping...")
|
||||
}
|
||||
}
|
||||
|
||||
func isPortInUse(port string) bool {
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:"+port)
|
||||
if err != nil {
|
||||
return true // Port is in use
|
||||
}
|
||||
defer listener.Close()
|
||||
return false // Port is available
|
||||
}
|
||||
|
||||
func startTunnelRequest(opt HiddifyOptions, installService bool) (bool, error) {
|
||||
if !isPortInUse("18020") {
|
||||
if installService {
|
||||
return runTunnelService(opt)
|
||||
}
|
||||
return false, fmt.Errorf("service is not running")
|
||||
}
|
||||
conn, err := grpc.Dial("127.0.0.1:18020", grpc.WithInsecure())
|
||||
if err != nil {
|
||||
log.Printf("did not connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
c := pb.NewTunnelServiceClient(conn)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
_, _ = c.Stop(ctx, &pb.Empty{})
|
||||
res, err := c.Start(ctx, &pb.TunnelStartRequest{
|
||||
Ipv6: opt.IPv6Mode == option.DomainStrategy(dns.DomainStrategyUseIPv4),
|
||||
ServerPort: int32(opt.InboundOptions.MixedPort),
|
||||
StrictRoute: opt.InboundOptions.StrictRoute,
|
||||
EndpointIndependentNat: true,
|
||||
Stack: opt.InboundOptions.TUNStack,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("could not greet: %+v %+v", res, err)
|
||||
|
||||
if installService {
|
||||
ExitTunnelService()
|
||||
return runTunnelService(opt)
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func stopTunnelRequest() (bool, error) {
|
||||
conn, err := grpc.Dial("127.0.0.1:18020", grpc.WithInsecure())
|
||||
if err != nil {
|
||||
log.Printf("did not connect: %v", err)
|
||||
return false, err
|
||||
}
|
||||
defer conn.Close()
|
||||
c := pb.NewTunnelServiceClient(conn)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancel()
|
||||
|
||||
res, err := c.Stop(ctx, &pb.Empty{})
|
||||
if err != nil {
|
||||
log.Printf("did not Stopped: %v %v", res, err)
|
||||
_, _ = c.Stop(ctx, &pb.Empty{})
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func ExitTunnelService() (bool, error) {
|
||||
conn, err := grpc.Dial("127.0.0.1:18020", grpc.WithInsecure())
|
||||
if err != nil {
|
||||
log.Printf("did not connect: %v", err)
|
||||
return false, err
|
||||
}
|
||||
defer conn.Close()
|
||||
c := pb.NewTunnelServiceClient(conn)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*1)
|
||||
defer cancel()
|
||||
|
||||
res, err := c.Exit(ctx, &pb.Empty{})
|
||||
if res != nil {
|
||||
log.Printf("did not exit: %v %v", res, err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func runTunnelService(opt HiddifyOptions) (bool, error) {
|
||||
executablePath := getTunnelServicePath()
|
||||
fmt.Printf("Executable path is %s", executablePath)
|
||||
out, err := ExecuteCmd(executablePath, false, "tunnel", "install")
|
||||
fmt.Println("Shell command executed:", out, err)
|
||||
if err != nil {
|
||||
out, err = ExecuteCmd(executablePath, true, "tunnel", "run")
|
||||
fmt.Println("Shell command executed without flag:", out, err)
|
||||
}
|
||||
if err == nil {
|
||||
<-time.After(1 * time.Second) // wait until service loaded completely
|
||||
}
|
||||
return startTunnelRequest(opt, false)
|
||||
}
|
||||
|
||||
func getTunnelServicePath() string {
|
||||
var fullPath string
|
||||
exePath, _ := os.Executable()
|
||||
binFolder := filepath.Dir(exePath)
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
fullPath = "HiddifyCli.exe"
|
||||
case "darwin":
|
||||
fallthrough
|
||||
default:
|
||||
fullPath = "HiddifyCli"
|
||||
}
|
||||
|
||||
abspath, _ := filepath.Abs(filepath.Join(binFolder, fullPath))
|
||||
return abspath
|
||||
}
|
||||
@@ -0,0 +1,869 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
dns "github.com/sagernet/sing-dns"
|
||||
)
|
||||
|
||||
const (
|
||||
DNSRemoteTag = "dns-remote"
|
||||
DNSLocalTag = "dns-local"
|
||||
DNSDirectTag = "dns-direct"
|
||||
DNSBlockTag = "dns-block"
|
||||
DNSFakeTag = "dns-fake"
|
||||
DNSTricksDirectTag = "dns-trick-direct"
|
||||
|
||||
OutboundDirectTag = "direct"
|
||||
OutboundBypassTag = "bypass"
|
||||
OutboundBlockTag = "block"
|
||||
OutboundSelectTag = "select"
|
||||
OutboundURLTestTag = "auto"
|
||||
OutboundDNSTag = "dns-out"
|
||||
OutboundDirectFragmentTag = "direct-fragment"
|
||||
|
||||
InboundTUNTag = "tun-in"
|
||||
InboundMixedTag = "mixed-in"
|
||||
InboundDNSTag = "dns-in"
|
||||
)
|
||||
|
||||
var OutboundMainProxyTag = OutboundSelectTag
|
||||
|
||||
func BuildConfigJson(configOpt HiddifyOptions, input option.Options) (string, error) {
|
||||
options, err := BuildConfig(configOpt, input)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
json.NewEncoder(&buffer)
|
||||
encoder := json.NewEncoder(&buffer)
|
||||
encoder.SetIndent("", " ")
|
||||
err = encoder.Encode(options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
||||
// TODO include selectors
|
||||
func BuildConfig(opt HiddifyOptions, input option.Options) (*option.Options, error) {
|
||||
fmt.Printf("config options: %++v\n", opt)
|
||||
|
||||
var options option.Options
|
||||
if opt.EnableFullConfig {
|
||||
options.Inbounds = input.Inbounds
|
||||
options.DNS = input.DNS
|
||||
options.Route = input.Route
|
||||
}
|
||||
|
||||
setClashAPI(&options, &opt)
|
||||
setLog(&options, &opt)
|
||||
setInbound(&options, &opt)
|
||||
setDns(&options, &opt)
|
||||
setRoutingOptions(&options, &opt)
|
||||
setFakeDns(&options, &opt)
|
||||
err := setOutbounds(&options, &input, &opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &options, nil
|
||||
}
|
||||
|
||||
func addForceDirect(options *option.Options, opt *HiddifyOptions, directDNSDomains map[string]bool) {
|
||||
remoteDNSAddress := opt.RemoteDnsAddress
|
||||
if strings.Contains(remoteDNSAddress, "://") {
|
||||
remoteDNSAddress = strings.SplitAfter(remoteDNSAddress, "://")[1]
|
||||
}
|
||||
parsedUrl, err := url.Parse(fmt.Sprintf("https://%s", remoteDNSAddress))
|
||||
if err == nil && net.ParseIP(parsedUrl.Host) == nil {
|
||||
directDNSDomains[parsedUrl.Host] = true
|
||||
}
|
||||
if len(directDNSDomains) > 0 {
|
||||
// trickDnsDomains := []string{}
|
||||
// directDNSDomains = removeDuplicateStr(directDNSDomains)
|
||||
// b, _ := batch.New(context.Background(), batch.WithConcurrencyNum[bool](10))
|
||||
// for _, d := range directDNSDomains {
|
||||
// b.Go(d, func() (bool, error) {
|
||||
// return isBlockedDomain(d), nil
|
||||
// })
|
||||
// }
|
||||
// b.Wait()
|
||||
// for domain, isBlock := range b.Result() {
|
||||
// if isBlock.Value {
|
||||
// trickDnsDomains = append(trickDnsDomains, domain)
|
||||
// }
|
||||
// }
|
||||
|
||||
// trickDomains := strings.Join(trickDnsDomains, ",")
|
||||
// trickRule := Rule{Domains: trickDomains, Outbound: OutboundBypassTag}
|
||||
// trickDnsRule := trickRule.MakeDNSRule()
|
||||
// trickDnsRule.Server = DNSTricksDirectTag
|
||||
// options.DNS.Rules = append([]option.DNSRule{{Type: C.RuleTypeDefault, DefaultOptions: trickDnsRule}}, options.DNS.Rules...)
|
||||
|
||||
directDNSDomainskeys := make([]string, 0, len(directDNSDomains))
|
||||
for key := range directDNSDomains {
|
||||
directDNSDomainskeys = append(directDNSDomainskeys, key)
|
||||
}
|
||||
|
||||
domains := strings.Join(directDNSDomainskeys, ",")
|
||||
directRule := Rule{Domains: domains, Outbound: OutboundBypassTag}
|
||||
dnsRule := directRule.MakeDNSRule()
|
||||
dnsRule.Server = DNSDirectTag
|
||||
options.DNS.Rules = append([]option.DNSRule{{Type: C.RuleTypeDefault, DefaultOptions: dnsRule}}, options.DNS.Rules...)
|
||||
}
|
||||
}
|
||||
|
||||
func setOutbounds(options *option.Options, input *option.Options, opt *HiddifyOptions) error {
|
||||
directDNSDomains := make(map[string]bool)
|
||||
var outbounds []option.Outbound
|
||||
var tags []string
|
||||
OutboundMainProxyTag = OutboundSelectTag
|
||||
// inbound==warp over proxies
|
||||
// outbound==proxies over warp
|
||||
if opt.Warp.EnableWarp {
|
||||
for _, out := range input.Outbounds {
|
||||
if out.Type == C.TypeCustom {
|
||||
if warp, ok := out.CustomOptions["warp"].(map[string]interface{}); ok {
|
||||
key, _ := warp["key"].(string)
|
||||
if key == "p1" {
|
||||
opt.Warp.EnableWarp = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if out.Type == C.TypeWireGuard && (out.WireGuardOptions.PrivateKey == opt.Warp.WireguardConfig.PrivateKey || out.WireGuardOptions.PrivateKey == "p1") {
|
||||
opt.Warp.EnableWarp = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if opt.Warp.EnableWarp && (opt.Warp.Mode == "warp_over_proxy" || opt.Warp.Mode == "proxy_over_warp") {
|
||||
out, err := GenerateWarpSingbox(opt.Warp.WireguardConfig, opt.Warp.CleanIP, opt.Warp.CleanPort, opt.Warp.FakePackets, opt.Warp.FakePacketSize, opt.Warp.FakePacketDelay, opt.Warp.FakePacketMode)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate warp config: %v", err)
|
||||
}
|
||||
out.Tag = "Hiddify Warp ✅"
|
||||
if opt.Warp.Mode == "warp_over_proxy" {
|
||||
out.WireGuardOptions.Detour = OutboundSelectTag
|
||||
OutboundMainProxyTag = out.Tag
|
||||
} else {
|
||||
out.WireGuardOptions.Detour = OutboundDirectTag
|
||||
}
|
||||
patchWarp(out, opt, true, nil)
|
||||
outbounds = append(outbounds, *out)
|
||||
// tags = append(tags, out.Tag)
|
||||
}
|
||||
for _, out := range input.Outbounds {
|
||||
outbound, serverDomain, err := patchOutbound(out, *opt, options.DNS.StaticIPs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if serverDomain != "" {
|
||||
directDNSDomains[serverDomain] = true
|
||||
}
|
||||
out = *outbound
|
||||
|
||||
switch out.Type {
|
||||
case C.TypeDirect, C.TypeBlock, C.TypeDNS:
|
||||
continue
|
||||
case C.TypeSelector, C.TypeURLTest:
|
||||
continue
|
||||
case C.TypeCustom:
|
||||
continue
|
||||
default:
|
||||
if !strings.Contains(out.Tag, "§hide§") {
|
||||
tags = append(tags, out.Tag)
|
||||
}
|
||||
out = patchHiddifyWarpFromConfig(out, *opt)
|
||||
outbounds = append(outbounds, out)
|
||||
}
|
||||
}
|
||||
|
||||
urlTest := option.Outbound{
|
||||
Type: C.TypeURLTest,
|
||||
Tag: OutboundURLTestTag,
|
||||
URLTestOptions: option.URLTestOutboundOptions{
|
||||
Outbounds: tags,
|
||||
URL: opt.ConnectionTestUrl,
|
||||
Interval: option.Duration(opt.URLTestInterval.Duration()),
|
||||
// IdleTimeout: option.Duration(opt.URLTestIdleTimeout.Duration()),
|
||||
Tolerance: 1,
|
||||
IdleTimeout: option.Duration(opt.URLTestInterval.Duration().Nanoseconds() * 3),
|
||||
InterruptExistConnections: true,
|
||||
},
|
||||
}
|
||||
defaultSelect := urlTest.Tag
|
||||
|
||||
for _, tag := range tags {
|
||||
if strings.Contains(tag, "§default§") {
|
||||
defaultSelect = "§default§"
|
||||
}
|
||||
}
|
||||
selector := option.Outbound{
|
||||
Type: C.TypeSelector,
|
||||
Tag: OutboundSelectTag,
|
||||
SelectorOptions: option.SelectorOutboundOptions{
|
||||
Outbounds: append([]string{urlTest.Tag}, tags...),
|
||||
Default: defaultSelect,
|
||||
InterruptExistConnections: true,
|
||||
},
|
||||
}
|
||||
|
||||
outbounds = append([]option.Outbound{selector, urlTest}, outbounds...)
|
||||
|
||||
options.Outbounds = append(
|
||||
outbounds,
|
||||
[]option.Outbound{
|
||||
{
|
||||
Tag: OutboundDNSTag,
|
||||
Type: C.TypeDNS,
|
||||
},
|
||||
{
|
||||
Tag: OutboundDirectTag,
|
||||
Type: C.TypeDirect,
|
||||
},
|
||||
{
|
||||
Tag: OutboundDirectFragmentTag,
|
||||
Type: C.TypeDirect,
|
||||
DirectOptions: option.DirectOutboundOptions{
|
||||
DialerOptions: option.DialerOptions{
|
||||
TCPFastOpen: false,
|
||||
TLSFragment: option.TLSFragmentOptions{
|
||||
Enabled: true,
|
||||
Size: opt.TLSTricks.FragmentSize,
|
||||
Sleep: opt.TLSTricks.FragmentSleep,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Tag: OutboundBypassTag,
|
||||
Type: C.TypeDirect,
|
||||
},
|
||||
{
|
||||
Tag: OutboundBlockTag,
|
||||
Type: C.TypeBlock,
|
||||
},
|
||||
}...,
|
||||
)
|
||||
|
||||
addForceDirect(options, opt, directDNSDomains)
|
||||
return nil
|
||||
}
|
||||
|
||||
func setClashAPI(options *option.Options, opt *HiddifyOptions) {
|
||||
if opt.EnableClashApi {
|
||||
if opt.ClashApiSecret == "" {
|
||||
opt.ClashApiSecret = generateRandomString(16)
|
||||
}
|
||||
options.Experimental = &option.ExperimentalOptions{
|
||||
ClashAPI: &option.ClashAPIOptions{
|
||||
ExternalController: fmt.Sprintf("%s:%d", "127.0.0.1", opt.ClashApiPort),
|
||||
Secret: opt.ClashApiSecret,
|
||||
},
|
||||
|
||||
CacheFile: &option.CacheFileOptions{
|
||||
Enabled: true,
|
||||
Path: "clash.db",
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setLog(options *option.Options, opt *HiddifyOptions) {
|
||||
options.Log = &option.LogOptions{
|
||||
Level: opt.LogLevel,
|
||||
Output: opt.LogFile,
|
||||
Disabled: false,
|
||||
Timestamp: true,
|
||||
DisableColor: true,
|
||||
}
|
||||
}
|
||||
|
||||
func setInbound(options *option.Options, opt *HiddifyOptions) {
|
||||
var inboundDomainStrategy option.DomainStrategy
|
||||
if !opt.ResolveDestination {
|
||||
inboundDomainStrategy = option.DomainStrategy(dns.DomainStrategyAsIS)
|
||||
} else {
|
||||
inboundDomainStrategy = opt.IPv6Mode
|
||||
}
|
||||
if opt.EnableTunService {
|
||||
ActivateTunnelService(*opt)
|
||||
} else if opt.EnableTun {
|
||||
tunInbound := option.Inbound{
|
||||
Type: C.TypeTun,
|
||||
Tag: InboundTUNTag,
|
||||
|
||||
TunOptions: option.TunInboundOptions{
|
||||
Stack: opt.TUNStack,
|
||||
MTU: opt.MTU,
|
||||
AutoRoute: true,
|
||||
StrictRoute: opt.StrictRoute,
|
||||
EndpointIndependentNat: true,
|
||||
// GSO: runtime.GOOS != "windows",
|
||||
InboundOptions: option.InboundOptions{
|
||||
SniffEnabled: true,
|
||||
SniffOverrideDestination: false,
|
||||
DomainStrategy: inboundDomainStrategy,
|
||||
},
|
||||
},
|
||||
}
|
||||
switch opt.IPv6Mode {
|
||||
case option.DomainStrategy(dns.DomainStrategyUseIPv4):
|
||||
tunInbound.TunOptions.Inet4Address = []netip.Prefix{
|
||||
netip.MustParsePrefix("172.19.0.1/28"),
|
||||
}
|
||||
case option.DomainStrategy(dns.DomainStrategyUseIPv6):
|
||||
tunInbound.TunOptions.Inet6Address = []netip.Prefix{
|
||||
netip.MustParsePrefix("fdfe:dcba:9876::1/126"),
|
||||
}
|
||||
default:
|
||||
tunInbound.TunOptions.Inet4Address = []netip.Prefix{
|
||||
netip.MustParsePrefix("172.19.0.1/28"),
|
||||
}
|
||||
tunInbound.TunOptions.Inet6Address = []netip.Prefix{
|
||||
netip.MustParsePrefix("fdfe:dcba:9876::1/126"),
|
||||
}
|
||||
}
|
||||
options.Inbounds = append(options.Inbounds, tunInbound)
|
||||
|
||||
}
|
||||
|
||||
var bind string
|
||||
if opt.AllowConnectionFromLAN {
|
||||
bind = "0.0.0.0"
|
||||
} else {
|
||||
bind = "127.0.0.1"
|
||||
}
|
||||
|
||||
options.Inbounds = append(
|
||||
options.Inbounds,
|
||||
option.Inbound{
|
||||
Type: C.TypeMixed,
|
||||
Tag: InboundMixedTag,
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.MustParseAddr(bind)),
|
||||
ListenPort: opt.MixedPort,
|
||||
InboundOptions: option.InboundOptions{
|
||||
SniffEnabled: true,
|
||||
SniffOverrideDestination: true,
|
||||
DomainStrategy: inboundDomainStrategy,
|
||||
},
|
||||
},
|
||||
SetSystemProxy: opt.SetSystemProxy,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
options.Inbounds = append(
|
||||
options.Inbounds,
|
||||
option.Inbound{
|
||||
Type: C.TypeDirect,
|
||||
Tag: InboundDNSTag,
|
||||
DirectOptions: option.DirectInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.MustParseAddr(bind)),
|
||||
ListenPort: opt.LocalDnsPort,
|
||||
},
|
||||
// OverrideAddress: "1.1.1.1",
|
||||
// OverridePort: 53,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func setDns(options *option.Options, opt *HiddifyOptions) {
|
||||
options.DNS = &option.DNSOptions{
|
||||
StaticIPs: map[string][]string{},
|
||||
DNSClientOptions: option.DNSClientOptions{
|
||||
IndependentCache: opt.IndependentDNSCache,
|
||||
},
|
||||
Final: DNSRemoteTag,
|
||||
Servers: []option.DNSServerOptions{
|
||||
{
|
||||
Tag: DNSRemoteTag,
|
||||
Address: opt.RemoteDnsAddress,
|
||||
AddressResolver: DNSDirectTag,
|
||||
Strategy: opt.RemoteDnsDomainStrategy,
|
||||
},
|
||||
{
|
||||
Tag: DNSTricksDirectTag,
|
||||
Address: "https://sky.rethinkdns.com/",
|
||||
// AddressResolver: "dns-local",
|
||||
Strategy: opt.DirectDnsDomainStrategy,
|
||||
Detour: OutboundDirectFragmentTag,
|
||||
},
|
||||
{
|
||||
Tag: DNSDirectTag,
|
||||
Address: opt.DirectDnsAddress,
|
||||
AddressResolver: DNSLocalTag,
|
||||
Strategy: opt.DirectDnsDomainStrategy,
|
||||
Detour: OutboundDirectTag,
|
||||
},
|
||||
{
|
||||
Tag: DNSLocalTag,
|
||||
Address: "local",
|
||||
Detour: OutboundDirectTag,
|
||||
},
|
||||
{
|
||||
Tag: DNSBlockTag,
|
||||
Address: "rcode://success",
|
||||
},
|
||||
},
|
||||
}
|
||||
sky_rethinkdns := getIPs([]string{"www.speedtest.net", "sky.rethinkdns.com"})
|
||||
if len(sky_rethinkdns) > 0 {
|
||||
options.DNS.StaticIPs["sky.rethinkdns.com"] = sky_rethinkdns
|
||||
}
|
||||
}
|
||||
|
||||
func setFakeDns(options *option.Options, opt *HiddifyOptions) {
|
||||
if opt.EnableFakeDNS {
|
||||
inet4Range := netip.MustParsePrefix("198.18.0.0/15")
|
||||
inet6Range := netip.MustParsePrefix("fc00::/18")
|
||||
options.DNS.FakeIP = &option.DNSFakeIPOptions{
|
||||
Enabled: true,
|
||||
Inet4Range: &inet4Range,
|
||||
Inet6Range: &inet6Range,
|
||||
}
|
||||
options.DNS.Servers = append(
|
||||
options.DNS.Servers,
|
||||
option.DNSServerOptions{
|
||||
Tag: DNSFakeTag,
|
||||
Address: "fakeip",
|
||||
Strategy: option.DomainStrategy(dns.DomainStrategyUseIPv4),
|
||||
},
|
||||
)
|
||||
options.DNS.Rules = append(
|
||||
options.DNS.Rules,
|
||||
option.DNSRule{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultDNSRule{
|
||||
Inbound: []string{InboundTUNTag},
|
||||
Server: DNSFakeTag,
|
||||
DisableCache: true,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func setRoutingOptions(options *option.Options, opt *HiddifyOptions) {
|
||||
dnsRules := []option.DefaultDNSRule{}
|
||||
routeRules := []option.Rule{}
|
||||
rulesets := []option.RuleSet{}
|
||||
|
||||
if opt.EnableTun && runtime.GOOS == "android" {
|
||||
routeRules = append(
|
||||
routeRules,
|
||||
option.Rule{
|
||||
Type: C.RuleTypeDefault,
|
||||
|
||||
DefaultOptions: option.DefaultRule{
|
||||
Inbound: []string{InboundTUNTag},
|
||||
PackageName: []string{"app.brAccelerator.com"},
|
||||
Outbound: OutboundBypassTag,
|
||||
},
|
||||
},
|
||||
)
|
||||
// routeRules = append(
|
||||
// routeRules,
|
||||
// option.Rule{
|
||||
// Type: C.RuleTypeDefault,
|
||||
// DefaultOptions: option.DefaultRule{
|
||||
// ProcessName: []string{"Hiddify", "Hiddify.exe", "HiddifyCli", "HiddifyCli.exe"},
|
||||
// Outbound: OutboundBypassTag,
|
||||
// },
|
||||
// },
|
||||
// )
|
||||
}
|
||||
routeRules = append(routeRules, option.Rule{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultRule{
|
||||
Inbound: []string{InboundDNSTag},
|
||||
Outbound: OutboundDNSTag,
|
||||
},
|
||||
})
|
||||
routeRules = append(routeRules, option.Rule{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultRule{
|
||||
Port: []uint16{53},
|
||||
Outbound: OutboundDNSTag,
|
||||
},
|
||||
})
|
||||
|
||||
// {
|
||||
// Type: C.RuleTypeDefault,
|
||||
// DefaultOptions: option.DefaultRule{
|
||||
// ClashMode: "Direct",
|
||||
// Outbound: OutboundDirectTag,
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// Type: C.RuleTypeDefault,
|
||||
// DefaultOptions: option.DefaultRule{
|
||||
// ClashMode: "Global",
|
||||
// Outbound: OutboundMainProxyTag,
|
||||
// },
|
||||
// }, }
|
||||
|
||||
if opt.BypassLAN {
|
||||
routeRules = append(
|
||||
routeRules,
|
||||
option.Rule{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultRule{
|
||||
// GeoIP: []string{"private"},
|
||||
IPIsPrivate: true,
|
||||
Outbound: OutboundBypassTag,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
for _, rule := range opt.Rules {
|
||||
routeRule := rule.MakeRule()
|
||||
switch rule.Outbound {
|
||||
case "bypass":
|
||||
routeRule.Outbound = OutboundBypassTag
|
||||
case "block":
|
||||
routeRule.Outbound = OutboundBlockTag
|
||||
case "proxy":
|
||||
routeRule.Outbound = OutboundMainProxyTag
|
||||
}
|
||||
|
||||
if routeRule.IsValid() {
|
||||
routeRules = append(
|
||||
routeRules,
|
||||
option.Rule{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: routeRule,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
dnsRule := rule.MakeDNSRule()
|
||||
switch rule.Outbound {
|
||||
case "bypass":
|
||||
dnsRule.Server = DNSDirectTag
|
||||
case "block":
|
||||
dnsRule.Server = DNSBlockTag
|
||||
dnsRule.DisableCache = true
|
||||
case "proxy":
|
||||
if opt.EnableFakeDNS {
|
||||
fakeDnsRule := dnsRule
|
||||
fakeDnsRule.Server = DNSFakeTag
|
||||
fakeDnsRule.Inbound = []string{InboundTUNTag, InboundMixedTag}
|
||||
dnsRules = append(dnsRules, fakeDnsRule)
|
||||
}
|
||||
dnsRule.Server = DNSRemoteTag
|
||||
}
|
||||
dnsRules = append(dnsRules, dnsRule)
|
||||
}
|
||||
|
||||
parsedURL, err := url.Parse(opt.ConnectionTestUrl)
|
||||
if err == nil {
|
||||
var dnsCPttl uint32 = 3000
|
||||
dnsRules = append(dnsRules, option.DefaultDNSRule{
|
||||
Domain: []string{parsedURL.Host},
|
||||
Server: DNSRemoteTag,
|
||||
RewriteTTL: &dnsCPttl,
|
||||
DisableCache: false,
|
||||
})
|
||||
}
|
||||
|
||||
if opt.BlockAds {
|
||||
rulesets = append(rulesets, option.RuleSet{
|
||||
Type: C.RuleSetTypeRemote,
|
||||
Tag: "geosite-ads",
|
||||
Format: C.RuleSetFormatBinary,
|
||||
RemoteOptions: option.RemoteRuleSet{
|
||||
URL: "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/block/geosite-category-ads-all.srs",
|
||||
UpdateInterval: option.Duration(5 * time.Hour * 24),
|
||||
},
|
||||
})
|
||||
rulesets = append(rulesets, option.RuleSet{
|
||||
Type: C.RuleSetTypeRemote,
|
||||
Tag: "geosite-malware",
|
||||
Format: C.RuleSetFormatBinary,
|
||||
RemoteOptions: option.RemoteRuleSet{
|
||||
URL: "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/block/geosite-malware.srs",
|
||||
UpdateInterval: option.Duration(5 * time.Hour * 24),
|
||||
},
|
||||
})
|
||||
rulesets = append(rulesets, option.RuleSet{
|
||||
Type: C.RuleSetTypeRemote,
|
||||
Tag: "geosite-phishing",
|
||||
Format: C.RuleSetFormatBinary,
|
||||
RemoteOptions: option.RemoteRuleSet{
|
||||
URL: "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/block/geosite-phishing.srs",
|
||||
UpdateInterval: option.Duration(5 * time.Hour * 24),
|
||||
},
|
||||
})
|
||||
rulesets = append(rulesets, option.RuleSet{
|
||||
Type: C.RuleSetTypeRemote,
|
||||
Tag: "geosite-cryptominers",
|
||||
Format: C.RuleSetFormatBinary,
|
||||
RemoteOptions: option.RemoteRuleSet{
|
||||
URL: "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/block/geosite-cryptominers.srs",
|
||||
UpdateInterval: option.Duration(5 * time.Hour * 24),
|
||||
},
|
||||
})
|
||||
rulesets = append(rulesets, option.RuleSet{
|
||||
Type: C.RuleSetTypeRemote,
|
||||
Tag: "geoip-phishing",
|
||||
Format: C.RuleSetFormatBinary,
|
||||
RemoteOptions: option.RemoteRuleSet{
|
||||
URL: "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/block/geoip-phishing.srs",
|
||||
UpdateInterval: option.Duration(5 * time.Hour * 24),
|
||||
},
|
||||
})
|
||||
rulesets = append(rulesets, option.RuleSet{
|
||||
Type: C.RuleSetTypeRemote,
|
||||
Tag: "geoip-malware",
|
||||
Format: C.RuleSetFormatBinary,
|
||||
RemoteOptions: option.RemoteRuleSet{
|
||||
URL: "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/block/geoip-malware.srs",
|
||||
UpdateInterval: option.Duration(5 * time.Hour * 24),
|
||||
},
|
||||
})
|
||||
|
||||
routeRules = append(routeRules, option.Rule{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultRule{
|
||||
RuleSet: []string{
|
||||
"geosite-ads",
|
||||
"geosite-malware",
|
||||
"geosite-phishing",
|
||||
"geosite-cryptominers",
|
||||
"geoip-malware",
|
||||
"geoip-phishing",
|
||||
},
|
||||
Outbound: OutboundBlockTag,
|
||||
},
|
||||
})
|
||||
dnsRules = append(dnsRules, option.DefaultDNSRule{
|
||||
RuleSet: []string{
|
||||
"geosite-ads",
|
||||
"geosite-malware",
|
||||
"geosite-phishing",
|
||||
"geosite-cryptominers",
|
||||
"geoip-malware",
|
||||
"geoip-phishing",
|
||||
},
|
||||
Server: DNSBlockTag,
|
||||
// DisableCache: true,
|
||||
})
|
||||
|
||||
}
|
||||
if opt.Region != "other" {
|
||||
dnsRules = append(dnsRules, option.DefaultDNSRule{
|
||||
DomainSuffix: []string{"." + opt.Region},
|
||||
Server: DNSDirectTag,
|
||||
})
|
||||
routeRules = append(routeRules, option.Rule{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultRule{
|
||||
DomainSuffix: []string{"." + opt.Region},
|
||||
Outbound: OutboundDirectTag,
|
||||
},
|
||||
})
|
||||
dnsRules = append(dnsRules, option.DefaultDNSRule{
|
||||
RuleSet: []string{
|
||||
"geoip-" + opt.Region,
|
||||
"geosite-" + opt.Region,
|
||||
},
|
||||
Server: DNSDirectTag,
|
||||
})
|
||||
|
||||
rulesets = append(rulesets, option.RuleSet{
|
||||
Type: C.RuleSetTypeRemote,
|
||||
Tag: "geoip-" + opt.Region,
|
||||
Format: C.RuleSetFormatBinary,
|
||||
RemoteOptions: option.RemoteRuleSet{
|
||||
URL: "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/country/geoip-" + opt.Region + ".srs",
|
||||
UpdateInterval: option.Duration(5 * time.Hour * 24),
|
||||
},
|
||||
})
|
||||
rulesets = append(rulesets, option.RuleSet{
|
||||
Type: C.RuleSetTypeRemote,
|
||||
Tag: "geosite-" + opt.Region,
|
||||
Format: C.RuleSetFormatBinary,
|
||||
RemoteOptions: option.RemoteRuleSet{
|
||||
URL: "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/country/geosite-" + opt.Region + ".srs",
|
||||
UpdateInterval: option.Duration(5 * time.Hour * 24),
|
||||
},
|
||||
})
|
||||
|
||||
routeRules = append(routeRules, option.Rule{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultRule{
|
||||
RuleSet: []string{
|
||||
"geoip-" + opt.Region,
|
||||
"geosite-" + opt.Region,
|
||||
},
|
||||
Outbound: OutboundDirectTag,
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
options.Route = &option.RouteOptions{
|
||||
Rules: routeRules,
|
||||
Final: OutboundMainProxyTag,
|
||||
AutoDetectInterface: true,
|
||||
OverrideAndroidVPN: true,
|
||||
RuleSet: rulesets,
|
||||
// GeoIP: &option.GeoIPOptions{
|
||||
// Path: opt.GeoIPPath,
|
||||
// },
|
||||
// Geosite: &option.GeositeOptions{
|
||||
// Path: opt.GeoSitePath,
|
||||
// },
|
||||
}
|
||||
if opt.EnableDNSRouting {
|
||||
for _, dnsRule := range dnsRules {
|
||||
if dnsRule.IsValid() {
|
||||
options.DNS.Rules = append(
|
||||
options.DNS.Rules,
|
||||
option.DNSRule{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: dnsRule,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func patchHiddifyWarpFromConfig(out option.Outbound, opt HiddifyOptions) option.Outbound {
|
||||
if opt.Warp.EnableWarp && opt.Warp.Mode == "proxy_over_warp" {
|
||||
if out.DirectOptions.Detour == "" {
|
||||
out.DirectOptions.Detour = "Hiddify Warp ✅"
|
||||
}
|
||||
if out.HTTPOptions.Detour == "" {
|
||||
out.HTTPOptions.Detour = "Hiddify Warp ✅"
|
||||
}
|
||||
if out.Hysteria2Options.Detour == "" {
|
||||
out.Hysteria2Options.Detour = "Hiddify Warp ✅"
|
||||
}
|
||||
if out.HysteriaOptions.Detour == "" {
|
||||
out.HysteriaOptions.Detour = "Hiddify Warp ✅"
|
||||
}
|
||||
if out.SSHOptions.Detour == "" {
|
||||
out.SSHOptions.Detour = "Hiddify Warp ✅"
|
||||
}
|
||||
if out.ShadowTLSOptions.Detour == "" {
|
||||
out.ShadowTLSOptions.Detour = "Hiddify Warp ✅"
|
||||
}
|
||||
if out.ShadowsocksOptions.Detour == "" {
|
||||
out.ShadowsocksOptions.Detour = "Hiddify Warp ✅"
|
||||
}
|
||||
if out.ShadowsocksROptions.Detour == "" {
|
||||
out.ShadowsocksROptions.Detour = "Hiddify Warp ✅"
|
||||
}
|
||||
if out.SocksOptions.Detour == "" {
|
||||
out.SocksOptions.Detour = "Hiddify Warp ✅"
|
||||
}
|
||||
if out.TUICOptions.Detour == "" {
|
||||
out.TUICOptions.Detour = "Hiddify Warp ✅"
|
||||
}
|
||||
if out.TorOptions.Detour == "" {
|
||||
out.TorOptions.Detour = "Hiddify Warp ✅"
|
||||
}
|
||||
if out.TrojanOptions.Detour == "" {
|
||||
out.TrojanOptions.Detour = "Hiddify Warp ✅"
|
||||
}
|
||||
if out.VLESSOptions.Detour == "" {
|
||||
out.VLESSOptions.Detour = "Hiddify Warp ✅"
|
||||
}
|
||||
if out.VMessOptions.Detour == "" {
|
||||
out.VMessOptions.Detour = "Hiddify Warp ✅"
|
||||
}
|
||||
if out.WireGuardOptions.Detour == "" {
|
||||
out.WireGuardOptions.Detour = "Hiddify Warp ✅"
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func getIPs(domains []string) []string {
|
||||
res := []string{}
|
||||
for _, d := range domains {
|
||||
ips, err := net.LookupHost(d)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, ip := range ips {
|
||||
if !strings.HasPrefix(ip, "10.") {
|
||||
res = append(res, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func isBlockedDomain(domain string) bool {
|
||||
if strings.HasPrefix("full:", domain) {
|
||||
return false
|
||||
}
|
||||
ips, err := net.LookupHost(domain)
|
||||
if err != nil {
|
||||
// fmt.Println(err)
|
||||
return true
|
||||
}
|
||||
|
||||
// Print the IP addresses associated with the domain
|
||||
fmt.Printf("IP addresses for %s:\n", domain)
|
||||
for _, ip := range ips {
|
||||
if strings.HasPrefix(ip, "10.") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func removeDuplicateStr(strSlice []string) []string {
|
||||
allKeys := make(map[string]bool)
|
||||
list := []string{}
|
||||
for _, item := range strSlice {
|
||||
if _, value := allKeys[item]; !value {
|
||||
allKeys[item] = true
|
||||
list = append(list, item)
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func generateRandomString(length int) string {
|
||||
// Determine the number of bytes needed
|
||||
bytesNeeded := (length*6 + 7) / 8
|
||||
|
||||
// Generate random bytes
|
||||
randomBytes := make([]byte, bytesNeeded)
|
||||
_, err := rand.Read(randomBytes)
|
||||
if err != nil {
|
||||
return "hiddify"
|
||||
}
|
||||
|
||||
// Encode random bytes to base64
|
||||
randomString := base64.URLEncoding.EncodeToString(randomBytes)
|
||||
|
||||
// Trim padding characters and return the string
|
||||
return randomString[:length]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"log": {},
|
||||
"dns": {},
|
||||
"inbounds": [],
|
||||
"outbounds": [],
|
||||
"route": {},
|
||||
"experimental": {}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package config
|
||||
|
||||
const (
|
||||
WarpOverProxy = "warp_over_proxy"
|
||||
ProxyOverWarp = "proxy_over_warp"
|
||||
)
|
||||
@@ -0,0 +1,389 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.33.0
|
||||
// protoc v4.25.3
|
||||
// source: core.proto
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type ParseConfigRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
TempPath string `protobuf:"bytes,1,opt,name=tempPath,proto3" json:"tempPath,omitempty"`
|
||||
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
|
||||
Debug bool `protobuf:"varint,3,opt,name=debug,proto3" json:"debug,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ParseConfigRequest) Reset() {
|
||||
*x = ParseConfigRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_core_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ParseConfigRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ParseConfigRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ParseConfigRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_core_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ParseConfigRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ParseConfigRequest) Descriptor() ([]byte, []int) {
|
||||
return file_core_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ParseConfigRequest) GetTempPath() string {
|
||||
if x != nil {
|
||||
return x.TempPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ParseConfigRequest) GetPath() string {
|
||||
if x != nil {
|
||||
return x.Path
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ParseConfigRequest) GetDebug() bool {
|
||||
if x != nil {
|
||||
return x.Debug
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type ParseConfigResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Error *string `protobuf:"bytes,1,opt,name=error,proto3,oneof" json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ParseConfigResponse) Reset() {
|
||||
*x = ParseConfigResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_core_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ParseConfigResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ParseConfigResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ParseConfigResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_core_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ParseConfigResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ParseConfigResponse) Descriptor() ([]byte, []int) {
|
||||
return file_core_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *ParseConfigResponse) GetError() string {
|
||||
if x != nil && x.Error != nil {
|
||||
return *x.Error
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GenerateConfigRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
|
||||
Debug bool `protobuf:"varint,2,opt,name=debug,proto3" json:"debug,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GenerateConfigRequest) Reset() {
|
||||
*x = GenerateConfigRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_core_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GenerateConfigRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GenerateConfigRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GenerateConfigRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_core_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GenerateConfigRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GenerateConfigRequest) Descriptor() ([]byte, []int) {
|
||||
return file_core_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *GenerateConfigRequest) GetPath() string {
|
||||
if x != nil {
|
||||
return x.Path
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GenerateConfigRequest) GetDebug() bool {
|
||||
if x != nil {
|
||||
return x.Debug
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type GenerateConfigResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Config string `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"`
|
||||
Error *string `protobuf:"bytes,2,opt,name=error,proto3,oneof" json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GenerateConfigResponse) Reset() {
|
||||
*x = GenerateConfigResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_core_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GenerateConfigResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GenerateConfigResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GenerateConfigResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_core_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GenerateConfigResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GenerateConfigResponse) Descriptor() ([]byte, []int) {
|
||||
return file_core_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *GenerateConfigResponse) GetConfig() string {
|
||||
if x != nil {
|
||||
return x.Config
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GenerateConfigResponse) GetError() string {
|
||||
if x != nil && x.Error != nil {
|
||||
return *x.Error
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_core_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_core_proto_rawDesc = []byte{
|
||||
0x0a, 0x0a, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x43, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x5a, 0x0a, 0x12, 0x50,
|
||||
0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x50, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74,
|
||||
0x68, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08,
|
||||
0x52, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x22, 0x3a, 0x0a, 0x13, 0x50, 0x61, 0x72, 0x73, 0x65,
|
||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x19,
|
||||
0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52,
|
||||
0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x65, 0x72,
|
||||
0x72, 0x6f, 0x72, 0x22, 0x41, 0x0a, 0x15, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x43,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04,
|
||||
0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68,
|
||||
0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x22, 0x55, 0x0a, 0x16, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61,
|
||||
0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f,
|
||||
0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
|
||||
0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x32, 0xc6, 0x01,
|
||||
0x0a, 0x0b, 0x43, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x54, 0x0a,
|
||||
0x0b, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x21, 0x2e, 0x43,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x50, 0x61, 0x72,
|
||||
0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x22, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
|
||||
0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x12, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x46,
|
||||
0x75, 0x6c, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x24, 0x2e, 0x43, 0x6f, 0x6e, 0x66,
|
||||
0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61,
|
||||
0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x25, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
|
||||
0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x2e, 0x2f, 0x63, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_core_proto_rawDescOnce sync.Once
|
||||
file_core_proto_rawDescData = file_core_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_core_proto_rawDescGZIP() []byte {
|
||||
file_core_proto_rawDescOnce.Do(func() {
|
||||
file_core_proto_rawDescData = protoimpl.X.CompressGZIP(file_core_proto_rawDescData)
|
||||
})
|
||||
return file_core_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_core_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_core_proto_goTypes = []interface{}{
|
||||
(*ParseConfigRequest)(nil), // 0: HiddifyOptions.ParseConfigRequest
|
||||
(*ParseConfigResponse)(nil), // 1: HiddifyOptions.ParseConfigResponse
|
||||
(*GenerateConfigRequest)(nil), // 2: HiddifyOptions.GenerateConfigRequest
|
||||
(*GenerateConfigResponse)(nil), // 3: HiddifyOptions.GenerateConfigResponse
|
||||
}
|
||||
var file_core_proto_depIdxs = []int32{
|
||||
0, // 0: HiddifyOptions.CoreService.ParseConfig:input_type -> HiddifyOptions.ParseConfigRequest
|
||||
2, // 1: HiddifyOptions.CoreService.GenerateFullConfig:input_type -> HiddifyOptions.GenerateConfigRequest
|
||||
1, // 2: HiddifyOptions.CoreService.ParseConfig:output_type -> HiddifyOptions.ParseConfigResponse
|
||||
3, // 3: HiddifyOptions.CoreService.GenerateFullConfig:output_type -> HiddifyOptions.GenerateConfigResponse
|
||||
2, // [2:4] is the sub-list for method output_type
|
||||
0, // [0:2] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_core_proto_init() }
|
||||
func file_core_proto_init() {
|
||||
if File_core_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_core_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ParseConfigRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_core_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ParseConfigResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_core_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GenerateConfigRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_core_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GenerateConfigResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_core_proto_msgTypes[1].OneofWrappers = []interface{}{}
|
||||
file_core_proto_msgTypes[3].OneofWrappers = []interface{}{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_core_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_core_proto_goTypes,
|
||||
DependencyIndexes: file_core_proto_depIdxs,
|
||||
MessageInfos: file_core_proto_msgTypes,
|
||||
}.Build()
|
||||
File_core_proto = out.File
|
||||
file_core_proto_rawDesc = nil
|
||||
file_core_proto_goTypes = nil
|
||||
file_core_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.3.0
|
||||
// - protoc v4.25.3
|
||||
// source: core.proto
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
const (
|
||||
CoreService_ParseConfig_FullMethodName = "/HiddifyOptions.CoreService/ParseConfig"
|
||||
CoreService_GenerateFullConfig_FullMethodName = "/HiddifyOptions.CoreService/GenerateFullConfig"
|
||||
)
|
||||
|
||||
// CoreServiceClient is the client API for CoreService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type CoreServiceClient interface {
|
||||
ParseConfig(ctx context.Context, in *ParseConfigRequest, opts ...grpc.CallOption) (*ParseConfigResponse, error)
|
||||
GenerateFullConfig(ctx context.Context, in *GenerateConfigRequest, opts ...grpc.CallOption) (*GenerateConfigResponse, error)
|
||||
}
|
||||
|
||||
type coreServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewCoreServiceClient(cc grpc.ClientConnInterface) CoreServiceClient {
|
||||
return &coreServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *coreServiceClient) ParseConfig(ctx context.Context, in *ParseConfigRequest, opts ...grpc.CallOption) (*ParseConfigResponse, error) {
|
||||
out := new(ParseConfigResponse)
|
||||
err := c.cc.Invoke(ctx, CoreService_ParseConfig_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *coreServiceClient) GenerateFullConfig(ctx context.Context, in *GenerateConfigRequest, opts ...grpc.CallOption) (*GenerateConfigResponse, error) {
|
||||
out := new(GenerateConfigResponse)
|
||||
err := c.cc.Invoke(ctx, CoreService_GenerateFullConfig_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// CoreServiceServer is the server API for CoreService service.
|
||||
// All implementations must embed UnimplementedCoreServiceServer
|
||||
// for forward compatibility
|
||||
type CoreServiceServer interface {
|
||||
ParseConfig(context.Context, *ParseConfigRequest) (*ParseConfigResponse, error)
|
||||
GenerateFullConfig(context.Context, *GenerateConfigRequest) (*GenerateConfigResponse, error)
|
||||
mustEmbedUnimplementedCoreServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedCoreServiceServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedCoreServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedCoreServiceServer) ParseConfig(context.Context, *ParseConfigRequest) (*ParseConfigResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ParseConfig not implemented")
|
||||
}
|
||||
func (UnimplementedCoreServiceServer) GenerateFullConfig(context.Context, *GenerateConfigRequest) (*GenerateConfigResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GenerateFullConfig not implemented")
|
||||
}
|
||||
func (UnimplementedCoreServiceServer) mustEmbedUnimplementedCoreServiceServer() {}
|
||||
|
||||
// UnsafeCoreServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to CoreServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeCoreServiceServer interface {
|
||||
mustEmbedUnimplementedCoreServiceServer()
|
||||
}
|
||||
|
||||
func RegisterCoreServiceServer(s grpc.ServiceRegistrar, srv CoreServiceServer) {
|
||||
s.RegisterService(&CoreService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _CoreService_ParseConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ParseConfigRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CoreServiceServer).ParseConfig(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CoreService_ParseConfig_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CoreServiceServer).ParseConfig(ctx, req.(*ParseConfigRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _CoreService_GenerateFullConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GenerateConfigRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CoreServiceServer).GenerateFullConfig(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CoreService_GenerateFullConfig_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CoreServiceServer).GenerateFullConfig(ctx, req.(*GenerateConfigRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// CoreService_ServiceDesc is the grpc.ServiceDesc for CoreService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var CoreService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "HiddifyOptions.CoreService",
|
||||
HandlerType: (*CoreServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "ParseConfig",
|
||||
Handler: _CoreService_ParseConfig_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GenerateFullConfig",
|
||||
Handler: _CoreService_GenerateFullConfig_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "core.proto",
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
func SaveCurrentConfig(path string, options option.Options) error {
|
||||
json, err := ToJson(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p, err := filepath.Abs(path)
|
||||
fmt.Printf("Saving config to %v %+v\n", p, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(p, []byte(json), 0644)
|
||||
}
|
||||
|
||||
func ToJson(options option.Options) (string, error) {
|
||||
var buffer bytes.Buffer
|
||||
encoder := json.NewEncoder(&buffer)
|
||||
encoder.SetIndent("", " ")
|
||||
// fmt.Printf("%+v\n", options)
|
||||
err := encoder.Encode(options)
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR in coding:%+v\n", err)
|
||||
return "", err
|
||||
}
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
||||
func DeferPanicToError(name string, err func(error)) {
|
||||
if r := recover(); r != nil {
|
||||
s := fmt.Errorf("%s panic: %s\n%s", name, r, string(debug.Stack()))
|
||||
err(s)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
dns "github.com/sagernet/sing-dns"
|
||||
)
|
||||
|
||||
type HiddifyOptions struct {
|
||||
EnableFullConfig bool `json:"enable-full-config"`
|
||||
LogLevel string `json:"log-level"`
|
||||
LogFile string `json:"log-file"`
|
||||
EnableClashApi bool `json:"enable-clash-api"`
|
||||
ClashApiPort uint16 `json:"clash-api-port"`
|
||||
ClashApiSecret string `json:"web-secret"`
|
||||
Region string `json:"region"`
|
||||
BlockAds bool `json:"block-ads"`
|
||||
UseXrayCoreWhenPossible bool `json:"use-xray-core-when-possible"`
|
||||
// GeoIPPath string `json:"geoip-path"`
|
||||
// GeoSitePath string `json:"geosite-path"`
|
||||
Rules []Rule `json:"rules"`
|
||||
Warp WarpOptions `json:"warp"`
|
||||
Warp2 WarpOptions `json:"warp2"`
|
||||
Mux MuxOptions `json:"mux"`
|
||||
TLSTricks TLSTricks `json:"tls-tricks"`
|
||||
DNSOptions
|
||||
InboundOptions
|
||||
URLTestOptions
|
||||
RouteOptions
|
||||
}
|
||||
|
||||
type DNSOptions struct {
|
||||
RemoteDnsAddress string `json:"remote-dns-address"`
|
||||
RemoteDnsDomainStrategy option.DomainStrategy `json:"remote-dns-domain-strategy"`
|
||||
DirectDnsAddress string `json:"direct-dns-address"`
|
||||
DirectDnsDomainStrategy option.DomainStrategy `json:"direct-dns-domain-strategy"`
|
||||
IndependentDNSCache bool `json:"independent-dns-cache"`
|
||||
EnableFakeDNS bool `json:"enable-fake-dns"`
|
||||
EnableDNSRouting bool `json:"enable-dns-routing"`
|
||||
}
|
||||
|
||||
type InboundOptions struct {
|
||||
EnableTun bool `json:"enable-tun"`
|
||||
EnableTunService bool `json:"enable-tun-service"`
|
||||
SetSystemProxy bool `json:"set-system-proxy"`
|
||||
MixedPort uint16 `json:"mixed-port"`
|
||||
TProxyPort uint16 `json:"tproxy-port"`
|
||||
LocalDnsPort uint16 `json:"local-dns-port"`
|
||||
MTU uint32 `json:"mtu"`
|
||||
StrictRoute bool `json:"strict-route"`
|
||||
TUNStack string `json:"tun-implementation"`
|
||||
}
|
||||
|
||||
type URLTestOptions struct {
|
||||
ConnectionTestUrl string `json:"connection-test-url"`
|
||||
URLTestInterval DurationInSeconds `json:"url-test-interval"`
|
||||
// URLTestIdleTimeout DurationInSeconds `json:"url-test-idle-timeout"`
|
||||
}
|
||||
|
||||
type RouteOptions struct {
|
||||
ResolveDestination bool `json:"resolve-destination"`
|
||||
IPv6Mode option.DomainStrategy `json:"ipv6-mode"`
|
||||
BypassLAN bool `json:"bypass-lan"`
|
||||
AllowConnectionFromLAN bool `json:"allow-connection-from-lan"`
|
||||
}
|
||||
|
||||
type TLSTricks struct {
|
||||
EnableFragment bool `json:"enable-fragment"`
|
||||
FragmentSize string `json:"fragment-size"`
|
||||
FragmentSleep string `json:"fragment-sleep"`
|
||||
MixedSNICase bool `json:"mixed-sni-case"`
|
||||
EnablePadding bool `json:"enable-padding"`
|
||||
PaddingSize string `json:"padding-size"`
|
||||
}
|
||||
|
||||
type MuxOptions struct {
|
||||
Enable bool `json:"enable"`
|
||||
Padding bool `json:"padding"`
|
||||
MaxStreams int `json:"max-streams"`
|
||||
Protocol string `json:"protocol"`
|
||||
}
|
||||
|
||||
type WarpOptions struct {
|
||||
Id string `json:"id"`
|
||||
EnableWarp bool `json:"enable"`
|
||||
Mode string `json:"mode"`
|
||||
WireguardConfigStr string `json:"wireguard-config"`
|
||||
WireguardConfig WarpWireguardConfig `json:"wireguardConfig"` // TODO check
|
||||
FakePackets string `json:"noise"`
|
||||
FakePacketSize string `json:"noise-size"`
|
||||
FakePacketDelay string `json:"noise-delay"`
|
||||
FakePacketMode string `json:"noise-mode"`
|
||||
CleanIP string `json:"clean-ip"`
|
||||
CleanPort uint16 `json:"clean-port"`
|
||||
Account WarpAccount
|
||||
}
|
||||
|
||||
func DefaultHiddifyOptions() *HiddifyOptions {
|
||||
return &HiddifyOptions{
|
||||
DNSOptions: DNSOptions{
|
||||
RemoteDnsAddress: "1.1.1.1",
|
||||
RemoteDnsDomainStrategy: option.DomainStrategy(dns.DomainStrategyAsIS),
|
||||
DirectDnsAddress: "1.1.1.1",
|
||||
DirectDnsDomainStrategy: option.DomainStrategy(dns.DomainStrategyAsIS),
|
||||
IndependentDNSCache: false,
|
||||
EnableFakeDNS: false,
|
||||
EnableDNSRouting: false,
|
||||
},
|
||||
InboundOptions: InboundOptions{
|
||||
EnableTun: false,
|
||||
SetSystemProxy: false,
|
||||
MixedPort: 12334,
|
||||
TProxyPort: 12335,
|
||||
LocalDnsPort: 16450,
|
||||
MTU: 9000,
|
||||
StrictRoute: true,
|
||||
TUNStack: "mixed",
|
||||
},
|
||||
URLTestOptions: URLTestOptions{
|
||||
ConnectionTestUrl: "http://cp.cloudflare.com/",
|
||||
URLTestInterval: DurationInSeconds(600),
|
||||
// URLTestIdleTimeout: DurationInSeconds(6000),
|
||||
},
|
||||
RouteOptions: RouteOptions{
|
||||
ResolveDestination: false,
|
||||
IPv6Mode: option.DomainStrategy(dns.DomainStrategyAsIS),
|
||||
BypassLAN: false,
|
||||
AllowConnectionFromLAN: false,
|
||||
},
|
||||
LogLevel: "warn",
|
||||
// LogFile: "/dev/null",
|
||||
LogFile: "box.log",
|
||||
Region: "other",
|
||||
EnableClashApi: true,
|
||||
ClashApiPort: 16756,
|
||||
ClashApiSecret: "",
|
||||
// GeoIPPath: "geoip.db",
|
||||
// GeoSitePath: "geosite.db",
|
||||
Rules: []Rule{},
|
||||
Mux: MuxOptions{
|
||||
Enable: false,
|
||||
Padding: true,
|
||||
MaxStreams: 8,
|
||||
Protocol: "h2mux",
|
||||
},
|
||||
TLSTricks: TLSTricks{
|
||||
EnableFragment: false,
|
||||
FragmentSize: "10-100",
|
||||
FragmentSleep: "50-200",
|
||||
MixedSNICase: false,
|
||||
EnablePadding: false,
|
||||
PaddingSize: "1200-1500",
|
||||
},
|
||||
UseXrayCoreWhenPossible: false,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
type outboundMap map[string]interface{}
|
||||
|
||||
func patchOutboundMux(base option.Outbound, configOpt HiddifyOptions, obj outboundMap) outboundMap {
|
||||
if configOpt.Mux.Enable {
|
||||
multiplex := option.OutboundMultiplexOptions{
|
||||
Enabled: true,
|
||||
Padding: configOpt.Mux.Padding,
|
||||
MaxStreams: configOpt.Mux.MaxStreams,
|
||||
Protocol: configOpt.Mux.Protocol,
|
||||
}
|
||||
obj["multiplex"] = multiplex
|
||||
// } else {
|
||||
// delete(obj, "multiplex")
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
func patchOutboundTLSTricks(base option.Outbound, configOpt HiddifyOptions, obj outboundMap) outboundMap {
|
||||
if base.Type == C.TypeSelector || base.Type == C.TypeURLTest || base.Type == C.TypeBlock || base.Type == C.TypeDNS {
|
||||
return obj
|
||||
}
|
||||
if isOutboundReality(base) {
|
||||
return obj
|
||||
}
|
||||
|
||||
var tls *option.OutboundTLSOptions
|
||||
var transport *option.V2RayTransportOptions
|
||||
if base.VLESSOptions.OutboundTLSOptionsContainer.TLS != nil {
|
||||
tls = base.VLESSOptions.OutboundTLSOptionsContainer.TLS
|
||||
transport = base.VLESSOptions.Transport
|
||||
} else if base.TrojanOptions.OutboundTLSOptionsContainer.TLS != nil {
|
||||
tls = base.TrojanOptions.OutboundTLSOptionsContainer.TLS
|
||||
transport = base.TrojanOptions.Transport
|
||||
} else if base.VMessOptions.OutboundTLSOptionsContainer.TLS != nil {
|
||||
tls = base.VMessOptions.OutboundTLSOptionsContainer.TLS
|
||||
transport = base.VMessOptions.Transport
|
||||
}
|
||||
if base.Type == C.TypeXray {
|
||||
if configOpt.TLSTricks.EnableFragment {
|
||||
if obj["xray_fragment"] == nil || obj["xray_fragment"].(map[string]any)["packets"] == "" {
|
||||
obj["xray_fragment"] = map[string]any{
|
||||
"packets": "tlshello",
|
||||
"length": configOpt.TLSTricks.FragmentSize,
|
||||
"interval": configOpt.TLSTricks.FragmentSleep,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if base.Type == C.TypeDirect {
|
||||
return patchOutboundFragment(base, configOpt, obj)
|
||||
}
|
||||
|
||||
if tls == nil || !tls.Enabled || transport == nil {
|
||||
return obj
|
||||
}
|
||||
|
||||
if transport.Type != C.V2RayTransportTypeWebsocket && transport.Type != C.V2RayTransportTypeGRPC && transport.Type != C.V2RayTransportTypeHTTPUpgrade {
|
||||
return obj
|
||||
}
|
||||
|
||||
if outtls, ok := obj["tls"].(map[string]interface{}); ok {
|
||||
obj = patchOutboundFragment(base, configOpt, obj)
|
||||
tlsTricks := tls.TLSTricks
|
||||
if tlsTricks == nil {
|
||||
tlsTricks = &option.TLSTricksOptions{}
|
||||
}
|
||||
tlsTricks.MixedCaseSNI = tlsTricks.MixedCaseSNI || configOpt.TLSTricks.MixedSNICase
|
||||
|
||||
if configOpt.TLSTricks.EnablePadding {
|
||||
tlsTricks.PaddingMode = "random"
|
||||
tlsTricks.PaddingSize = configOpt.TLSTricks.PaddingSize
|
||||
// fmt.Printf("--------------------%+v----%+v", tlsTricks.PaddingSize, configOpt)
|
||||
outtls["utls"] = map[string]interface{}{
|
||||
"enabled": true,
|
||||
"fingerprint": "custom",
|
||||
}
|
||||
}
|
||||
|
||||
outtls["tls_tricks"] = tlsTricks
|
||||
// if tlsTricks.MixedCaseSNI || tlsTricks.PaddingMode != "" {
|
||||
// // } else {
|
||||
// // tls["tls_tricks"] = nil
|
||||
// }
|
||||
// fmt.Printf("-------%+v------------- ", tlsTricks)
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
func patchOutboundFragment(base option.Outbound, configOpt HiddifyOptions, obj outboundMap) outboundMap {
|
||||
if configOpt.TLSTricks.EnableFragment {
|
||||
obj["tcp_fast_open"] = false
|
||||
obj["tls_fragment"] = option.TLSFragmentOptions{
|
||||
Enabled: configOpt.TLSTricks.EnableFragment,
|
||||
Size: configOpt.TLSTricks.FragmentSize,
|
||||
Sleep: configOpt.TLSTricks.FragmentSleep,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
func isOutboundReality(base option.Outbound) bool {
|
||||
// this function checks reality status ONLY FOR VLESS.
|
||||
// Some other protocols can also use reality, but it's discouraged as stated in the reality document
|
||||
if base.Type != C.TypeVLESS {
|
||||
return false
|
||||
}
|
||||
if base.VLESSOptions.OutboundTLSOptionsContainer.TLS == nil {
|
||||
return false
|
||||
}
|
||||
if base.VLESSOptions.OutboundTLSOptionsContainer.TLS.Reality == nil {
|
||||
return false
|
||||
}
|
||||
return base.VLESSOptions.OutboundTLSOptionsContainer.TLS.Reality.Enabled
|
||||
}
|
||||
|
||||
func patchOutbound(base option.Outbound, configOpt HiddifyOptions, staticIpsDns map[string][]string) (*option.Outbound, string, error) {
|
||||
formatErr := func(err error) error {
|
||||
return fmt.Errorf("error patching outbound[%s][%s]: %w", base.Tag, base.Type, err)
|
||||
}
|
||||
err := patchWarp(&base, &configOpt, true, staticIpsDns)
|
||||
if err != nil {
|
||||
return nil, "", formatErr(err)
|
||||
}
|
||||
var outbound option.Outbound
|
||||
|
||||
jsonData, err := base.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, "", formatErr(err)
|
||||
}
|
||||
|
||||
var obj outboundMap
|
||||
err = json.Unmarshal(jsonData, &obj)
|
||||
if err != nil {
|
||||
return nil, "", formatErr(err)
|
||||
}
|
||||
var serverDomain string
|
||||
if detour, ok := obj["detour"].(string); !ok || detour == "" {
|
||||
if server, ok := obj["server"].(string); ok {
|
||||
if server != "" && net.ParseIP(server) == nil {
|
||||
serverDomain = fmt.Sprintf("full:%s", server)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj = patchOutboundTLSTricks(base, configOpt, obj)
|
||||
|
||||
switch base.Type {
|
||||
case C.TypeVMess, C.TypeVLESS, C.TypeTrojan, C.TypeShadowsocks:
|
||||
obj = patchOutboundMux(base, configOpt, obj)
|
||||
}
|
||||
|
||||
modifiedJson, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return nil, "", formatErr(err)
|
||||
}
|
||||
|
||||
err = outbound.UnmarshalJSON(modifiedJson)
|
||||
if err != nil {
|
||||
return nil, "", formatErr(err)
|
||||
}
|
||||
|
||||
return &outbound, serverDomain, nil
|
||||
}
|
||||
|
||||
// func (o outboundMap) transportType() string {
|
||||
// if transport, ok := o["transport"].(map[string]interface{}); ok {
|
||||
// if transportType, ok := transport["type"].(string); ok {
|
||||
// return transportType
|
||||
// }
|
||||
// }
|
||||
// return ""
|
||||
// }
|
||||
@@ -0,0 +1,142 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hiddify/ray2sing/ray2sing"
|
||||
"github.com/sagernet/sing-box/experimental/libbox"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/batch"
|
||||
SJ "github.com/sagernet/sing/common/json"
|
||||
"github.com/xmdhs/clash2singbox/convert"
|
||||
"github.com/xmdhs/clash2singbox/model/clash"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
//go:embed config.json.template
|
||||
var configByte []byte
|
||||
|
||||
func ParseConfig(path string, debug bool) ([]byte, error) {
|
||||
content, err := os.ReadFile(path)
|
||||
os.Chdir(filepath.Dir(path))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseConfigContent(string(content), debug, nil, false)
|
||||
}
|
||||
|
||||
func ParseConfigContentToOptions(contentstr string, debug bool, configOpt *HiddifyOptions, fullConfig bool) (*option.Options, error) {
|
||||
content, err := ParseConfigContent(contentstr, debug, configOpt, fullConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var options option.Options
|
||||
err = json.Unmarshal(content, &options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &options, nil
|
||||
}
|
||||
|
||||
func ParseConfigContent(contentstr string, debug bool, configOpt *HiddifyOptions, fullConfig bool) ([]byte, error) {
|
||||
if configOpt == nil {
|
||||
configOpt = DefaultHiddifyOptions()
|
||||
}
|
||||
content := []byte(contentstr)
|
||||
var jsonObj map[string]interface{} = make(map[string]interface{})
|
||||
|
||||
fmt.Printf("Convert using json\n")
|
||||
var tmpJsonResult any
|
||||
jsonDecoder := json.NewDecoder(SJ.NewCommentFilter(bytes.NewReader(content)))
|
||||
if err := jsonDecoder.Decode(&tmpJsonResult); err == nil {
|
||||
if tmpJsonObj, ok := tmpJsonResult.(map[string]interface{}); ok {
|
||||
if tmpJsonObj["outbounds"] == nil {
|
||||
jsonObj["outbounds"] = []interface{}{jsonObj}
|
||||
} else {
|
||||
if fullConfig || (configOpt != nil && configOpt.EnableFullConfig) {
|
||||
jsonObj = tmpJsonObj
|
||||
} else {
|
||||
jsonObj["outbounds"] = tmpJsonObj["outbounds"]
|
||||
}
|
||||
}
|
||||
} else if jsonArray, ok := tmpJsonResult.([]map[string]interface{}); ok {
|
||||
jsonObj["outbounds"] = jsonArray
|
||||
} else {
|
||||
return nil, fmt.Errorf("[SingboxParser] Incorrect Json Format")
|
||||
}
|
||||
|
||||
newContent, _ := json.MarshalIndent(jsonObj, "", " ")
|
||||
|
||||
return patchConfig(newContent, "SingboxParser", configOpt)
|
||||
}
|
||||
|
||||
v2rayStr, err := ray2sing.Ray2Singbox(string(content), configOpt.UseXrayCoreWhenPossible)
|
||||
if err == nil {
|
||||
return patchConfig([]byte(v2rayStr), "V2rayParser", configOpt)
|
||||
}
|
||||
fmt.Printf("Convert using clash\n")
|
||||
clashObj := clash.Clash{}
|
||||
if err := yaml.Unmarshal(content, &clashObj); err == nil && clashObj.Proxies != nil {
|
||||
if len(clashObj.Proxies) == 0 {
|
||||
return nil, fmt.Errorf("[ClashParser] no outbounds found")
|
||||
}
|
||||
converted, err := convert.Clash2sing(clashObj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[ClashParser] converting clash to sing-box error: %w", err)
|
||||
}
|
||||
output := configByte
|
||||
output, err = convert.Patch(output, converted, "", "", nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[ClashParser] patching clash config error: %w", err)
|
||||
}
|
||||
return patchConfig(output, "ClashParser", configOpt)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unable to determine config format")
|
||||
}
|
||||
|
||||
func patchConfig(content []byte, name string, configOpt *HiddifyOptions) ([]byte, error) {
|
||||
options := option.Options{}
|
||||
err := json.Unmarshal(content, &options)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[SingboxParser] unmarshal error: %w", err)
|
||||
}
|
||||
b, _ := batch.New(context.Background(), batch.WithConcurrencyNum[*option.Outbound](2))
|
||||
for _, base := range options.Outbounds {
|
||||
out := base
|
||||
b.Go(base.Tag, func() (*option.Outbound, error) {
|
||||
err := patchWarp(&out, configOpt, false, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[Warp] patch warp error: %w", err)
|
||||
}
|
||||
// options.Outbounds[i] = base
|
||||
return &out, nil
|
||||
})
|
||||
}
|
||||
if res, err := b.WaitAndGetResult(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
for i, base := range options.Outbounds {
|
||||
options.Outbounds[i] = *res[base.Tag].Value
|
||||
}
|
||||
}
|
||||
|
||||
content, _ = json.MarshalIndent(options, "", " ")
|
||||
|
||||
fmt.Printf("%s\n", content)
|
||||
return validateResult(content, name)
|
||||
}
|
||||
|
||||
func validateResult(content []byte, name string) ([]byte, error) {
|
||||
err := libbox.CheckConfig(string(content))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[%s] invalid sing-box config: %w", name, err)
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
type Rule struct {
|
||||
RuleSetUrl string `json:"rule-set-url"`
|
||||
Domains string `json:"domains"`
|
||||
IP string `json:"ip"`
|
||||
Port string `json:"port"`
|
||||
Network string `json:"network"`
|
||||
Protocol string `json:"protocol"`
|
||||
Outbound string `json:"outbound"`
|
||||
}
|
||||
|
||||
func (r *Rule) MakeRule() option.DefaultRule {
|
||||
rule := option.DefaultRule{}
|
||||
if len(r.Domains) > 0 {
|
||||
rule = makeDomainRule(rule, strings.Split(r.Domains, ","))
|
||||
}
|
||||
if len(r.IP) > 0 {
|
||||
rule = makeIpRule(rule, strings.Split(r.IP, ","))
|
||||
}
|
||||
if len(r.Port) > 0 {
|
||||
rule = makePortRule(rule, strings.Split(r.Port, ","))
|
||||
}
|
||||
if len(r.Network) > 0 {
|
||||
rule.Network = append(rule.Network, r.Network)
|
||||
}
|
||||
if len(r.Protocol) > 0 {
|
||||
rule.Protocol = append(rule.Protocol, strings.Split(r.Protocol, ",")...)
|
||||
}
|
||||
return rule
|
||||
}
|
||||
|
||||
func (r *Rule) MakeDNSRule() option.DefaultDNSRule {
|
||||
rule := option.DefaultDNSRule{}
|
||||
domains := strings.Split(r.Domains, ",")
|
||||
for _, item := range domains {
|
||||
if strings.HasPrefix(item, "geosite:") {
|
||||
rule.Geosite = append(rule.Geosite, strings.TrimPrefix(item, "geosite:"))
|
||||
} else if strings.HasPrefix(item, "full:") {
|
||||
rule.Domain = append(rule.Domain, strings.ToLower(strings.TrimPrefix(item, "full:")))
|
||||
} else if strings.HasPrefix(item, "domain:") {
|
||||
rule.DomainSuffix = append(rule.DomainSuffix, strings.ToLower(strings.TrimPrefix(item, "domain:")))
|
||||
} else if strings.HasPrefix(item, "regexp:") {
|
||||
rule.DomainRegex = append(rule.DomainRegex, strings.ToLower(strings.TrimPrefix(item, "regexp:")))
|
||||
} else if strings.HasPrefix(item, "keyword:") {
|
||||
rule.DomainKeyword = append(rule.DomainKeyword, strings.ToLower(strings.TrimPrefix(item, "keyword:")))
|
||||
}
|
||||
}
|
||||
return rule
|
||||
}
|
||||
|
||||
func makeDomainRule(options option.DefaultRule, list []string) option.DefaultRule {
|
||||
for _, item := range list {
|
||||
if strings.HasPrefix(item, "geosite:") {
|
||||
options.Geosite = append(options.Geosite, strings.TrimPrefix(item, "geosite:"))
|
||||
} else if strings.HasPrefix(item, "full:") {
|
||||
options.Domain = append(options.Domain, strings.ToLower(strings.TrimPrefix(item, "full:")))
|
||||
} else if strings.HasPrefix(item, "domain:") {
|
||||
options.DomainSuffix = append(options.DomainSuffix, strings.ToLower(strings.TrimPrefix(item, "domain:")))
|
||||
} else if strings.HasPrefix(item, "regexp:") {
|
||||
options.DomainRegex = append(options.DomainRegex, strings.ToLower(strings.TrimPrefix(item, "regexp:")))
|
||||
} else if strings.HasPrefix(item, "keyword:") {
|
||||
options.DomainKeyword = append(options.DomainKeyword, strings.ToLower(strings.TrimPrefix(item, "keyword:")))
|
||||
}
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func makeIpRule(options option.DefaultRule, list []string) option.DefaultRule {
|
||||
for _, item := range list {
|
||||
if strings.HasPrefix(item, "geoip:") {
|
||||
options.GeoIP = append(options.GeoIP, strings.TrimPrefix(item, "geoip:"))
|
||||
} else {
|
||||
options.IPCIDR = append(options.IPCIDR, item)
|
||||
}
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func makePortRule(options option.DefaultRule, list []string) option.DefaultRule {
|
||||
for _, item := range list {
|
||||
if strings.Contains(item, ":") {
|
||||
options.PortRange = append(options.PortRange, item)
|
||||
} else if i, err := strconv.Atoi(item); err == nil {
|
||||
options.Port = append(options.Port, uint16(i))
|
||||
}
|
||||
}
|
||||
return options
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
context "context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type server struct {
|
||||
UnimplementedCoreServiceServer
|
||||
}
|
||||
|
||||
func String(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func (s *server) ParseConfig(ctx context.Context, in *ParseConfigRequest) (*ParseConfigResponse, error) {
|
||||
config, err := ParseConfig(in.TempPath, in.Debug)
|
||||
if err != nil {
|
||||
return &ParseConfigResponse{Error: String(err.Error())}, nil
|
||||
}
|
||||
err = os.WriteFile(in.Path, config, 0o644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ParseConfigResponse{Error: String("")}, nil
|
||||
}
|
||||
|
||||
func (s *server) GenerateFullConfig(ctx context.Context, in *GenerateConfigRequest) (*GenerateConfigResponse, error) {
|
||||
os.Chdir(filepath.Dir(in.Path))
|
||||
content, err := os.ReadFile(in.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var options option.Options
|
||||
err = options.UnmarshalJSON(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config, err := BuildConfigJson(*DefaultHiddifyOptions(), options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &GenerateConfigResponse{
|
||||
Config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func StartGRPCServer(port uint16) error {
|
||||
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to listen: %v", err)
|
||||
}
|
||||
|
||||
s := grpc.NewServer()
|
||||
RegisterCoreServiceServer(s, &server{})
|
||||
|
||||
log.Println("Server started on :", port)
|
||||
go func() {
|
||||
if err := s.Serve(lis); err != nil {
|
||||
log.Fatalf("Failed to serve: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DurationInSeconds int
|
||||
|
||||
func (d DurationInSeconds) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(int64(d))
|
||||
}
|
||||
|
||||
func (d *DurationInSeconds) UnmarshalJSON(bytes []byte) error {
|
||||
var v int64
|
||||
if err := json.Unmarshal(bytes, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
*d = DurationInSeconds(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d DurationInSeconds) Duration() time.Duration {
|
||||
return time.Duration(d) * time.Second
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/bepass-org/warp-plus/warp"
|
||||
"github.com/hiddify/hiddify-core/v2/common"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
|
||||
// "github.com/bepass-org/wireguard-go/warp"
|
||||
"github.com/hiddify/hiddify-core/v2/db"
|
||||
|
||||
"github.com/sagernet/sing-box/option"
|
||||
T "github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
type SingboxConfig struct {
|
||||
Type string `json:"type"`
|
||||
Tag string `json:"tag"`
|
||||
Server string `json:"server"`
|
||||
ServerPort int `json:"server_port"`
|
||||
LocalAddress []string `json:"local_address"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
PeerPublicKey string `json:"peer_public_key"`
|
||||
Reserved []int `json:"reserved"`
|
||||
MTU int `json:"mtu"`
|
||||
}
|
||||
|
||||
func wireGuardToSingbox(wgConfig WarpWireguardConfig, server string, port uint16) (*T.Outbound, error) {
|
||||
clientID, _ := base64.StdEncoding.DecodeString(wgConfig.ClientID)
|
||||
if len(clientID) < 2 {
|
||||
clientID = []byte{0, 0, 0}
|
||||
}
|
||||
out := T.Outbound{
|
||||
Type: "wireguard",
|
||||
Tag: "WARP",
|
||||
WireGuardOptions: T.WireGuardOutboundOptions{
|
||||
ServerOptions: T.ServerOptions{
|
||||
Server: server,
|
||||
ServerPort: port,
|
||||
},
|
||||
|
||||
PrivateKey: wgConfig.PrivateKey,
|
||||
PeerPublicKey: wgConfig.PeerPublicKey,
|
||||
Reserved: []uint8{clientID[0], clientID[1], clientID[2]},
|
||||
// Reserved: []uint8{0, 0, 0},
|
||||
MTU: 1330,
|
||||
},
|
||||
}
|
||||
ips := []string{wgConfig.LocalAddressIPv4 + "/24", wgConfig.LocalAddressIPv6 + "/128"}
|
||||
|
||||
for _, addr := range ips {
|
||||
if addr == "" {
|
||||
continue
|
||||
}
|
||||
prefix, err := netip.ParsePrefix(addr)
|
||||
if err != nil {
|
||||
return nil, err // Handle the error appropriately
|
||||
}
|
||||
out.WireGuardOptions.LocalAddress = append(out.WireGuardOptions.LocalAddress, prefix)
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
func getRandomIP() string {
|
||||
ipPort, err := warp.RandomWarpEndpoint(true, true)
|
||||
if err == nil {
|
||||
return ipPort.Addr().String()
|
||||
}
|
||||
return "engage.cloudflareclient.com"
|
||||
}
|
||||
|
||||
func generateWarp(license string, host string, port uint16, fakePackets string, fakePacketsSize string, fakePacketsDelay string, fakePacketsMode string) (*T.Outbound, error) {
|
||||
_, _, wgConfig, err := GenerateWarpInfo(license, "", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if wgConfig == nil {
|
||||
return nil, fmt.Errorf("invalid warp config")
|
||||
}
|
||||
|
||||
return GenerateWarpSingbox(*wgConfig, host, port, fakePackets, fakePacketsSize, fakePacketsDelay, fakePacketsMode)
|
||||
}
|
||||
|
||||
func GenerateWarpSingbox(wgConfig WarpWireguardConfig, host string, port uint16, fakePackets string, fakePacketsSize string, fakePacketsDelay string, fakePacketMode string) (*T.Outbound, error) {
|
||||
if host == "" {
|
||||
host = "auto4"
|
||||
}
|
||||
|
||||
if (host == "auto" || host == "auto4" || host == "auto6") && fakePackets == "" {
|
||||
fakePackets = "1-3"
|
||||
}
|
||||
if fakePackets != "" && fakePacketsSize == "" {
|
||||
fakePacketsSize = "10-30"
|
||||
}
|
||||
if fakePackets != "" && fakePacketsDelay == "" {
|
||||
fakePacketsDelay = "10-30"
|
||||
}
|
||||
singboxConfig, err := wireGuardToSingbox(wgConfig, host, port)
|
||||
if err != nil {
|
||||
fmt.Printf("%v %v", singboxConfig, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
singboxConfig.WireGuardOptions.FakePackets = fakePackets
|
||||
singboxConfig.WireGuardOptions.FakePacketsSize = fakePacketsSize
|
||||
singboxConfig.WireGuardOptions.FakePacketsDelay = fakePacketsDelay
|
||||
singboxConfig.WireGuardOptions.FakePacketsMode = fakePacketMode
|
||||
|
||||
return singboxConfig, nil
|
||||
}
|
||||
|
||||
func GenerateWarpInfo(license string, oldAccountId string, oldAccessToken string) (*warp.Identity, string, *WarpWireguardConfig, error) {
|
||||
if oldAccountId != "" && oldAccessToken != "" {
|
||||
err := warp.DeleteDevice(oldAccessToken, oldAccountId)
|
||||
if err != nil {
|
||||
fmt.Printf("Error in removing old device: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("Old Device Removed")
|
||||
}
|
||||
}
|
||||
l := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
|
||||
identity, err := warp.CreateIdentityOnly(l, license)
|
||||
res := "Error!"
|
||||
var warpcfg WarpWireguardConfig
|
||||
if err == nil {
|
||||
res = "Success"
|
||||
res = fmt.Sprintf("Warp+ enabled: %t\n", identity.Account.WarpPlus)
|
||||
res += fmt.Sprintf("\nAccount type: %s\n", identity.Account.AccountType)
|
||||
warpcfg = WarpWireguardConfig{
|
||||
PrivateKey: identity.PrivateKey,
|
||||
PeerPublicKey: identity.Config.Peers[0].PublicKey,
|
||||
LocalAddressIPv4: identity.Config.Interface.Addresses.V4,
|
||||
LocalAddressIPv6: identity.Config.Interface.Addresses.V6,
|
||||
ClientID: identity.Config.ClientID,
|
||||
}
|
||||
}
|
||||
|
||||
return &identity, res, &warpcfg, err
|
||||
}
|
||||
|
||||
func getOrGenerateWarpLocallyIfNeeded(warpOptions *WarpOptions) WarpWireguardConfig {
|
||||
if warpOptions.WireguardConfig.PrivateKey != "" {
|
||||
return warpOptions.WireguardConfig
|
||||
}
|
||||
table := db.GetTable[WarpOptions]()
|
||||
dbWarpOptions, err := table.Get(warpOptions.Id)
|
||||
if err == nil && dbWarpOptions.WireguardConfig.PrivateKey != "" {
|
||||
return warpOptions.WireguardConfig
|
||||
}
|
||||
license := ""
|
||||
if len(warpOptions.Id) == 26 { // warp key is 26 characters long
|
||||
license = warpOptions.Id
|
||||
} else if len(warpOptions.Id) > 28 && warpOptions.Id[2] == '_' { // warp key is 26 characters long
|
||||
license = warpOptions.Id[3:]
|
||||
}
|
||||
|
||||
accountidentity, _, wireguardConfig, err := GenerateWarpInfo(license, warpOptions.Account.AccountID, warpOptions.Account.AccessToken)
|
||||
if err != nil {
|
||||
return WarpWireguardConfig{}
|
||||
}
|
||||
warpOptions.Account = WarpAccount{
|
||||
AccountID: accountidentity.ID,
|
||||
AccessToken: accountidentity.Token,
|
||||
}
|
||||
warpOptions.WireguardConfig = *wireguardConfig
|
||||
table.UpdateInsert(warpOptions)
|
||||
|
||||
return *wireguardConfig
|
||||
}
|
||||
|
||||
func patchWarp(base *option.Outbound, configOpt *HiddifyOptions, final bool, staticIpsDns map[string][]string) error {
|
||||
if base.Type == C.TypeCustom {
|
||||
if warp, ok := base.CustomOptions["warp"].(map[string]interface{}); ok {
|
||||
key, _ := warp["key"].(string)
|
||||
host, _ := warp["host"].(string)
|
||||
port, _ := warp["port"].(uint16)
|
||||
detour, _ := warp["detour"].(string)
|
||||
fakePackets, _ := warp["fake_packets"].(string)
|
||||
fakePacketsSize, _ := warp["fake_packets_size"].(string)
|
||||
fakePacketsDelay, _ := warp["fake_packets_delay"].(string)
|
||||
fakePacketsMode, _ := warp["fake_packets_mode"].(string)
|
||||
var warpOutbound *T.Outbound
|
||||
var err error
|
||||
|
||||
is_saved_key := len(key) > 1 && key[0] == 'p'
|
||||
|
||||
if (configOpt == nil || !final) && is_saved_key {
|
||||
return nil
|
||||
}
|
||||
var wireguardConfig WarpWireguardConfig
|
||||
if is_saved_key {
|
||||
var warpOpt *WarpOptions
|
||||
if key == "p1" {
|
||||
warpOpt = &configOpt.Warp
|
||||
} else if key == "p2" {
|
||||
warpOpt = &configOpt.Warp2
|
||||
} else {
|
||||
warpOpt = &WarpOptions{
|
||||
Id: key,
|
||||
}
|
||||
}
|
||||
warpOpt.Id = key
|
||||
|
||||
wireguardConfig = getOrGenerateWarpLocallyIfNeeded(warpOpt)
|
||||
} else {
|
||||
_, _, wgConfig, err := GenerateWarpInfo(key, "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wireguardConfig = *wgConfig
|
||||
}
|
||||
warpOutbound, err = GenerateWarpSingbox(wireguardConfig, host, port, fakePackets, fakePacketsSize, fakePacketsDelay, fakePacketsMode)
|
||||
if err != nil {
|
||||
fmt.Printf("Error generating warp config: %v", err)
|
||||
return err
|
||||
}
|
||||
warpOutbound.WireGuardOptions.Detour = detour
|
||||
base.Type = C.TypeWireGuard
|
||||
base.WireGuardOptions = warpOutbound.WireGuardOptions
|
||||
}
|
||||
}
|
||||
|
||||
if final && base.Type == C.TypeWireGuard {
|
||||
host := base.WireGuardOptions.Server
|
||||
|
||||
if host == "default" || host == "random" || host == "auto" || host == "auto4" || host == "auto6" || isBlockedDomain(host) {
|
||||
// if base.WireGuardOptions.Detour != "" {
|
||||
// base.WireGuardOptions.Server = "162.159.192.1"
|
||||
// } else {
|
||||
rndDomain := strings.ToLower(generateRandomString(20))
|
||||
staticIpsDns[rndDomain] = []string{}
|
||||
if host != "auto4" {
|
||||
if host == "auto6" || common.CanConnectIPv6() {
|
||||
randomIpPort, _ := warp.RandomWarpEndpoint(false, true)
|
||||
staticIpsDns[rndDomain] = append(staticIpsDns[rndDomain], randomIpPort.Addr().String())
|
||||
}
|
||||
}
|
||||
if host != "auto6" {
|
||||
randomIpPort, _ := warp.RandomWarpEndpoint(true, false)
|
||||
staticIpsDns[rndDomain] = append(staticIpsDns[rndDomain], randomIpPort.Addr().String())
|
||||
}
|
||||
base.WireGuardOptions.Server = rndDomain
|
||||
// }
|
||||
}
|
||||
if base.WireGuardOptions.ServerPort == 0 {
|
||||
port := warp.RandomWarpPort()
|
||||
base.WireGuardOptions.ServerPort = port
|
||||
}
|
||||
|
||||
if base.WireGuardOptions.Detour != "" {
|
||||
if base.WireGuardOptions.MTU < 100 {
|
||||
base.WireGuardOptions.MTU = 1280
|
||||
}
|
||||
base.WireGuardOptions.FakePackets = ""
|
||||
base.WireGuardOptions.FakePacketsDelay = ""
|
||||
base.WireGuardOptions.FakePacketsSize = ""
|
||||
}
|
||||
// if base.WireGuardOptions.Detour == "" {
|
||||
// base.WireGuardOptions.GSO = runtime.GOOS != "windows"
|
||||
// }
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type WarpAccount struct {
|
||||
AccountID string `json:"account-id"`
|
||||
AccessToken string `json:"access-token"`
|
||||
}
|
||||
|
||||
type WarpWireguardConfig struct {
|
||||
PrivateKey string `json:"private-key"`
|
||||
LocalAddressIPv4 string `json:"local-address-ipv4"`
|
||||
LocalAddressIPv6 string `json:"local-address-ipv6"`
|
||||
PeerPublicKey string `json:"peer-public-key"`
|
||||
ClientID string `json:"client-id"`
|
||||
}
|
||||
|
||||
type WarpGenerationResponse struct {
|
||||
WarpAccount
|
||||
Log string `json:"log"`
|
||||
Config WarpWireguardConfig `json:"config"`
|
||||
}
|
||||
|
||||
func GenerateWarpAccount(licenseKey string, accountId string, accessToken string) (string, error) {
|
||||
identity, log, wg, err := GenerateWarpInfo(licenseKey, accountId, accessToken)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
warpAccount := WarpAccount{
|
||||
AccountID: identity.ID,
|
||||
AccessToken: identity.Token,
|
||||
}
|
||||
warpConfig := WarpWireguardConfig{
|
||||
PrivateKey: wg.PrivateKey,
|
||||
LocalAddressIPv4: wg.LocalAddressIPv4,
|
||||
LocalAddressIPv6: wg.LocalAddressIPv6,
|
||||
PeerPublicKey: wg.PeerPublicKey,
|
||||
ClientID: wg.ClientID,
|
||||
}
|
||||
response := WarpGenerationResponse{warpAccount, log, warpConfig}
|
||||
|
||||
responseJson, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(responseJson), nil
|
||||
}
|
||||
Reference in New Issue
Block a user