4 // Author: Jeffrey Stedfast <jeff@xamarin.com>
6 // Copyright (c) 2012-2014 Xamarin Inc.
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 using System.Collections.Generic;
29 using System.Runtime.InteropServices;
30 using System.Threading;
34 internal class CFObject : IDisposable
36 public const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
37 const string SystemLibrary = "/usr/lib/libSystem.dylib";
39 [DllImport (SystemLibrary)]
40 public static extern IntPtr dlopen (string path, int mode);
42 [DllImport (SystemLibrary)]
43 public static extern IntPtr dlsym (IntPtr handle, string symbol);
45 [DllImport (SystemLibrary)]
46 public static extern void dlclose (IntPtr handle);
48 public static IntPtr GetIndirect (IntPtr handle, string symbol)
50 return dlsym (handle, symbol);
53 public static IntPtr GetCFObjectHandle (IntPtr handle, string symbol)
55 var indirect = dlsym (handle, symbol);
56 if (indirect == IntPtr.Zero)
59 return Marshal.ReadIntPtr (indirect);
62 public CFObject (IntPtr handle, bool own)
75 public IntPtr Handle { get; private set; }
77 [DllImport (CoreFoundationLibrary)]
78 extern static IntPtr CFRetain (IntPtr handle);
85 [DllImport (CoreFoundationLibrary)]
86 extern static void CFRelease (IntPtr handle);
93 protected virtual void Dispose (bool disposing)
95 if (Handle != IntPtr.Zero) {
101 public void Dispose ()
104 GC.SuppressFinalize (this);
108 internal class CFArray : CFObject
110 public CFArray (IntPtr handle, bool own) : base (handle, own) { }
112 [DllImport (CoreFoundationLibrary)]
113 extern static IntPtr CFArrayCreate (IntPtr allocator, IntPtr values, /* CFIndex */ IntPtr numValues, IntPtr callbacks);
114 static readonly IntPtr kCFTypeArrayCallbacks;
118 var handle = dlopen (CoreFoundationLibrary, 0);
119 if (handle == IntPtr.Zero)
123 kCFTypeArrayCallbacks = GetIndirect (handle, "kCFTypeArrayCallBacks");
129 static unsafe CFArray Create (params IntPtr[] values)
132 throw new ArgumentNullException ("values");
134 fixed (IntPtr *pv = values) {
135 IntPtr handle = CFArrayCreate (IntPtr.Zero, (IntPtr) pv, (IntPtr) values.Length, kCFTypeArrayCallbacks);
137 return new CFArray (handle, false);
141 public static CFArray Create (params CFObject[] values)
144 throw new ArgumentNullException ("values");
146 IntPtr[] _values = new IntPtr [values.Length];
147 for (int i = 0; i < _values.Length; i++)
148 _values[i] = values[i].Handle;
150 return Create (_values);
153 [DllImport (CoreFoundationLibrary)]
154 extern static /* CFIndex */ IntPtr CFArrayGetCount (IntPtr handle);
157 get { return (int) CFArrayGetCount (Handle); }
160 [DllImport (CoreFoundationLibrary)]
161 extern static IntPtr CFArrayGetValueAtIndex (IntPtr handle, /* CFIndex */ IntPtr index);
163 public IntPtr this[int index] {
165 return CFArrayGetValueAtIndex (Handle, (IntPtr) index);
170 internal class CFNumber : CFObject
172 public CFNumber (IntPtr handle, bool own) : base (handle, own) { }
174 [DllImport (CoreFoundationLibrary)]
175 [return: MarshalAs (UnmanagedType.I1)]
176 extern static bool CFNumberGetValue (IntPtr handle, /* CFNumberType */ IntPtr type, [MarshalAs (UnmanagedType.I1)] out bool value);
178 public static bool AsBool (IntPtr handle)
182 if (handle == IntPtr.Zero)
185 CFNumberGetValue (handle, (IntPtr) 1, out value);
190 public static implicit operator bool (CFNumber number)
192 return AsBool (number.Handle);
195 [DllImport (CoreFoundationLibrary)]
196 [return: MarshalAs (UnmanagedType.I1)]
197 extern static bool CFNumberGetValue (IntPtr handle, /* CFNumberType */ IntPtr type, out int value);
199 public static int AsInt32 (IntPtr handle)
203 if (handle == IntPtr.Zero)
206 // 9 == kCFNumberIntType == C int
207 CFNumberGetValue (handle, (IntPtr) 9, out value);
212 public static implicit operator int (CFNumber number)
214 return AsInt32 (number.Handle);
218 internal struct CFRange {
219 public IntPtr Location, Length;
221 public CFRange (int loc, int len)
223 Location = (IntPtr) loc;
224 Length = (IntPtr) len;
228 internal struct CFStreamClientContext {
229 public IntPtr Version;
231 public IntPtr Retain;
232 public IntPtr Release;
233 public IntPtr CopyDescription;
236 internal class CFString : CFObject
240 public CFString (IntPtr handle, bool own) : base (handle, own) { }
242 [DllImport (CoreFoundationLibrary)]
243 extern static IntPtr CFStringCreateWithCharacters (IntPtr alloc, IntPtr chars, /* CFIndex */ IntPtr length);
245 public static CFString Create (string value)
250 fixed (char *ptr = value) {
251 handle = CFStringCreateWithCharacters (IntPtr.Zero, (IntPtr) ptr, (IntPtr) value.Length);
255 if (handle == IntPtr.Zero)
258 return new CFString (handle, true);
261 [DllImport (CoreFoundationLibrary)]
262 extern static /* CFIndex */ IntPtr CFStringGetLength (IntPtr handle);
269 return (int) CFStringGetLength (Handle);
273 [DllImport (CoreFoundationLibrary)]
274 extern static IntPtr CFStringGetCharactersPtr (IntPtr handle);
276 [DllImport (CoreFoundationLibrary)]
277 extern static IntPtr CFStringGetCharacters (IntPtr handle, CFRange range, IntPtr buffer);
279 public static string AsString (IntPtr handle)
281 if (handle == IntPtr.Zero)
284 int len = (int) CFStringGetLength (handle);
289 IntPtr chars = CFStringGetCharactersPtr (handle);
290 IntPtr buffer = IntPtr.Zero;
292 if (chars == IntPtr.Zero) {
293 CFRange range = new CFRange (0, len);
294 buffer = Marshal.AllocHGlobal (len * 2);
295 CFStringGetCharacters (handle, range, buffer);
302 str = new string ((char *) chars, 0, len);
305 if (buffer != IntPtr.Zero)
306 Marshal.FreeHGlobal (buffer);
311 public override string ToString ()
314 str = AsString (Handle);
319 public static implicit operator string (CFString str)
321 return str.ToString ();
324 public static implicit operator CFString (string str)
330 internal class CFDictionary : CFObject
332 public CFDictionary (IntPtr handle, bool own) : base (handle, own) { }
334 [DllImport (CoreFoundationLibrary)]
335 extern static IntPtr CFDictionaryGetValue (IntPtr handle, IntPtr key);
337 public IntPtr GetValue (IntPtr key)
339 return CFDictionaryGetValue (Handle, key);
342 public IntPtr this[IntPtr key] {
344 return GetValue (key);
349 internal class CFUrl : CFObject
351 public CFUrl (IntPtr handle, bool own) : base (handle, own) { }
353 [DllImport (CoreFoundationLibrary)]
354 extern static IntPtr CFURLCreateWithString (IntPtr allocator, IntPtr str, IntPtr baseURL);
356 public static CFUrl Create (string absolute)
358 if (string.IsNullOrEmpty (absolute))
361 CFString str = CFString.Create (absolute);
362 IntPtr handle = CFURLCreateWithString (IntPtr.Zero, str.Handle, IntPtr.Zero);
365 if (handle == IntPtr.Zero)
368 return new CFUrl (handle, true);
372 internal class CFRunLoop : CFObject
374 [DllImport (CFObject.CoreFoundationLibrary)]
375 static extern void CFRunLoopAddSource (IntPtr rl, IntPtr source, IntPtr mode);
377 [DllImport (CFObject.CoreFoundationLibrary)]
378 static extern void CFRunLoopRemoveSource (IntPtr rl, IntPtr source, IntPtr mode);
380 [DllImport (CFObject.CoreFoundationLibrary)]
381 static extern int CFRunLoopRunInMode (IntPtr mode, double seconds, bool returnAfterSourceHandled);
383 [DllImport (CFObject.CoreFoundationLibrary)]
384 static extern IntPtr CFRunLoopGetCurrent ();
386 [DllImport (CFObject.CoreFoundationLibrary)]
387 static extern void CFRunLoopStop (IntPtr rl);
389 public CFRunLoop (IntPtr handle, bool own): base (handle, own)
393 public static CFRunLoop CurrentRunLoop {
394 get { return new CFRunLoop (CFRunLoopGetCurrent (), false); }
397 public void AddSource (IntPtr source, CFString mode)
399 CFRunLoopAddSource (Handle, source, mode.Handle);
402 public void RemoveSource (IntPtr source, CFString mode)
404 CFRunLoopRemoveSource (Handle, source, mode.Handle);
407 public int RunInMode (CFString mode, double seconds, bool returnAfterSourceHandled)
409 return CFRunLoopRunInMode (mode.Handle, seconds, returnAfterSourceHandled);
414 CFRunLoopStop (Handle);
418 internal enum CFProxyType {
420 AutoConfigurationUrl,
421 AutoConfigurationJavaScript,
428 internal class CFProxy {
429 //static IntPtr kCFProxyAutoConfigurationHTTPResponseKey;
430 static IntPtr kCFProxyAutoConfigurationJavaScriptKey;
431 static IntPtr kCFProxyAutoConfigurationURLKey;
432 static IntPtr kCFProxyHostNameKey;
433 static IntPtr kCFProxyPasswordKey;
434 static IntPtr kCFProxyPortNumberKey;
435 static IntPtr kCFProxyTypeKey;
436 static IntPtr kCFProxyUsernameKey;
438 //static IntPtr kCFProxyTypeNone;
439 static IntPtr kCFProxyTypeAutoConfigurationURL;
440 static IntPtr kCFProxyTypeAutoConfigurationJavaScript;
441 static IntPtr kCFProxyTypeFTP;
442 static IntPtr kCFProxyTypeHTTP;
443 static IntPtr kCFProxyTypeHTTPS;
444 static IntPtr kCFProxyTypeSOCKS;
448 IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
450 //kCFProxyAutoConfigurationHTTPResponseKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationHTTPResponseKey");
451 kCFProxyAutoConfigurationJavaScriptKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationJavaScriptKey");
452 kCFProxyAutoConfigurationURLKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationURLKey");
453 kCFProxyHostNameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyHostNameKey");
454 kCFProxyPasswordKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPasswordKey");
455 kCFProxyPortNumberKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPortNumberKey");
456 kCFProxyTypeKey = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeKey");
457 kCFProxyUsernameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyUsernameKey");
459 //kCFProxyTypeNone = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeNone");
460 kCFProxyTypeAutoConfigurationURL = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationURL");
461 kCFProxyTypeAutoConfigurationJavaScript = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationJavaScript");
462 kCFProxyTypeFTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeFTP");
463 kCFProxyTypeHTTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTP");
464 kCFProxyTypeHTTPS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTPS");
465 kCFProxyTypeSOCKS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeSOCKS");
467 CFObject.dlclose (handle);
470 CFDictionary settings;
472 internal CFProxy (CFDictionary settings)
474 this.settings = settings;
477 static CFProxyType CFProxyTypeToEnum (IntPtr type)
479 if (type == kCFProxyTypeAutoConfigurationJavaScript)
480 return CFProxyType.AutoConfigurationJavaScript;
482 if (type == kCFProxyTypeAutoConfigurationURL)
483 return CFProxyType.AutoConfigurationUrl;
485 if (type == kCFProxyTypeFTP)
486 return CFProxyType.FTP;
488 if (type == kCFProxyTypeHTTP)
489 return CFProxyType.HTTP;
491 if (type == kCFProxyTypeHTTPS)
492 return CFProxyType.HTTPS;
494 if (type == kCFProxyTypeSOCKS)
495 return CFProxyType.SOCKS;
497 return CFProxyType.None;
501 // AFAICT these get used with CFNetworkExecuteProxyAutoConfiguration*()
503 // TODO: bind CFHTTPMessage so we can return the proper type here.
504 public IntPtr AutoConfigurationHTTPResponse {
505 get { return settings[kCFProxyAutoConfigurationHTTPResponseKey]; }
509 public IntPtr AutoConfigurationJavaScript {
511 if (kCFProxyAutoConfigurationJavaScriptKey == IntPtr.Zero)
514 return settings[kCFProxyAutoConfigurationJavaScriptKey];
518 public IntPtr AutoConfigurationUrl {
520 if (kCFProxyAutoConfigurationURLKey == IntPtr.Zero)
523 return settings[kCFProxyAutoConfigurationURLKey];
527 public string HostName {
529 if (kCFProxyHostNameKey == IntPtr.Zero)
532 return CFString.AsString (settings[kCFProxyHostNameKey]);
536 public string Password {
538 if (kCFProxyPasswordKey == IntPtr.Zero)
541 return CFString.AsString (settings[kCFProxyPasswordKey]);
547 if (kCFProxyPortNumberKey == IntPtr.Zero)
550 return CFNumber.AsInt32 (settings[kCFProxyPortNumberKey]);
554 public CFProxyType ProxyType {
556 if (kCFProxyTypeKey == IntPtr.Zero)
557 return CFProxyType.None;
559 return CFProxyTypeToEnum (settings[kCFProxyTypeKey]);
563 public string Username {
565 if (kCFProxyUsernameKey == IntPtr.Zero)
568 return CFString.AsString (settings[kCFProxyUsernameKey]);
573 internal class CFProxySettings {
574 static IntPtr kCFNetworkProxiesHTTPEnable;
575 static IntPtr kCFNetworkProxiesHTTPPort;
576 static IntPtr kCFNetworkProxiesHTTPProxy;
577 static IntPtr kCFNetworkProxiesProxyAutoConfigEnable;
578 static IntPtr kCFNetworkProxiesProxyAutoConfigJavaScript;
579 static IntPtr kCFNetworkProxiesProxyAutoConfigURLString;
581 static CFProxySettings ()
583 IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
585 kCFNetworkProxiesHTTPEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPEnable");
586 kCFNetworkProxiesHTTPPort = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPPort");
587 kCFNetworkProxiesHTTPProxy = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPProxy");
588 kCFNetworkProxiesProxyAutoConfigEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigEnable");
589 kCFNetworkProxiesProxyAutoConfigJavaScript = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigJavaScript");
590 kCFNetworkProxiesProxyAutoConfigURLString = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigURLString");
592 CFObject.dlclose (handle);
595 CFDictionary settings;
597 public CFProxySettings (CFDictionary settings)
599 this.settings = settings;
602 public CFDictionary Dictionary {
603 get { return settings; }
606 public bool HTTPEnable {
608 if (kCFNetworkProxiesHTTPEnable == IntPtr.Zero)
611 return CFNumber.AsBool (settings[kCFNetworkProxiesHTTPEnable]);
615 public int HTTPPort {
617 if (kCFNetworkProxiesHTTPPort == IntPtr.Zero)
620 return CFNumber.AsInt32 (settings[kCFNetworkProxiesHTTPPort]);
624 public string HTTPProxy {
626 if (kCFNetworkProxiesHTTPProxy == IntPtr.Zero)
629 return CFString.AsString (settings[kCFNetworkProxiesHTTPProxy]);
633 public bool ProxyAutoConfigEnable {
635 if (kCFNetworkProxiesProxyAutoConfigEnable == IntPtr.Zero)
638 return CFNumber.AsBool (settings[kCFNetworkProxiesProxyAutoConfigEnable]);
642 public string ProxyAutoConfigJavaScript {
644 if (kCFNetworkProxiesProxyAutoConfigJavaScript == IntPtr.Zero)
647 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigJavaScript]);
651 public string ProxyAutoConfigURLString {
653 if (kCFNetworkProxiesProxyAutoConfigURLString == IntPtr.Zero)
656 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigURLString]);
661 internal static class CFNetwork {
663 public const string CFNetworkLibrary = "/System/Library/Frameworks/CoreServices.framework/Frameworks/CFNetwork.framework/CFNetwork";
665 public const string CFNetworkLibrary = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
668 [DllImport (CFNetworkLibrary, EntryPoint = "CFNetworkCopyProxiesForAutoConfigurationScript")]
669 // CFArrayRef CFNetworkCopyProxiesForAutoConfigurationScript (CFStringRef proxyAutoConfigurationScript, CFURLRef targetURL, CFErrorRef* error);
670 extern static IntPtr CFNetworkCopyProxiesForAutoConfigurationScriptSequential (IntPtr proxyAutoConfigurationScript, IntPtr targetURL, out IntPtr error);
672 [DllImport (CFNetworkLibrary)]
673 extern static IntPtr CFNetworkExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, IntPtr targetURL, CFProxyAutoConfigurationResultCallback cb, ref CFStreamClientContext clientContext);
676 class GetProxyData : IDisposable {
677 public IntPtr script;
678 public IntPtr targetUri;
680 public IntPtr result;
681 public ManualResetEvent evt = new ManualResetEvent (false);
683 public void Dispose ()
689 static object lock_obj = new object ();
690 static Queue<GetProxyData> get_proxy_queue;
691 static AutoResetEvent proxy_event;
693 static void CFNetworkCopyProxiesForAutoConfigurationScriptThread ()
696 var data_left = true;
699 proxy_event.WaitOne ();
703 if (get_proxy_queue.Count == 0)
705 data = get_proxy_queue.Dequeue ();
706 data_left = get_proxy_queue.Count > 0;
709 data.result = CFNetworkCopyProxiesForAutoConfigurationScriptSequential (data.script, data.targetUri, out data.error);
715 static IntPtr CFNetworkCopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, IntPtr targetURL, out IntPtr error)
717 // This method must only be called on only one thread during an application's life time.
718 // Note that it's not enough to use a lock to make calls sequential across different threads,
719 // it has to be one thread. Also note that that thread can't be the main thread, because the
720 // main thread might be blocking waiting for this network request to finish.
721 // Another possibility would be to use JavaScriptCore to execute this piece of
722 // javascript ourselves, but unfortunately it's not available before iOS7.
723 // See bug #7923 comment #21+.
725 using (var data = new GetProxyData ()) {
726 data.script = proxyAutoConfigurationScript;
727 data.targetUri = targetURL;
730 if (get_proxy_queue == null) {
731 get_proxy_queue = new Queue<GetProxyData> ();
732 proxy_event = new AutoResetEvent (false);
733 new Thread (CFNetworkCopyProxiesForAutoConfigurationScriptThread) {
737 get_proxy_queue.Enqueue (data);
749 static CFArray CopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
751 IntPtr err = IntPtr.Zero;
752 IntPtr native = CFNetworkCopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL.Handle, out err);
754 if (native == IntPtr.Zero)
757 return new CFArray (native, true);
760 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
762 if (proxyAutoConfigurationScript == IntPtr.Zero)
763 throw new ArgumentNullException ("proxyAutoConfigurationScript");
765 if (targetURL == null)
766 throw new ArgumentNullException ("targetURL");
768 CFArray array = CopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
773 CFProxy[] proxies = new CFProxy [array.Count];
774 for (int i = 0; i < proxies.Length; i++) {
775 CFDictionary dict = new CFDictionary (array[i], false);
776 proxies[i] = new CFProxy (dict);
784 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, Uri targetUri)
786 if (proxyAutoConfigurationScript == IntPtr.Zero)
787 throw new ArgumentNullException ("proxyAutoConfigurationScript");
789 if (targetUri == null)
790 throw new ArgumentNullException ("targetUri");
792 CFUrl targetURL = CFUrl.Create (targetUri.AbsoluteUri);
793 CFProxy[] proxies = GetProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
794 targetURL.Dispose ();
799 delegate void CFProxyAutoConfigurationResultCallback (IntPtr client, IntPtr proxyList, IntPtr error);
801 public static CFProxy[] ExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, Uri targetURL)
803 CFUrl url = CFUrl.Create (targetURL.AbsoluteUri);
807 CFProxy[] proxies = null;
809 var runLoop = CFRunLoop.CurrentRunLoop;
811 // Callback that will be called after executing the configuration script
812 CFProxyAutoConfigurationResultCallback cb = delegate (IntPtr client, IntPtr proxyList, IntPtr error) {
813 if (proxyList != IntPtr.Zero) {
814 var array = new CFArray (proxyList, false);
815 proxies = new CFProxy [array.Count];
816 for (int i = 0; i < proxies.Length; i++) {
817 CFDictionary dict = new CFDictionary (array[i], false);
818 proxies[i] = new CFProxy (dict);
825 var clientContext = new CFStreamClientContext ();
826 var loopSource = CFNetworkExecuteProxyAutoConfigurationURL (proxyAutoConfigURL, url.Handle, cb, ref clientContext);
828 // Create a private mode
829 var mode = CFString.Create ("Mono.MacProxy");
831 runLoop.AddSource (loopSource, mode);
832 runLoop.RunInMode (mode, double.MaxValue, false);
833 runLoop.RemoveSource (loopSource, mode);
838 [DllImport (CFNetworkLibrary)]
839 // CFArrayRef CFNetworkCopyProxiesForURL (CFURLRef url, CFDictionaryRef proxySettings);
840 extern static IntPtr CFNetworkCopyProxiesForURL (IntPtr url, IntPtr proxySettings);
842 static CFArray CopyProxiesForURL (CFUrl url, CFDictionary proxySettings)
844 IntPtr native = CFNetworkCopyProxiesForURL (url.Handle, proxySettings != null ? proxySettings.Handle : IntPtr.Zero);
846 if (native == IntPtr.Zero)
849 return new CFArray (native, true);
852 public static CFProxy[] GetProxiesForURL (CFUrl url, CFProxySettings proxySettings)
854 if (url == null || url.Handle == IntPtr.Zero)
855 throw new ArgumentNullException ("url");
857 if (proxySettings == null)
858 proxySettings = GetSystemProxySettings ();
860 CFArray array = CopyProxiesForURL (url, proxySettings.Dictionary);
865 CFProxy[] proxies = new CFProxy [array.Count];
866 for (int i = 0; i < proxies.Length; i++) {
867 CFDictionary dict = new CFDictionary (array[i], false);
868 proxies[i] = new CFProxy (dict);
876 public static CFProxy[] GetProxiesForUri (Uri uri, CFProxySettings proxySettings)
879 throw new ArgumentNullException ("uri");
881 CFUrl url = CFUrl.Create (uri.AbsoluteUri);
885 CFProxy[] proxies = GetProxiesForURL (url, proxySettings);
891 [DllImport (CFNetworkLibrary)]
892 // CFDictionaryRef CFNetworkCopySystemProxySettings (void);
893 extern static IntPtr CFNetworkCopySystemProxySettings ();
895 public static CFProxySettings GetSystemProxySettings ()
897 IntPtr native = CFNetworkCopySystemProxySettings ();
899 if (native == IntPtr.Zero)
902 var dict = new CFDictionary (native, true);
904 return new CFProxySettings (dict);
907 class CFWebProxy : IWebProxy {
908 ICredentials credentials;
915 public ICredentials Credentials {
916 get { return credentials; }
918 userSpecified = true;
923 static Uri GetProxyUri (CFProxy proxy, out NetworkCredential credentials)
927 switch (proxy.ProxyType) {
928 case CFProxyType.FTP:
931 case CFProxyType.HTTP:
932 case CFProxyType.HTTPS:
933 protocol = "http://";
940 string username = proxy.Username;
941 string password = proxy.Password;
942 string hostname = proxy.HostName;
943 int port = proxy.Port;
946 if (username != null)
947 credentials = new NetworkCredential (username, password);
951 uri = protocol + hostname + (port != 0 ? ':' + port.ToString () : string.Empty);
953 return new Uri (uri, UriKind.Absolute);
956 static Uri GetProxyUriFromScript (IntPtr script, Uri targetUri, out NetworkCredential credentials)
958 CFProxy[] proxies = CFNetwork.GetProxiesForAutoConfigurationScript (script, targetUri);
959 return SelectProxy (proxies, targetUri, out credentials);
962 static Uri ExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, Uri targetUri, out NetworkCredential credentials)
964 CFProxy[] proxies = CFNetwork.ExecuteProxyAutoConfigurationURL (proxyAutoConfigURL, targetUri);
965 return SelectProxy (proxies, targetUri, out credentials);
969 static Uri SelectProxy (CFProxy[] proxies, Uri targetUri, out NetworkCredential credentials)
971 if (proxies == null) {
976 for (int i = 0; i < proxies.Length; i++) {
977 switch (proxies[i].ProxyType) {
978 case CFProxyType.HTTPS:
979 case CFProxyType.HTTP:
980 case CFProxyType.FTP:
981 // create a Uri based on the hostname/port/etc info
982 return GetProxyUri (proxies[i], out credentials);
983 case CFProxyType.SOCKS:
985 // unsupported proxy type, try the next one
987 case CFProxyType.None:
988 // no proxy should be used
999 public Uri GetProxy (Uri targetUri)
1001 NetworkCredential credentials = null;
1004 if (targetUri == null)
1005 throw new ArgumentNullException ("targetUri");
1008 CFProxySettings settings = CFNetwork.GetSystemProxySettings ();
1009 CFProxy[] proxies = CFNetwork.GetProxiesForUri (targetUri, settings);
1011 if (proxies != null) {
1012 for (int i = 0; i < proxies.Length && proxy == null; i++) {
1013 switch (proxies[i].ProxyType) {
1014 case CFProxyType.AutoConfigurationJavaScript:
1015 proxy = GetProxyUriFromScript (proxies[i].AutoConfigurationJavaScript, targetUri, out credentials);
1017 case CFProxyType.AutoConfigurationUrl:
1018 proxy = ExecuteProxyAutoConfigurationURL (proxies[i].AutoConfigurationUrl, targetUri, out credentials);
1020 case CFProxyType.HTTPS:
1021 case CFProxyType.HTTP:
1022 case CFProxyType.FTP:
1023 // create a Uri based on the hostname/port/etc info
1024 proxy = GetProxyUri (proxies[i], out credentials);
1026 case CFProxyType.SOCKS:
1027 // unsupported proxy type, try the next one
1029 case CFProxyType.None:
1030 // no proxy should be used
1036 if (proxy == null) {
1037 // no supported proxies for this Uri, fall back to trying to connect to targetUri directly
1044 // ignore errors while retrieving proxy data
1049 this.credentials = credentials;
1054 public bool IsBypassed (Uri targetUri)
1056 if (targetUri == null)
1057 throw new ArgumentNullException ("targetUri");
1059 return GetProxy (targetUri) == targetUri;
1063 public static IWebProxy GetDefaultProxy ()
1065 return new CFWebProxy ();