iOSに設定されているプロキシの自動構成スクリプトを使ってみたい

.NETのHttpWebRequestクラスに対してiOSWiFi接続に設定されている自動構成スクリプトの設定を適用するにはどうしたらいいかの調査。

.NET本来の方法

System.Net.WebRequest.GetSystemWebProxy()関数で取得すればあとは勝手にやってくれる模様。この方式をXamarin.iOSで使うと環境変数HTTP_PROXYをごにょごにょしているだけだった。これでは使えない。

iOS本来の方法

CFNetworkCopyProxiesForAutoConfigurationScript関数に対して自動構成スクリプトの内容とURLを渡せば適切なプロキシのアドレスを返してくれる模様。プロキシアドレスが取得できればそれをSystem.Net.WebProxyクラスに渡してやればよい。
これをXamarin.iOSで書いてみた。

string url = "http://www.google.co.jp";

// 自動構成スクリプトの内容は本来なら別の手段で取得するべき
string script = @"function FindProxyForURL(url,host) {
 if(isPlainHostName(host)||
    isInNet(host,""127.0.0.1"",""255.255.255.255""))
    return ""DIRECT"";
 else if(isPlainHostName(host) ||
    isInNet(host,""192.0.0.0"",""255.0.0.0""))
    return ""DIRECT"";
 else
    return ""PROXY myproxy:8888; DIRECT"";
}
";

MonoTouch.CoreFoundation.CFProxy[] proxy = MonoTouch.CoreFoundation.CFNetwork.GetProxiesForAutoConfigurationScript(new NSString(script), NSUrl.FromString(url));

この場合何故かCFNetwork.GetProxiesForAutoConfigurationScriptの内部のObjective-Cの段階で例外が発生してしまう。リフレクションで調べるとCFNetworkCopyProxiesForAutoConfigurationScript関数は引数が3つだが、Xamarin.iOSは引数を2つしか渡していない。これが原因だろうか(バグ?)。

ならばと強引に呼んでみた。

[DllImport("/System/Library/Frameworks/CFNetwork.framework/CFNetwork")]
static extern IntPtr CFNetworkCopyProxiesForAutoConfigurationScript(IntPtr proxyAutoConfigurationScript, IntPtr targetURL, IntPtr error);

// 自動構成スクリプトの内容は本来なら別の手段で取得するべき
string script = @"function FindProxyForURL(url,host) {
 if(isPlainHostName(host)||
    isInNet(host,""127.0.0.1"",""255.255.255.255""))
    return ""DIRECT"";
 else if(isPlainHostName(host) ||
    isInNet(host,""192.0.0.0"",""255.0.0.0""))
    return ""DIRECT"";
 else
    return ""PROXY myproxy:8888; DIRECT"";
}
";

NSObject error = new NSObject();
NSString aaa = new NSString(script);
NSUrl u = NSUrl.FromString(url);
IntPtr ret = CFNetworkCopyProxiesForAutoConfigurationScript(aaa.Handle, u.Handle, error.Handle);
NSArray array = new NSArray(ret);
NSDictionary[] dictionaryArray = NSArray.ArrayFromHandle<NSDictionary>(array.Handle);
array.Dispose();
System.Console.WriteLine("p={0}",dictionaryArray[0]); // => myproxy:8888

とりあえずプロキシアドレスを取得することができた。あとはこのコードを使用に耐えられるようにしなければならないが、

等を考えるとこのままではコードが足りなさすぎる。ここで時間切れ。