Compare commits

...

7 Commits

Author SHA1 Message Date
2dust
35c5d64863 Merge pull request #1070 from yuhan6665/bypass-fix
bypass local also bypass multicast address
2021-05-29 10:29:25 +08:00
yuhan6665
0b05756d12 bypass local also bypass multicast address
https://www.iana.org/assignments/ipv4-address-space/ipv4-address-space.xhtml
2021-05-28 19:51:08 -04:00
2dust
3548dcbb67 Merge pull request #1065 from yuhan6665/domestic
Always add domestic dns to config
2021-05-24 08:14:43 +08:00
yuhan6665
b897b2a0e9 Always add domestic dns to config
Previously, domestic dns is only added to config under
"local dns mode". However, it should be used for V2ray
core routing DNS as well.
2021-05-23 11:56:23 -04:00
2dust
eb60e4a0e4 Merge pull request #1058 from yuhan6665/grpc
Grpc
2021-05-16 19:35:16 +08:00
yuhan6665
c3dfa8cedc Support gRPC import and export
New Format https://github.com/XTLS/Xray-core/issues/91
For Vmess QRcode https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2)
gRPC mode is mapped to "type"
2021-05-15 23:18:09 -04:00
yuhan6665
f58bf85b6d Update UI for gRPC
Separate transport header types for different networks
Change UI dynamically based on user selection of network
2021-05-15 23:04:58 -04:00
9 changed files with 170 additions and 126 deletions

View File

