Compare commits

..

14 Commits

Author SHA1 Message Date
2dust
8cdc7fb3c9 Merge pull request #1001 from yuhan6665/new-storage-fix
Rollback parsed custom config
2021-04-11 19:28:47 +08:00
yuhan6665
d0a2fa0086 Rollback parsed custom config
To minimize change, the data structure for ServerConfig still stay the same
Add another table for server raw config
2021-04-10 21:30:34 -04:00
2dust
f6c54841d2 Merge pull request #990 from yuhan6665/new-storage-fix
New storage fix
2021-04-05 20:21:51 +08:00
yuhan6665
54fa356999 Add some new config for v2fly 4.37.0 2021-04-04 19:25:52 -04:00
2dust
9642b7f64f up grpc 2021-04-04 19:25:52 -04:00
yuhan6665
dd2d2c1638 Add some missing config from Xray 2021-04-04 19:25:51 -04:00
yuhan6665
e21950dbcd Improve custom config error toast 2021-04-04 19:25:51 -04:00
2dust
d016ab06d4 Merge pull request #973 from yuhan6665/new-storage-fix
New storage fix
2021-03-29 08:06:43 +08:00
yuhan6665
4d9aced5a4 Gson display to not escape HTML character like "=" 2021-03-28 11:34:41 -04:00
yuhan6665
62b928e6a0 Support Trojan flow and email 2021-03-28 11:34:34 -04:00
2dust
0ce60eae73 Update MainRecyclerAdapter.kt 2021-03-28 11:34:12 -04:00
2dust
5930a6a9eb Update V2rayConfig.kt 2021-03-28 11:34:01 -04:00
2dust
a360310be2 fix header type none 2021-03-28 11:33:51 -04:00
2dust
820e6cdf36 fix migration inbound port parsed as 0 2021-03-28 11:31:18 -04:00
12 changed files with 111 additions and 58 deletions

View File

