mirror of
https://github.com/MatsuriDayo/NekoBoxForAndroid.git
synced 2025-12-19 06:30:05 +08:00
new LocalDNSTransport by sfa
This commit is contained in:
parent
ad0dd1d63f
commit
4438c4f0e5
@ -22,6 +22,7 @@ import io.nekohasekai.sagernet.plugin.PluginManager
|
||||
import kotlinx.coroutines.*
|
||||
import libcore.BoxInstance
|
||||
import libcore.Libcore
|
||||
import moe.matsuri.nb4a.net.LocalResolverImpl
|
||||
import moe.matsuri.nb4a.plugin.NekoPluginManager
|
||||
import moe.matsuri.nb4a.proxy.neko.NekoBean
|
||||
import moe.matsuri.nb4a.proxy.neko.NekoJSInterface
|
||||
@ -111,6 +112,7 @@ abstract class BoxInstance(
|
||||
}
|
||||
}
|
||||
}
|
||||
Libcore.registerLocalDNSTransport(LocalResolverImpl)
|
||||
loadConfig()
|
||||
}
|
||||
|
||||
@ -277,6 +279,8 @@ abstract class BoxInstance(
|
||||
if (::box.isInitialized) {
|
||||
box.close()
|
||||
}
|
||||
|
||||
Libcore.registerLocalDNSTransport(null)
|
||||
}
|
||||
|
||||
}
|
||||
@ -54,7 +54,7 @@ const val TAG_DNS_IN = "dns-in"
|
||||
const val TAG_DNS_OUT = "dns-out"
|
||||
|
||||
const val LOCALHOST = "127.0.0.1"
|
||||
const val LOCAL_DNS_SERVER = "underlying://0.0.0.0"
|
||||
const val LOCAL_DNS_SERVER = "local"
|
||||
|
||||
class ConfigBuildResult(
|
||||
var config: String,
|
||||
|
||||
@ -94,17 +94,17 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
|
||||
val enableDnsRouting = findPreference<SwitchPreference>(Key.ENABLE_DNS_ROUTING)!!
|
||||
val enableFakeDns = findPreference<SwitchPreference>(Key.ENABLE_FAKEDNS)!!
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
DataStore.directDnsUseSystem = false
|
||||
directDnsUseSystem.remove()
|
||||
} else {
|
||||
// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
// DataStore.directDnsUseSystem = false
|
||||
// directDnsUseSystem.remove()
|
||||
// } else {
|
||||
directDns.isEnabled = !directDnsUseSystem.isChecked
|
||||
directDnsUseSystem.setOnPreferenceChangeListener { _, newValue ->
|
||||
directDns.isEnabled = !(newValue as Boolean)
|
||||
needReload()
|
||||
true
|
||||
}
|
||||
}
|
||||
// }
|
||||
|
||||
val requireTransproxy = findPreference<SwitchPreference>(Key.REQUIRE_TRANSPROXY)!!
|
||||
val transproxyPort = findPreference<EditTextPreference>(Key.TRANSPROXY_PORT)!!
|
||||
|
||||
@ -3,16 +3,25 @@ package moe.matsuri.nb4a.net
|
||||
import android.net.DnsResolver
|
||||
import android.os.Build
|
||||
import android.os.CancellationSignal
|
||||
import android.system.ErrnoException
|
||||
import androidx.annotation.RequiresApi
|
||||
import io.nekohasekai.sagernet.SagerNet
|
||||
import io.nekohasekai.sagernet.ktx.tryResume
|
||||
import io.nekohasekai.sagernet.ktx.tryResumeWithException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.asExecutor
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import libcore.ExchangeContext
|
||||
import libcore.LocalDNSTransport
|
||||
import libcore.LocalResolver
|
||||
import java.net.InetAddress
|
||||
import java.net.UnknownHostException
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
object LocalResolverImpl : libcore.LocalResolver {
|
||||
object LocalResolverImpl : LocalResolver, LocalDNSTransport {
|
||||
|
||||
// old
|
||||
|
||||
override fun lookupIP(network: String, domain: String): String {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
@ -28,11 +37,13 @@ object LocalResolverImpl : libcore.LocalResolver {
|
||||
continuation.tryResume((answer as Collection<InetAddress?>).mapNotNull { it?.hostAddress }
|
||||
.joinToString(","))
|
||||
}
|
||||
|
||||
rcode == 0 -> {
|
||||
// fuck AAAA no record
|
||||
// features/dns/client.go
|
||||
continuation.tryResume("")
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Need return rcode
|
||||
// proxy/dns/dns.go
|
||||
@ -77,4 +88,120 @@ object LocalResolverImpl : libcore.LocalResolver {
|
||||
}
|
||||
}
|
||||
|
||||
// new local
|
||||
|
||||
private const val RCODE_NXDOMAIN = 3
|
||||
|
||||
override fun raw(): Boolean {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
override fun exchange(ctx: ExchangeContext, message: ByteArray) {
|
||||
return runBlocking {
|
||||
suspendCoroutine { continuation ->
|
||||
val signal = CancellationSignal()
|
||||
ctx.onCancel(signal::cancel)
|
||||
val callback = object : DnsResolver.Callback<ByteArray> {
|
||||
override fun onAnswer(answer: ByteArray, rcode: Int) {
|
||||
if (rcode == 0) {
|
||||
ctx.rawSuccess(answer)
|
||||
} else {
|
||||
ctx.errorCode(rcode)
|
||||
}
|
||||
continuation.resume(Unit)
|
||||
}
|
||||
|
||||
override fun onError(error: DnsResolver.DnsException) {
|
||||
when (val cause = error.cause) {
|
||||
is ErrnoException -> {
|
||||
ctx.errnoCode(cause.errno)
|
||||
continuation.resume(Unit)
|
||||
return
|
||||
}
|
||||
}
|
||||
continuation.tryResumeWithException(error)
|
||||
}
|
||||
}
|
||||
DnsResolver.getInstance().rawQuery(
|
||||
SagerNet.underlyingNetwork,
|
||||
message,
|
||||
DnsResolver.FLAG_NO_RETRY,
|
||||
Dispatchers.IO.asExecutor(),
|
||||
signal,
|
||||
callback
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun lookup(ctx: ExchangeContext, network: String, domain: String) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
return runBlocking {
|
||||
suspendCoroutine { continuation ->
|
||||
val signal = CancellationSignal()
|
||||
ctx.onCancel(signal::cancel)
|
||||
val callback = object : DnsResolver.Callback<Collection<InetAddress>> {
|
||||
override fun onAnswer(answer: Collection<InetAddress>, rcode: Int) {
|
||||
if (rcode == 0) {
|
||||
ctx.success((answer as Collection<InetAddress?>).mapNotNull { it?.hostAddress }
|
||||
.joinToString("\n"))
|
||||
} else {
|
||||
ctx.errorCode(rcode)
|
||||
}
|
||||
continuation.resume(Unit)
|
||||
}
|
||||
|
||||
override fun onError(error: DnsResolver.DnsException) {
|
||||
when (val cause = error.cause) {
|
||||
is ErrnoException -> {
|
||||
ctx.errnoCode(cause.errno)
|
||||
continuation.resume(Unit)
|
||||
return
|
||||
}
|
||||
}
|
||||
continuation.tryResumeWithException(error)
|
||||
}
|
||||
}
|
||||
val type = when {
|
||||
network.endsWith("4") -> DnsResolver.TYPE_A
|
||||
network.endsWith("6") -> DnsResolver.TYPE_AAAA
|
||||
else -> null
|
||||
}
|
||||
if (type != null) {
|
||||
DnsResolver.getInstance().query(
|
||||
SagerNet.underlyingNetwork,
|
||||
domain,
|
||||
type,
|
||||
DnsResolver.FLAG_NO_RETRY,
|
||||
Dispatchers.IO.asExecutor(),
|
||||
signal,
|
||||
callback
|
||||
)
|
||||
} else {
|
||||
DnsResolver.getInstance().query(
|
||||
SagerNet.underlyingNetwork,
|
||||
domain,
|
||||
DnsResolver.FLAG_NO_RETRY,
|
||||
Dispatchers.IO.asExecutor(),
|
||||
signal,
|
||||
callback
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val underlyingNetwork =
|
||||
SagerNet.underlyingNetwork ?: error("upstream network not found")
|
||||
val answer = try {
|
||||
underlyingNetwork.getAllByName(domain)
|
||||
} catch (e: UnknownHostException) {
|
||||
ctx.errorCode(RCODE_NXDOMAIN)
|
||||
return
|
||||
}
|
||||
ctx.success(answer.mapNotNull { it.hostAddress }.joinToString("\n"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
164
libcore/dns_box.go
Normal file
164
libcore/dns_box.go
Normal file
@ -0,0 +1,164 @@
|
||||
// libbox/dns.go
|
||||
|
||||
package libcore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
dns "github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type LocalDNSTransport interface {
|
||||
Raw() bool
|
||||
Lookup(ctx *ExchangeContext, network string, domain string) error
|
||||
Exchange(ctx *ExchangeContext, message []byte) error
|
||||
}
|
||||
|
||||
func RegisterLocalDNSTransport(transport LocalDNSTransport) {
|
||||
if transport == nil {
|
||||
dns.RegisterTransport([]string{"local"}, dns.CreateLocalTransport)
|
||||
} else {
|
||||
dns.RegisterTransport([]string{"local"}, func(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
|
||||
return &platformLocalDNSTransport{
|
||||
iif: transport,
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var _ dns.Transport = (*platformLocalDNSTransport)(nil)
|
||||
|
||||
type platformLocalDNSTransport struct {
|
||||
iif LocalDNSTransport
|
||||
}
|
||||
|
||||
func (p *platformLocalDNSTransport) Name() string {
|
||||
return "local"
|
||||
}
|
||||
|
||||
func (p *platformLocalDNSTransport) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *platformLocalDNSTransport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *platformLocalDNSTransport) Raw() bool {
|
||||
return p.iif.Raw()
|
||||
}
|
||||
|
||||
func (p *platformLocalDNSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
messageBytes, err := message.Pack()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response := &ExchangeContext{
|
||||
context: ctx,
|
||||
}
|
||||
var responseMessage *mDNS.Msg
|
||||
return responseMessage, task.Run(ctx, func() error {
|
||||
err = p.iif.Exchange(response, messageBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response.error != nil {
|
||||
return response.error
|
||||
}
|
||||
responseMessage = &response.message
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (p *platformLocalDNSTransport) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) {
|
||||
var network string
|
||||
switch strategy {
|
||||
case dns.DomainStrategyUseIPv4:
|
||||
network = "ip4"
|
||||
case dns.DomainStrategyPreferIPv6:
|
||||
network = "ip6"
|
||||
default:
|
||||
network = "ip"
|
||||
}
|
||||
response := &ExchangeContext{
|
||||
context: ctx,
|
||||
}
|
||||
var responseAddr []netip.Addr
|
||||
return responseAddr, task.Run(ctx, func() error {
|
||||
err := p.iif.Lookup(response, network, domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response.error != nil {
|
||||
return response.error
|
||||
}
|
||||
switch strategy {
|
||||
case dns.DomainStrategyUseIPv4:
|
||||
responseAddr = common.Filter(response.addresses, func(it netip.Addr) bool {
|
||||
return it.Is4()
|
||||
})
|
||||
case dns.DomainStrategyPreferIPv6:
|
||||
responseAddr = common.Filter(response.addresses, func(it netip.Addr) bool {
|
||||
return it.Is6()
|
||||
})
|
||||
default:
|
||||
responseAddr = response.addresses
|
||||
}
|
||||
/*if len(responseAddr) == 0 {
|
||||
response.error = dns.RCodeSuccess
|
||||
}*/
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
type Func interface {
|
||||
Invoke() error
|
||||
}
|
||||
|
||||
type ExchangeContext struct {
|
||||
context context.Context
|
||||
message mDNS.Msg
|
||||
addresses []netip.Addr
|
||||
error error
|
||||
}
|
||||
|
||||
func (c *ExchangeContext) OnCancel(callback Func) {
|
||||
go func() {
|
||||
<-c.context.Done()
|
||||
callback.Invoke()
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *ExchangeContext) Success(result string) {
|
||||
c.addresses = common.Map(common.Filter(strings.Split(result, "\n"), func(it string) bool {
|
||||
return !common.IsEmpty(it)
|
||||
}), func(it string) netip.Addr {
|
||||
return M.ParseSocksaddrHostPort(it, 0).Unwrap().Addr
|
||||
})
|
||||
}
|
||||
|
||||
func (c *ExchangeContext) RawSuccess(result []byte) {
|
||||
err := c.message.Unpack(result)
|
||||
if err != nil {
|
||||
c.error = E.Cause(err, "parse response")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ExchangeContext) ErrorCode(code int32) {
|
||||
c.error = dns.RCodeError(code)
|
||||
}
|
||||
|
||||
func (c *ExchangeContext) ErrnoCode(code int32) {
|
||||
c.error = syscall.Errno(code)
|
||||
}
|
||||
@ -17,7 +17,7 @@ type LocalResolver interface {
|
||||
var localResolver LocalResolver // Android: passed from java (only when VPNService)
|
||||
|
||||
func SetLocalResolver(lr LocalResolver) {
|
||||
localResolver = lr
|
||||
localResolver = lr // old "underlyig://0.0.0.0"
|
||||
}
|
||||
|
||||
type BoxPlatformInterface interface {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user