Closed Mygod closed 7 years ago
The configuration activity looks a great idea.
I'm not sure if JVM plugin could work on Android 4.4, as it requires per-app proxy feature of VPN service.
Let me dig into the whole proposal tomorrow. 😪
Hmm yeah native runnable plugins and JVM plugins might not work. We could put those off for now.
UPDATE: Added "Plugin platform versioning" section.
UPDATE: Changed "Possible problems" section.
Btw I'm planning to implement this at least after kcptun is migrated to SIP003, probably even later.
@madeye What do you think? If you need more time, meanwhile I can help you migrate kcptun to SIP003.
Just not sure JVM mode could work.No more questions about other part.
What about implement a SIP003 example for simple-obfs first?
Okay.
Some questions towards compatibility:
My answer:
Surprisingly, your answer == my answer. 😃
Updated this draft to use the new SS_PLUGIN_OPTIONS
in https://github.com/shadowsocks/shadowsocks-org/issues/28.
Interface for plugins in shadowsocks-android. See develop branch for more info.
Further development is blocked by https://github.com/shadowsocks/shadowsocks-libev/issues/1070, etc.
@madeye Please check out 70e0b3991d49e137a6b29cf783ab371f5e1fc5e0 and simple-obfs-android. There are >2k of code changes and at this point all the basic features are implemented and everything is just barely building. Nothing is guaranteed working. I wish to take a break here and please do some bugfixes for me. Thanks.
Thanks for the big changes... I will help to test and fix bugs.
Yeah I think it's safe to say that I have nuked almost every part of this repo at least once now.
I tried this change locally, but failed to load plugins. I saw fetchPlugins()
is correctly called but no plugin can be found in that function.
Am I missing anything here? (It's tested on Android 6.0 emulator and Google Pixel Android 7.1.1)
It's possible. I haven't even tested this code once. 🤣
Oh My God... 😓
Maybe querying a provider through category doesn't work? Let me try the old ways...
@madeye Okay let me get back to work now. 😅 Feel free to help me fix JNI related stuff since I'm not familiar with the native code base yet...
@madeye The problem is querying through category doesn't work. It seems only querying with action is supported.
EDIT: Updated doc.
@madeye Okay check it out now. Everything looks fine now except connection isn't still working. There are no traffic until E/shadowsocks: accept: Too many open files
after which huge outbound traffic is seen in the monitor. Full log. And I'm going to take another break now. :stuck_out_tongue:
I have also changed a lot of the original interface. Updating doc later.
P.S. Run sbt plugin/publish
to use it in simple-obfs-android. We'll deal with publishing it to public repository later.
@Mygod Great! I think now it's working. Please try d734e9f.
@madeye It's working now. We can make a new beta release when kcptun is ported as well.
@Mygod OK. BTW, I've added SIP003 to kcptun. It works well now with shadowsocks-libev.
@Mygod What about publishing android plugins in your own Google Play account? i think it would encourage more people developing plugins for this project.
I don't think that would make a big difference though. :stuck_out_tongue:
I'm working on native mode without binary copying now. After that I'll start creating kcptun-android. The important thing is native mode without binary copying requires plugin app has the same signature as host, so I'd rather not.
@madeye Hmm it turns out implementing native mode without binary copying is very convenient as long as the binary has execute permission for other (fortunately default mode for binary under lib is 755) and plugin app only need to implement 1 more method which returns executable path (it probably already does anyway). We don't even need shared user id and/or same signature. I guess we are done here for simple-obfs. (I will push the source code shortly after I post this comment)
For kcptun, there's one thing that makes it complicated. In 3.x we workaround a bug by restarting kcptun process whenever a network change is detected. This would be problematic when putting it in the current plugin platform. Here are some possible solutions:
Oh speaking of ss-local and ss-tunnel if we are using pdnsd for DNS, there will be two instances of plugin running simultaneously which is kind of a waste of resources. Any thoughts toward that?
I'm still worrying about passing file descriptors between different UIDs. If it really works, should it be a security issue of Android? 😈
A small enough autoexpire
could be a workaround. Besides, I'm thinking a "need-to-restart" parameter for plugin that shadowsocks needs to restart ss-local/ss-tunnel when the plugin is enabled and network changes.
The duplicates of one plugin is definitely a problem. I'm thinking of using other dns clients in the future, e.g. dns-over-https. If so, there is no need to use ss-tunnel
anymore.
ParcelFileDescriptor
?edns-client-subnet
, we will never worry about CDN related geo IP issue anymore.Some references for dns-over-https:
So maybe we should do the JVM mode for now? sigh
I see. But that requires a DNS-over-HTTPS-capable server. Fortunately there is. :raised_hands:
Currently, let's just restart ss-local/ss-tunnel...
I found something looks better: https://github.com/aarond10/https_dns_proxy
But I don't want to add a meta-data to a plugin named restart_ss_local_and_ss_tunnel_whenever_network_changes nor add kcptun as a special case. Both of them look ugly.
Hmm... Maybe JVM mode is more reasonable here.
It'd be best if we can fix this in kcptun. I'm going to copy simple-obfs-android for now...
kcptun-android has been built. Next, TODOs before we can finally close this issue:
plugin/doc.md
(or else?) and refine it;plugin/README.md
including fantastic "official" plugins and where to find them, developer's quick start and a link to doc.md
for technical details;Btw do you think we should create a configuration activity for kcptun as well? Currently this plugin looks exactly the same as 3.x, except that it's a plugin now.
It turns out to be a config related issue, fixed via https://github.com/shadowsocks/kcptun/commit/b2d702ff5e51cd62814f9046637baa5178452091
Let's submit to Sonatype now. I think it's ready.
@madeye Okay. Please follow this guide and create a ticket with your account (or maybe create a public one?). I think you should be able to do the plugin publishing since you're publishing the main app.
@Mygod Hmm.. How to create a public account?
@Mygod What about publishing the library in your account? I found it's quite complicated....
Let's just use your account to publish for now so that in the future we can push out updates to plugin lib and main app simultaneously... 😌
It seems that we need to merge the develop branch to master first. A pull request or I directly rebase to master?
Why's that?
It looks that I need to provide the path to git repo for Sonatype. Not sure if I can specify the branch name there.
Add a comment perhaps?
@madeye Okay I pushed everything to beta
branch. We can make a beta release after publishing our 0.0.1 library...
I'm not sure if I have publish the package correctly. But it seems to work now.
LGTM. Please checkout beta
branch and publish 0.0.1. :turkey:
Nice. Closing this.
Plugin should be bundled as an apk.
$PLUGIN_ID
in this draft corresponds to the executable name for the plugin in order to be cross-platform, e.g.simple-obfs
. An apk can have more than one plugins bundled. We don't care as long as they have different$PLUGIN_ID
. Duplicated plugins will be disabled so user has to uninstall them until there's only not more than one left.There is no restrictions/requirements on package name, but you're suggested to use
com.github.shadowsocks.plugin.$PLUGIN_ID
if it only contains a single plugin to prevent duplicate plugins.There will be a library to implement the base framework contrived in this draft and some example plugins for the ease of developers. :smile:
1. Plugin configuration
Plugins get their args configured via one of the following two options:
Configuration activity
If the plugin provides a configuration activity, it will be started when user picks your plugin and taps configure. It:
com.github.shadowsocks.plugin.$PLUGIN_ID.ACTION_CONFIGURE
;com.github.shadowsocks.plugin.EXTRA_OPTIONS
(allarg
as a single string) and display the old settings;com.github.shadowsocks.plugin.EXTRA_OPTIONS
;simple_obfs
,obfs
is a server setting andobfs_host
is a feature setting;Help activity/callback
If the plugin doesn't provide a configuration activity, it's highly recommended to provide a help message in the form of an Activity. It:
com.github.shadowsocks.plugin.$PLUGIN_ID.ACTION_HELP
;com.github.shadowsocks.plugin.EXTRA_OPTIONS
and display some more relevant information;com.github.shadowsocks.plugin.EXTRA_HELP_MESSAGE
in the data intent; (in this case, default dialog will be shown containing the message)simple_obfs
,obfs
is a server setting andobfs_host
is a feature setting.2. Plugin implementation
Every plugin can be either in native mode or JVM mode.
Native mode
In native mode, plugins are provided as native executables and
shadowsocks-libev
's plugin mode will be used.Every native mode plugin MUST have a content provider to provide the native executables (since they can exceed 1M which is the limit of Intent size) that:
android:label
andandroid:icon
; (may be configured byactivity
orapplication
)com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN
. (used for discovering plugins)android:authorities
set tocom.github.shadowsocks.plugin.$PLUGIN_ID
;query
that returns the file list which MUST include$PLUGIN_ID
when having these as arguments:uri = "content://com.github.shadowsocks.plugin.$PLUGIN_ID"
;projection = ["path"]
; (relative path, for exampleobfs-local
)selection = null
;selectionArgs = null
;sortOrder = null
;openFile
that for files returned inquery
,openFile
withmode = "r"
returns a validParcelFileDescriptor
for reading. For example,uri
can becontent://com.github.shadowsocks.plugin.kcptun/kcptun
.Native mode without binary copying
By taking advantage of
android:protectionLevel="signature"
, plugins that have the same signature as the base package (shadowsocks-android
in this case) should be able to be accessed directly byss-local
. If any native mode plugin wishes to support this mode, it must have an Activity which:com.github.shadowsocks.plugin.$PLUGIN_ID.ACTION_START
;com.github.shadowsocks.plugin.EXTRA_EXECUTABLE
with executable path (e.g./data/data/com.github.shadowsocks.plugin.kcptun/lib/libkcptun.so
).All plugins in this mode HAVE to support native mode with binary copy.
P.S. If this is implemented, "official" plugins will perform a tiny little bit better than "unofficial" ones.
JVM mode
Note: This can actually be hybrid mode since I'm not caring what you do under the hood. But this should probably be a good idea if your plugin was written in a JVM language.
Every JVM mode plugin MUST have a service intent filter that:
android:label
andandroid:icon
; (may be configured byactivity
orapplication
)com.github.shadowsocks.plugin.$PLUGIN_ID.ACTION_START
;com.github.shadowsocks.plugin.ACTION_JVM_PLUGIN
; (used for discovering plugins)onStartCommand(intent, flags, startId)
, where caller package name,server_ip
,server_port
andcom.github.shadowsocks.plugin.EXTRA_OPTIONS
will be passed via intent extra. Within 5000ms, it MUST broadcastcom.github.shadowsocks.plugin.ACTION_JVM_PLUGIN_READY
withlocal_port
(should be the result ofnew ServerSocket(0).getLocalPort()
) as extra orcom.github.shadowsocks.plugin.ACTION_JVM_PLUGIN_FAIL
with optional error message as string extra to caller package.stopService
will be called on target service. The plugin MUST gracefully handle this and terminate.Default configurations
If a plugin need to supply default configuration, it can put it under the ContentProvider/Service's meta-data tag, for example:
If you need to provide different default configurations for different plugins, you have to use multiple ContentProvider/Service. It should be easy to implement with sub-classing.
3. Plugin security considerations
Plugins are certified using package signatures and shadowsocks-android will consider these signatures as trusted:
A warning will be shown for untrusted plugins. No arbitrary restriction will be applied.
4. Plugin platform versioning
In order to be able to identify compatible and incompatible plugins, Semantic Versioning will be used.
Plugin app must include this in their application tag: (which should be automatically included if you are using our library)
5. Possible problems
-
should be changed into underscores_
. For example, the package name forobfs-local
would probably becom.github.shadowsocks.plugin.obfs_local
(if it turns out that we really can't use hyphens in package name).References
Opinions are welcome!