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.wireguard.WireGuardBean
import io.nekohasekai.sagernet.ktx.app
import io.nekohasekai.sagernet.ktx.applyDefaultValues
import io.nekohasekai.sagernet.ui.profile.*
import moe.matsuri.nb4a.SingBoxOptions.MultiplexOptions
import moe.matsuri.nb4a.proxy.anytls.AnyTLSBean
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.ConfigSettingActivity
import moe.matsuri.nb4a.proxy.neko.*
@ -98,10 +98,8 @@ data class ProxyEntity(
val chainName by lazy { app.getString(R.string.proxy_chain) }
private val placeHolderBean = SOCKSBean().applyDefaultValues()
@JvmField
val CREATOR = object : Serializable.CREATOR<ProxyEntity>() {
val CREATOR = object : CREATOR<ProxyEntity>() {
override fun newInstance(): ProxyEntity {
return ProxyEntity()
@ -241,7 +239,6 @@ data class ProxyEntity(
is SSHBean -> false
is WireGuardBean -> false
is ShadowTLSBean -> false
is AnyTLSBean -> false
is NekoBean -> nekoBean!!.haveStandardLink()
is ConfigBean -> false
else -> true
@ -259,6 +256,7 @@ data class ProxyEntity(
is NaiveBean -> toUri()
is HysteriaBean -> toUri()
is TuicBean -> toUri()
is AnyTLSBean -> toUri()
is NekoBean -> shareLink()
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.v2ray.parseV2Ray
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.parseShareLink
import moe.matsuri.nb4a.utils.JavaUtil.gson
@ -203,6 +204,13 @@ suspend fun parseProxies(text: String): List<AbstractBean> {
}.onFailure {
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
NekoPluginManager.getProtocols().forEach { obj ->
obj.protocolConfig.optJSONArray("links")?.forEach { _, any ->
@ -228,12 +236,12 @@ suspend fun parseProxies(text: String): List<AbstractBean> {
for (link in linksByLine) {
link.parseLink(entitiesByLine)
}
var isBadLink = false
// var isBadLink = false
if (entities.onEach { it.initializeDefaultValues() }.size == entitiesByLine.onEach { it.initializeDefaultValues() }.size) run test@{
entities.forEachIndexed { index, bean ->
val lineBean = entitiesByLine[index]
if (bean == lineBean && bean.displayName() != lineBean.displayName()) {
isBadLink = true
// isBadLink = true
return@test
}
}

View File

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

View File

@ -1,8 +1,12 @@
package moe.matsuri.nb4a.proxy.anytls
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.utils.listByLineOrComma
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
fun buildSingBoxOutboundAnyTLSBean(bean: AnyTLSBean): SingBoxOptions.Outbound_AnyTLSOptions {
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
}
}
}