{
internal sealed class SafeCFArrayHandle : SafeHandle
{
- internal SafeCFArrayHandle()
+ private SafeCFArrayHandle()
: base(IntPtr.Zero, ownsHandle: true)
{
}
+ internal SafeCFArrayHandle(IntPtr handle, bool ownsHandle)
+ : base(handle, ownsHandle)
+ {
+ }
+
protected override bool ReleaseHandle()
{
Interop.CoreFoundation.CFRelease(handle);
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class CoreFoundation
+ {
+ [DllImport(Libraries.CoreFoundationLibrary)]
+ internal static extern IntPtr CFDictionaryGetValue(SafeCFDictionaryHandle handle, IntPtr key);
+ }
+}
+
+namespace Microsoft.Win32.SafeHandles
+{
+ internal sealed class SafeCFDictionaryHandle : SafeHandle
+ {
+ private SafeCFDictionaryHandle()
+ : base(IntPtr.Zero, ownsHandle: true)
+ {
+ }
+
+ internal SafeCFDictionaryHandle(IntPtr handle, bool ownsHandle)
+ : base(handle, ownsHandle)
+ {
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.CoreFoundation.CFRelease(handle);
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class CoreFoundation
+ {
+ internal enum CFNumberType
+ {
+ kCFNumberIntType = 9,
+ }
+
+ [DllImport(Libraries.CoreFoundationLibrary)]
+ private static extern int CFNumberGetValue(IntPtr handle, CFNumberType type, out int value);
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+
+using CFRunLoopSourceRef = System.IntPtr;
+
+internal static partial class Interop
+{
+ internal static partial class CoreFoundation
+ {
+ [DllImport(Libraries.CFNetworkLibrary)]
+ internal static extern SafeCFDictionaryHandle CFNetworkCopySystemProxySettings();
+
+ [DllImport(Libraries.CFNetworkLibrary)]
+ internal static extern SafeCFArrayHandle CFNetworkCopyProxiesForURL(SafeCreateHandle url, SafeCFDictionaryHandle proxySettings);
+
+ internal delegate void CFProxyAutoConfigurationResultCallback(IntPtr client, IntPtr proxyList, IntPtr error);
+
+ [DllImport(Libraries.CFNetworkLibrary)]
+ internal static extern CFRunLoopSourceRef CFNetworkExecuteProxyAutoConfigurationURL(
+ IntPtr proxyAutoConfigURL,
+ SafeCreateHandle targetURL,
+ CFProxyAutoConfigurationResultCallback cb,
+ ref CFStreamClientContext clientContext);
+
+ [DllImport(Libraries.CFNetworkLibrary)]
+ internal static extern CFRunLoopSourceRef CFNetworkExecuteProxyAutoConfigurationScript(
+ IntPtr proxyAutoConfigurationScript,
+ SafeCreateHandle targetURL,
+ CFProxyAutoConfigurationResultCallback cb,
+ ref CFStreamClientContext clientContext);
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct CFStreamClientContext
+ {
+ public IntPtr Version;
+ public IntPtr Info;
+ public IntPtr Retain;
+ public IntPtr Release;
+ public IntPtr CopyDescription;
+ }
+
+ internal class CFProxy
+ {
+ private SafeCFDictionaryHandle _dictionary;
+
+ internal static readonly string kCFProxyTypeAutoConfigurationURL;
+ internal static readonly string kCFProxyTypeAutoConfigurationJavaScript;
+ internal static readonly string kCFProxyTypeFTP;
+ internal static readonly string kCFProxyTypeHTTP;
+ internal static readonly string kCFProxyTypeHTTPS;
+ internal static readonly string kCFProxyTypeSOCKS;
+
+ private static readonly IntPtr kCFProxyAutoConfigurationJavaScriptKey;
+ private static readonly IntPtr kCFProxyAutoConfigurationURLKey;
+ private static readonly IntPtr kCFProxyHostNameKey;
+ private static readonly IntPtr kCFProxyPasswordKey;
+ private static readonly IntPtr kCFProxyPortNumberKey;
+ private static readonly IntPtr kCFProxyTypeKey;
+ private static readonly IntPtr kCFProxyUsernameKey;
+
+ static CFProxy()
+ {
+ IntPtr lib = NativeLibrary.Load(Interop.Libraries.CFNetworkLibrary);
+ if (lib != IntPtr.Zero)
+ {
+ kCFProxyTypeAutoConfigurationURL = LoadCFStringSymbol(lib, "kCFProxyTypeAutoConfigurationURL");
+ kCFProxyTypeAutoConfigurationJavaScript = LoadCFStringSymbol(lib, "kCFProxyTypeAutoConfigurationJavaScript");
+ kCFProxyTypeFTP = LoadCFStringSymbol(lib, "kCFProxyTypeFTP");
+ kCFProxyTypeHTTP = LoadCFStringSymbol(lib, "kCFProxyTypeHTTP");
+ kCFProxyTypeHTTPS = LoadCFStringSymbol(lib, "kCFProxyTypeHTTPS");
+ kCFProxyTypeSOCKS = LoadCFStringSymbol(lib, "kCFProxyTypeSOCKS");
+
+ kCFProxyAutoConfigurationJavaScriptKey = LoadSymbol(lib, "kCFProxyAutoConfigurationJavaScriptKey");
+ kCFProxyAutoConfigurationURLKey = LoadSymbol(lib, "kCFProxyAutoConfigurationURLKey");
+ kCFProxyHostNameKey = LoadSymbol(lib, "kCFProxyHostNameKey");
+ kCFProxyPasswordKey = LoadSymbol(lib, "kCFProxyPasswordKey");
+ kCFProxyPortNumberKey = LoadSymbol(lib, "kCFProxyPortNumberKey");
+ kCFProxyTypeKey = LoadSymbol(lib, "kCFProxyTypeKey");
+ kCFProxyUsernameKey = LoadSymbol(lib, "kCFProxyUsernameKey");
+ }
+ }
+
+ public CFProxy(SafeCFDictionaryHandle dictionary)
+ {
+ _dictionary = dictionary;
+ }
+
+ private static IntPtr LoadSymbol(IntPtr lib, string name)
+ {
+ IntPtr indirect = NativeLibrary.GetExport(lib, name);
+ return indirect == IntPtr.Zero ? IntPtr.Zero : Marshal.ReadIntPtr(indirect);
+ }
+
+ private static string LoadCFStringSymbol(IntPtr lib, string name)
+ {
+ using (SafeCFStringHandle cfString = new SafeCFStringHandle(LoadSymbol(lib, name), false))
+ {
+ Debug.Assert(!cfString.IsInvalid);
+ return Interop.CoreFoundation.CFStringToString(cfString);
+ }
+ }
+
+ private string GetString(IntPtr key)
+ {
+ IntPtr dictValue = CFDictionaryGetValue(_dictionary, key);
+ if (dictValue != IntPtr.Zero)
+ {
+ using (SafeCFStringHandle handle = new SafeCFStringHandle(dictValue, false))
+ {
+ return CFStringToString(handle);
+ }
+ }
+ return null;
+ }
+
+ public string ProxyType => GetString(kCFProxyTypeKey);
+ public string HostName => GetString(kCFProxyHostNameKey);
+ public string Username => GetString(kCFProxyUsernameKey);
+ public string Password => GetString(kCFProxyPasswordKey);
+
+ public int PortNumber
+ {
+ get
+ {
+ IntPtr dictValue = CFDictionaryGetValue(_dictionary, kCFProxyPortNumberKey);
+ if (dictValue != IntPtr.Zero && CFNumberGetValue(dictValue, CFNumberType.kCFNumberIntType, out int value) > 0)
+ {
+ return value;
+ }
+ return -1;
+ }
+ }
+
+ public IntPtr AutoConfigurationURL => CFDictionaryGetValue(_dictionary, kCFProxyAutoConfigurationURLKey);
+ public IntPtr AutoConfigurationJavaScript => CFDictionaryGetValue(_dictionary, kCFProxyAutoConfigurationJavaScriptKey);
+ }
+ }
+}
{
}
+ internal SafeCFStringHandle(IntPtr handle, bool ownsHandle)
+ : base(handle, ownsHandle)
+ {
+ }
+
protected override bool ReleaseHandle()
{
Interop.CoreFoundation.CFRelease(handle);
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class CoreFoundation
+ {
+ [DllImport(Libraries.CoreFoundationLibrary)]
+ private static extern SafeCreateHandle CFURLCreateWithString(
+ IntPtr allocator,
+ SafeCreateHandle str,
+ IntPtr baseUrl);
+
+ internal static SafeCreateHandle CFURLCreateWithString(string url)
+ {
+ Debug.Assert(url != null);
+ using (SafeCreateHandle stringHandle = CFStringCreateWithCString(url))
+ {
+ return CFURLCreateWithString(IntPtr.Zero, stringHandle, IntPtr.Zero);
+ }
+ }
+ }
+}
internal static partial class Libraries
{
internal const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
- internal const string CoreServicesLibrary = "/System/Library/Frameworks/CoreServices.framework/CoreServices";
+ internal const string CoreServicesLibrary = "/System/Library/Frameworks/CoreServices.framework/CoreServices";
+ internal const string CFNetworkLibrary = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
internal const string libproc = "libproc";
internal const string LibSystemCommonCrypto = "/usr/lib/system/libcommonCrypto";
internal const string LibSystemKernel = "/usr/lib/system/libsystem_kernel";
internal extern static void CFRunLoopRun();
/// <summary>
+ /// Runs the current thread’s CFRunLoop object in a particular mode.
+ /// </summary>
+ [DllImport(Interop.Libraries.CoreFoundationLibrary)]
+ internal extern static int CFRunLoopRunInMode(CFStringRef mode, double seconds, int returnAfterSourceHandled);
+
+ /// <summary>
/// Notifies a RunLoop to stop and return control to the execution context that called CFRunLoopRun
/// </summary>
/// <param name="rl">The RunLoop to notify to stop</param>
/// <param name="mode">The run loop mode of rl from which to remove source.</param>
[DllImport(Interop.Libraries.CoreFoundationLibrary)]
internal static extern void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode);
-
+
+ /// <summary>
+ /// Invalidates a CFRunLoopSource object, stopping it from ever firing again.
+ /// </summary>
+ /// <param name="source">The run loop source to invalidate.</param>
+ [DllImport(Interop.Libraries.CoreFoundationLibrary)]
+ internal static extern void CFRunLoopSourceInvalidate(CFRunLoopSourceRef source);
+
/// <summary>
/// Returns a bool that indicates whether the run loop is waiting for an event.
/// </summary>
</ItemGroup>
<!-- SocketsHttpHandler platform parts -->
<ItemGroup Condition=" '$(TargetsUnix)' == 'true' And '$(TargetGroup)' == 'netcoreapp'">
- <Compile Include="System\Net\Http\SocketsHttpHandler\SystemProxyInfo.Unix.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\HttpEnvironmentProxy.cs" />
<Compile Include="$(CommonPath)\System\Net\ContextAwareResult.Unix.cs">
<Link>Common\System\Net\ContextAwareResult.Unix.cs</Link>
<Link>Common\System\Net\Security\Unix\SafeDeleteNegoContext.cs</Link>
</Compile>
</ItemGroup>
+ <ItemGroup Condition=" '$(TargetsUnix)' == 'true' And '$(TargetsOSX)' != 'true' And '$(TargetGroup)' == 'netcoreapp'">
+ <Compile Include="System\Net\Http\SocketsHttpHandler\SystemProxyInfo.Unix.cs" />
+ </ItemGroup>
+ <ItemGroup Condition=" '$(TargetsOSX)' == 'true' And '$(TargetGroup)' == 'netcoreapp'">
+ <Compile Include="System\Net\Http\SocketsHttpHandler\SystemProxyInfo.OSX.cs" />
+ <Compile Include="System\Net\Http\SocketsHttpHandler\MacProxy.cs" />
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFArray.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFArray.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFData.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFData.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFDictionary.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFDictionary.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFProxy.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFProxy.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFUrl.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFUrl.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFString.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFString.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFNumber.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFString.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFError.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFError.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.RunLoop.cs">
+ <Link>Common\Interop\OSX\Interop.RunLoop.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.Libraries.cs">
+ <Link>Common\Interop\OSX\Interop.Libraries.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeCreateHandle.OSX.cs">
+ <Link>Common\Microsoft\Win32\SafeHandles\SafeCreateHandle.OSX.cs</Link>
+ </Compile>
+ </ItemGroup>
<ItemGroup Condition=" '$(TargetsWindows)' == 'true' And '$(TargetGroup)' == 'netcoreapp'">
<Compile Include="System\Net\Http\SocketsHttpHandler\SystemProxyInfo.Windows.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\HttpSystemProxy.cs" />
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Net.Http;
+using System.Net;
+using System.Collections.Generic;
+using Microsoft.Win32.SafeHandles;
+using static Interop.CoreFoundation;
+using static Interop.RunLoop;
+
+using CFRunLoopRef = System.IntPtr;
+using CFRunLoopSourceRef = System.IntPtr;
+
+namespace System.Net.Http
+{
+ internal sealed class MacProxy : IWebProxy
+ {
+ public ICredentials Credentials
+ {
+ get => null;
+ set => throw new NotSupportedException();
+ }
+
+ private static Uri GetProxyUri(string scheme, CFProxy proxy)
+ {
+ var uriBuilder = new UriBuilder(
+ scheme,
+ proxy.HostName,
+ proxy.PortNumber);
+
+ // TODO: Issue #26593 - Credentials are not propagated
+
+ return uriBuilder.Uri;
+ }
+
+ public Uri ExecuteProxyAutoConfiguration(SafeCreateHandle cfurl, CFProxy proxy)
+ {
+ Uri result = null;
+ CFRunLoopRef runLoop = CFRunLoopGetCurrent();
+
+ // Callback that will be called after executing the configuration script
+ CFProxyAutoConfigurationResultCallback cb = (IntPtr client, IntPtr proxyListHandle, IntPtr error) =>
+ {
+ if (proxyListHandle != IntPtr.Zero)
+ {
+ using (var proxyList = new SafeCFArrayHandle(proxyListHandle, false))
+ {
+ long proxyCount = CFArrayGetCount(proxyList);
+ for (int i = 0; i < proxyCount; i++)
+ {
+ IntPtr proxyValue = CFArrayGetValueAtIndex(proxyList, i);
+ using (SafeCFDictionaryHandle proxyDict = new SafeCFDictionaryHandle(proxyValue, false))
+ {
+ CFProxy proxy = new CFProxy(proxyDict);
+ if (proxy.ProxyType == CFProxy.kCFProxyTypeHTTP || proxy.ProxyType == CFProxy.kCFProxyTypeHTTPS)
+ {
+ result = GetProxyUri("http", proxy);
+ break;
+ }
+ }
+ }
+ }
+ }
+ CFRunLoopStop(runLoop);
+ };
+
+ var clientContext = new CFStreamClientContext();
+ CFRunLoopSourceRef loopSource =
+ proxy.ProxyType == CFProxy.kCFProxyTypeAutoConfigurationURL ?
+ CFNetworkExecuteProxyAutoConfigurationURL(proxy.AutoConfigurationURL, cfurl, cb, ref clientContext) :
+ CFNetworkExecuteProxyAutoConfigurationScript(proxy.AutoConfigurationJavaScript, cfurl, cb, ref clientContext);
+
+ using (var mode = CFStringCreateWithCString(typeof(MacProxy).FullName))
+ {
+ IntPtr modeHandle = mode.DangerousGetHandle();
+ CFRunLoopAddSource(runLoop, loopSource, modeHandle);
+ CFRunLoopRunInMode(modeHandle, double.MaxValue, 0);
+ CFRunLoopSourceInvalidate(loopSource);
+ }
+
+ GC.KeepAlive(cb);
+
+ return result;
+ }
+
+ public Uri GetProxy(Uri targetUri)
+ {
+ using (SafeCFDictionaryHandle systemProxySettings = CFNetworkCopySystemProxySettings())
+ using (SafeCreateHandle cfurl = CFURLCreateWithString(targetUri.AbsoluteUri))
+ using (SafeCFArrayHandle proxies = CFNetworkCopyProxiesForURL(cfurl, systemProxySettings))
+ {
+ long proxyCount = CFArrayGetCount(proxies);
+ for (int i = 0; i < proxyCount; i++)
+ {
+ IntPtr proxyValue = CFArrayGetValueAtIndex(proxies, i);
+ using (SafeCFDictionaryHandle proxyDict = new SafeCFDictionaryHandle(proxyValue, false))
+ {
+ CFProxy proxy = new CFProxy(proxyDict);
+
+ if (proxy.ProxyType == CFProxy.kCFProxyTypeAutoConfigurationURL || proxy.ProxyType == CFProxy.kCFProxyTypeAutoConfigurationJavaScript)
+ {
+ Uri result = ExecuteProxyAutoConfiguration(cfurl, proxy);
+ if (result != null)
+ return result;
+ }
+ else if (proxy.ProxyType == CFProxy.kCFProxyTypeHTTP || proxy.ProxyType == CFProxy.kCFProxyTypeHTTPS)
+ {
+ return GetProxyUri("http", proxy);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public bool IsBypassed(Uri targetUri)
+ {
+ if (targetUri == null)
+ throw new ArgumentNullException(nameof(targetUri));
+
+ Uri proxyUri = GetProxy(targetUri);
+ return Equals(proxyUri, targetUri) || proxyUri == null;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Net.Http
+{
+ internal static class SystemProxyInfo
+ {
+ // On Unix we get default proxy configuration from environment variables
+ public static IWebProxy ConstructSystemProxy()
+ {
+ return HttpEnvironmentProxy.TryCreate(out IWebProxy proxy) ? proxy : new MacProxy();
+ }
+ }
+}
+