diff --git a/core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt b/core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt index 4882a3fe58..80d5cb2ee8 100644 --- a/core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt +++ b/core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt @@ -64,6 +64,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro } private var configFile: File? = null + private var socks5AuthFile: File? = null var trafficMonitor: TrafficMonitor? = null val plugin by lazy { PluginManager.init(PluginConfiguration(profile.plugin ?: "")) } @@ -86,6 +87,11 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro } config.put("dns", "unix://local_dns_path") config.put("mode", mode) + val socksPassword = DataStore.socksPassword + // compute auth file path before building JSON so the absolute path can be embedded + val authFile = if (socksPassword.isNotEmpty()) { + File(configFile.parentFile, "${configFile.name}_auth") + } else null config.put("locals", JSONArray().apply { // local SOCKS5 proxy put(JSONObject().apply { @@ -94,6 +100,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro put("local_udp_address", DataStore.listenAddress) put("local_udp_port", DataStore.portProxy) put("mode", mode) + if (authFile != null) put("socks5_auth_config_path", authFile.absolutePath) }) // local DNS proxy @@ -114,6 +121,21 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro }) configFile.writeText(config.toString()) + // write SOCKS5 auth config file if password protection is enabled + if (authFile != null) { + socks5AuthFile = authFile + authFile.writeText(JSONObject().apply { + put("password", JSONObject().apply { + put("users", JSONArray().apply { + put(JSONObject().apply { + put("user_name", "shadowsocks") + put("password", socksPassword) + }) + }) + }) + }.toString()) + } + // build the command line val cmd = arrayListOf( File((service as Context).applicationInfo.nativeLibraryDir, Executable.SS_LOCAL).absolutePath, @@ -143,5 +165,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro trafficMonitor = null configFile?.delete() // remove old config possibly in device storage configFile = null + socks5AuthFile?.delete() + socks5AuthFile = null } } diff --git a/core/src/main/java/com/github/shadowsocks/bg/VpnService.kt b/core/src/main/java/com/github/shadowsocks/bg/VpnService.kt index 5716d1fc09..17c58d7020 100644 --- a/core/src/main/java/com/github/shadowsocks/bg/VpnService.kt +++ b/core/src/main/java/com/github/shadowsocks/bg/VpnService.kt @@ -214,6 +214,13 @@ class VpnService : BaseVpnService(), BaseService.Interface { "--sock-path", "sock_path", "--dnsgw", "127.0.0.1:${DataStore.portLocalDns}", "--loglevel", "warning") + val socksPassword = DataStore.socksPassword + if (socksPassword.isNotEmpty()) { + cmd += "--username" + cmd += "shadowsocks" + cmd += "--password" + cmd += socksPassword + } if (profile.ipv6) { cmd += "--netif-ip6addr" cmd += PRIVATE_VLAN6_ROUTER diff --git a/core/src/main/java/com/github/shadowsocks/preference/DataStore.kt b/core/src/main/java/com/github/shadowsocks/preference/DataStore.kt index 5f3a772bf4..44dac24343 100644 --- a/core/src/main/java/com/github/shadowsocks/preference/DataStore.kt +++ b/core/src/main/java/com/github/shadowsocks/preference/DataStore.kt @@ -76,6 +76,9 @@ object DataStore : OnPreferenceDataStoreChangeListener { var portTransproxy: Int get() = getLocalPort(Key.portTransproxy, 8200) set(value) = publicStore.putString(Key.portTransproxy, value.toString()) + var socksPassword: String + get() = publicStore.getString(Key.socksPassword) ?: "" + set(value) = publicStore.putString(Key.socksPassword, value) /** * Initialize settings that have complicated default values. diff --git a/core/src/main/java/com/github/shadowsocks/preference/EditTextPreferenceModifiers.kt b/core/src/main/java/com/github/shadowsocks/preference/EditTextPreferenceModifiers.kt index 1385c77ed5..8c09f5fbed 100644 --- a/core/src/main/java/com/github/shadowsocks/preference/EditTextPreferenceModifiers.kt +++ b/core/src/main/java/com/github/shadowsocks/preference/EditTextPreferenceModifiers.kt @@ -22,6 +22,7 @@ package com.github.shadowsocks.preference import android.graphics.Typeface import android.text.InputFilter +import android.text.InputType import android.view.inputmethod.EditorInfo import android.widget.EditText import androidx.preference.EditTextPreference @@ -33,6 +34,13 @@ object EditTextPreferenceModifiers { } } + object Password : EditTextPreference.OnBindEditTextListener { + override fun onBindEditText(editText: EditText) { + editText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD + editText.setSingleLine() + } + } + object Port : EditTextPreference.OnBindEditTextListener { private val portLengthFilter = arrayOf(InputFilter.LengthFilter(5)) diff --git a/core/src/main/java/com/github/shadowsocks/utils/Constants.kt b/core/src/main/java/com/github/shadowsocks/utils/Constants.kt index 52b6de20ec..0644672ad3 100644 --- a/core/src/main/java/com/github/shadowsocks/utils/Constants.kt +++ b/core/src/main/java/com/github/shadowsocks/utils/Constants.kt @@ -40,6 +40,7 @@ object Key { const val portProxy = "portProxy" const val portLocalDns = "portLocalDns" const val portTransproxy = "portTransproxy" + const val socksPassword = "socksPassword" const val route = "route" diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 04ed7d9d67..87071c002e 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -14,6 +14,8 @@ SOCKS5 proxy port Local DNS port Transproxy port + SOCKS5 proxy password + Set a password to protect the local SOCKS5 proxy from unauthorized use Remote DNS %1$s↑\t%2$s↓ diff --git a/mobile/src/main/java/com/github/shadowsocks/GlobalSettingsPreferenceFragment.kt b/mobile/src/main/java/com/github/shadowsocks/GlobalSettingsPreferenceFragment.kt index cc7ac91647..2b503a6696 100644 --- a/mobile/src/main/java/com/github/shadowsocks/GlobalSettingsPreferenceFragment.kt +++ b/mobile/src/main/java/com/github/shadowsocks/GlobalSettingsPreferenceFragment.kt @@ -59,6 +59,7 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() { portLocalDns.setOnBindEditTextListener(EditTextPreferenceModifiers.Port) val portTransproxy = findPreference(Key.portTransproxy)!! portTransproxy.setOnBindEditTextListener(EditTextPreferenceModifiers.Port) + val socksPassword = findPreference(Key.socksPassword)!! val onServiceModeChange = Preference.OnPreferenceChangeListener { _, newValue -> portTransproxy.isEnabled = newValue as String? == Key.modeTransproxy true @@ -68,6 +69,7 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() { serviceMode.isEnabled = stopped portProxy.isEnabled = stopped portLocalDns.isEnabled = stopped + socksPassword.isEnabled = stopped if (stopped) onServiceModeChange.onPreferenceChange(serviceMode, DataStore.serviceMode) else { portTransproxy.isEnabled = false } diff --git a/mobile/src/main/res/xml/pref_global.xml b/mobile/src/main/res/xml/pref_global.xml index 9a4808d8b3..29b3369332 100644 --- a/mobile/src/main/res/xml/pref_global.xml +++ b/mobile/src/main/res/xml/pref_global.xml @@ -34,4 +34,10 @@ app:key="portTransproxy" app:title="@string/port_transproxy" app:useSimpleSummaryProvider="true"/> + diff --git a/tv/src/main/java/com/github/shadowsocks/tv/MainPreferenceFragment.kt b/tv/src/main/java/com/github/shadowsocks/tv/MainPreferenceFragment.kt index 198245f728..182eecead0 100644 --- a/tv/src/main/java/com/github/shadowsocks/tv/MainPreferenceFragment.kt +++ b/tv/src/main/java/com/github/shadowsocks/tv/MainPreferenceFragment.kt @@ -56,6 +56,7 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo private lateinit var portProxy: EditTextPreference private lateinit var portLocalDns: EditTextPreference private lateinit var portTransproxy: EditTextPreference + private lateinit var socksPassword: EditTextPreference private val onServiceModeChange = Preference.OnPreferenceChangeListener { _, newValue -> portTransproxy.isEnabled = newValue as String? == Key.modeTransproxy true @@ -101,6 +102,7 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo shareOverLan.isEnabled = stopped portProxy.isEnabled = stopped portLocalDns.isEnabled = stopped + socksPassword.isEnabled = stopped if (stopped) onServiceModeChange.onPreferenceChange(serviceMode, DataStore.serviceMode) else { portTransproxy.isEnabled = false } @@ -141,6 +143,8 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo portLocalDns.setOnBindEditTextListener(EditTextPreferenceModifiers.Port) portTransproxy = findPreference(Key.portTransproxy)!! portTransproxy.setOnBindEditTextListener(EditTextPreferenceModifiers.Port) + socksPassword = findPreference(Key.socksPassword)!! + socksPassword.setOnBindEditTextListener(EditTextPreferenceModifiers.Password) serviceMode.onPreferenceChangeListener = onServiceModeChange findPreference(Key.about)!!.summary = getString(R.string.about_title, BuildConfig.VERSION_NAME) diff --git a/tv/src/main/res/xml/pref_main.xml b/tv/src/main/res/xml/pref_main.xml index 1e60152712..67e8dc963d 100644 --- a/tv/src/main/res/xml/pref_main.xml +++ b/tv/src/main/res/xml/pref_main.xml @@ -48,6 +48,11 @@ app:key="portTransproxy" app:title="@string/port_transproxy" app:useSimpleSummaryProvider="true"/> +