diff --git a/component/js/function.go b/component/js/function.go new file mode 100644 index 00000000..55b8524a --- /dev/null +++ b/component/js/function.go @@ -0,0 +1,69 @@ +//go:build !no_script + +package js + +import ( + "github.com/Dreamacro/clash/component/resolver" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" + "github.com/dop251/goja" + "github.com/dop251/goja_nodejs/require" + "net/netip" +) + +type Context struct { + runtime *goja.Runtime +} + +func (c *Context) Resolve(host string, dnsType C.DnsType) []string { + var ips []string + var ipAddrs []netip.Addr + var err error + switch dnsType { + case C.IPv4: + ipAddrs, err = resolver.ResolveAllIPv4(host) + case C.IPv6: + ipAddrs, err = resolver.ResolveAllIPv6(host) + case C.All: + ipAddrs, err = resolver.ResolveAllIP(host) + } + + if err != nil { + log.Errorln("Script resolve %s failed, error: %v", host, err) + return ips + } + + for _, addr := range ipAddrs { + ips = append(ips, addr.String()) + } + + return ips +} + +func newContext() require.ModuleLoader { + return func(runtime *goja.Runtime, object *goja.Object) { + ctx := Context{ + runtime: runtime, + } + + o := object.Get("exports").(*goja.Object) + o.Set("resolve", func(call goja.FunctionCall) goja.Value { + if len(call.Arguments) < 1 { + return runtime.ToValue([]string{}) + } + + host := call.Argument(0).String() + dnsType := C.IPv4 + if len(call.Arguments) == 2 { + dnsType = int(call.Argument(1).ToInteger()) + } + + ips := ctx.Resolve(host, C.DnsType(dnsType)) + return runtime.ToValue(ips) + }) + } +} + +func enable(rt *goja.Runtime) { + rt.Set("context", require.Require(rt, "context")) +} diff --git a/component/js/js.go b/component/js/js.go index 91748422..110c447e 100644 --- a/component/js/js.go +++ b/component/js/js.go @@ -13,6 +13,8 @@ import ( func init() { logPrinter := console.RequireWithPrinter(&JsLog{}) require.RegisterNativeModule("console", logPrinter) + contextFuncLoader := newContext() + require.RegisterNativeModule("context", contextFuncLoader) } func preSetting(rt *goja.Runtime) { @@ -20,6 +22,7 @@ func preSetting(rt *goja.Runtime) { registry.Enable(rt) console.Enable(rt) + enable(rt) eventloop.EnableConsole(true) } diff --git a/component/js/core.go b/component/js/script.go similarity index 100% rename from component/js/core.go rename to component/js/script.go diff --git a/component/js/core_no_script.go b/component/js/script_no_script.go similarity index 100% rename from component/js/core_no_script.go rename to component/js/script_no_script.go diff --git a/constant/rule.go b/constant/rule.go index 7b27649f..535f0264 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -89,16 +89,3 @@ type Rule interface { RuleExtra() *RuleExtra SetRuleExtra(re *RuleExtra) } - -type JSMetadata struct { - Type string `json:"type"` - Network string `json:"network"` - Host string `json:"host"` - SrcIP string `json:"srcIP"` - DstIP string `json:"dstIP"` - SrcPort string `json:"srcPort"` - DstPort string `json:"dstPort"` - Uid *int32 `json:"uid"` - Process string `json:"process"` - ProcessPath string `json:"processPath"` -} diff --git a/constant/script.go b/constant/script.go new file mode 100644 index 00000000..74454779 --- /dev/null +++ b/constant/script.go @@ -0,0 +1,27 @@ +package constant + +type JSRuleMetadata struct { + Type string `json:"type"` + Network string `json:"network"` + Host string `json:"host"` + SrcIP string `json:"srcIP"` + DstIP string `json:"dstIP"` + SrcPort string `json:"srcPort"` + DstPort string `json:"dstPort"` + Uid *int32 `json:"uid"` + Process string `json:"process"` + ProcessPath string `json:"processPath"` +} + +type DnsType int + +const ( + IPv4 = 1 << iota + IPv6 + All +) + +type JSFunction interface { + //Resolve host to ip by Clash DNS + Resolve(host string, resolveType DnsType) []string +} diff --git a/rule/common/script.go b/rule/common/script.go index ccc0e7e9..55f422b7 100644 --- a/rule/common/script.go +++ b/rule/common/script.go @@ -19,7 +19,7 @@ func (s *Script) RuleType() C.RuleType { func (s *Script) Match(metadata *C.Metadata) bool { res := false js.Run(s.name, map[string]any{ - "metadata": C.JSMetadata{ + "metadata": C.JSRuleMetadata{ Host: metadata.Host, Network: metadata.NetWork.String(), Type: metadata.Type.String(), @@ -54,7 +54,7 @@ func (s *Script) Payload() string { } func (s *Script) ShouldResolveIP() bool { - return true + return false } func NewScript(script string, adapter string) (*Script, error) {