Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
com.ccwangluo.accelerator
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
sheteng
com.ccwangluo.accelerator
Commits
6d1f9310
Unverified
Commit
6d1f9310
authored
Feb 01, 2019
by
Mygod
Committed by
GitHub
Feb 01, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #3 from Mygod/dns-server-no-dnsgw
DNS over UDP and various other improvements
parents
5425b068
c9ba87da
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
47 additions
and
103 deletions
+47
-103
core/src/main/java/com/github/shadowsocks/bg/Executable.kt
core/src/main/java/com/github/shadowsocks/bg/Executable.kt
+1
-2
core/src/main/java/com/github/shadowsocks/bg/LocalDnsService.kt
...rc/main/java/com/github/shadowsocks/bg/LocalDnsService.kt
+5
-3
core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt
.../src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt
+2
-2
core/src/main/java/com/github/shadowsocks/bg/TransproxyService.kt
.../main/java/com/github/shadowsocks/bg/TransproxyService.kt
+0
-15
core/src/main/java/com/github/shadowsocks/bg/VpnService.kt
core/src/main/java/com/github/shadowsocks/bg/VpnService.kt
+13
-22
core/src/main/java/com/github/shadowsocks/net/HttpsTest.kt
core/src/main/java/com/github/shadowsocks/net/HttpsTest.kt
+5
-1
core/src/main/java/com/github/shadowsocks/net/SocketListener.kt
...rc/main/java/com/github/shadowsocks/net/SocketListener.kt
+14
-1
core/src/main/java/com/github/shadowsocks/preference/DataStore.kt
.../main/java/com/github/shadowsocks/preference/DataStore.kt
+1
-2
core/src/main/java/com/github/shadowsocks/utils/Utils.kt
core/src/main/java/com/github/shadowsocks/utils/Utils.kt
+0
-17
core/src/main/jni/Android.mk
core/src/main/jni/Android.mk
+3
-35
core/src/main/jni/badvpn
core/src/main/jni/badvpn
+1
-1
core/src/main/res/values/strings.xml
core/src/main/res/values/strings.xml
+2
-2
No files found.
core/src/main/java/com/github/shadowsocks/bg/Executable.kt
View file @
6d1f9310
...
...
@@ -33,11 +33,10 @@ import java.io.FileNotFoundException
object
Executable
{
const
val
REDSOCKS
=
"libredsocks.so"
const
val
SS_LOCAL
=
"libss-local.so"
const
val
SS_TUNNEL
=
"libss-tunnel.so"
const
val
TUN2SOCKS
=
"libtun2socks.so"
const
val
OVERTURE
=
"liboverture.so"
private
val
EXECUTABLES
=
setOf
(
SS_LOCAL
,
SS_TUNNEL
,
REDSOCKS
,
TUN2SOCKS
,
OVERTURE
)
private
val
EXECUTABLES
=
setOf
(
SS_LOCAL
,
REDSOCKS
,
TUN2SOCKS
,
OVERTURE
)
fun
killAll
()
{
for
(
process
in
File
(
"/proc"
).
listFiles
{
_
,
name
->
TextUtils
.
isDigitsOnly
(
name
)
})
{
...
...
core/src/main/java/com/github/shadowsocks/bg/LocalDnsService.kt
View file @
6d1f9310
...
...
@@ -28,6 +28,7 @@ import com.github.shadowsocks.net.Socks5Endpoint
import
com.github.shadowsocks.net.Subnet
import
com.github.shadowsocks.preference.DataStore
import
java.net.InetSocketAddress
import
java.net.URI
import
java.util.*
object
LocalDnsService
{
...
...
@@ -43,11 +44,12 @@ object LocalDnsService {
interface
Interface
:
BaseService
.
Interface
{
override
suspend
fun
startProcesses
()
{
super
.
startProcesses
()
val
data
=
data
val
profile
=
data
.
proxy
!!
.
profile
if
(!
profile
.
udpdns
)
servers
[
this
]
=
LocalDnsServer
(
this
::
resolver
,
Socks5Endpoint
(
profile
.
remoteDns
.
split
(
","
).
first
(),
53
),
val
dns
=
URI
(
"dns://${profile.remoteDns}"
)
servers
[
this
]
=
LocalDnsServer
(
this
::
resolver
,
Socks5Endpoint
(
dns
.
host
,
if
(
dns
.
port
<
0
)
53
else
dns
.
port
),
DataStore
.
proxyAddress
).
apply
{
tcp
=
!
profile
.
udpdns
when
(
profile
.
route
)
{
Acl
.
BYPASS_CHN
,
Acl
.
BYPASS_LAN_CHN
,
Acl
.
GFWLIST
,
Acl
.
CUSTOM_RULES
->
{
remoteDomainMatcher
=
googleApisTester
...
...
core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt
View file @
6d1f9310
...
...
@@ -47,7 +47,7 @@ import java.security.MessageDigest
* This class sets up environment for ss-local.
*/
class
ProxyInstance
(
val
profile
:
Profile
,
private
val
route
:
String
=
profile
.
route
)
:
AutoCloseable
{
var
configFile
:
File
?
=
null
private
var
configFile
:
File
?
=
null
var
trafficMonitor
:
TrafficMonitor
?
=
null
private
val
plugin
=
PluginConfiguration
(
profile
.
plugin
?:
""
).
selectedOptions
val
pluginPath
by
lazy
{
PluginManager
.
init
(
plugin
)
}
...
...
@@ -115,7 +115,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
}
// for UDP profile, it's only going to operate in UDP relay mode-only so this flag has no effect
if
(
profile
.
udpdns
)
cmd
+=
"-D"
if
(
profile
.
route
==
Acl
.
ALL
||
profile
.
route
==
Acl
.
BYPASS_LAN
)
cmd
+=
"-D"
if
(
DataStore
.
tcpFastOpen
)
cmd
+=
"--fast-open"
...
...
core/src/main/java/com/github/shadowsocks/bg/TransproxyService.kt
View file @
6d1f9310
...
...
@@ -36,20 +36,6 @@ class TransproxyService : Service(), LocalDnsService.Interface {
override
fun
onStartCommand
(
intent
:
Intent
?,
flags
:
Int
,
startId
:
Int
):
Int
=
super
<
LocalDnsService
.
Interface
>.
onStartCommand
(
intent
,
flags
,
startId
)
private
fun
startDNSTunnel
()
{
val
proxy
=
data
.
proxy
!!
val
cmd
=
arrayListOf
(
File
(
applicationInfo
.
nativeLibraryDir
,
Executable
.
SS_TUNNEL
).
absolutePath
,
"-t"
,
"10"
,
"-b"
,
DataStore
.
listenAddress
,
"-u"
,
"-l"
,
DataStore
.
portLocalDns
.
toString
(),
// ss-tunnel listens on the same port as overture
"-L"
,
proxy
.
profile
.
remoteDns
.
split
(
","
).
first
().
trim
()
+
":53"
,
// config is already built by BaseService.Interface
"-c"
,
(
data
.
udpFallback
?:
proxy
).
configFile
!!
.
absolutePath
)
if
(
DataStore
.
tcpFastOpen
)
cmd
+=
"--fast-open"
data
.
processes
!!
.
start
(
cmd
)
}
private
fun
startRedsocksDaemon
()
{
File
(
Core
.
deviceStorage
.
noBackupFilesDir
,
"redsocks.conf"
).
writeText
(
"""base {
log_debug = off;
...
...
@@ -73,7 +59,6 @@ redsocks {
override
suspend
fun
startProcesses
()
{
startRedsocksDaemon
()
super
.
startProcesses
()
if
(
data
.
proxy
!!
.
profile
.
udpdns
)
startDNSTunnel
()
}
override
fun
onDestroy
()
{
...
...
core/src/main/java/com/github/shadowsocks/bg/VpnService.kt
View file @
6d1f9310
...
...
@@ -39,7 +39,6 @@ import com.github.shadowsocks.net.DefaultNetworkListener
import
com.github.shadowsocks.net.Subnet
import
com.github.shadowsocks.preference.DataStore
import
com.github.shadowsocks.utils.Key
import
com.github.shadowsocks.utils.parseNumericAddress
import
com.github.shadowsocks.utils.printLog
import
kotlinx.coroutines.delay
import
java.io.Closeable
...
...
@@ -52,8 +51,10 @@ import android.net.VpnService as BaseVpnService
class
VpnService
:
BaseVpnService
(),
LocalDnsService
.
Interface
{
companion
object
{
private
const
val
VPN_MTU
=
1500
private
const
val
PRIVATE_VLAN
=
"172.19.0.%s"
private
const
val
PRIVATE_VLAN6
=
"fdfe:dcba:9876::%s"
private
const
val
PRIVATE_VLAN4_CLIENT
=
"172.19.0.1"
private
const
val
PRIVATE_VLAN4_ROUTER
=
"172.19.0.2"
private
const
val
PRIVATE_VLAN6_CLIENT
=
"fdfe:dcba:9876::1"
private
const
val
PRIVATE_VLAN6_ROUTER
=
"fdfe:dcba:9876::2"
/**
* https://android.googlesource.com/platform/prebuilts/runtime/+/94fec32/appcompat/hiddenapi-light-greylist.txt#9466
...
...
@@ -136,9 +137,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
override
suspend
fun
startProcesses
()
{
worker
=
ProtectWorker
().
apply
{
start
()
}
super
.
startProcesses
()
sendFd
(
startVpn
())
}
...
...
@@ -153,12 +152,11 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
.
setConfigureIntent
(
Core
.
configureIntent
(
this
))
.
setSession
(
profile
.
formattedName
)
.
setMtu
(
VPN_MTU
)
.
addAddress
(
PRIVATE_VLAN
.
format
(
Locale
.
ENGLISH
,
"1"
),
24
)
profile
.
remoteDns
.
split
(
","
).
forEach
{
builder
.
addDnsServer
(
it
.
trim
())
}
.
addAddress
(
PRIVATE_VLAN4_CLIENT
,
30
)
.
addDnsServer
(
PRIVATE_VLAN4_ROUTER
)
if
(
profile
.
ipv6
)
{
builder
.
addAddress
(
PRIVATE_VLAN6
.
format
(
Locale
.
ENGLISH
,
"1"
)
,
126
)
builder
.
addAddress
(
PRIVATE_VLAN6
_CLIENT
,
126
)
builder
.
addRoute
(
"::"
,
0
)
}
...
...
@@ -179,13 +177,9 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
when
(
profile
.
route
)
{
Acl
.
ALL
,
Acl
.
BYPASS_CHN
,
Acl
.
CUSTOM_RULES
->
builder
.
addRoute
(
"0.0.0.0"
,
0
)
else
->
{
resources
.
getStringArray
(
R
.
array
.
bypass_private_route
).
forEach
{
val
subnet
=
Subnet
.
fromString
(
it
)
!!
builder
.
addRoute
(
subnet
.
address
.
hostAddress
,
subnet
.
prefixSize
)
}
profile
.
remoteDns
.
split
(
","
).
mapNotNull
{
it
.
trim
().
parseNumericAddress
()
}
.
forEach
{
builder
.
addRoute
(
it
,
it
.
address
.
size
shl
3
)
}
else
->
resources
.
getStringArray
(
R
.
array
.
bypass_private_route
).
forEach
{
val
subnet
=
Subnet
.
fromString
(
it
)
!!
builder
.
addRoute
(
subnet
.
address
.
hostAddress
,
subnet
.
prefixSize
)
}
}
...
...
@@ -197,22 +191,19 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
val
fd
=
conn
.
fd
val
cmd
=
arrayListOf
(
File
(
applicationInfo
.
nativeLibraryDir
,
Executable
.
TUN2SOCKS
).
absolutePath
,
"--netif-ipaddr"
,
PRIVATE_VLAN
.
format
(
Locale
.
ENGLISH
,
"2"
)
,
"--netif-ipaddr"
,
PRIVATE_VLAN
4_ROUTER
,
"--netif-netmask"
,
"255.255.255.0"
,
"--socks-server-addr"
,
"${DataStore.listenAddress}:${DataStore.portProxy}"
,
"--tunfd"
,
fd
.
toString
(),
"--tunmtu"
,
VPN_MTU
.
toString
(),
"--sock-path"
,
"sock_path"
,
"--dnsgw"
,
"127.0.0.1:${DataStore.portLocalDns}"
,
"--loglevel"
,
"3"
)
if
(
profile
.
ipv6
)
{
cmd
+=
"--netif-ip6addr"
cmd
+=
PRIVATE_VLAN6
.
format
(
Locale
.
ENGLISH
,
"2"
)
cmd
+=
PRIVATE_VLAN6
_ROUTER
}
cmd
+=
"--enable-udprelay"
if
(!
profile
.
udpdns
)
{
cmd
+=
"--dnsgw"
cmd
+=
"127.0.0.1:${DataStore.portLocalDns}"
}
data
.
processes
!!
.
start
(
cmd
,
onRestartCallback
=
{
try
{
sendFd
(
conn
.
fileDescriptor
)
...
...
core/src/main/java/com/github/shadowsocks/net/HttpsTest.kt
View file @
6d1f9310
...
...
@@ -20,6 +20,7 @@
package
com.github.shadowsocks.net
import
android.os.Build
import
android.os.SystemClock
import
androidx.lifecycle.MutableLiveData
import
androidx.lifecycle.ViewModel
...
...
@@ -29,12 +30,12 @@ import com.github.shadowsocks.acl.Acl
import
com.github.shadowsocks.core.R
import
com.github.shadowsocks.preference.DataStore
import
com.github.shadowsocks.utils.Key
import
com.github.shadowsocks.utils.responseLength
import
kotlinx.coroutines.*
import
java.io.IOException
import
java.net.HttpURLConnection
import
java.net.Proxy
import
java.net.URL
import
java.net.URLConnection
/**
* Based on: https://android.googlesource.com/platform/frameworks/base/+/b19a838/services/core/java/com/android/server/connectivity/NetworkMonitor.java#1071
...
...
@@ -116,4 +117,7 @@ class HttpsTest : ViewModel() {
cancelTest
()
status
.
value
=
Status
.
Idle
}
private
val
URLConnection
.
responseLength
:
Long
get
()
=
if
(
Build
.
VERSION
.
SDK_INT
>=
24
)
contentLengthLong
else
contentLength
.
toLong
()
}
core/src/main/java/com/github/shadowsocks/net/SocketListener.kt
View file @
6d1f9310
package
com.github.shadowsocks.net
import
com.github.shadowsocks.utils.shutdown
import
android.system.ErrnoException
import
android.system.Os
import
android.system.OsConstants
import
java.io.FileDescriptor
import
java.io.IOException
abstract
class
SocketListener
(
name
:
String
)
:
Thread
(
name
),
AutoCloseable
{
protected
abstract
val
fileDescriptor
:
FileDescriptor
@Volatile
protected
var
running
=
true
private
fun
FileDescriptor
.
shutdown
()
{
// see also: https://issuetracker.google.com/issues/36945762#comment15
if
(
valid
())
try
{
Os
.
shutdown
(
this
,
OsConstants
.
SHUT_RDWR
)
}
catch
(
e
:
ErrnoException
)
{
// suppress fd inactive or already closed
if
(
e
.
errno
!=
OsConstants
.
EBADF
&&
e
.
errno
!=
OsConstants
.
ENOTCONN
)
throw
IOException
(
e
)
}
}
override
fun
close
()
{
running
=
false
fileDescriptor
.
shutdown
()
...
...
core/src/main/java/com/github/shadowsocks/preference/DataStore.kt
View file @
6d1f9310
...
...
@@ -25,13 +25,12 @@ import androidx.preference.PreferenceDataStore
import
com.github.shadowsocks.Core
import
com.github.shadowsocks.database.PrivateDatabase
import
com.github.shadowsocks.database.PublicDatabase
import
com.github.shadowsocks.net.TcpFastOpen
import
com.github.shadowsocks.utils.DirectBoot
import
com.github.shadowsocks.utils.Key
import
com.github.shadowsocks.net.TcpFastOpen
import
com.github.shadowsocks.utils.parsePort
import
java.net.InetSocketAddress
import
java.net.NetworkInterface
import
java.net.Proxy
import
java.net.SocketException
object
DataStore
:
OnPreferenceDataStoreChangeListener
{
...
...
core/src/main/java/com/github/shadowsocks/utils/Utils.kt
View file @
6d1f9310
...
...
@@ -30,17 +30,13 @@ import android.graphics.BitmapFactory
import
android.graphics.ImageDecoder
import
android.net.Uri
import
android.os.Build
import
android.system.ErrnoException
import
android.system.Os
import
android.system.OsConstants
import
android.util.TypedValue
import
androidx.annotation.AttrRes
import
androidx.preference.Preference
import
com.crashlytics.android.Crashlytics
import
java.io.FileDescriptor
import
java.io.IOException
import
java.net.InetAddress
import
java.net.URLConnection
private
val
parseNumericAddress
by
lazy
{
InetAddress
::
class
.
java
.
getDeclaredMethod
(
"parseNumericAddress"
,
String
::
class
.
java
).
apply
{
...
...
@@ -55,16 +51,6 @@ private val parseNumericAddress by lazy {
fun
String
?.
parseNumericAddress
():
InetAddress
?
=
Os
.
inet_pton
(
OsConstants
.
AF_INET
,
this
)
?:
Os
.
inet_pton
(
OsConstants
.
AF_INET6
,
this
)
?.
let
{
parseNumericAddress
.
invoke
(
null
,
this
)
as
InetAddress
}
fun
FileDescriptor
.
shutdown
()
{
// see also: https://issuetracker.google.com/issues/36945762#comment15
if
(
valid
())
try
{
Os
.
shutdown
(
this
,
OsConstants
.
SHUT_RDWR
)
}
catch
(
e
:
ErrnoException
)
{
// suppress fd inactive or already closed
if
(
e
.
errno
!=
OsConstants
.
EBADF
&&
e
.
errno
!=
OsConstants
.
ENOTCONN
)
throw
IOException
(
e
)
}
}
fun
parsePort
(
str
:
String
?,
default
:
Int
,
min
:
Int
=
1025
):
Int
{
val
value
=
str
?.
toIntOrNull
()
?:
default
return
if
(
value
<
min
||
value
>
65535
)
default
else
value
...
...
@@ -74,9 +60,6 @@ fun broadcastReceiver(callback: (Context, Intent) -> Unit): BroadcastReceiver =
override
fun
onReceive
(
context
:
Context
,
intent
:
Intent
)
=
callback
(
context
,
intent
)
}
val
URLConnection
.
responseLength
:
Long
get
()
=
if
(
Build
.
VERSION
.
SDK_INT
>=
24
)
contentLengthLong
else
contentLength
.
toLong
()
fun
ContentResolver
.
openBitmap
(
uri
:
Uri
)
=
if
(
Build
.
VERSION
.
SDK_INT
>=
28
)
ImageDecoder
.
decodeBitmap
(
ImageDecoder
.
createSource
(
this
,
uri
))
else
BitmapFactory
.
decodeStream
(
openInputStream
(
uri
))
...
...
core/src/main/jni/Android.mk
View file @
6d1f9310
...
...
@@ -278,39 +278,6 @@ LOCAL_LDLIBS := -llog
include
$(BUILD_SHARED_EXECUTABLE)
########################################################
## shadowsocks-libev tunnel
########################################################
include
$(CLEAR_VARS)
SHADOWSOCKS_SOURCES
:=
tunnel.c
\
cache.c udprelay.c utils.c netutils.c json.c jconf.c
\
crypto.c aead.c stream.c base64.c
\
plugin.c ppbloom.c
\
android.c
LOCAL_MODULE
:=
ss-tunnel
LOCAL_SRC_FILES
:=
$(
addprefix
shadowsocks-libev/src/,
$(SHADOWSOCKS_SOURCES)
)
LOCAL_CFLAGS
:=
-Wall
-fno-strict-aliasing
-DMODULE_TUNNEL
\
-DUSE_CRYPTO_MBEDTLS
-DHAVE_CONFIG_H
-DSSTUNNEL_JNI
\
-DCONNECT_IN_PROGRESS
=
EINPROGRESS
\
-I
$(LOCAL_PATH)
/libancillary
\
-I
$(LOCAL_PATH)
/include
\
-I
$(LOCAL_PATH)
/libsodium/src/libsodium/include
\
-I
$(LOCAL_PATH)
/libsodium/src/libsodium/include/sodium
\
-I
$(LOCAL_PATH)
/mbedtls/include
\
-I
$(LOCAL_PATH)
/libev
\
-I
$(LOCAL_PATH)
/shadowsocks-libev/libcork/include
\
-I
$(LOCAL_PATH)
/shadowsocks-libev/libbloom
\
-I
$(LOCAL_PATH)
/include/shadowsocks-libev
LOCAL_STATIC_LIBRARIES
:=
libev libmbedtls libsodium libcork libbloom libancillary
LOCAL_LDLIBS
:=
-llog
include
$(BUILD_SHARED_EXECUTABLE)
########################################################
## tun2socks
########################################################
...
...
@@ -358,7 +325,6 @@ TUN2SOCKS_SOURCES := \
flow/PacketProtoDecoder.c
\
socksclient/BSocksClient.c
\
tuntap/BTap.c
\
lwip/src/core/timers.c
\
lwip/src/core/udp.c
\
lwip/src/core/memp.c
\
lwip/src/core/init.c
\
...
...
@@ -367,14 +333,16 @@ TUN2SOCKS_SOURCES := \
lwip/src/core/tcp_out.c
\
lwip/src/core/netif.c
\
lwip/src/core/def.c
\
lwip/src/core/ip.c
\
lwip/src/core/mem.c
\
lwip/src/core/tcp_in.c
\
lwip/src/core/stats.c
\
lwip/src/core/inet_chksum.c
\
lwip/src/core/timeouts.c
\
lwip/src/core/ipv4/icmp.c
\
lwip/src/core/ipv4/igmp.c
\
lwip/src/core/ipv4/ip4_addr.c
\
lwip/src/core/ipv4/ip_frag.c
\
lwip/src/core/ipv4/ip
4
_frag.c
\
lwip/src/core/ipv4/ip4.c
\
lwip/src/core/ipv4/autoip.c
\
lwip/src/core/ipv6/ethip6.c
\
...
...
badvpn
@
ba4c45d6
Subproject commit
4c0050878618423da53e7e335fbb55ec2af6be06
Subproject commit
ba4c45d6b4d30fd6b2557e92a770ccae12ec9257
core/src/main/res/values/strings.xml
View file @
6d1f9310
...
...
@@ -57,8 +57,8 @@
<string
name=
"tcp_fastopen_summary"
>
Toggling might require ROOT permission
</string>
<string
name=
"tcp_fastopen_summary_unsupported"
>
Unsupported kernel version: %s
<
3.7.1
</string>
<string
name=
"tcp_fastopen_failure"
>
Toggle failed
</string>
<string
name=
"udp_dns"
>
DNS Forwarding
</string>
<string
name=
"udp_dns_summary"
>
Forward all DNS requests to remot
e
</string>
<string
name=
"udp_dns"
>
Send DNS over UDP
</string>
<string
name=
"udp_dns_summary"
>
Requires UDP forwarding on server sid
e
</string>
<string
name=
"udp_fallback"
>
UDP Fallback
</string>
<!-- notification category -->
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment