"ProxyDroid"はHTTP(S), SOCKS4, SOCKS5に対応したプロキシ設定アプリです。
root化前提ですが、Android端末内のアプリ全てに対して強制的に外部HTTP(S)/SOCKS4,5プロキシを通すように設定できます。
本記事では、その裏舞台を簡単に紹介します。
幸いなことにProxyDroidはソースコードが公開されています。ライセンスはGNU GPL v3です。
早速hgでリポジトリをcloneして中身を確認してみると、"assets"ディレクトリの中に興味深いファイルが見つかりました。
assets/ cntlm iptables proxy.sh redsocks tproxy
これらはインストール時に以下のディレクトリにコピーされます。
/data/data/org.proxydroid/
この辺の処理はorg.proxydroid.ProxyDroidのrecovery(), CopyAssets(), onCreate()辺りを参照してみてください。
では1つずつ見ていきます。
HTTP(S), SOCKS4/5 の透過型Proxyです。
ProxyDroidから起動されるシェルスクリプトで、引数に応じたredsocks用の設定ファイルを生成し、redsocksを起動します。
これ・・・だと思います。多分。NTLM認証用のHTTPプロキシのようです。
"tproxy"で検索すると似たようなのが結構出てくるので、ちょっとアタリがつけづらいのですが・・・多分これかな?
ちょっとコマンドラインオプションが異なるようですが。
いずれにせよ、透過型プロキシの一種であることは確かなようです。
いずれもARM用にコンパイルされているようです。
iptablesと透過型プロキシが同梱されている時点で気づいた人も居ると思いますが、ProxyDroidのやっていることは以下のとおりです。
図にするとこんな感じです:
Androidアプリ <-> Kernel <-> 透過型プロキシ <-> 外部プロキシ <-> インターネット
iptablesのコマンド実行や、上で紹介したツールの実行は org.proxydroid.ProxyDroidService クラスの enableProxy() メソッドで処理されています。実行されるコマンドラインについてはちゃんとログ出力されてますので、LogCatなどで確かめてみてください。
iptablesでroot権限が必要で、例えば org.proxydroid.ProxyDroidService クラスの runRootCommand(String command) メソッドでは以下のように処理しています。
process = Runtime.getRuntime().exec("su"); os = new DataOutputStream(process.getOutputStream()); os.writeBytes(command + "\n"); os.writeBytes("exit\n"); os.flush(); process.waitFor();
ProxyDroidがiptablesで強制的に透過型プロキシにルーティングしているのは、80番や443番など標準的なポート番号を使用している場合に限ります。従って標準以外の独自ポート番号を使用しているHTTP通信の場合は、直接インターネットに出て行ってしまい、外部Proxyを通せない可能性が高いです。
もしそのようなアプリにも透過型プロキシを適用させたいのであれば、ProxyDroidのソースを取得しで独自にビルドすればOKです。
試してないので断言できないのですが、多分 enableProxy() の中でiptablesの設定コマンドを実行してますので、適当にコピペしてポート番号を書き換えればOKだと思います。
あと、バージョン2.0.4ではローカルIPへの通信がProxyに流れない現象が見られました。enableProxy()の中で
if (intranetAddr == null || intranetAddr.equals("")) { // Reference: // http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces if (localIp != null) { String[] prefix = localIp.split("\\."); if (prefix.length == 4) { String intranet = localIp; if (localIp.startsWith("192.168.")) intranet = "192.168.0.0/16"; else if (localIp.startsWith("10.")) intranet = "10.0.0.0/8"; else if (localIp.startsWith("172.")) { int prefix2 = Integer.valueOf(prefix[1]); if (prefix2 <= 31 && prefix2 >= 16) { intranet = "172.16.0.0/12"; } } rules = rules.replace("-p tcp", "-p tcp " + "! -d " + intranet); } } } else { rules = rules.replace("-p tcp", "-p tcp " + "! -d " + intranetAddr); }
この辺の処理だと思います。一応画面上にもintranetAddrを指定する箇所があります。
色々弄ってみたのですが、結局上のifブロクをelseも含めてまるごとコメントアウトして、ローカルIPについても強制的にProxyを通すように修正してビルドしたのを使ってます。
当面の間、Android 2.x系ではHTTPプロキシ設定は公式にはサポートされない状況が続くと思います。
もしもroot化が可能であれば、ProxyDroidを使うことで無料で外部HTTP(S), SOCKS4, 5のプロキシを設定可能です。
しかもオープンソースですので、独自に改造してiptablesや透過型プロキシの設定を修正できます。
以上、ProxyDroidの舞台裏のごく簡単な紹介でした。