@@ -192,10 +192,10 @@ data class V2rayConfig(
}
data class GrpcSettingsBean(var serviceName: String = "",
val multiMode: Boolean? = null)
var multiMode: Boolean? = null)
fun populateTransportSettings(transport: String, headerType: String?, host: String?, path: String?, seed: String?,
quicSecurity: String?, key: String?): String {
quicSecurity: String?, key: String?, mode: String?, serviceName: String?): String {
var sni = ""
network = transport
when (network) {
@@ -250,7 +250,8 @@ data class V2rayConfig(
}
"grpc" -> {
val grpcSetting = GrpcSettingsBean()
grpcSetting.serviceName = path ?: ""
grpcSetting.multiMode = mode == "multi"
grpcSetting.serviceName = serviceName ?: ""
sni = host ?: ""
grpcSettings = grpcSetting
}
@@ -357,7 +358,7 @@ data class V2rayConfig(
}
"grpc" -> {
val grpcSetting = streamSettings?.grpcSettings ?: return null
listOf("",
listOf(if (grpcSetting.multiMode == true) "multi" else "gun",
"",
grpcSetting.serviceName)
}
@@ -368,7 +369,7 @@ data class V2rayConfig(
}
}
data class DnsBean(var servers: List<Any>? = null,
data class DnsBean(var servers: ArrayList<Any>? = null,
var hosts: Map<String, String>? = null,
val clientIp: String? = null,
val disableCache: Boolean? = null,

View File

@@ -5,6 +5,9 @@ import android.support.v7.app.AlertDialog
import android.text.TextUtils
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import com.tencent.mmkv.MMKV
import com.v2ray.ang.R
import com.v2ray.ang.dto.EConfigType
@@ -28,6 +31,7 @@ import kotlinx.android.synthetic.main.activity_server_vmess.et_remarks
import kotlinx.android.synthetic.main.activity_server_vmess.et_request_host
import kotlinx.android.synthetic.main.activity_server_vmess.sp_allow_insecure
import kotlinx.android.synthetic.main.activity_server_vmess.sp_header_type
import kotlinx.android.synthetic.main.activity_server_vmess.sp_header_type_title
import kotlinx.android.synthetic.main.activity_server_vmess.sp_network
import kotlinx.android.synthetic.main.activity_server_vmess.sp_stream_security
@@ -56,8 +60,14 @@ class ServerActivity : BaseActivity() {
private val networks: Array<out String> by lazy {
resources.getStringArray(R.array.networks)
}
private val headertypes: Array<out String> by lazy {
resources.getStringArray(R.array.headertypes)
private val tcpTypes: Array<out String> by lazy {
resources.getStringArray(R.array.header_type_tcp)
}
private val kcpAndQuicTypes: Array<out String> by lazy {
resources.getStringArray(R.array.header_type_kcp_and_quic)
}
private val grpcModes: Array<out String> by lazy {
resources.getStringArray(R.array.mode_type_grpc)
}
private val streamSecuritys: Array<out String> by lazy {
resources.getStringArray(R.array.streamsecurityxs)
@@ -79,6 +89,26 @@ class ServerActivity : BaseActivity() {
// EConfigType.VLESS -> setContentView(R.layout.activity_server_vless)
// EConfigType.TROJAN -> setContentView(R.layout.activity_server_trojan)
}
sp_network?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val types = transportTypes(networks[position])
sp_header_type?.isEnabled = types.size > 1
val adapter = ArrayAdapter(this@ServerActivity, android.R.layout.simple_spinner_item, types)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
sp_header_type?.adapter = adapter
sp_header_type_title?.text = if (networks[position] == "grpc")
getString(R.string.server_lab_mode_type) else
getString(R.string.server_lab_head_type)
config?.getProxyOutbound()?.getTransportSettingDetails()?.let { transportDetails ->
sp_header_type.setSelection(Utils.arrayFind(types, transportDetails[0]))
et_request_host.text = Utils.getEditable(transportDetails[1])
et_path.text = Utils.getEditable(transportDetails[2])
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
// do nothing
}
}
if (config != null) {
bindingServer(config)
} else {
@@ -129,11 +159,6 @@ class ServerActivity : BaseActivity() {
if (network >= 0) {
sp_network?.setSelection(network)
}
outbound.getTransportSettingDetails()?.let { transportDetails ->
sp_header_type.setSelection(Utils.arrayFind(headertypes, transportDetails[0]))
et_request_host.text = Utils.getEditable(transportDetails[1])
et_path.text = Utils.getEditable(transportDetails[2])
}
return true
}
@@ -246,16 +271,20 @@ class ServerActivity : BaseActivity() {
}
private fun saveStreamSettings(streamSetting: V2rayConfig.OutboundBean.StreamSettingsBean, config: ServerConfig) {
val network = if (sp_network != null) networks[sp_network.selectedItemPosition] else DEFAULT_NETWORK
val type = if (sp_header_type != null) transportTypes(network)[sp_header_type.selectedItemPosition] else "";
val requestHost = if (et_request_host != null) et_request_host.text.toString().trim() else ""
val path = if (et_path != null) et_path.text.toString().trim() else ""
var sni = streamSetting.populateTransportSettings(
if (sp_network != null) networks[sp_network.selectedItemPosition] else DEFAULT_NETWORK,
if (sp_header_type != null) headertypes[sp_header_type.selectedItemPosition] else "",
requestHost,
path,
path,
requestHost,
path
transport = network,
headerType = type,
host = requestHost,
path = path,
seed = path,
quicSecurity = requestHost,
key = path,
mode = type,
serviceName = path
)
val allowInsecure = if (sp_allow_insecure == null || allowinsecures[sp_allow_insecure.selectedItemPosition].isBlank()) {
false//settingsStorage?.decodeBool(PREF_ALLOW_INSECURE) ?: false
@@ -270,6 +299,18 @@ class ServerActivity : BaseActivity() {
)
}
private fun transportTypes(network: String?): Array<out String> {
return if (network == "tcp") {
tcpTypes
} else if (network == "kcp" || network == "quic") {
kcpAndQuicTypes
} else if (network == "grpc") {
grpcModes
} else {
arrayOf("---")
}
}
/**
* save server config
*/

View File

@@ -131,7 +131,8 @@ object AngConfigManager {
}
config.outboundBean?.streamSettings?.let { streamSetting ->
val sni = streamSetting.populateTransportSettings(vmessBean.network, vmessBean.headerType,
vmessBean.requestHost, vmessBean.path, vmessBean.path, vmessBean.requestHost, vmessBean.path)
vmessBean.requestHost, vmessBean.path, vmessBean.path, vmessBean.requestHost, vmessBean.path,
vmessBean.headerType, vmessBean.path)
// val allowInsecure = if (vmessBean.allowInsecure.isBlank()) {
// settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
// } else {
@@ -209,7 +210,7 @@ object AngConfigManager {
vnext.users[0].alterId = Utils.parseInt(vmessQRCode.aid)
}
val sni = streamSetting.populateTransportSettings(vmessQRCode.net, vmessQRCode.type, vmessQRCode.host,
vmessQRCode.path, vmessQRCode.path, vmessQRCode.host, vmessQRCode.path)
vmessQRCode.path, vmessQRCode.path, vmessQRCode.host, vmessQRCode.path, vmessQRCode.type, vmessQRCode.path)
streamSetting.populateTlsSettings(vmessQRCode.tls, allowInsecure,
if (vmessQRCode.sni.isNotBlank()) vmessQRCode.sni else sni)
}
@@ -312,7 +313,8 @@ object AngConfigManager {
}
val sni = streamSetting.populateTransportSettings(queryParam["type"] ?: "tcp", queryParam["headerType"],
queryParam["host"], queryParam["path"], queryParam["seed"], queryParam["quicSecurity"], queryParam["key"])
queryParam["host"], queryParam["path"], queryParam["seed"], queryParam["quicSecurity"], queryParam["key"],
queryParam["mode"], queryParam["serviceName"])
streamSetting.populateTlsSettings(queryParam["security"] ?: "", allowInsecure, queryParam["sni"] ?: sni)
}
if (config == null){
@@ -337,7 +339,7 @@ object AngConfigManager {
val uri = URI(uriString)
check(uri.scheme == "vmess")
val (_, protocol, tlsStr, uuid, alterId) =
Regex("(tcp|http|ws|kcp|quic)(\\+tls)?:([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})-([0-9]+)")
Regex("(tcp|http|ws|kcp|quic|grpc)(\\+tls)?:([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})-([0-9]+)")
.matchEntire(uri.userInfo)?.groupValues
?: error("parse user info fail.")
val tls = tlsStr.isNotBlank()
@@ -357,8 +359,8 @@ object AngConfigManager {
val sni = streamSetting.populateTransportSettings(protocol, queryParam["type"],
queryParam["host"]?.split("|")?.get(0) ?: "",
queryParam["path"]?.takeIf { it.trim() != "/" } ?: "",
queryParam["seed"], queryParam["security"], queryParam["key"])
queryParam["path"]?.takeIf { it.trim() != "/" } ?: "", queryParam["seed"], queryParam["security"],
queryParam["key"], queryParam["mode"], queryParam["serviceName"])
streamSetting.populateTlsSettings(if (tls) TLS else "", allowInsecure, sni)
true
}.getOrElse { false }
@@ -501,6 +503,10 @@ object AngConfigManager {
dicQuery["quicSecurity"] = Utils.urlEncode(transportDetails[1])
dicQuery["key"] = Utils.urlEncode(transportDetails[2])
}
"grpc" -> {
dicQuery["mode"] = transportDetails[0]
dicQuery["serviceName"] = transportDetails[2]
}
}
}
val query = "?" + dicQuery.toList().joinToString(

View File

@@ -69,10 +69,10 @@ object V2rayConfigUtil {
fakedns(v2rayConfig)
dns(v2rayConfig)
if (settingsStorage?.decodeBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) {
customLocalDns(v2rayConfig)
} else {
customRemoteDns(v2rayConfig)
}
if (settingsStorage?.decodeBool(AppConfig.PREF_SPEED_ENABLED) != true) {
v2rayConfig.stats = null
@@ -261,50 +261,19 @@ object V2rayConfigUtil {
*/
private fun customLocalDns(v2rayConfig: V2rayConfig): Boolean {
try {
val hosts = mutableMapOf<String, String>()
val servers = ArrayList<Any>()
val remoteDns = Utils.getRemoteDnsServers()
val domesticDns = Utils.getDomesticDnsServers()
val geositeCn = arrayListOf("geosite:cn")
val geoipCn = arrayListOf("geoip:cn")
val proxyDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
?: "")
val directDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
?: "")
if (settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) {
val geositeCn = arrayListOf("geosite:cn")
val proxyDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
?: "")
val directDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
?: "")
// fakedns with all domains to make it always top priority
servers.add(V2rayConfig.DnsBean.ServersBean(address = "fakedns", domains = geositeCn.plus(proxyDomain).plus(directDomain)))
v2rayConfig.dns.servers?.add(0,
V2rayConfig.DnsBean.ServersBean(address = "fakedns", domains = geositeCn.plus(proxyDomain).plus(directDomain)))
}
remoteDns.forEach {
servers.add(it)
}
if (proxyDomain.size > 0) {
servers.add(V2rayConfig.DnsBean.ServersBean(remoteDns.first(), 53, proxyDomain, null))
}
if (directDomain.size > 0) {
servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, directDomain, geoipCn))
}
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: "0"
if (routingMode == "2" || routingMode == "3") {
servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, geositeCn, geoipCn))
}
val blkDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED)
?: "")
if (blkDomain.size > 0) {
hosts.putAll(blkDomain.map { it to "127.0.0.1" })
}
// hardcode googleapi rule to fix play store problems
hosts.put("domain:googleapis.cn", "googleapis.com")
// DNS dns对象
v2rayConfig.dns = V2rayConfig.DnsBean(
servers = servers,
hosts = hosts)
// DNS inbound对象
val remoteDns = Utils.getRemoteDnsServers()
if (v2rayConfig.inbounds.none { e -> e.protocol == "dokodemo-door" && e.tag == "dns-in" }) {
val dnsInboundSettings = V2rayConfig.InboundBean.InSettingsBean(
address = if (remoteDns.first().startsWith("https")) "1.1.1.1" else remoteDns.first(),
@@ -333,16 +302,75 @@ object V2rayConfigUtil {
mux = null))
}
// DNS routing
if (!domesticDns.first().startsWith("https")) {
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
type = "field",
outboundTag = AppConfig.TAG_DIRECT,
port = "53",
ip = arrayListOf(domesticDns.first()),
domain = null)
)
// DNS routing tag
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
type = "field",
inboundTag = arrayListOf("dns-in"),
outboundTag = "dns-out",
domain = null)
)
} catch (e: Exception) {
e.printStackTrace()
return false
}
return true
}
private fun dns(v2rayConfig: V2rayConfig): Boolean {
try {
val hosts = mutableMapOf<String, String>()
val servers = ArrayList<Any>()
val remoteDns = Utils.getRemoteDnsServers()
val proxyDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT)
?: "")
remoteDns.forEach {
servers.add(it)
}
if (proxyDomain.size > 0) {
servers.add(V2rayConfig.DnsBean.ServersBean(remoteDns.first(), 53, proxyDomain, null))
}
// domestic DNS
val directDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
?: "")
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: "0"
if (directDomain.size > 0 || routingMode == "2" || routingMode == "3") {
val domesticDns = Utils.getDomesticDnsServers()
val geositeCn = arrayListOf("geosite:cn")
val geoipCn = arrayListOf("geoip:cn")
if (directDomain.size > 0) {
servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, directDomain, geoipCn))
}
if (routingMode == "2" || routingMode == "3") {
servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, geositeCn, geoipCn))
}
if (!domesticDns.first().startsWith("https")) {
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
type = "field",
outboundTag = AppConfig.TAG_DIRECT,
port = "53",
ip = arrayListOf(domesticDns.first()),
domain = null)
)
}
}
val blkDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED)
?: "")
if (blkDomain.size > 0) {
hosts.putAll(blkDomain.map { it to "127.0.0.1" })
}
// hardcode googleapi rule to fix play store problems
hosts.put("domain:googleapis.cn", "googleapis.com")
// DNS dns对象
v2rayConfig.dns = V2rayConfig.DnsBean(
servers = servers,
hosts = hosts)
// DNS routing
if (!remoteDns.first().startsWith("https")) {
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
type = "field",
@@ -352,37 +380,6 @@ object V2rayConfigUtil {
domain = null)
)
}
// DNS routing tag
v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean(
type = "field",
inboundTag = arrayListOf("dns-in"),
outboundTag = "dns-out",
domain = null)
)
} catch (e: Exception) {
e.printStackTrace()
return false
}
return true
}
/**
* Custom Remote Dns
*/
private fun customRemoteDns(v2rayConfig: V2rayConfig): Boolean {
try {
val servers = ArrayList<Any>()
val hosts = mutableMapOf<String, String>()
Utils.getRemoteDnsServers().forEach {
servers.add(it)
}
// hardcode googleapi rule to fix play store problems
hosts.put("domain:googleapis.cn", "googleapis.com")
v2rayConfig.dns = V2rayConfig.DnsBean(servers = servers, hosts = hosts)
} catch (e: Exception) {
e.printStackTrace()
return false

View File

@@ -174,6 +174,7 @@
android:orientation="vertical">
<TextView
android:id="@+id/sp_header_type_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_lab_head_type" />
@@ -181,8 +182,7 @@
<Spinner
android:id="@+id/sp_header_type"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:entries="@array/headertypes" />
android:layout_height="@dimen/edit_height" />
</LinearLayout>
<LinearLayout

View File

@@ -43,8 +43,9 @@
<string name="server_lab_network">传输协议(network)</string>
<string name="server_lab_more_function">功能设置(不清楚则保持默认值)</string>
<string name="server_lab_head_type">伪装类型(type)</string>
<string name="server_lab_mode_type">gRPC 传输模式 (mode)</string>
<string name="server_lab_request_host">伪装域名(host)(host/ws host/h2 host)/QUIC 加密方式</string>
<string name="server_lab_path">path(ws path/h2 path)/QUIC 加密密钥/kcp seed/grpc serviceName</string>
<string name="server_lab_path">path(ws path/h2 path)/QUIC 加密密钥/kcp seed/gRPC serviceName</string>
<string name="server_lab_stream_security">底层传输安全(tls)</string>
<string name="server_lab_allow_insecure">跳过证书验证(allowInsecure)</string>
<string name="server_lab_address3">服务器地址</string>

View File

@@ -43,8 +43,9 @@
<string name="server_lab_network">網路</string>
<string name="server_lab_more_function">更多功能</string>
<string name="server_lab_head_type">標頭類型</string>
<string name="server_lab_mode_type">gRPC 傳輸模式 (mode)</string>
<string name="server_lab_request_host">要求主機(host)(host/ws host/h2 host)/QUIC加密方式</string>
<string name="server_lab_path">path(ws path/h2 path)/QUIC加密密鑰/kcp seed/grpc serviceName</string>
<string name="server_lab_path">path(ws path/h2 path)/QUIC加密密鑰/kcp seed/gRPC serviceName</string>
<string name="server_lab_stream_security">傳輸層安全性(tls)</string>
<string name="server_lab_allow_insecure">跳過證書驗證(allowInsecure)</string>
<string name="server_lab_address3">伺服器位址</string>

View File

@@ -27,9 +27,13 @@
<item>grpc</item>
</string-array>
<string-array name="headertypes" translatable="false">
<string-array name="header_type_tcp" translatable="false">
<item>none</item>
<item>http</item>
</string-array>
<string-array name="header_type_kcp_and_quic" translatable="false">
<item>none</item>
<item>srtp</item>
<item>utp</item>
<item>wechat-video</item>
@@ -37,18 +41,10 @@
<item>wireguard</item>
</string-array>
<string-array name="headertypetcps" translatable="false">
<item>none</item>
<item>http</item>
</string-array>
<string-array name="headertypekcps" translatable="false">
<item>none</item>
<item>srtp</item>
<item>utp</item>
<item>wechat-video</item>
<item>dtls</item>
<item>wireguard</item>
<string-array name="mode_type_grpc" translatable="false">
<item>gun</item>
<item>multi</item>
<!--Hide this option until core support it <item>guna</item>-->
</string-array>
<string-array name="streamsecuritys" translatable="false">
@@ -136,6 +132,6 @@
<item>196.0.0.0/6</item>
<item>200.0.0.0/5</item>
<item>208.0.0.0/4</item>
<item>224.0.0.0/3</item>
<item>240.0.0.0/4</item>
</string-array>
</resources>

View File

@@ -40,11 +40,12 @@
<string name="server_lab_id">id</string>
<string name="server_lab_alterid">alterId</string>
<string name="server_lab_security">security</string>
<string name="server_lab_network">network</string>
<string name="server_lab_network">Network</string>
<string name="server_lab_more_function">more function</string>
<string name="server_lab_head_type">head type</string>
<string name="server_lab_mode_type">gRPC mode</string>
<string name="server_lab_request_host">request host(host/ws host/h2 host)/QUIC security</string>
<string name="server_lab_path">path(ws path/h2 path)/QUIC key/kcp seed/grpc serviceName</string>
<string name="server_lab_path">path(ws path/h2 path)/QUIC key/kcp seed/gRPC serviceName</string>
<string name="server_lab_stream_security">tls</string>
<string name="server_lab_allow_insecure">allowInsecure</string>
<string name="server_lab_address3">address</string>