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 void Dispose (bool disposing)
93 if (Handle != IntPtr.Zero) {
99 public void Dispose ()
105 internal class CFArray : CFObject
107 public CFArray (IntPtr handle, bool own) : base (handle, own) { }
109 [DllImport (CoreFoundationLibrary)]
110 extern static IntPtr CFArrayCreate (IntPtr allocator, IntPtr values, int numValues, IntPtr callbacks);
111 static readonly IntPtr kCFTypeArrayCallbacks;
115 var handle = dlopen (CoreFoundationLibrary, 0);
116 if (handle == IntPtr.Zero)
120 kCFTypeArrayCallbacks = GetIndirect (handle, "kCFTypeArrayCallBacks");
126 static unsafe CFArray Create (params IntPtr[] values)
129 throw new ArgumentNullException ("values");
131 fixed (IntPtr *pv = values) {
132 IntPtr handle = CFArrayCreate (IntPtr.Zero, (IntPtr) pv, values.Length, kCFTypeArrayCallbacks);
134 return new CFArray (handle, false);
138 public static CFArray Create (params CFObject[] values)
141 throw new ArgumentNullException ("values");
143 IntPtr[] _values = new IntPtr [values.Length];
144 for (int i = 0; i < _values.Length; i++)
145 _values[i] = values[i].Handle;
147 return Create (_values);
150 [DllImport (CoreFoundationLibrary)]
151 extern static int CFArrayGetCount (IntPtr handle);
154 get { return CFArrayGetCount (Handle); }
157 [DllImport (CoreFoundationLibrary)]
158 extern static IntPtr CFArrayGetValueAtIndex (IntPtr handle, int index);
160 public IntPtr this[int index] {
162 return CFArrayGetValueAtIndex (Handle, index);
167 internal class CFNumber : CFObject
169 public CFNumber (IntPtr handle, bool own) : base (handle, own) { }
171 [DllImport (CoreFoundationLibrary)]
172 extern static bool CFNumberGetValue (IntPtr handle, int type, out bool value);
174 public static bool AsBool (IntPtr handle)
178 if (handle == IntPtr.Zero)
181 CFNumberGetValue (handle, 1, out value);
186 public static implicit operator bool (CFNumber number)
188 return AsBool (number.Handle);
191 [DllImport (CoreFoundationLibrary)]
192 extern static bool CFNumberGetValue (IntPtr handle, int type, out int value);
194 public static int AsInt32 (IntPtr handle)
198 if (handle == IntPtr.Zero)
201 CFNumberGetValue (handle, 9, out value);
206 public static implicit operator int (CFNumber number)
208 return AsInt32 (number.Handle);
212 internal struct CFRange {
213 public int Location, Length;
215 public CFRange (int loc, int len)
222 internal class CFString : CFObject
226 public CFString (IntPtr handle, bool own) : base (handle, own) { }
228 [DllImport (CoreFoundationLibrary)]
229 extern static IntPtr CFStringCreateWithCharacters (IntPtr alloc, IntPtr chars, int length);
231 public static CFString Create (string value)
236 fixed (char *ptr = value) {
237 handle = CFStringCreateWithCharacters (IntPtr.Zero, (IntPtr) ptr, value.Length);
241 if (handle == IntPtr.Zero)
244 return new CFString (handle, true);
247 [DllImport (CoreFoundationLibrary)]
248 extern static int CFStringGetLength (IntPtr handle);
255 return CFStringGetLength (Handle);
259 [DllImport (CoreFoundationLibrary)]
260 extern static IntPtr CFStringGetCharactersPtr (IntPtr handle);
262 [DllImport (CoreFoundationLibrary)]
263 extern static IntPtr CFStringGetCharacters (IntPtr handle, CFRange range, IntPtr buffer);
265 public static string AsString (IntPtr handle)
267 if (handle == IntPtr.Zero)
270 int len = CFStringGetLength (handle);
275 IntPtr chars = CFStringGetCharactersPtr (handle);
276 IntPtr buffer = IntPtr.Zero;
278 if (chars == IntPtr.Zero) {
279 CFRange range = new CFRange (0, len);
280 buffer = Marshal.AllocHGlobal (len * 2);
281 CFStringGetCharacters (handle, range, buffer);
288 str = new string ((char *) chars, 0, len);
291 if (buffer != IntPtr.Zero)
292 Marshal.FreeHGlobal (buffer);
297 public override string ToString ()
300 str = AsString (Handle);
305 public static implicit operator string (CFString str)
307 return str.ToString ();
310 public static implicit operator CFString (string str)
316 internal class CFDictionary : CFObject
318 public CFDictionary (IntPtr handle, bool own) : base (handle, own) { }
320 [DllImport (CoreFoundationLibrary)]
321 extern static IntPtr CFDictionaryGetValue (IntPtr handle, IntPtr key);
323 public IntPtr GetValue (IntPtr key)
325 return CFDictionaryGetValue (Handle, key);
328 public IntPtr this[IntPtr key] {
330 return GetValue (key);
335 internal class CFUrl : CFObject
337 public CFUrl (IntPtr handle, bool own) : base (handle, own) { }
339 [DllImport (CoreFoundationLibrary)]
340 extern static IntPtr CFURLCreateWithString (IntPtr allocator, IntPtr str, IntPtr baseURL);
342 public static CFUrl Create (string absolute)
344 if (string.IsNullOrEmpty (absolute))
347 CFString str = CFString.Create (absolute);
348 IntPtr handle = CFURLCreateWithString (IntPtr.Zero, str.Handle, IntPtr.Zero);
351 if (handle == IntPtr.Zero)
354 return new CFUrl (handle, true);
358 internal enum CFProxyType {
360 AutoConfigurationUrl,
361 AutoConfigurationJavaScript,
368 internal class CFProxy {
369 //static IntPtr kCFProxyAutoConfigurationHTTPResponseKey;
370 static IntPtr kCFProxyAutoConfigurationJavaScriptKey;
371 static IntPtr kCFProxyAutoConfigurationURLKey;
372 static IntPtr kCFProxyHostNameKey;
373 static IntPtr kCFProxyPasswordKey;
374 static IntPtr kCFProxyPortNumberKey;
375 static IntPtr kCFProxyTypeKey;
376 static IntPtr kCFProxyUsernameKey;
378 //static IntPtr kCFProxyTypeNone;
379 static IntPtr kCFProxyTypeAutoConfigurationURL;
380 static IntPtr kCFProxyTypeAutoConfigurationJavaScript;
381 static IntPtr kCFProxyTypeFTP;
382 static IntPtr kCFProxyTypeHTTP;
383 static IntPtr kCFProxyTypeHTTPS;
384 static IntPtr kCFProxyTypeSOCKS;
388 IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
390 //kCFProxyAutoConfigurationHTTPResponseKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationHTTPResponseKey");
391 kCFProxyAutoConfigurationJavaScriptKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationJavaScriptKey");
392 kCFProxyAutoConfigurationURLKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationURLKey");
393 kCFProxyHostNameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyHostNameKey");
394 kCFProxyPasswordKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPasswordKey");
395 kCFProxyPortNumberKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPortNumberKey");
396 kCFProxyTypeKey = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeKey");
397 kCFProxyUsernameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyUsernameKey");
399 //kCFProxyTypeNone = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeNone");
400 kCFProxyTypeAutoConfigurationURL = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationURL");
401 kCFProxyTypeAutoConfigurationJavaScript = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationJavaScript");
402 kCFProxyTypeFTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeFTP");
403 kCFProxyTypeHTTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTP");
404 kCFProxyTypeHTTPS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTPS");
405 kCFProxyTypeSOCKS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeSOCKS");
407 CFObject.dlclose (handle);
410 CFDictionary settings;
412 internal CFProxy (CFDictionary settings)
414 this.settings = settings;
417 static CFProxyType CFProxyTypeToEnum (IntPtr type)
419 if (type == kCFProxyTypeAutoConfigurationJavaScript)
420 return CFProxyType.AutoConfigurationJavaScript;
422 if (type == kCFProxyTypeAutoConfigurationURL)
423 return CFProxyType.AutoConfigurationUrl;
425 if (type == kCFProxyTypeFTP)
426 return CFProxyType.FTP;
428 if (type == kCFProxyTypeHTTP)
429 return CFProxyType.HTTP;
431 if (type == kCFProxyTypeHTTPS)
432 return CFProxyType.HTTPS;
434 if (type == kCFProxyTypeSOCKS)
435 return CFProxyType.SOCKS;
437 return CFProxyType.None;
441 // AFAICT these get used with CFNetworkExecuteProxyAutoConfiguration*()
443 // TODO: bind CFHTTPMessage so we can return the proper type here.
444 public IntPtr AutoConfigurationHTTPResponse {
445 get { return settings[kCFProxyAutoConfigurationHTTPResponseKey]; }
449 public IntPtr AutoConfigurationJavaScript {
451 if (kCFProxyAutoConfigurationJavaScriptKey == IntPtr.Zero)
454 return settings[kCFProxyAutoConfigurationJavaScriptKey];
458 public IntPtr AutoConfigurationUrl {
460 if (kCFProxyAutoConfigurationURLKey == IntPtr.Zero)
463 return settings[kCFProxyAutoConfigurationURLKey];
467 public string HostName {
469 if (kCFProxyHostNameKey == IntPtr.Zero)
472 return CFString.AsString (settings[kCFProxyHostNameKey]);
476 public string Password {
478 if (kCFProxyPasswordKey == IntPtr.Zero)
481 return CFString.AsString (settings[kCFProxyPasswordKey]);
487 if (kCFProxyPortNumberKey == IntPtr.Zero)
490 return CFNumber.AsInt32 (settings[kCFProxyPortNumberKey]);
494 public CFProxyType ProxyType {
496 if (kCFProxyTypeKey == IntPtr.Zero)
497 return CFProxyType.None;
499 return CFProxyTypeToEnum (settings[kCFProxyTypeKey]);
503 public string Username {
505 if (kCFProxyUsernameKey == IntPtr.Zero)
508 return CFString.AsString (settings[kCFProxyUsernameKey]);
513 internal class CFProxySettings {
514 static IntPtr kCFNetworkProxiesHTTPEnable;
515 static IntPtr kCFNetworkProxiesHTTPPort;
516 static IntPtr kCFNetworkProxiesHTTPProxy;
517 static IntPtr kCFNetworkProxiesProxyAutoConfigEnable;
518 static IntPtr kCFNetworkProxiesProxyAutoConfigJavaScript;
519 static IntPtr kCFNetworkProxiesProxyAutoConfigURLString;
521 static CFProxySettings ()
523 IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
525 kCFNetworkProxiesHTTPEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPEnable");
526 kCFNetworkProxiesHTTPPort = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPPort");
527 kCFNetworkProxiesHTTPProxy = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPProxy");
528 kCFNetworkProxiesProxyAutoConfigEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigEnable");
529 kCFNetworkProxiesProxyAutoConfigJavaScript = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigJavaScript");
530 kCFNetworkProxiesProxyAutoConfigURLString = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigURLString");
532 CFObject.dlclose (handle);
535 CFDictionary settings;
537 public CFProxySettings (CFDictionary settings)
539 this.settings = settings;
542 public CFDictionary Dictionary {
543 get { return settings; }
546 public bool HTTPEnable {
548 if (kCFNetworkProxiesHTTPEnable == IntPtr.Zero)
551 return CFNumber.AsBool (settings[kCFNetworkProxiesHTTPEnable]);
555 public int HTTPPort {
557 if (kCFNetworkProxiesHTTPPort == IntPtr.Zero)
560 return CFNumber.AsInt32 (settings[kCFNetworkProxiesHTTPPort]);
564 public string HTTPProxy {
566 if (kCFNetworkProxiesHTTPProxy == IntPtr.Zero)
569 return CFString.AsString (settings[kCFNetworkProxiesHTTPProxy]);
573 public bool ProxyAutoConfigEnable {
575 if (kCFNetworkProxiesProxyAutoConfigEnable == IntPtr.Zero)
578 return CFNumber.AsBool (settings[kCFNetworkProxiesProxyAutoConfigEnable]);
582 public string ProxyAutoConfigJavaScript {
584 if (kCFNetworkProxiesProxyAutoConfigJavaScript == IntPtr.Zero)
587 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigJavaScript]);
591 public string ProxyAutoConfigURLString {
593 if (kCFNetworkProxiesProxyAutoConfigURLString == IntPtr.Zero)
596 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigURLString]);
601 internal static class CFNetwork {
603 public const string CFNetworkLibrary = "/System/Library/Frameworks/CoreServices.framework/Frameworks/CFNetwork.framework/CFNetwork";
605 public const string CFNetworkLibrary = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
608 [DllImport (CFNetworkLibrary)]
609 // CFArrayRef CFNetworkCopyProxiesForAutoConfigurationScript (CFStringRef proxyAutoConfigurationScript, CFURLRef targetURL);
610 extern static IntPtr CFNetworkCopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, IntPtr targetURL);
612 static CFArray CopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
614 IntPtr native = CFNetworkCopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL.Handle);
616 if (native == IntPtr.Zero)
619 return new CFArray (native, true);
622 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
624 if (proxyAutoConfigurationScript == IntPtr.Zero)
625 throw new ArgumentNullException ("proxyAutoConfigurationScript");
627 if (targetURL == null)
628 throw new ArgumentNullException ("targetURL");
630 CFArray array = CopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
635 CFProxy[] proxies = new CFProxy [array.Count];
636 for (int i = 0; i < proxies.Length; i++) {
637 CFDictionary dict = new CFDictionary (array[i], false);
638 proxies[i] = new CFProxy (dict);
646 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, Uri targetUri)
648 if (proxyAutoConfigurationScript == IntPtr.Zero)
649 throw new ArgumentNullException ("proxyAutoConfigurationScript");
651 if (targetUri == null)
652 throw new ArgumentNullException ("targetUri");
654 CFUrl targetURL = CFUrl.Create (targetUri.AbsoluteUri);
655 CFProxy[] proxies = GetProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
656 targetURL.Dispose ();
661 [DllImport (CFNetworkLibrary)]
662 // CFArrayRef CFNetworkCopyProxiesForURL (CFURLRef url, CFDictionaryRef proxySettings);
663 extern static IntPtr CFNetworkCopyProxiesForURL (IntPtr url, IntPtr proxySettings);
665 static CFArray CopyProxiesForURL (CFUrl url, CFDictionary proxySettings)
667 IntPtr native = CFNetworkCopyProxiesForURL (url.Handle, proxySettings != null ? proxySettings.Handle : IntPtr.Zero);
669 if (native == IntPtr.Zero)
672 return new CFArray (native, true);
675 public static CFProxy[] GetProxiesForURL (CFUrl url, CFProxySettings proxySettings)
677 if (url == null || url.Handle == IntPtr.Zero)
678 throw new ArgumentNullException ("url");
680 if (proxySettings == null)
681 proxySettings = GetSystemProxySettings ();
683 CFArray array = CopyProxiesForURL (url, proxySettings.Dictionary);
688 CFProxy[] proxies = new CFProxy [array.Count];
689 for (int i = 0; i < proxies.Length; i++) {
690 CFDictionary dict = new CFDictionary (array[i], false);
691 proxies[i] = new CFProxy (dict);
699 public static CFProxy[] GetProxiesForUri (Uri uri, CFProxySettings proxySettings)
702 throw new ArgumentNullException ("uri");
704 CFUrl url = CFUrl.Create (uri.AbsoluteUri);
708 CFProxy[] proxies = GetProxiesForURL (url, proxySettings);
714 [DllImport (CFNetworkLibrary)]
715 // CFDictionaryRef CFNetworkCopySystemProxySettings (void);
716 extern static IntPtr CFNetworkCopySystemProxySettings ();
718 public static CFProxySettings GetSystemProxySettings ()
720 IntPtr native = CFNetworkCopySystemProxySettings ();
722 if (native == IntPtr.Zero)
725 var dict = new CFDictionary (native, true);
727 return new CFProxySettings (dict);
730 class CFWebProxy : IWebProxy {
736 public ICredentials Credentials {
740 static Uri GetProxyUri (CFProxy proxy)
744 switch (proxy.ProxyType) {
745 case CFProxyType.FTP:
748 case CFProxyType.HTTP:
749 case CFProxyType.HTTPS:
750 protocol = "http://";
756 string username = proxy.Username;
757 string password = proxy.Password;
758 string hostname = proxy.HostName;
759 int port = proxy.Port;
763 if (username != null) {
764 if (password != null)
765 userinfo = Uri.EscapeDataString (username) + ':' + Uri.EscapeDataString (password) + '@';
767 userinfo = Uri.EscapeDataString (username) + '@';
769 userinfo = string.Empty;
772 uri = protocol + userinfo + hostname + (port != 0 ? ':' + port.ToString () : string.Empty);
774 return new Uri (uri, UriKind.Absolute);
777 static Uri GetProxyUriFromScript (IntPtr script, Uri targetUri)
779 CFProxy[] proxies = CFNetwork.GetProxiesForAutoConfigurationScript (script, targetUri);
784 for (int i = 0; i < proxies.Length; i++) {
785 switch (proxies[i].ProxyType) {
786 case CFProxyType.HTTPS:
787 case CFProxyType.HTTP:
788 case CFProxyType.FTP:
789 // create a Uri based on the hostname/port/etc info
790 return GetProxyUri (proxies[i]);
791 case CFProxyType.SOCKS:
793 // unsupported proxy type, try the next one
795 case CFProxyType.None:
796 // no proxy should be used
804 public Uri GetProxy (Uri targetUri)
806 if (targetUri == null)
807 throw new ArgumentNullException ("targetUri");
810 CFProxySettings settings = CFNetwork.GetSystemProxySettings ();
811 CFProxy[] proxies = CFNetwork.GetProxiesForUri (targetUri, settings);
817 for (int i = 0; i < proxies.Length; i++) {
818 switch (proxies[i].ProxyType) {
819 case CFProxyType.AutoConfigurationJavaScript:
820 if ((uri = GetProxyUriFromScript (proxies[i].AutoConfigurationJavaScript, targetUri)) != null)
823 case CFProxyType.AutoConfigurationUrl:
824 // unsupported proxy type (requires fetching script from remote url)
826 case CFProxyType.HTTPS:
827 case CFProxyType.HTTP:
828 case CFProxyType.FTP:
829 // create a Uri based on the hostname/port/etc info
830 return GetProxyUri (proxies[i]);
831 case CFProxyType.SOCKS:
832 // unsupported proxy type, try the next one
834 case CFProxyType.None:
835 // no proxy should be used
840 // ignore errors while retrieving proxy data
842 // no supported proxies for this Uri, fall back to trying to connect to targetUri directly.
846 public bool IsBypassed (Uri targetUri)
848 if (targetUri == null)
849 throw new ArgumentNullException ("targetUri");
851 return GetProxy (targetUri) == targetUri;
855 static CFWebProxy defaultWebProxy;
856 public static IWebProxy GetDefaultProxy ()
858 if (defaultWebProxy == null)
859 defaultWebProxy = new CFWebProxy ();
861 return defaultWebProxy;