new LocalDNSTransport by sfa

This commit is contained in:
arm64v8a 2023-07-01 18:10:35 +09:00
parent ad0dd1d63f
commit 4438c4f0e5
6 changed files with 303 additions and 8 deletions

View File

@ -22,6 +22,7 @@ import io.nekohasekai.sagernet.plugin.PluginManager
import kotlinx.coroutines.* import kotlinx.coroutines.*
import libcore.BoxInstance import libcore.BoxInstance
import libcore.Libcore import libcore.Libcore
import moe.matsuri.nb4a.net.LocalResolverImpl
import moe.matsuri.nb4a.plugin.NekoPluginManager import moe.matsuri.nb4a.plugin.NekoPluginManager
import moe.matsuri.nb4a.proxy.neko.NekoBean import moe.matsuri.nb4a.proxy.neko.NekoBean
import moe.matsuri.nb4a.proxy.neko.NekoJSInterface import moe.matsuri.nb4a.proxy.neko.NekoJSInterface
@ -111,6 +112,7 @@ abstract class BoxInstance(
} }
} }
} }
Libcore.registerLocalDNSTransport(LocalResolverImpl)
loadConfig() loadConfig()
} }
@ -277,6 +279,8 @@ abstract class BoxInstance(
if (::box.isInitialized) { if (::box.isInitialized) {
box.close() box.close()
} }
Libcore.registerLocalDNSTransport(null)
} }
} }

View File

@ -54,7 +54,7 @@ const val TAG_DNS_IN = "dns-in"
const val TAG_DNS_OUT = "dns-out" const val TAG_DNS_OUT = "dns-out"
const val LOCALHOST = "127.0.0.1" 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( class ConfigBuildResult(
var config: String, var config: String,

View File

@ -94,17 +94,17 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
val enableDnsRouting = findPreference<SwitchPreference>(Key.ENABLE_DNS_ROUTING)!! val enableDnsRouting = findPreference<SwitchPreference>(Key.ENABLE_DNS_ROUTING)!!
val enableFakeDns = findPreference<SwitchPreference>(Key.ENABLE_FAKEDNS)!! val enableFakeDns = findPreference<SwitchPreference>(Key.ENABLE_FAKEDNS)!!
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { // if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
DataStore.directDnsUseSystem = false // DataStore.directDnsUseSystem = false
directDnsUseSystem.remove() // directDnsUseSystem.remove()
} else { // } else {
directDns.isEnabled = !directDnsUseSystem.isChecked directDns.isEnabled = !directDnsUseSystem.isChecked
directDnsUseSystem.setOnPreferenceChangeListener { _, newValue -> directDnsUseSystem.setOnPreferenceChangeListener { _, newValue ->
directDns.isEnabled = !(newValue as Boolean) directDns.isEnabled = !(newValue as Boolean)
needReload() needReload()
true true
} }
} // }
val requireTransproxy = findPreference<SwitchPreference>(Key.REQUIRE_TRANSPROXY)!! val requireTransproxy = findPreference<SwitchPreference>(Key.REQUIRE_TRANSPROXY)!!
val transproxyPort = findPreference<EditTextPreference>(Key.TRANSPROXY_PORT)!! val transproxyPort = findPreference<EditTextPreference>(Key.TRANSPROXY_PORT)!!

View File

@ -3,16 +3,25 @@ package moe.matsuri.nb4a.net
import android.net.DnsResolver import android.net.DnsResolver
import android.os.Build import android.os.Build
import android.os.CancellationSignal import android.os.CancellationSignal
import android.system.ErrnoException
import androidx.annotation.RequiresApi
import io.nekohasekai.sagernet.SagerNet import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.ktx.tryResume import io.nekohasekai.sagernet.ktx.tryResume
import io.nekohasekai.sagernet.ktx.tryResumeWithException import io.nekohasekai.sagernet.ktx.tryResumeWithException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import libcore.ExchangeContext
import libcore.LocalDNSTransport
import libcore.LocalResolver
import java.net.InetAddress import java.net.InetAddress
import java.net.UnknownHostException
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
object LocalResolverImpl : libcore.LocalResolver { object LocalResolverImpl : LocalResolver, LocalDNSTransport {
// old
override fun lookupIP(network: String, domain: String): String { override fun lookupIP(network: String, domain: String): String {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 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 } continuation.tryResume((answer as Collection<InetAddress?>).mapNotNull { it?.hostAddress }
.joinToString(",")) .joinToString(","))
} }
rcode == 0 -> { rcode == 0 -> {
// fuck AAAA no record // fuck AAAA no record
// features/dns/client.go // features/dns/client.go
continuation.tryResume("") continuation.tryResume("")
} }
else -> { else -> {
// Need return rcode // Need return rcode
// proxy/dns/dns.go // 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
View 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)
}

View File

@ -17,7 +17,7 @@ type LocalResolver interface {
var localResolver LocalResolver // Android: passed from java (only when VPNService) var localResolver LocalResolver // Android: passed from java (only when VPNService)
func SetLocalResolver(lr LocalResolver) { func SetLocalResolver(lr LocalResolver) {
localResolver = lr localResolver = lr // old "underlyig://0.0.0.0"
} }
type BoxPlatformInterface interface { type BoxPlatformInterface interface {