anytls share link

This commit is contained in:
armv9 2025-03-01 17:40:50 +09:00
parent 56f90f2ea1
commit a1b5813e55
4 changed files with 60 additions and 10 deletions

View File

@ -30,11 +30,11 @@ import io.nekohasekai.sagernet.fmt.tuic.toUri
import io.nekohasekai.sagernet.fmt.v2ray.* import io.nekohasekai.sagernet.fmt.v2ray.*
import io.nekohasekai.sagernet.fmt.wireguard.WireGuardBean import io.nekohasekai.sagernet.fmt.wireguard.WireGuardBean
import io.nekohasekai.sagernet.ktx.app import io.nekohasekai.sagernet.ktx.app
import io.nekohasekai.sagernet.ktx.applyDefaultValues
import io.nekohasekai.sagernet.ui.profile.* import io.nekohasekai.sagernet.ui.profile.*
import moe.matsuri.nb4a.SingBoxOptions.MultiplexOptions import moe.matsuri.nb4a.SingBoxOptions.MultiplexOptions
import moe.matsuri.nb4a.proxy.anytls.AnyTLSBean import moe.matsuri.nb4a.proxy.anytls.AnyTLSBean
import moe.matsuri.nb4a.proxy.anytls.AnyTLSSettingsActivity import moe.matsuri.nb4a.proxy.anytls.AnyTLSSettingsActivity
import moe.matsuri.nb4a.proxy.anytls.toUri
import moe.matsuri.nb4a.proxy.config.ConfigBean import moe.matsuri.nb4a.proxy.config.ConfigBean
import moe.matsuri.nb4a.proxy.config.ConfigSettingActivity import moe.matsuri.nb4a.proxy.config.ConfigSettingActivity
import moe.matsuri.nb4a.proxy.neko.* import moe.matsuri.nb4a.proxy.neko.*
@ -98,10 +98,8 @@ data class ProxyEntity(
val chainName by lazy { app.getString(R.string.proxy_chain) } val chainName by lazy { app.getString(R.string.proxy_chain) }
private val placeHolderBean = SOCKSBean().applyDefaultValues()
@JvmField @JvmField
val CREATOR = object : Serializable.CREATOR<ProxyEntity>() { val CREATOR = object : CREATOR<ProxyEntity>() {
override fun newInstance(): ProxyEntity { override fun newInstance(): ProxyEntity {
return ProxyEntity() return ProxyEntity()
@ -241,7 +239,6 @@ data class ProxyEntity(
is SSHBean -> false is SSHBean -> false
is WireGuardBean -> false is WireGuardBean -> false
is ShadowTLSBean -> false is ShadowTLSBean -> false
is AnyTLSBean -> false
is NekoBean -> nekoBean!!.haveStandardLink() is NekoBean -> nekoBean!!.haveStandardLink()
is ConfigBean -> false is ConfigBean -> false
else -> true else -> true
@ -259,6 +256,7 @@ data class ProxyEntity(
is NaiveBean -> toUri() is NaiveBean -> toUri()
is HysteriaBean -> toUri() is HysteriaBean -> toUri()
is TuicBean -> toUri() is TuicBean -> toUri()
is AnyTLSBean -> toUri()
is NekoBean -> shareLink() is NekoBean -> shareLink()
else -> toUniversalLink() else -> toUniversalLink()
} }

View File

@ -15,6 +15,7 @@ import io.nekohasekai.sagernet.fmt.tuic.parseTuic
import io.nekohasekai.sagernet.fmt.trojan_go.parseTrojanGo import io.nekohasekai.sagernet.fmt.trojan_go.parseTrojanGo
import io.nekohasekai.sagernet.fmt.v2ray.parseV2Ray import io.nekohasekai.sagernet.fmt.v2ray.parseV2Ray
import moe.matsuri.nb4a.plugin.NekoPluginManager import moe.matsuri.nb4a.plugin.NekoPluginManager
import moe.matsuri.nb4a.proxy.anytls.parseAnytls
import moe.matsuri.nb4a.proxy.neko.NekoJSInterface import moe.matsuri.nb4a.proxy.neko.NekoJSInterface
import moe.matsuri.nb4a.proxy.neko.parseShareLink import moe.matsuri.nb4a.proxy.neko.parseShareLink
import moe.matsuri.nb4a.utils.JavaUtil.gson import moe.matsuri.nb4a.utils.JavaUtil.gson
@ -203,6 +204,13 @@ suspend fun parseProxies(text: String): List<AbstractBean> {
}.onFailure { }.onFailure {
Logs.w(it) Logs.w(it)
} }
} else if (startsWith("anytls://")) {
Logs.d("Try parse anytls link: $this")
runCatching {
entities.add(parseAnytls(this))
}.onFailure {
Logs.w(it)
}
} else { // Neko Plugins } else { // Neko Plugins
NekoPluginManager.getProtocols().forEach { obj -> NekoPluginManager.getProtocols().forEach { obj ->
obj.protocolConfig.optJSONArray("links")?.forEach { _, any -> obj.protocolConfig.optJSONArray("links")?.forEach { _, any ->
@ -228,12 +236,12 @@ suspend fun parseProxies(text: String): List<AbstractBean> {
for (link in linksByLine) { for (link in linksByLine) {
link.parseLink(entitiesByLine) link.parseLink(entitiesByLine)
} }
var isBadLink = false // var isBadLink = false
if (entities.onEach { it.initializeDefaultValues() }.size == entitiesByLine.onEach { it.initializeDefaultValues() }.size) run test@{ if (entities.onEach { it.initializeDefaultValues() }.size == entitiesByLine.onEach { it.initializeDefaultValues() }.size) run test@{
entities.forEachIndexed { index, bean -> entities.forEachIndexed { index, bean ->
val lineBean = entitiesByLine[index] val lineBean = entitiesByLine[index]
if (bean == lineBean && bean.displayName() != lineBean.displayName()) { if (bean == lineBean && bean.displayName() != lineBean.displayName()) {
isBadLink = true // isBadLink = true
return@test return@test
} }
} }

View File

@ -1661,8 +1661,8 @@ class ConfigurationFragment @JvmOverloads constructor(
try { try {
currentName = entity.displayName()!! currentName = entity.displayName()!!
when (item.itemId) { when (item.itemId) {
R.id.action_standard_qr -> showCode(entity.toStdLink()!!) R.id.action_standard_qr -> showCode(entity.toStdLink())
R.id.action_standard_clipboard -> export(entity.toStdLink()!!) R.id.action_standard_clipboard -> export(entity.toStdLink())
R.id.action_universal_qr -> showCode(entity.requireBean().toUniversalLink()) R.id.action_universal_qr -> showCode(entity.requireBean().toUniversalLink())
R.id.action_universal_clipboard -> export( R.id.action_universal_clipboard -> export(
entity.requireBean().toUniversalLink() entity.requireBean().toUniversalLink()

View File

@ -1,8 +1,12 @@
package moe.matsuri.nb4a.proxy.anytls package moe.matsuri.nb4a.proxy.anytls
import io.nekohasekai.sagernet.ktx.blankAsNull import io.nekohasekai.sagernet.ktx.blankAsNull
import io.nekohasekai.sagernet.ktx.linkBuilder
import io.nekohasekai.sagernet.ktx.toLink
import io.nekohasekai.sagernet.ktx.urlSafe
import moe.matsuri.nb4a.SingBoxOptions import moe.matsuri.nb4a.SingBoxOptions
import moe.matsuri.nb4a.utils.listByLineOrComma import moe.matsuri.nb4a.utils.listByLineOrComma
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
fun buildSingBoxOutboundAnyTLSBean(bean: AnyTLSBean): SingBoxOptions.Outbound_AnyTLSOptions { fun buildSingBoxOutboundAnyTLSBean(bean: AnyTLSBean): SingBoxOptions.Outbound_AnyTLSOptions {
return SingBoxOptions.Outbound_AnyTLSOptions().apply { return SingBoxOptions.Outbound_AnyTLSOptions().apply {
@ -35,3 +39,43 @@ fun buildSingBoxOutboundAnyTLSBean(bean: AnyTLSBean): SingBoxOptions.Outbound_An
} }
} }
} }
fun AnyTLSBean.toUri(): String {
val builder = linkBuilder()
.host(serverAddress)
.port(serverPort)
.username(password)
if (!name.isNullOrBlank()) {
builder.encodedFragment(name.urlSafe())
}
if (allowInsecure) {
builder.addQueryParameter("insecure", "1")
}
if (!sni.isNullOrBlank()) {
builder.addQueryParameter("sni", sni)
}
if (!utlsFingerprint.isNullOrBlank()) {
builder.addQueryParameter("fp", utlsFingerprint)
}
return builder.toLink("anytls")
}
fun parseAnytls(url: String): AnyTLSBean {
// https://github.com/anytls/anytls-go/blob/main/docs/uri_scheme.md
val link = url.replace("anytls://", "https://").toHttpUrlOrNull() ?: error(
"invalid anytls link $url"
)
return AnyTLSBean().apply {
serverAddress = link.host
serverPort = link.port
name = link.fragment
password = link.username
sni = link.queryParameter("sni") ?: ""
link.queryParameter("insecure")?.also {
allowInsecure = it == "1" || it == "true"
}
link.queryParameter("fp")?.let {
utlsFingerprint = it
}
}
}