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
29 using System.Collections.Generic;
30 using System.Runtime.InteropServices;
31 using System.Threading;
35 internal class CFObject : IDisposable
37 public const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
38 const string SystemLibrary = "/usr/lib/libSystem.dylib";
40 [DllImport (SystemLibrary)]
41 public static extern IntPtr dlopen (string path, int mode);
43 [DllImport (SystemLibrary)]
44 public static extern IntPtr dlsym (IntPtr handle, string symbol);
46 [DllImport (SystemLibrary)]
47 public static extern void dlclose (IntPtr handle);
49 public static IntPtr GetIndirect (IntPtr handle, string symbol)
51 return dlsym (handle, symbol);
54 public static IntPtr GetCFObjectHandle (IntPtr handle, string symbol)
56 var indirect = dlsym (handle, symbol);
57 if (indirect == IntPtr.Zero)
60 return Marshal.ReadIntPtr (indirect);
63 public CFObject (IntPtr handle, bool own)
76 public IntPtr Handle { get; private set; }
78 [DllImport (CoreFoundationLibrary)]
79 extern static IntPtr CFRetain (IntPtr handle);
86 [DllImport (CoreFoundationLibrary)]
87 extern static void CFRelease (IntPtr handle);
94 protected virtual void Dispose (bool disposing)
96 if (Handle != IntPtr.Zero) {
102 public void Dispose ()
105 GC.SuppressFinalize (this);
109 internal class CFArray : CFObject
111 public CFArray (IntPtr handle, bool own) : base (handle, own) { }
113 [DllImport (CoreFoundationLibrary)]
114 extern static IntPtr CFArrayCreate (IntPtr allocator, IntPtr values, /* CFIndex */ IntPtr numValues, IntPtr callbacks);
115 static readonly IntPtr kCFTypeArrayCallbacks;
119 var handle = dlopen (CoreFoundationLibrary, 0);
120 if (handle == IntPtr.Zero)
124 kCFTypeArrayCallbacks = GetIndirect (handle, "kCFTypeArrayCallBacks");
130 static unsafe CFArray Create (params IntPtr[] values)
133 throw new ArgumentNullException ("values");
135 fixed (IntPtr *pv = values) {
136 IntPtr handle = CFArrayCreate (IntPtr.Zero, (IntPtr) pv, (IntPtr) values.Length, kCFTypeArrayCallbacks);
138 return new CFArray (handle, false);
142 public static CFArray Create (params CFObject[] values)
145 throw new ArgumentNullException ("values");
147 IntPtr[] _values = new IntPtr [values.Length];
148 for (int i = 0; i < _values.Length; i++)
149 _values[i] = values[i].Handle;
151 return Create (_values);
154 [DllImport (CoreFoundationLibrary)]
155 extern static /* CFIndex */ IntPtr CFArrayGetCount (IntPtr handle);
158 get { return (int) CFArrayGetCount (Handle); }
161 [DllImport (CoreFoundationLibrary)]
162 extern static IntPtr CFArrayGetValueAtIndex (IntPtr handle, /* CFIndex */ IntPtr index);
164 public IntPtr this[int index] {
166 return CFArrayGetValueAtIndex (Handle, (IntPtr) index);
171 internal class CFNumber : CFObject
173 public CFNumber (IntPtr handle, bool own) : base (handle, own) { }
175 [DllImport (CoreFoundationLibrary)]
176 [return: MarshalAs (UnmanagedType.I1)]
177 extern static bool CFNumberGetValue (IntPtr handle, /* CFNumberType */ IntPtr type, [MarshalAs (UnmanagedType.I1)] out bool value);
179 public static bool AsBool (IntPtr handle)
183 if (handle == IntPtr.Zero)
186 CFNumberGetValue (handle, (IntPtr) 1, out value);
191 public static implicit operator bool (CFNumber number)
193 return AsBool (number.Handle);
196 [DllImport (CoreFoundationLibrary)]
197 [return: MarshalAs (UnmanagedType.I1)]
198 extern static bool CFNumberGetValue (IntPtr handle, /* CFNumberType */ IntPtr type, out int value);
200 public static int AsInt32 (IntPtr handle)
204 if (handle == IntPtr.Zero)
207 // 9 == kCFNumberIntType == C int
208 CFNumberGetValue (handle, (IntPtr) 9, out value);
213 public static implicit operator int (CFNumber number)
215 return AsInt32 (number.Handle);
219 internal struct CFRange {
220 public IntPtr Location, Length;
222 public CFRange (int loc, int len)
224 Location = (IntPtr) loc;
225 Length = (IntPtr) len;
229 internal struct CFStreamClientContext {
230 public IntPtr Version;
232 public IntPtr Retain;
233 public IntPtr Release;
234 public IntPtr CopyDescription;
237 internal class CFString : CFObject
241 public CFString (IntPtr handle, bool own) : base (handle, own) { }
243 [DllImport (CoreFoundationLibrary)]
244 extern static IntPtr CFStringCreateWithCharacters (IntPtr alloc, IntPtr chars, /* CFIndex */ IntPtr length);
246 public static CFString Create (string value)
251 fixed (char *ptr = value) {
252 handle = CFStringCreateWithCharacters (IntPtr.Zero, (IntPtr) ptr, (IntPtr) value.Length);
256 if (handle == IntPtr.Zero)
259 return new CFString (handle, true);
262 [DllImport (CoreFoundationLibrary)]
263 extern static /* CFIndex */ IntPtr CFStringGetLength (IntPtr handle);
270 return (int) CFStringGetLength (Handle);
274 [DllImport (CoreFoundationLibrary)]
275 extern static IntPtr CFStringGetCharactersPtr (IntPtr handle);
277 [DllImport (CoreFoundationLibrary)]
278 extern static IntPtr CFStringGetCharacters (IntPtr handle, CFRange range, IntPtr buffer);
280 public static string AsString (IntPtr handle)
282 if (handle == IntPtr.Zero)
285 int len = (int) CFStringGetLength (handle);
290 IntPtr chars = CFStringGetCharactersPtr (handle);
291 IntPtr buffer = IntPtr.Zero;
293 if (chars == IntPtr.Zero) {
294 CFRange range = new CFRange (0, len);
295 buffer = Marshal.AllocHGlobal (len * 2);
296 CFStringGetCharacters (handle, range, buffer);
303 str = new string ((char *) chars, 0, len);
306 if (buffer != IntPtr.Zero)
307 Marshal.FreeHGlobal (buffer);
312 public override string ToString ()
315 str = AsString (Handle);
320 public static implicit operator string (CFString str)
322 return str.ToString ();
325 public static implicit operator CFString (string str)
331 internal class CFDictionary : CFObject
333 public CFDictionary (IntPtr handle, bool own) : base (handle, own) { }
335 [DllImport (CoreFoundationLibrary)]
336 extern static IntPtr CFDictionaryGetValue (IntPtr handle, IntPtr key);
338 public IntPtr GetValue (IntPtr key)
340 return CFDictionaryGetValue (Handle, key);
343 public IntPtr this[IntPtr key] {
345 return GetValue (key);
350 internal class CFUrl : CFObject
352 public CFUrl (IntPtr handle, bool own) : base (handle, own) { }
354 [DllImport (CoreFoundationLibrary)]
355 extern static IntPtr CFURLCreateWithString (IntPtr allocator, IntPtr str, IntPtr baseURL);
357 public static CFUrl Create (string absolute)
359 if (string.IsNullOrEmpty (absolute))
362 CFString str = CFString.Create (absolute);
363 IntPtr handle = CFURLCreateWithString (IntPtr.Zero, str.Handle, IntPtr.Zero);
366 if (handle == IntPtr.Zero)
369 return new CFUrl (handle, true);
373 internal class CFRunLoop : CFObject
375 [DllImport (CFObject.CoreFoundationLibrary)]
376 static extern void CFRunLoopAddSource (IntPtr rl, IntPtr source, IntPtr mode);
378 [DllImport (CFObject.CoreFoundationLibrary)]
379 static extern void CFRunLoopRemoveSource (IntPtr rl, IntPtr source, IntPtr mode);
381 [DllImport (CFObject.CoreFoundationLibrary)]
382 static extern int CFRunLoopRunInMode (IntPtr mode, double seconds, bool returnAfterSourceHandled);
384 [DllImport (CFObject.CoreFoundationLibrary)]
385 static extern IntPtr CFRunLoopGetCurrent ();
387 [DllImport (CFObject.CoreFoundationLibrary)]
388 static extern void CFRunLoopStop (IntPtr rl);
390 public CFRunLoop (IntPtr handle, bool own): base (handle, own)
394 public static CFRunLoop CurrentRunLoop {
395 get { return new CFRunLoop (CFRunLoopGetCurrent (), false); }
398 public void AddSource (IntPtr source, CFString mode)
400 CFRunLoopAddSource (Handle, source, mode.Handle);
403 public void RemoveSource (IntPtr source, CFString mode)
405 CFRunLoopRemoveSource (Handle, source, mode.Handle);
408 public int RunInMode (CFString mode, double seconds, bool returnAfterSourceHandled)
410 return CFRunLoopRunInMode (mode.Handle, seconds, returnAfterSourceHandled);
415 CFRunLoopStop (Handle);
419 internal enum CFProxyType {
421 AutoConfigurationUrl,
422 AutoConfigurationJavaScript,
429 internal class CFProxy {
430 //static IntPtr kCFProxyAutoConfigurationHTTPResponseKey;
431 static IntPtr kCFProxyAutoConfigurationJavaScriptKey;
432 static IntPtr kCFProxyAutoConfigurationURLKey;
433 static IntPtr kCFProxyHostNameKey;
434 static IntPtr kCFProxyPasswordKey;
435 static IntPtr kCFProxyPortNumberKey;
436 static IntPtr kCFProxyTypeKey;
437 static IntPtr kCFProxyUsernameKey;
439 //static IntPtr kCFProxyTypeNone;
440 static IntPtr kCFProxyTypeAutoConfigurationURL;
441 static IntPtr kCFProxyTypeAutoConfigurationJavaScript;
442 static IntPtr kCFProxyTypeFTP;
443 static IntPtr kCFProxyTypeHTTP;
444 static IntPtr kCFProxyTypeHTTPS;
445 static IntPtr kCFProxyTypeSOCKS;
449 IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
451 //kCFProxyAutoConfigurationHTTPResponseKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationHTTPResponseKey");
452 kCFProxyAutoConfigurationJavaScriptKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationJavaScriptKey");
453 kCFProxyAutoConfigurationURLKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationURLKey");
454 kCFProxyHostNameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyHostNameKey");
455 kCFProxyPasswordKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPasswordKey");
456 kCFProxyPortNumberKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPortNumberKey");
457 kCFProxyTypeKey = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeKey");
458 kCFProxyUsernameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyUsernameKey");
460 //kCFProxyTypeNone = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeNone");
461 kCFProxyTypeAutoConfigurationURL = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationURL");
462 kCFProxyTypeAutoConfigurationJavaScript = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationJavaScript");
463 kCFProxyTypeFTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeFTP");
464 kCFProxyTypeHTTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTP");
465 kCFProxyTypeHTTPS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTPS");
466 kCFProxyTypeSOCKS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeSOCKS");
468 CFObject.dlclose (handle);
471 CFDictionary settings;
473 internal CFProxy (CFDictionary settings)
475 this.settings = settings;
478 static CFProxyType CFProxyTypeToEnum (IntPtr type)
480 if (type == kCFProxyTypeAutoConfigurationJavaScript)
481 return CFProxyType.AutoConfigurationJavaScript;
483 if (type == kCFProxyTypeAutoConfigurationURL)
484 return CFProxyType.AutoConfigurationUrl;
486 if (type == kCFProxyTypeFTP)
487 return CFProxyType.FTP;
489 if (type == kCFProxyTypeHTTP)
490 return CFProxyType.HTTP;
492 if (type == kCFProxyTypeHTTPS)
493 return CFProxyType.HTTPS;
495 if (type == kCFProxyTypeSOCKS)
496 return CFProxyType.SOCKS;
498 return CFProxyType.None;
502 // AFAICT these get used with CFNetworkExecuteProxyAutoConfiguration*()
504 // TODO: bind CFHTTPMessage so we can return the proper type here.
505 public IntPtr AutoConfigurationHTTPResponse {
506 get { return settings[kCFProxyAutoConfigurationHTTPResponseKey]; }
510 public IntPtr AutoConfigurationJavaScript {
512 if (kCFProxyAutoConfigurationJavaScriptKey == IntPtr.Zero)
515 return settings[kCFProxyAutoConfigurationJavaScriptKey];
519 public IntPtr AutoConfigurationUrl {
521 if (kCFProxyAutoConfigurationURLKey == IntPtr.Zero)
524 return settings[kCFProxyAutoConfigurationURLKey];
528 public string HostName {
530 if (kCFProxyHostNameKey == IntPtr.Zero)
533 return CFString.AsString (settings[kCFProxyHostNameKey]);
537 public string Password {
539 if (kCFProxyPasswordKey == IntPtr.Zero)
542 return CFString.AsString (settings[kCFProxyPasswordKey]);
548 if (kCFProxyPortNumberKey == IntPtr.Zero)
551 return CFNumber.AsInt32 (settings[kCFProxyPortNumberKey]);
555 public CFProxyType ProxyType {
557 if (kCFProxyTypeKey == IntPtr.Zero)
558 return CFProxyType.None;
560 return CFProxyTypeToEnum (settings[kCFProxyTypeKey]);
564 public string Username {
566 if (kCFProxyUsernameKey == IntPtr.Zero)
569 return CFString.AsString (settings[kCFProxyUsernameKey]);
574 internal class CFProxySettings {
575 static IntPtr kCFNetworkProxiesHTTPEnable;
576 static IntPtr kCFNetworkProxiesHTTPPort;
577 static IntPtr kCFNetworkProxiesHTTPProxy;
578 static IntPtr kCFNetworkProxiesProxyAutoConfigEnable;
579 static IntPtr kCFNetworkProxiesProxyAutoConfigJavaScript;
580 static IntPtr kCFNetworkProxiesProxyAutoConfigURLString;
582 static CFProxySettings ()
584 IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
586 kCFNetworkProxiesHTTPEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPEnable");
587 kCFNetworkProxiesHTTPPort = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPPort");
588 kCFNetworkProxiesHTTPProxy = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPProxy");
589 kCFNetworkProxiesProxyAutoConfigEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigEnable");
590 kCFNetworkProxiesProxyAutoConfigJavaScript = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigJavaScript");
591 kCFNetworkProxiesProxyAutoConfigURLString = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigURLString");
593 CFObject.dlclose (handle);
596 CFDictionary settings;
598 public CFProxySettings (CFDictionary settings)
600 this.settings = settings;
603 public CFDictionary Dictionary {
604 get { return settings; }
607 public bool HTTPEnable {
609 if (kCFNetworkProxiesHTTPEnable == IntPtr.Zero)
612 return CFNumber.AsBool (settings[kCFNetworkProxiesHTTPEnable]);
616 public int HTTPPort {
618 if (kCFNetworkProxiesHTTPPort == IntPtr.Zero)
621 return CFNumber.AsInt32 (settings[kCFNetworkProxiesHTTPPort]);
625 public string HTTPProxy {
627 if (kCFNetworkProxiesHTTPProxy == IntPtr.Zero)
630 return CFString.AsString (settings[kCFNetworkProxiesHTTPProxy]);
634 public bool ProxyAutoConfigEnable {
636 if (kCFNetworkProxiesProxyAutoConfigEnable == IntPtr.Zero)
639 return CFNumber.AsBool (settings[kCFNetworkProxiesProxyAutoConfigEnable]);
643 public string ProxyAutoConfigJavaScript {
645 if (kCFNetworkProxiesProxyAutoConfigJavaScript == IntPtr.Zero)
648 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigJavaScript]);
652 public string ProxyAutoConfigURLString {
654 if (kCFNetworkProxiesProxyAutoConfigURLString == IntPtr.Zero)
657 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigURLString]);
662 internal static class CFNetwork {
664 public const string CFNetworkLibrary = "/System/Library/Frameworks/CoreServices.framework/Frameworks/CFNetwork.framework/CFNetwork";
666 public const string CFNetworkLibrary = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
669 [DllImport (CFNetworkLibrary, EntryPoint = "CFNetworkCopyProxiesForAutoConfigurationScript")]
670 // CFArrayRef CFNetworkCopyProxiesForAutoConfigurationScript (CFStringRef proxyAutoConfigurationScript, CFURLRef targetURL, CFErrorRef* error);
671 extern static IntPtr CFNetworkCopyProxiesForAutoConfigurationScriptSequential (IntPtr proxyAutoConfigurationScript, IntPtr targetURL, out IntPtr error);
673 [DllImport (CFNetworkLibrary)]
674 extern static IntPtr CFNetworkExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, IntPtr targetURL, CFProxyAutoConfigurationResultCallback cb, ref CFStreamClientContext clientContext);
677 class GetProxyData : IDisposable {
678 public IntPtr script;
679 public IntPtr targetUri;
681 public IntPtr result;
682 public ManualResetEvent evt = new ManualResetEvent (false);
684 public void Dispose ()
690 static object lock_obj = new object ();
691 static Queue<GetProxyData> get_proxy_queue;
692 static AutoResetEvent proxy_event;
694 static void CFNetworkCopyProxiesForAutoConfigurationScriptThread ()
697 var data_left = true;
700 proxy_event.WaitOne ();
704 if (get_proxy_queue.Count == 0)
706 data = get_proxy_queue.Dequeue ();
707 data_left = get_proxy_queue.Count > 0;
710 data.result = CFNetworkCopyProxiesForAutoConfigurationScriptSequential (data.script, data.targetUri, out data.error);
716 static IntPtr CFNetworkCopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, IntPtr targetURL, out IntPtr error)
718 // This method must only be called on only one thread during an application's life time.
719 // Note that it's not enough to use a lock to make calls sequential across different threads,
720 // it has to be one thread. Also note that that thread can't be the main thread, because the
721 // main thread might be blocking waiting for this network request to finish.
722 // Another possibility would be to use JavaScriptCore to execute this piece of
723 // javascript ourselves, but unfortunately it's not available before iOS7.
724 // See bug #7923 comment #21+.
726 using (var data = new GetProxyData ()) {
727 data.script = proxyAutoConfigurationScript;
728 data.targetUri = targetURL;
731 if (get_proxy_queue == null) {
732 get_proxy_queue = new Queue<GetProxyData> ();
733 proxy_event = new AutoResetEvent (false);
734 new Thread (CFNetworkCopyProxiesForAutoConfigurationScriptThread) {
738 get_proxy_queue.Enqueue (data);
750 static CFArray CopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
752 IntPtr err = IntPtr.Zero;
753 IntPtr native = CFNetworkCopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL.Handle, out err);
755 if (native == IntPtr.Zero)
758 return new CFArray (native, true);
761 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
763 if (proxyAutoConfigurationScript == IntPtr.Zero)
764 throw new ArgumentNullException ("proxyAutoConfigurationScript");
766 if (targetURL == null)
767 throw new ArgumentNullException ("targetURL");
769 CFArray array = CopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
774 CFProxy[] proxies = new CFProxy [array.Count];
775 for (int i = 0; i < proxies.Length; i++) {
776 CFDictionary dict = new CFDictionary (array[i], false);
777 proxies[i] = new CFProxy (dict);
785 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, Uri targetUri)
787 if (proxyAutoConfigurationScript == IntPtr.Zero)
788 throw new ArgumentNullException ("proxyAutoConfigurationScript");
790 if (targetUri == null)
791 throw new ArgumentNullException ("targetUri");
793 CFUrl targetURL = CFUrl.Create (targetUri.AbsoluteUri);
794 CFProxy[] proxies = GetProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
795 targetURL.Dispose ();
800 delegate void CFProxyAutoConfigurationResultCallback (IntPtr client, IntPtr proxyList, IntPtr error);
802 public static CFProxy[] ExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, Uri targetURL)
804 CFUrl url = CFUrl.Create (targetURL.AbsoluteUri);
808 CFProxy[] proxies = null;
810 var runLoop = CFRunLoop.CurrentRunLoop;
812 // Callback that will be called after executing the configuration script
813 CFProxyAutoConfigurationResultCallback cb = delegate (IntPtr client, IntPtr proxyList, IntPtr error) {
814 if (proxyList != IntPtr.Zero) {
815 var array = new CFArray (proxyList, false);
816 proxies = new CFProxy [array.Count];
817 for (int i = 0; i < proxies.Length; i++) {
818 CFDictionary dict = new CFDictionary (array[i], false);
819 proxies[i] = new CFProxy (dict);
826 var clientContext = new CFStreamClientContext ();
827 var loopSource = CFNetworkExecuteProxyAutoConfigurationURL (proxyAutoConfigURL, url.Handle, cb, ref clientContext);
829 // Create a private mode
830 var mode = CFString.Create ("Mono.MacProxy");
832 runLoop.AddSource (loopSource, mode);
833 runLoop.RunInMode (mode, double.MaxValue, false);
834 runLoop.RemoveSource (loopSource, mode);
839 [DllImport (CFNetworkLibrary)]
840 // CFArrayRef CFNetworkCopyProxiesForURL (CFURLRef url, CFDictionaryRef proxySettings);
841 extern static IntPtr CFNetworkCopyProxiesForURL (IntPtr url, IntPtr proxySettings);
843 static CFArray CopyProxiesForURL (CFUrl url, CFDictionary proxySettings)
845 IntPtr native = CFNetworkCopyProxiesForURL (url.Handle, proxySettings != null ? proxySettings.Handle : IntPtr.Zero);
847 if (native == IntPtr.Zero)
850 return new CFArray (native, true);
853 public static CFProxy[] GetProxiesForURL (CFUrl url, CFProxySettings proxySettings)
855 if (url == null || url.Handle == IntPtr.Zero)
856 throw new ArgumentNullException ("url");
858 if (proxySettings == null)
859 proxySettings = GetSystemProxySettings ();
861 CFArray array = CopyProxiesForURL (url, proxySettings.Dictionary);
866 CFProxy[] proxies = new CFProxy [array.Count];
867 for (int i = 0; i < proxies.Length; i++) {
868 CFDictionary dict = new CFDictionary (array[i], false);
869 proxies[i] = new CFProxy (dict);
877 public static CFProxy[] GetProxiesForUri (Uri uri, CFProxySettings proxySettings)
880 throw new ArgumentNullException ("uri");
882 CFUrl url = CFUrl.Create (uri.AbsoluteUri);
886 CFProxy[] proxies = GetProxiesForURL (url, proxySettings);
892 [DllImport (CFNetworkLibrary)]
893 // CFDictionaryRef CFNetworkCopySystemProxySettings (void);
894 extern static IntPtr CFNetworkCopySystemProxySettings ();
896 public static CFProxySettings GetSystemProxySettings ()
898 IntPtr native = CFNetworkCopySystemProxySettings ();
900 if (native == IntPtr.Zero)
903 var dict = new CFDictionary (native, true);
905 return new CFProxySettings (dict);
908 class CFWebProxy : IWebProxy {
909 ICredentials credentials;
916 public ICredentials Credentials {
917 get { return credentials; }
919 userSpecified = true;
924 static Uri GetProxyUri (CFProxy proxy, out NetworkCredential credentials)
928 switch (proxy.ProxyType) {
929 case CFProxyType.FTP:
932 case CFProxyType.HTTP:
933 case CFProxyType.HTTPS:
934 protocol = "http://";
941 string username = proxy.Username;
942 string password = proxy.Password;
943 string hostname = proxy.HostName;
944 int port = proxy.Port;
947 if (username != null)
948 credentials = new NetworkCredential (username, password);
952 uri = protocol + hostname + (port != 0 ? ':' + port.ToString () : string.Empty);
954 return new Uri (uri, UriKind.Absolute);
957 static Uri GetProxyUriFromScript (IntPtr script, Uri targetUri, out NetworkCredential credentials)
959 CFProxy[] proxies = CFNetwork.GetProxiesForAutoConfigurationScript (script, targetUri);
960 return SelectProxy (proxies, targetUri, out credentials);
963 static Uri ExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, Uri targetUri, out NetworkCredential credentials)
965 CFProxy[] proxies = CFNetwork.ExecuteProxyAutoConfigurationURL (proxyAutoConfigURL, targetUri);
966 return SelectProxy (proxies, targetUri, out credentials);
970 static Uri SelectProxy (CFProxy[] proxies, Uri targetUri, out NetworkCredential credentials)
972 if (proxies == null) {
977 for (int i = 0; i < proxies.Length; i++) {
978 switch (proxies[i].ProxyType) {
979 case CFProxyType.HTTPS:
980 case CFProxyType.HTTP:
981 case CFProxyType.FTP:
982 // create a Uri based on the hostname/port/etc info
983 return GetProxyUri (proxies[i], out credentials);
984 case CFProxyType.SOCKS:
986 // unsupported proxy type, try the next one
988 case CFProxyType.None:
989 // no proxy should be used
1000 public Uri GetProxy (Uri targetUri)
1002 NetworkCredential credentials = null;
1005 if (targetUri == null)
1006 throw new ArgumentNullException ("targetUri");
1009 CFProxySettings settings = CFNetwork.GetSystemProxySettings ();
1010 CFProxy[] proxies = CFNetwork.GetProxiesForUri (targetUri, settings);
1012 if (proxies != null) {
1013 for (int i = 0; i < proxies.Length && proxy == null; i++) {
1014 switch (proxies[i].ProxyType) {
1015 case CFProxyType.AutoConfigurationJavaScript:
1016 proxy = GetProxyUriFromScript (proxies[i].AutoConfigurationJavaScript, targetUri, out credentials);
1018 case CFProxyType.AutoConfigurationUrl:
1019 proxy = ExecuteProxyAutoConfigurationURL (proxies[i].AutoConfigurationUrl, targetUri, out credentials);
1021 case CFProxyType.HTTPS:
1022 case CFProxyType.HTTP:
1023 case CFProxyType.FTP:
1024 // create a Uri based on the hostname/port/etc info
1025 proxy = GetProxyUri (proxies[i], out credentials);
1027 case CFProxyType.SOCKS:
1028 // unsupported proxy type, try the next one
1030 case CFProxyType.None:
1031 // no proxy should be used
1037 if (proxy == null) {
1038 // no supported proxies for this Uri, fall back to trying to connect to targetUri directly
1045 // ignore errors while retrieving proxy data
1050 this.credentials = credentials;
1055 public bool IsBypassed (Uri targetUri)
1057 if (targetUri == null)
1058 throw new ArgumentNullException ("targetUri");
1060 return GetProxy (targetUri) == targetUri;
1064 public static IWebProxy GetDefaultProxy ()
1066 return new CFWebProxy ();