4 // Author: Jeffrey Stedfast <jeff@xamarin.com>
6 // Copyright (c) 2012 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.Runtime.InteropServices;
32 internal class CFObject : IDisposable
34 public const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
35 const string SystemLibrary = "/usr/lib/libSystem.dylib";
37 [DllImport (SystemLibrary)]
38 public static extern IntPtr dlopen (string path, int mode);
40 [DllImport (SystemLibrary)]
41 public static extern IntPtr dlsym (IntPtr handle, string symbol);
43 [DllImport (SystemLibrary)]
44 public static extern void dlclose (IntPtr handle);
46 public static IntPtr GetIndirect (IntPtr handle, string symbol)
48 return dlsym (handle, symbol);
51 public static IntPtr GetCFObjectHandle (IntPtr handle, string symbol)
53 var indirect = dlsym (handle, symbol);
54 if (indirect == IntPtr.Zero)
57 return Marshal.ReadIntPtr (indirect);
60 public CFObject (IntPtr handle, bool own)
73 public IntPtr Handle { get; private set; }
75 [DllImport (CoreFoundationLibrary)]
76 extern static IntPtr CFRetain (IntPtr handle);
83 [DllImport (CoreFoundationLibrary)]
84 extern static IntPtr CFRelease (IntPtr handle);
91 protected virtual void Dispose (bool disposing)
93 if (Handle != IntPtr.Zero) {
99 public void Dispose ()
102 GC.SuppressFinalize (this);
106 internal class CFArray : CFObject
108 public CFArray (IntPtr handle, bool own) : base (handle, own) { }
110 [DllImport (CoreFoundationLibrary)]
111 extern static IntPtr CFArrayCreate (IntPtr allocator, IntPtr values, int numValues, IntPtr callbacks);
112 static readonly IntPtr kCFTypeArrayCallbacks;
116 var handle = dlopen (CoreFoundationLibrary, 0);
117 if (handle == IntPtr.Zero)
121 kCFTypeArrayCallbacks = GetIndirect (handle, "kCFTypeArrayCallBacks");
127 static unsafe CFArray Create (params IntPtr[] values)
130 throw new ArgumentNullException ("values");
132 fixed (IntPtr *pv = values) {
133 IntPtr handle = CFArrayCreate (IntPtr.Zero, (IntPtr) pv, values.Length, kCFTypeArrayCallbacks);
135 return new CFArray (handle, false);
139 public static CFArray Create (params CFObject[] values)
142 throw new ArgumentNullException ("values");
144 IntPtr[] _values = new IntPtr [values.Length];
145 for (int i = 0; i < _values.Length; i++)
146 _values[i] = values[i].Handle;
148 return Create (_values);
151 [DllImport (CoreFoundationLibrary)]
152 extern static int CFArrayGetCount (IntPtr handle);
155 get { return CFArrayGetCount (Handle); }
158 [DllImport (CoreFoundationLibrary)]
159 extern static IntPtr CFArrayGetValueAtIndex (IntPtr handle, int index);
161 public IntPtr this[int index] {
163 return CFArrayGetValueAtIndex (Handle, index);
168 internal class CFNumber : CFObject
170 public CFNumber (IntPtr handle, bool own) : base (handle, own) { }
172 [DllImport (CoreFoundationLibrary)]
173 extern static bool CFNumberGetValue (IntPtr handle, int type, out bool value);
175 public static bool AsBool (IntPtr handle)
179 if (handle == IntPtr.Zero)
182 CFNumberGetValue (handle, 1, out value);
187 public static implicit operator bool (CFNumber number)
189 return AsBool (number.Handle);
192 [DllImport (CoreFoundationLibrary)]
193 extern static bool CFNumberGetValue (IntPtr handle, int type, out int value);
195 public static int AsInt32 (IntPtr handle)
199 if (handle == IntPtr.Zero)
202 CFNumberGetValue (handle, 9, out value);
207 public static implicit operator int (CFNumber number)
209 return AsInt32 (number.Handle);
213 internal struct CFRange {
214 public int Location, Length;
216 public CFRange (int loc, int len)
223 internal class CFString : CFObject
227 public CFString (IntPtr handle, bool own) : base (handle, own) { }
229 [DllImport (CoreFoundationLibrary)]
230 extern static IntPtr CFStringCreateWithCharacters (IntPtr alloc, IntPtr chars, int length);
232 public static CFString Create (string value)
237 fixed (char *ptr = value) {
238 handle = CFStringCreateWithCharacters (IntPtr.Zero, (IntPtr) ptr, value.Length);
242 if (handle == IntPtr.Zero)
245 return new CFString (handle, true);
248 [DllImport (CoreFoundationLibrary)]
249 extern static int CFStringGetLength (IntPtr handle);
256 return CFStringGetLength (Handle);
260 [DllImport (CoreFoundationLibrary)]
261 extern static IntPtr CFStringGetCharactersPtr (IntPtr handle);
263 [DllImport (CoreFoundationLibrary)]
264 extern static IntPtr CFStringGetCharacters (IntPtr handle, CFRange range, IntPtr buffer);
266 public static string AsString (IntPtr handle)
268 if (handle == IntPtr.Zero)
271 int len = CFStringGetLength (handle);
276 IntPtr chars = CFStringGetCharactersPtr (handle);
277 IntPtr buffer = IntPtr.Zero;
279 if (chars == IntPtr.Zero) {
280 CFRange range = new CFRange (0, len);
281 buffer = Marshal.AllocHGlobal (len * 2);
282 CFStringGetCharacters (handle, range, buffer);
289 str = new string ((char *) chars, 0, len);
292 if (buffer != IntPtr.Zero)
293 Marshal.FreeHGlobal (buffer);
298 public override string ToString ()
301 str = AsString (Handle);
306 public static implicit operator string (CFString str)
308 return str.ToString ();
311 public static implicit operator CFString (string str)
317 internal class CFDictionary : CFObject
319 public CFDictionary (IntPtr handle, bool own) : base (handle, own) { }
321 [DllImport (CoreFoundationLibrary)]
322 extern static IntPtr CFDictionaryGetValue (IntPtr handle, IntPtr key);
324 public IntPtr GetValue (IntPtr key)
326 return CFDictionaryGetValue (Handle, key);
329 public IntPtr this[IntPtr key] {
331 return GetValue (key);
336 internal class CFUrl : CFObject
338 public CFUrl (IntPtr handle, bool own) : base (handle, own) { }
340 [DllImport (CoreFoundationLibrary)]
341 extern static IntPtr CFURLCreateWithString (IntPtr allocator, IntPtr str, IntPtr baseURL);
343 public static CFUrl Create (string absolute)
345 if (string.IsNullOrEmpty (absolute))
348 CFString str = CFString.Create (absolute);
349 IntPtr handle = CFURLCreateWithString (IntPtr.Zero, str.Handle, IntPtr.Zero);
352 if (handle == IntPtr.Zero)
355 return new CFUrl (handle, true);
359 internal enum CFProxyType {
361 AutoConfigurationUrl,
362 AutoConfigurationJavaScript,
369 internal class CFProxy {
370 //static IntPtr kCFProxyAutoConfigurationHTTPResponseKey;
371 static IntPtr kCFProxyAutoConfigurationJavaScriptKey;
372 static IntPtr kCFProxyAutoConfigurationURLKey;
373 static IntPtr kCFProxyHostNameKey;
374 static IntPtr kCFProxyPasswordKey;
375 static IntPtr kCFProxyPortNumberKey;
376 static IntPtr kCFProxyTypeKey;
377 static IntPtr kCFProxyUsernameKey;
379 //static IntPtr kCFProxyTypeNone;
380 static IntPtr kCFProxyTypeAutoConfigurationURL;
381 static IntPtr kCFProxyTypeAutoConfigurationJavaScript;
382 static IntPtr kCFProxyTypeFTP;
383 static IntPtr kCFProxyTypeHTTP;
384 static IntPtr kCFProxyTypeHTTPS;
385 static IntPtr kCFProxyTypeSOCKS;
389 IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
391 //kCFProxyAutoConfigurationHTTPResponseKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationHTTPResponseKey");
392 kCFProxyAutoConfigurationJavaScriptKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationJavaScriptKey");
393 kCFProxyAutoConfigurationURLKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationURLKey");
394 kCFProxyHostNameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyHostNameKey");
395 kCFProxyPasswordKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPasswordKey");
396 kCFProxyPortNumberKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPortNumberKey");
397 kCFProxyTypeKey = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeKey");
398 kCFProxyUsernameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyUsernameKey");
400 //kCFProxyTypeNone = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeNone");
401 kCFProxyTypeAutoConfigurationURL = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationURL");
402 kCFProxyTypeAutoConfigurationJavaScript = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationJavaScript");
403 kCFProxyTypeFTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeFTP");
404 kCFProxyTypeHTTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTP");
405 kCFProxyTypeHTTPS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTPS");
406 kCFProxyTypeSOCKS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeSOCKS");
408 CFObject.dlclose (handle);
411 CFDictionary settings;
413 internal CFProxy (CFDictionary settings)
415 this.settings = settings;
418 static CFProxyType CFProxyTypeToEnum (IntPtr type)
420 if (type == kCFProxyTypeAutoConfigurationJavaScript)
421 return CFProxyType.AutoConfigurationJavaScript;
423 if (type == kCFProxyTypeAutoConfigurationURL)
424 return CFProxyType.AutoConfigurationUrl;
426 if (type == kCFProxyTypeFTP)
427 return CFProxyType.FTP;
429 if (type == kCFProxyTypeHTTP)
430 return CFProxyType.HTTP;
432 if (type == kCFProxyTypeHTTPS)
433 return CFProxyType.HTTPS;
435 if (type == kCFProxyTypeSOCKS)
436 return CFProxyType.SOCKS;
438 return CFProxyType.None;
442 // AFAICT these get used with CFNetworkExecuteProxyAutoConfiguration*()
444 // TODO: bind CFHTTPMessage so we can return the proper type here.
445 public IntPtr AutoConfigurationHTTPResponse {
446 get { return settings[kCFProxyAutoConfigurationHTTPResponseKey]; }
450 public IntPtr AutoConfigurationJavaScript {
452 if (kCFProxyAutoConfigurationJavaScriptKey == IntPtr.Zero)
455 return settings[kCFProxyAutoConfigurationJavaScriptKey];
459 public IntPtr AutoConfigurationUrl {
461 if (kCFProxyAutoConfigurationURLKey == IntPtr.Zero)
464 return settings[kCFProxyAutoConfigurationURLKey];
468 public string HostName {
470 if (kCFProxyHostNameKey == IntPtr.Zero)
473 return CFString.AsString (settings[kCFProxyHostNameKey]);
477 public string Password {
479 if (kCFProxyPasswordKey == IntPtr.Zero)
482 return CFString.AsString (settings[kCFProxyPasswordKey]);
488 if (kCFProxyPortNumberKey == IntPtr.Zero)
491 return CFNumber.AsInt32 (settings[kCFProxyPortNumberKey]);
495 public CFProxyType ProxyType {
497 if (kCFProxyTypeKey == IntPtr.Zero)
498 return CFProxyType.None;
500 return CFProxyTypeToEnum (settings[kCFProxyTypeKey]);
504 public string Username {
506 if (kCFProxyUsernameKey == IntPtr.Zero)
509 return CFString.AsString (settings[kCFProxyUsernameKey]);
514 internal class CFProxySettings {
515 static IntPtr kCFNetworkProxiesHTTPEnable;
516 static IntPtr kCFNetworkProxiesHTTPPort;
517 static IntPtr kCFNetworkProxiesHTTPProxy;
518 static IntPtr kCFNetworkProxiesProxyAutoConfigEnable;
519 static IntPtr kCFNetworkProxiesProxyAutoConfigJavaScript;
520 static IntPtr kCFNetworkProxiesProxyAutoConfigURLString;
522 static CFProxySettings ()
524 IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
526 kCFNetworkProxiesHTTPEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPEnable");
527 kCFNetworkProxiesHTTPPort = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPPort");
528 kCFNetworkProxiesHTTPProxy = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPProxy");
529 kCFNetworkProxiesProxyAutoConfigEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigEnable");
530 kCFNetworkProxiesProxyAutoConfigJavaScript = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigJavaScript");
531 kCFNetworkProxiesProxyAutoConfigURLString = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigURLString");
533 CFObject.dlclose (handle);
536 CFDictionary settings;
538 public CFProxySettings (CFDictionary settings)
540 this.settings = settings;
543 public CFDictionary Dictionary {
544 get { return settings; }
547 public bool HTTPEnable {
549 if (kCFNetworkProxiesHTTPEnable == IntPtr.Zero)
552 return CFNumber.AsBool (settings[kCFNetworkProxiesHTTPEnable]);
556 public int HTTPPort {
558 if (kCFNetworkProxiesHTTPPort == IntPtr.Zero)
561 return CFNumber.AsInt32 (settings[kCFNetworkProxiesHTTPPort]);
565 public string HTTPProxy {
567 if (kCFNetworkProxiesHTTPProxy == IntPtr.Zero)
570 return CFString.AsString (settings[kCFNetworkProxiesHTTPProxy]);
574 public bool ProxyAutoConfigEnable {
576 if (kCFNetworkProxiesProxyAutoConfigEnable == IntPtr.Zero)
579 return CFNumber.AsBool (settings[kCFNetworkProxiesProxyAutoConfigEnable]);
583 public string ProxyAutoConfigJavaScript {
585 if (kCFNetworkProxiesProxyAutoConfigJavaScript == IntPtr.Zero)
588 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigJavaScript]);
592 public string ProxyAutoConfigURLString {
594 if (kCFNetworkProxiesProxyAutoConfigURLString == IntPtr.Zero)
597 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigURLString]);
602 internal static class CFNetwork {
604 public const string CFNetworkLibrary = "/System/Library/Frameworks/CoreServices.framework/Frameworks/CFNetwork.framework/CFNetwork";
606 public const string CFNetworkLibrary = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
609 [DllImport (CFNetworkLibrary)]
610 // CFArrayRef CFNetworkCopyProxiesForAutoConfigurationScript (CFStringRef proxyAutoConfigurationScript, CFURLRef targetURL);
611 extern static IntPtr CFNetworkCopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, IntPtr targetURL);
613 static CFArray CopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
615 IntPtr native = CFNetworkCopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL.Handle);
617 if (native == IntPtr.Zero)
620 return new CFArray (native, true);
623 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
625 if (proxyAutoConfigurationScript == IntPtr.Zero)
626 throw new ArgumentNullException ("proxyAutoConfigurationScript");
628 if (targetURL == null)
629 throw new ArgumentNullException ("targetURL");
631 CFArray array = CopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
636 CFProxy[] proxies = new CFProxy [array.Count];
637 for (int i = 0; i < proxies.Length; i++) {
638 CFDictionary dict = new CFDictionary (array[i], false);
639 proxies[i] = new CFProxy (dict);
647 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, Uri targetUri)
649 if (proxyAutoConfigurationScript == IntPtr.Zero)
650 throw new ArgumentNullException ("proxyAutoConfigurationScript");
652 if (targetUri == null)
653 throw new ArgumentNullException ("targetUri");
655 CFUrl targetURL = CFUrl.Create (targetUri.AbsoluteUri);
656 CFProxy[] proxies = GetProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
657 targetURL.Dispose ();
662 [DllImport (CFNetworkLibrary)]
663 // CFArrayRef CFNetworkCopyProxiesForURL (CFURLRef url, CFDictionaryRef proxySettings);
664 extern static IntPtr CFNetworkCopyProxiesForURL (IntPtr url, IntPtr proxySettings);
666 static CFArray CopyProxiesForURL (CFUrl url, CFDictionary proxySettings)
668 IntPtr native = CFNetworkCopyProxiesForURL (url.Handle, proxySettings != null ? proxySettings.Handle : IntPtr.Zero);
670 if (native == IntPtr.Zero)
673 return new CFArray (native, true);
676 public static CFProxy[] GetProxiesForURL (CFUrl url, CFProxySettings proxySettings)
678 if (url == null || url.Handle == IntPtr.Zero)
679 throw new ArgumentNullException ("url");
681 if (proxySettings == null)
682 proxySettings = GetSystemProxySettings ();
684 CFArray array = CopyProxiesForURL (url, proxySettings.Dictionary);
689 CFProxy[] proxies = new CFProxy [array.Count];
690 for (int i = 0; i < proxies.Length; i++) {
691 CFDictionary dict = new CFDictionary (array[i], false);
692 proxies[i] = new CFProxy (dict);
700 public static CFProxy[] GetProxiesForUri (Uri uri, CFProxySettings proxySettings)
703 throw new ArgumentNullException ("uri");
705 CFUrl url = CFUrl.Create (uri.AbsoluteUri);
709 CFProxy[] proxies = GetProxiesForURL (url, proxySettings);
715 [DllImport (CFNetworkLibrary)]
716 // CFDictionaryRef CFNetworkCopySystemProxySettings (void);
717 extern static IntPtr CFNetworkCopySystemProxySettings ();
719 public static CFProxySettings GetSystemProxySettings ()
721 IntPtr native = CFNetworkCopySystemProxySettings ();
723 if (native == IntPtr.Zero)
726 var dict = new CFDictionary (native, true);
728 return new CFProxySettings (dict);
731 class CFWebProxy : IWebProxy {
732 ICredentials credentials;
739 public ICredentials Credentials {
740 get { return credentials; }
742 userSpecified = true;
747 static Uri GetProxyUri (CFProxy proxy, out NetworkCredential credentials)
751 switch (proxy.ProxyType) {
752 case CFProxyType.FTP:
755 case CFProxyType.HTTP:
756 case CFProxyType.HTTPS:
757 protocol = "http://";
764 string username = proxy.Username;
765 string password = proxy.Password;
766 string hostname = proxy.HostName;
767 int port = proxy.Port;
770 if (username != null)
771 credentials = new NetworkCredential (username, password);
775 uri = protocol + hostname + (port != 0 ? ':' + port.ToString () : string.Empty);
777 return new Uri (uri, UriKind.Absolute);
780 static Uri GetProxyUriFromScript (IntPtr script, Uri targetUri, out NetworkCredential credentials)
782 CFProxy[] proxies = CFNetwork.GetProxiesForAutoConfigurationScript (script, targetUri);
784 if (proxies == null) {
789 for (int i = 0; i < proxies.Length; i++) {
790 switch (proxies[i].ProxyType) {
791 case CFProxyType.HTTPS:
792 case CFProxyType.HTTP:
793 case CFProxyType.FTP:
794 // create a Uri based on the hostname/port/etc info
795 return GetProxyUri (proxies[i], out credentials);
796 case CFProxyType.SOCKS:
798 // unsupported proxy type, try the next one
800 case CFProxyType.None:
801 // no proxy should be used
812 public Uri GetProxy (Uri targetUri)
814 NetworkCredential credentials = null;
817 if (targetUri == null)
818 throw new ArgumentNullException ("targetUri");
821 CFProxySettings settings = CFNetwork.GetSystemProxySettings ();
822 CFProxy[] proxies = CFNetwork.GetProxiesForUri (targetUri, settings);
824 if (proxies != null) {
825 for (int i = 0; i < proxies.Length && proxy == null; i++) {
826 switch (proxies[i].ProxyType) {
827 case CFProxyType.AutoConfigurationJavaScript:
828 proxy = GetProxyUriFromScript (proxies[i].AutoConfigurationJavaScript, targetUri, out credentials);
830 case CFProxyType.AutoConfigurationUrl:
831 // unsupported proxy type (requires fetching script from remote url)
833 case CFProxyType.HTTPS:
834 case CFProxyType.HTTP:
835 case CFProxyType.FTP:
836 // create a Uri based on the hostname/port/etc info
837 proxy = GetProxyUri (proxies[i], out credentials);
839 case CFProxyType.SOCKS:
840 // unsupported proxy type, try the next one
842 case CFProxyType.None:
843 // no proxy should be used
850 // no supported proxies for this Uri, fall back to trying to connect to targetUri directly
857 // ignore errors while retrieving proxy data
862 this.credentials = credentials;
867 public bool IsBypassed (Uri targetUri)
869 if (targetUri == null)
870 throw new ArgumentNullException ("targetUri");
872 return GetProxy (targetUri) == targetUri;
876 public static IWebProxy GetDefaultProxy ()
878 return new CFWebProxy ();