@@ -5,6 +5,7 @@ import com.google.gson.GsonBuilder
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import com.google.gson.annotations.SerializedName
import com.google.gson.reflect.TypeToken
import java.lang.reflect.Type
@@ -19,7 +20,8 @@ data class V2rayConfig(
val api: Any? = null,
val transport: Any? = null,
val reverse: Any? = null,
val fakedns: Any? = null) {
val fakedns: Any? = null,
val browserForwarder: Any? = null) {
companion object {
const val DEFAULT_PORT = 443
const val DEFAULT_SECURITY = "auto"
@@ -34,7 +36,8 @@ data class V2rayConfig(
data class LogBean(val access: String,
val error: String,
var loglevel: String?)
var loglevel: String?,
val dnsLog: Boolean? = null)
data class InboundBean(
var tag: String,
@@ -68,17 +71,17 @@ data class V2rayConfig(
data class OutSettingsBean(var vnext: List<VnextBean>? = null,
var servers: List<ServersBean>? = null,
/*Blackhole*/
/*Blackhole*/
var response: Response? = null,
/*DNS*/
/*DNS*/
val network: String? = null,
val address: String? = null,
val port: Int? = null,
/*Freedom*/
/*Freedom*/
val domainStrategy: String? = null,
val redirect: String? = null,
val userLevel: Int? = null,
/*Loopback*/
/*Loopback*/
val inboundTag: String? = null) {
data class VnextBean(var address: String = "",
@@ -99,6 +102,9 @@ data class V2rayConfig(
var password: String = "",
var port: Int = DEFAULT_PORT,
var level: Int = DEFAULT_LEVEL,
val email: String? = null,
val flow: String? = null,
val ivCheck: Boolean? = null,
var users: List<SocksUsersBean>? = null) {
@@ -119,18 +125,27 @@ data class V2rayConfig(
var tlsSettings: TlsSettingsBean? = null,
var quicSettings: QuicSettingBean? = null,
var xtlsSettings: TlsSettingsBean? = null,
val grpcSettings: Any? = null,
var grpcSettings: GrpcSettingsBean? = null,
val dsSettings: Any? = null,
val sockopt: Any? = null
) {
data class TcpSettingsBean(var header: HeaderBean = HeaderBean()) {
data class TcpSettingsBean(var header: HeaderBean = HeaderBean(),
val acceptProxyProtocol: Boolean? = null) {
data class HeaderBean(var type: String = "none",
var request: RequestBean? = null,
var response: Any? = null) {
data class RequestBean(var path: List<String> = ArrayList(),
var headers: HeadersBean = HeadersBean()) {
data class HeadersBean(var Host: List<String> = ArrayList())
var headers: HeadersBean = HeadersBean(),
val version: String? = null,
val method: String? = null) {
data class HeadersBean(var Host: List<String> = ArrayList(),
@SerializedName("User-Agent")
val userAgent: List<String>? = null,
@SerializedName("Accept-Encoding")
val acceptEncoding: List<String>? = null,
val Connection: List<String>? = null,
val Pragma: String? = null)
}
}
}
@@ -148,7 +163,10 @@ data class V2rayConfig(
}
data class WsSettingsBean(var path: String = "",
var headers: HeadersBean = HeadersBean()) {
var headers: HeadersBean = HeadersBean(),
val maxEarlyData: Int? = null,
val useBrowserForwarding: Boolean? = null,
val acceptProxyProtocol: Boolean? = null) {
data class HeadersBean(var Host: String = "")
}
@@ -158,8 +176,14 @@ data class V2rayConfig(
data class TlsSettingsBean(var allowInsecure: Boolean = false,
var serverName: String = "",
val alpn: List<String>? = null,
val minVersion: String? = null,
val maxVersion: String? = null,
val preferServerCipherSuites: Boolean? = null,
val cipherSuites: String? = null,
val fingerprint: String? = null,
val certificates: List<Any>? = null,
val disableSystemRoot: Boolean? = null)
val disableSystemRoot: Boolean? = null,
val enableSessionResumption: Boolean? = null)
data class QuicSettingBean(var security: String = "none",
var key: String = "",
@@ -167,20 +191,28 @@ data class V2rayConfig(
data class HeaderBean(var type: String = "none")
}
data class GrpcSettingsBean(var serviceName: String = "",
val multiMode: Boolean? = null)
fun populateTransportSettings(transport: String, headerType: String?, host: String?, path: String?, seed: String?,
quicSecurity: String?, key: String?): String {
var sni = ""
network = transport
when (network) {
"tcp" -> if (headerType == HTTP) {
"tcp" -> {
val tcpSetting = TcpSettingsBean()
tcpSetting.header.type = HTTP
if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(path)) {
val requestObj = TcpSettingsBean.HeaderBean.RequestBean()
requestObj.headers.Host = (host ?: "").split(",").map { it.trim() }
requestObj.path = (path ?: "").split(",").map { it.trim() }
tcpSetting.header.request = requestObj
sni = requestObj.headers.Host.getOrNull(0) ?: sni
if (headerType == HTTP) {
tcpSetting.header.type = HTTP
if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(path)) {
val requestObj = TcpSettingsBean.HeaderBean.RequestBean()
requestObj.headers.Host = (host ?: "").split(",").map { it.trim() }
requestObj.path = (path ?: "").split(",").map { it.trim() }
tcpSetting.header.request = requestObj
sni = requestObj.headers.Host.getOrNull(0) ?: sni
}
} else {
tcpSetting.header.type = "none"
sni = host ?: ""
}
tcpSettings = tcpSetting
}
@@ -216,6 +248,12 @@ data class V2rayConfig(
quicsetting.header.type = headerType ?: "none"
quicSettings = quicsetting
}
"grpc" -> {
val grpcSetting = GrpcSettingsBean()
grpcSetting.serviceName = path ?: ""
sni = host ?: ""
grpcSettings = grpcSetting
}
}
return sni
}
@@ -286,7 +324,7 @@ data class V2rayConfig(
if (protocol.equals(EConfigType.VMESS.name, true)
|| protocol.equals(EConfigType.VLESS.name, true)) {
val transport = streamSettings?.network ?: return null
return when(transport) {
return when (transport) {
"tcp" -> {
val tcpSetting = streamSettings?.tcpSettings ?: return null
listOf(tcpSetting.header.type,
@@ -317,6 +355,12 @@ data class V2rayConfig(
quicSetting.security,
quicSetting.key)
}
"grpc" -> {
val grpcSetting = streamSettings?.grpcSettings ?: return null
listOf("",
"",
grpcSetting.serviceName)
}
else -> null
}
}
@@ -328,6 +372,7 @@ data class V2rayConfig(
var hosts: Map<String, String>? = null,
val clientIp: String? = null,
val disableCache: Boolean? = null,
val queryStrategy: String? = null,
val tag: String? = null
) {
data class ServersBean(var address: String = "",
@@ -387,8 +432,9 @@ data class V2rayConfig(
fun toPrettyPrinting(): String {
return GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.registerTypeAdapter( // custom serialiser is needed here since JSON by default parse number as Double, core will fail to start
object: TypeToken<Double>() {}.type,
object : TypeToken<Double>() {}.type,
JsonSerializer { src: Double?, _: Type?, _: JsonSerializationContext? -> JsonPrimitive(src?.toInt()) }
)
.create()

View File

@@ -16,6 +16,7 @@ import android.util.Log
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import com.tbruyelle.rxpermissions.RxPermissions
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
@@ -28,13 +29,13 @@ import com.v2ray.ang.service.V2RayServiceManager
import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.Utils
import com.v2ray.ang.util.V2rayConfigUtil
import com.v2ray.ang.viewmodel.MainViewModel
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import libv2ray.Libv2ray
import me.drakeet.support.toast.ToastCompat
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import java.net.URL
@@ -475,14 +476,11 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
toast(R.string.toast_none_data)
return
}
if (!V2rayConfigUtil.isValidConfig(server)) {
toast(R.string.toast_config_file_invalid)
return
}
mainViewModel.appendCustomConfigServer(server)
toast(R.string.toast_success)
adapter.notifyItemInserted(mainViewModel.serverList.lastIndex)
} catch (e: Exception) {
ToastCompat.makeText(this, "${getString(R.string.toast_malformed_josn)} ${e.cause?.message}", Toast.LENGTH_LONG).show()
e.printStackTrace()
return
}

View File

@@ -48,7 +48,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
if (holder is MainViewHolder) {
val guid = mActivity.mainViewModel.serverList.getOrNull(position) ?: return
val config = MmkvManager.decodeServerConfig(guid) ?: return
val outbound = config.getProxyOutbound() ?: return
val outbound = config.getProxyOutbound()
val aff = MmkvManager.decodeServerAffiliationInfo(guid)
holder.name.text = config.remarks
@@ -76,7 +76,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
} else {
holder.type.text = config.configType.name.toLowerCase()
}
holder.statistics.text = "${outbound.getServerAddress()} : ${outbound.getServerPort()}"
holder.statistics.text = "${outbound?.getServerAddress()} : ${outbound?.getServerPort()}"
holder.layout_share.setOnClickListener {
AlertDialog.Builder(mActivity).setItems(shareOptions.toTypedArray()) { _, i ->

View File

@@ -5,6 +5,7 @@ import android.support.v7.app.AlertDialog
import android.text.TextUtils
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import com.google.gson.Gson
import com.tencent.mmkv.MMKV
import com.v2ray.ang.R
@@ -15,10 +16,12 @@ import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.activity_server_custom_config.*
import me.drakeet.support.toast.ToastCompat
class ServerCustomConfigActivity : BaseActivity() {
private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
private val serverRawStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) }
private val editGuid by lazy { intent.getStringExtra("guid").orEmpty() }
private val isRunning by lazy {
intent.getBooleanExtra("isRunning", false)
@@ -45,7 +48,12 @@ class ServerCustomConfigActivity : BaseActivity() {
*/
private fun bindingServer(config: ServerConfig): Boolean {
et_remarks.text = Utils.getEditable(config.remarks)
tv_content.text = Utils.getEditable(config.fullConfig?.toPrettyPrinting().orEmpty())
val raw = serverRawStorage?.decodeString(editGuid)
if (raw.isNullOrBlank()) {
tv_content.text = Utils.getEditable(config.fullConfig?.toPrettyPrinting().orEmpty())
} else {
tv_content.text = Utils.getEditable(raw)
}
return true
}
@@ -70,7 +78,7 @@ class ServerCustomConfigActivity : BaseActivity() {
Gson().fromJson(tv_content.text.toString(), V2rayConfig::class.java)
} catch (e: Exception) {
e.printStackTrace()
toast(R.string.toast_malformed_josn)
ToastCompat.makeText(this, "${getString(R.string.toast_malformed_josn)} ${e.cause?.message}", Toast.LENGTH_LONG).show()
return false
}
@@ -79,6 +87,7 @@ class ServerCustomConfigActivity : BaseActivity() {
config.fullConfig = v2rayConfig
MmkvManager.encodeServerConfig(editGuid, config)
serverRawStorage?.encode(editGuid, tv_content.text.toString())
toast(R.string.toast_success)
finish()
return true

View File

@@ -22,6 +22,7 @@ import java.util.*
object AngConfigManager {
private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
private val serverRawStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) }
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
private val subStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SUB, MMKV.MULTI_PROCESS_MODE) }
@@ -65,7 +66,7 @@ object AngConfigManager {
AppConfig.PREF_V2RAY_ROUTING_AGENT,
AppConfig.PREF_V2RAY_ROUTING_BLOCKED,
AppConfig.PREF_V2RAY_ROUTING_DIRECT,).forEach { key ->
settingsStorage?.encode(key, sharedPreferences.getString(key, ""))
settingsStorage?.encode(key, sharedPreferences.getString(key, null))
}
listOf(AppConfig.PREF_SPEED_ENABLED,
AppConfig.PREF_SNIFFING_ENABLED,
@@ -95,6 +96,7 @@ object AngConfigManager {
return@forEachIndexed
}
config.fullConfig = v2rayConfig
serverRawStorage?.encode(vmessBean.guid, jsonConfig)
} else {
config.outboundBean?.settings?.vnext?.get(0)?.let { vnext ->
vnext.address = vmessBean.address

View File

@@ -9,6 +9,7 @@ import com.v2ray.ang.dto.SubscriptionItem
object MmkvManager {
const val ID_MAIN = "MAIN"
const val ID_SERVER_CONFIG = "SERVER_CONFIG"
const val ID_SERVER_RAW = "SERVER_RAW"
const val ID_SERVER_AFF = "SERVER_AFF"
const val ID_SUB = "SUB"
const val ID_SETTING = "SETTING"

View File

@@ -7,8 +7,6 @@ import com.google.gson.*
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.V2rayConfig
import org.json.JSONException
import org.json.JSONObject
import com.v2ray.ang.dto.EConfigType
object V2rayConfigUtil {
@@ -19,6 +17,7 @@ object V2rayConfigUtil {
// JSONObject("""{"version":"1.1","status":"200","reason":"OK","headers":{"Content-Type":["application/octet-stream","video/mpeg"],"Transfer-Encoding":["chunked"],"Connection":["keep-alive"],"Pragma":"no-cache"}}""")
// }
private val serverRawStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) }
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
data class Result(var status: Boolean, var content: String)
@@ -30,7 +29,12 @@ object V2rayConfigUtil {
try {
val config = MmkvManager.decodeServerConfig(guid) ?: return Result(false, "")
if (config.configType == EConfigType.CUSTOM) {
val customConfig = config.fullConfig?.toPrettyPrinting() ?: return Result(false, "")
val raw = serverRawStorage?.decodeString(guid)
val customConfig = if (raw.isNullOrBlank()) {
config.fullConfig?.toPrettyPrinting() ?: return Result(false, "")
} else {
raw
}
Log.d("V2rayConfigUtilGoLog", customConfig)
return Result(true, customConfig)
}
@@ -360,17 +364,4 @@ object V2rayConfigUtil {
}
return true
}
/**
* is valid config
*/
fun isValidConfig(conf: String): Boolean {
return try {
val jObj = JSONObject(conf)
//hasBound = (jObj.has("outbounds") and jObj.has("inbounds")) or (jObj.has("outbound") and jObj.has("inbound"))
jObj.has("outbounds") or jObj.has("outbound")
} catch (e: JSONException) {
false
}
}
}

View File

@@ -24,6 +24,7 @@ import java.util.*
class MainViewModel(application: Application) : AndroidViewModel(application) {
private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
private val serverRawStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) }
var serverList= MmkvManager.decodeServerList()
private set
@@ -61,7 +62,9 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
val config = ServerConfig.create(EConfigType.CUSTOM)
config.remarks = System.currentTimeMillis().toString()
config.fullConfig = Gson().fromJson(server, V2rayConfig::class.java)
serverList.add(MmkvManager.encodeServerConfig("", config))
val key = MmkvManager.encodeServerConfig("", config)
serverRawStorage?.encode(key, server)
serverList.add(key)
}
fun swapServer(fromPosition: Int, toPosition: Int) {

View File

@@ -43,10 +43,10 @@
<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_request_host">伪装域名host(host/ws host/h2 host)/QUIC 加密方式</string>
<string name="server_lab_path">path(ws path/h2 path)/QUIC 加密密钥</string>
<string name="server_lab_stream_security">底层传输安全</string>
<string name="server_lab_allow_insecure">allowInsecure</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_stream_security">底层传输安全(tls)</string>
<string name="server_lab_allow_insecure">跳过证书验证(allowInsecure)</string>
<string name="server_lab_address3">服务器地址</string>
<string name="server_lab_port3">服务器端口</string>
<string name="server_lab_id3">密码</string>

View File

@@ -43,10 +43,10 @@
<string name="server_lab_network">網路</string>
<string name="server_lab_more_function">更多功能</string>
<string name="server_lab_head_type">標頭類型</string>
<string name="server_lab_request_host">要求主機(host/ws host/h2 host)/QUIC加密方式</string>
<string name="server_lab_path">path(ws path/h2 path)/QUIC加密密鑰</string>
<string name="server_lab_stream_security">傳輸層安全性</string>
<string name="server_lab_allow_insecure">allowInsecure</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_stream_security">傳輸層安全性(tls)</string>
<string name="server_lab_allow_insecure">跳過證書驗證(allowInsecure)</string>
<string name="server_lab_address3">伺服器位址</string>
<string name="server_lab_port3">伺服器埠</string>
<string name="server_lab_id3">密碼</string>

View File

@@ -5,6 +5,7 @@
<item>aes-128-gcm</item>
<item>auto</item>
<item>none</item>
<item>zero</item>
</string-array>
<string-array name="ss_securitys" translatable="false">
<item>aes-256-cfb</item>
@@ -23,6 +24,7 @@
<item>ws</item>
<item>h2</item>
<item>quic</item>
<item>grpc</item>
</string-array>
<string-array name="headertypes" translatable="false">
@@ -98,6 +100,7 @@
<item>xtls-rprx-direct</item>
<item>xtls-rprx-direct-udp443</item>
<item>xtls-rprx-splice</item>
<item>xtls-rprx-splice-udp443</item>
</string-array>

View File

@@ -44,7 +44,7 @@
<string name="server_lab_more_function">more function</string>
<string name="server_lab_head_type">head type</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</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>