[System] Remove any CFNetwork usage from the watchOS profile. Fixes #45847.
[mono-project.git] / mcs / class / referencesource / System / net / System / Net / webproxy.cs
blobb61133a980a9b7703f1a428ec686b7a199bd1d55
1 //------------------------------------------------------------------------------
2 // <copyright file="webproxy.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
7 namespace System.Net {
8 using System.Net.NetworkInformation;
9 using System.Globalization;
10 using System.Security.Permissions;
11 using System.Text;
12 using System.Text.RegularExpressions;
13 using System.Collections;
14 using System.Runtime.Serialization;
15 using System.Collections.Generic;
16 using System.Diagnostics.CodeAnalysis;
18 class WebProxyData {
19 internal bool bypassOnLocal;
20 internal bool automaticallyDetectSettings;
21 internal Uri proxyAddress;
22 internal Hashtable proxyHostAddresses;
23 internal Uri scriptLocation;
24 #if USE_WINIET_AUTODETECT_CACHE
25 internal Uri lkgScriptLocation;
26 #endif
27 internal ArrayList bypassList;
30 // Handles default proxy setting implementation for the Http proxy.
32 // The following order is used when determinig proxy settings:
33 // 1. web.config/app.config: if available, use settings specified in <system.net><defaultProxy>
34 // 2. If the config file doesn't contain proxy settings, read the IE proxy settings
36 // If the IE proxy settings contain invalid settings (e.g. "invalid;host" - note the semicolon), then
37 // a FormatException will be thrown.
38 [Serializable]
39 public class WebProxy : IAutoWebProxy, ISerializable {
40 // these are settable by the user
41 private bool _UseRegistry; // This is just around for serialization. Can we get rid of it?
42 private bool _BypassOnLocal;
43 private bool m_EnableAutoproxy;
44 private Uri _ProxyAddress;
45 private ArrayList _BypassList;
46 private ICredentials _Credentials;
48 // these are computed on the fly
49 private Regex[] _RegExBypassList;
50 private Hashtable _ProxyHostAddresses;
52 /// <devdoc>
53 /// <para>[To be supplied.]</para>
54 /// </devdoc>
55 public WebProxy()
56 : this((Uri) null, false, null, null) {
59 /// <devdoc>
60 /// <para>[To be supplied.]</para>
61 /// </devdoc>
62 public WebProxy(Uri Address)
63 : this(Address, false, null, null) {
66 /// <devdoc>
67 /// <para>[To be supplied.]</para>
68 /// </devdoc>
69 public WebProxy(Uri Address, bool BypassOnLocal)
70 : this(Address, BypassOnLocal, null, null) {
73 /// <devdoc>
74 /// <para>[To be supplied.]</para>
75 /// </devdoc>
76 public WebProxy(Uri Address, bool BypassOnLocal, string[] BypassList)
77 : this(Address, BypassOnLocal, BypassList, null) {
80 /// <devdoc>
81 /// <para>[To be supplied.]</para>
82 /// </devdoc>
83 public WebProxy(Uri Address, bool BypassOnLocal, string[] BypassList, ICredentials Credentials) {
84 _ProxyAddress = Address;
85 _BypassOnLocal = BypassOnLocal;
86 if (BypassList != null) {
87 _BypassList = new ArrayList(BypassList);
88 UpdateRegExList(true);
90 _Credentials = Credentials;
91 m_EnableAutoproxy = true;
94 /// <devdoc>
95 /// <para>[To be supplied.]</para>
96 /// </devdoc>
97 public WebProxy(string Host, int Port)
98 : this(new Uri("http://" + Host + ":" + Port.ToString(CultureInfo.InvariantCulture)), false, null, null) {
101 /// <devdoc>
102 /// <para>[To be supplied.]</para>
103 /// </devdoc>
104 public WebProxy(string Address)
105 : this(CreateProxyUri(Address), false, null, null) {
108 /// <devdoc>
109 /// <para>[To be supplied.]</para>
110 /// </devdoc>
111 public WebProxy(string Address, bool BypassOnLocal)
112 : this(CreateProxyUri(Address), BypassOnLocal, null, null) {
115 /// <devdoc>
116 /// <para>[To be supplied.]</para>
117 /// </devdoc>
118 public WebProxy(string Address, bool BypassOnLocal, string[] BypassList)
119 : this(CreateProxyUri(Address), BypassOnLocal, BypassList, null) {
122 /// <devdoc>
123 /// <para>[To be supplied.]</para>
124 /// </devdoc>
125 public WebProxy(string Address, bool BypassOnLocal, string[] BypassList, ICredentials Credentials)
126 : this(CreateProxyUri(Address), BypassOnLocal, BypassList, Credentials) {
129 /// <devdoc>
130 /// <para>[To be supplied.]</para>
131 /// </devdoc>
132 public Uri Address {
133 get {
134 #if !FEATURE_PAL
135 CheckForChanges();
136 #endif // !FEATURE_PAL
137 return _ProxyAddress;
139 set {
140 _UseRegistry = false;
141 DeleteScriptEngine();
142 _ProxyHostAddresses = null; // hash list of proxies
143 _ProxyAddress = value;
147 /// <devdoc>
148 /// <para>[To be supplied.]</para>
149 /// </devdoc>
150 internal bool AutoDetect
152 set {
153 GlobalLog.Assert(_UseRegistry == false, "Cannot set AutoDetect if we are using registry for proxy settings");
154 GlobalLog.Assert(m_EnableAutoproxy, "WebProxy#{0}::.ctor()|Cannot set AutoDetect if usesystemdefault is set.", ValidationHelper.HashString(this));
156 if (ScriptEngine == null)
158 ScriptEngine = new AutoWebProxyScriptEngine(this, false);
160 ScriptEngine.AutomaticallyDetectSettings = value;
164 /// <devdoc>
165 /// <para>[To be supplied.]</para>
166 /// </devdoc>
167 internal Uri ScriptLocation {
168 set {
169 GlobalLog.Assert(value != null, "Cannot set ScriptLocation to null");
170 GlobalLog.Assert(_UseRegistry == false, "Cannot set AutoDetect if we are using registry for proxy settings");
171 GlobalLog.Assert(m_EnableAutoproxy, "WebProxy#{0}::.ctor()|Cannot set ScriptLocation if usesystemdefault is set.", ValidationHelper.HashString(this));
173 if (ScriptEngine == null)
175 ScriptEngine = new AutoWebProxyScriptEngine(this, false);
177 ScriptEngine.AutomaticConfigurationScript = value;
181 /// <devdoc>
182 /// <para>[To be supplied.]</para>
183 /// </devdoc>
184 public bool BypassProxyOnLocal {
185 get {
186 #if !FEATURE_PAL
187 CheckForChanges();
188 #endif // !FEATURE_PAL
189 return _BypassOnLocal;
191 set {
192 _UseRegistry = false;
193 DeleteScriptEngine();
194 _BypassOnLocal = value;
198 /// <devdoc>
199 /// <para>[To be supplied.]</para>
200 /// </devdoc>
201 public string[] BypassList {
202 get {
203 #if !FEATURE_PAL
204 CheckForChanges();
205 #endif // !FEATURE_PAL
206 if (_BypassList == null) {
207 _BypassList = new ArrayList();
209 return (string[])_BypassList.ToArray(typeof(string));
211 set {
212 _UseRegistry = false;
213 DeleteScriptEngine();
214 _BypassList = new ArrayList(value);
215 UpdateRegExList(true);
219 /// <devdoc>
220 /// <para>[To be supplied.]</para>
221 /// </devdoc>
222 public ICredentials Credentials {
223 get {
224 return _Credentials;
226 set {
227 _Credentials = value;
231 /// <devdoc>
232 /// <para>Sets Credentials to CredentialCache.DefaultCredentials</para>
233 /// </devdoc>
234 public bool UseDefaultCredentials {
235 get {
236 return (Credentials is SystemNetworkCredential) ? true : false;
238 set {
239 _Credentials = value ? CredentialCache.DefaultCredentials : null;
243 /// <devdoc>
244 /// <para>[To be supplied.]</para>
245 /// </devdoc>
246 public ArrayList BypassArrayList {
247 get {
248 #if !FEATURE_PAL
249 CheckForChanges();
250 #endif // !FEATURE_PAL
251 if ( _BypassList == null ) {
252 _BypassList = new ArrayList();
254 return _BypassList;
258 internal void CheckForChanges() {
259 if (ScriptEngine != null)
261 ScriptEngine.CheckForChanges();
265 /// <devdoc>
266 /// <para>[To be supplied.]</para>
267 /// </devdoc>
268 public Uri GetProxy(Uri destination) {
269 GlobalLog.Print("WebProxy#" + ValidationHelper.HashString(this) + "::GetProxy() destination:" + ValidationHelper.ToString(destination));
270 if (destination == null)
272 throw new ArgumentNullException("destination");
275 Uri result;
276 if (GetProxyAuto(destination, out result)) {
277 return result;
279 if (IsBypassedManual(destination)) {
280 return destination;
282 Hashtable proxyHostAddresses = _ProxyHostAddresses;
283 Uri proxy = proxyHostAddresses!=null ? proxyHostAddresses[destination.Scheme] as Uri : _ProxyAddress;
284 return proxy!=null? proxy : destination;
288 // CreateProxyUri - maps string to Uri
291 private static Uri CreateProxyUri(string address) {
292 if (address == null) {
293 return null;
295 if (address.IndexOf("://") == -1) {
296 address = "http://" + address;
298 return new Uri(address);
302 // UpdateRegExList - Update internal _RegExBypassList
303 // warning - can throw if the RegEx doesn't parse??
305 private void UpdateRegExList(bool canThrow) {
306 Regex[] regExBypassList = null;
307 ArrayList bypassList = _BypassList;
308 try {
309 if ( bypassList != null && bypassList.Count > 0 ) {
310 regExBypassList = new Regex[bypassList.Count];
311 for (int i = 0; i < bypassList.Count; i++ ) {
312 regExBypassList[i] = new Regex((string)bypassList[i], RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
316 catch {
317 if (!canThrow) {
318 _RegExBypassList = null;
319 return;
321 throw;
323 // only update here, cause it could throw earlier in the loop
324 _RegExBypassList = regExBypassList;
328 // IsMatchInBypassList - match input against _RegExBypassList
330 private bool IsMatchInBypassList(Uri input) {
331 UpdateRegExList(false);
332 if ( _RegExBypassList == null ) {
333 return false;
335 string matchUriString = input.Scheme + "://" + input.Host + (!input.IsDefaultPort ? (":"+input.Port) : "" );
336 for (int i = 0; i < _BypassList.Count; i++ ) {
337 if (_RegExBypassList[i].IsMatch(matchUriString)) {
338 return true;
341 return false;
344 /// <devdoc>
345 /// Determines if the host Uri should be routed locally or go through the proxy.
346 /// </devdoc>
347 private bool IsLocal(Uri host) {
348 string hostString = host.Host;
350 IPAddress hostAddress;
351 if (IPAddress.TryParse(hostString, out hostAddress))
353 return (IPAddress.IsLoopback(hostAddress) || NclUtilities.IsAddressLocal(hostAddress));
356 int dot = hostString.IndexOf('.');
358 // No dot? Local.
359 if (dot == -1)
361 return true;
364 // If it matches the primary domain, it's local. (Whether or not the hostname matches.)
365 string local = "." + IPGlobalProperties.InternalGetIPGlobalProperties().DomainName;
366 if (local != null && local.Length == (hostString.Length - dot) &&
367 string.Compare(local, 0, hostString, dot, local.Length, StringComparison.OrdinalIgnoreCase ) == 0) {
368 return true;
370 return false;
373 /// <devdoc>
374 /// Determines if the host Uri should be routed locally or go through a proxy.
375 /// </devdoc>
376 private bool IsLocalInProxyHash(Uri host) {
377 Hashtable proxyHostAddresses = _ProxyHostAddresses;
378 if (proxyHostAddresses != null) {
379 Uri proxy = (Uri) proxyHostAddresses[host.Scheme];
380 if (proxy == null) {
381 return true; // no proxy entry for this scheme, then bypass
384 return false;
388 /// <devdoc>
389 /// <para>[To be supplied.]</para>
390 /// </devdoc>
391 public bool IsBypassed(Uri host) {
392 GlobalLog.Print("WebProxy#" + ValidationHelper.HashString(this) + "::IsBypassed() destination:" + ValidationHelper.ToString(host));
393 if (host == null)
395 throw new ArgumentNullException("host");
398 bool result;
399 if (IsBypassedAuto(host, out result)) {
400 return result;
402 return IsBypassedManual(host);
405 private bool IsBypassedManual(Uri host) {
406 if (host.IsLoopback) {
407 return true; // bypass localhost from using a proxy.
409 return (_ProxyAddress==null && _ProxyHostAddresses==null) || (_BypassOnLocal && IsLocal(host)) || IsMatchInBypassList(host) || IsLocalInProxyHash(host);
412 /// <devdoc>
413 /// <para>[To be supplied.]</para>
414 /// </devdoc>
415 [Obsolete("This method has been deprecated. Please use the proxy selected for you by default. http://go.microsoft.com/fwlink/?linkid=14202")]
416 public static WebProxy GetDefaultProxy() {
417 #if FEATURE_MONO_CAS
418 ExceptionHelper.WebPermissionUnrestricted.Demand();
419 #endif
420 return new WebProxy(true);
424 // ISerializable constructor
426 /// <devdoc>
427 /// <para>[To be supplied.]</para>
428 /// </devdoc>
429 protected WebProxy(SerializationInfo serializationInfo, StreamingContext streamingContext) {
430 // first check for useRegistry on the serialized proxy
431 bool useRegistry = false;
432 try {
433 useRegistry = serializationInfo.GetBoolean("_UseRegistry");
435 catch {
437 if (useRegistry) {
438 // just make the proxy advanced, don't populate with any settings
439 // note - this will happen in the context of the user performing the deserialization (their proxy settings get read)
440 #if FEATURE_MONO_CAS
441 ExceptionHelper.WebPermissionUnrestricted.Demand();
442 #endif
443 UnsafeUpdateFromRegistry();
444 return;
446 // normal proxy
447 _ProxyAddress = (Uri)serializationInfo.GetValue("_ProxyAddress", typeof(Uri));
448 _BypassOnLocal = serializationInfo.GetBoolean("_BypassOnLocal");
449 _BypassList = (ArrayList)serializationInfo.GetValue("_BypassList", typeof(ArrayList));
450 try {
451 UseDefaultCredentials = serializationInfo.GetBoolean("_UseDefaultCredentials");
453 catch {
458 // ISerializable method
460 /// <internalonly/>
461 /// <devdoc>
462 /// <para>[To be supplied.]</para>
463 /// </devdoc>
464 [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.dll is still using pre-v4 security model and needs this demand")]
465 [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter, SerializationFormatter=true)]
466 void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext)
468 GetObjectData(serializationInfo, streamingContext);
472 // FxCop: provide a way for derived classes to access this method even if they reimplement ISerializable.
474 [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter=true)]
475 protected virtual void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext)
477 serializationInfo.AddValue("_BypassOnLocal", _BypassOnLocal);
478 serializationInfo.AddValue("_ProxyAddress", _ProxyAddress);
479 serializationInfo.AddValue("_BypassList", _BypassList);
480 serializationInfo.AddValue("_UseDefaultCredentials", UseDefaultCredentials);
481 if (_UseRegistry) {
482 serializationInfo.AddValue("_UseRegistry", true);
487 /// <summary>
488 /// Handles proxy settings by using Internet Explorer based settings,
489 /// keep in mind the security implications when downloading and running
490 /// script from any network source configured in Internet Explorer.
491 /// </summary>
493 private AutoWebProxyScriptEngine m_ScriptEngine;
495 internal AutoWebProxyScriptEngine ScriptEngine {
496 get {
497 return m_ScriptEngine;
499 set {
500 m_ScriptEngine = value;
504 #if MONO
505 public static IWebProxy CreateDefaultProxy ()
507 #if MONOTOUCH_WATCH
508 throw new PlatformNotSupportedException ();
509 #elif MONOTOUCH
510 return Mono.Net.CFNetwork.GetDefaultProxy ();
511 #elif MONODROID
512 // Return the system web proxy. This only works for ICS+.
513 var data = AndroidPlatform.GetDefaultProxy ();
514 if (data != null)
515 return data;
516 #else
517 if (Platform.IsMacOS) {
518 var data = Mono.Net.CFNetwork.GetDefaultProxy ();
519 if (data != null)
520 return data;
522 #endif
524 return new WebProxy (true);
526 #endif
528 // This constructor is used internally to make WebProxies that read their state from the registry.
530 internal WebProxy(bool enableAutoproxy)
532 m_EnableAutoproxy = enableAutoproxy;
533 UnsafeUpdateFromRegistry();
536 internal void DeleteScriptEngine() {
537 if (ScriptEngine != null) {
538 ScriptEngine.Close();
539 ScriptEngine = null;
543 internal void UnsafeUpdateFromRegistry() {
544 GlobalLog.Assert(!_UseRegistry, "WebProxy#{0}::UnsafeUpdateFromRegistry()|_UseRegistry ScriptEngine#{1}", ValidationHelper.HashString(this), ValidationHelper.HashString(m_ScriptEngine));
545 _UseRegistry = true;
546 #if !FEATURE_PAL || !MOBILE
547 ScriptEngine = new AutoWebProxyScriptEngine(this, true);
548 WebProxyData webProxyData = ScriptEngine.GetWebProxyData();
550 Update(webProxyData);
551 #endif
554 internal void Update(WebProxyData webProxyData) {
555 #if TRAVE
556 GlobalLog.Print("WebProxy#" + ValidationHelper.HashString(this) + "::Update() Before " + DumpIWebProxy(this));
557 #endif
558 // update needs to happen atomically
559 lock (this) {
560 _BypassOnLocal = webProxyData.bypassOnLocal;
561 _ProxyAddress = webProxyData.proxyAddress;
562 _ProxyHostAddresses = webProxyData.proxyHostAddresses;
563 _BypassList = webProxyData.bypassList;
565 ScriptEngine.AutomaticallyDetectSettings = m_EnableAutoproxy && webProxyData.automaticallyDetectSettings;
566 ScriptEngine.AutomaticConfigurationScript = m_EnableAutoproxy ? webProxyData.scriptLocation : null;
568 #if TRAVE
569 GlobalLog.Print("WebProxy#" + ValidationHelper.HashString(this) + "::Update() After " + DumpIWebProxy(this));
570 #endif
573 /// <devdoc>
574 /// <para>
575 /// We really didn't want to expose this. IWebProxy is kind of broken so we needed
576 /// a different way of calling into IsBypassed/GetProxy with a single method call.
577 /// We need to make it public though, so it is. This API will return null if
578 /// the proxy is to be bypassed, otherwise it returns an array of Uri to proxise
579 /// that may be used to access the destination. If an entry in the array is null
580 /// we want to try a direct access. Today we only attempt using the first entry.
581 /// </para>
582 /// </devdoc>
583 ProxyChain IAutoWebProxy.GetProxies(Uri destination) {
584 GlobalLog.Print("WebProxy#" + ValidationHelper.HashString(this) + "::GetProxies() destination:" + ValidationHelper.ToString(destination));
585 if (destination == null)
587 throw new ArgumentNullException("destination");
589 return new ProxyScriptChain(this, destination);
592 #if TRAVE
593 internal static string DumpIWebProxy(IWebProxy proxy) {
594 StringBuilder stringBuilder = new StringBuilder();
595 stringBuilder.Append(" Type: " + ValidationHelper.ToString(proxy.GetType()) + "\r\n");
596 WebProxy webProxy = proxy as WebProxy;
597 if (webProxy!=null) {
598 stringBuilder.Append(" - Address: " + ValidationHelper.ToString(webProxy._ProxyAddress) + "\r\n");
599 stringBuilder.Append(" - BypassProxyOnLocal: " + ValidationHelper.ToString(webProxy._BypassOnLocal) + "\r\n");
601 stringBuilder.Append(" - -------------------------------------------------");
602 return stringBuilder.ToString();
604 #endif
607 // IWebProxy implementation
610 // Get proxies can never return null in the case of ExecutionSuccess.
611 private bool GetProxyAuto(Uri destination, out Uri proxyUri) {
612 GlobalLog.Print("WebProxy#" + ValidationHelper.HashString(this) + "::GetProxyAuto() destination:" + ValidationHelper.ToString(destination));
614 proxyUri = null;
615 if (ScriptEngine == null) {
616 return false;
618 IList<string> proxies = null;
619 if (!ScriptEngine.GetProxies(destination, out proxies)) {
620 return false;
623 // Returning null in case 'proxies.Count == 0' means, no proxy available (incl. DIRECT), the request is prohibited.
624 if (proxies.Count > 0) {
625 if (AreAllBypassed(proxies, true)) {
626 // this is the broken behaviour of IWebProxy. Returning the same destination means bypass
627 proxyUri = destination;
629 else {
630 proxyUri = ProxyUri(proxies[0]);
633 return true;
636 private bool IsBypassedAuto(Uri destination, out bool isBypassed) {
637 GlobalLog.Print("WebProxy#" + ValidationHelper.HashString(this) + "::IsBypassedAuto() destination:" + ValidationHelper.ToString(destination));
639 isBypassed = true;
641 if (ScriptEngine == null) {
642 return false;
644 IList<string> proxyList;
645 if (!ScriptEngine.GetProxies(destination, out proxyList)) {
646 return false;
648 if (proxyList.Count == 0) {
649 isBypassed = false;
651 else {
652 isBypassed = AreAllBypassed(proxyList, true);
654 return true;
657 internal Uri[] GetProxiesAuto(Uri destination, ref int syncStatus)
659 GlobalLog.Print("WebProxy#" + ValidationHelper.HashString(this) + "::GetProxiesAuto() destination:" + ValidationHelper.ToString(destination));
661 if (ScriptEngine == null) {
662 return null;
665 IList<string> proxyList = null;
666 if (!ScriptEngine.GetProxies(destination, out proxyList, ref syncStatus)) {
667 return null;
670 Uri[] proxyUris = null;
671 if (proxyList.Count == 0) {
672 proxyUris = new Uri[] { };
674 else if (AreAllBypassed(proxyList, false)) {
675 proxyUris = new Uri[] { null };
677 else {
678 proxyUris = new Uri[proxyList.Count];
679 for (int i = 0; i < proxyList.Count; i++) {
680 proxyUris[i] = ProxyUri(proxyList[i]);
683 return proxyUris;
686 internal void AbortGetProxiesAuto(ref int syncStatus)
688 if (ScriptEngine != null)
690 ScriptEngine.Abort(ref syncStatus);
694 internal Uri GetProxyAutoFailover(Uri destination)
696 if (IsBypassedManual(destination))
698 return null;
701 Uri proxy = _ProxyAddress;
702 Hashtable proxyHostAddresses = _ProxyHostAddresses;
703 if (proxyHostAddresses != null)
705 proxy = proxyHostAddresses[destination.Scheme] as Uri;
707 return proxy;
710 private static bool AreAllBypassed(IEnumerable<string> proxies, bool checkFirstOnly) {
711 bool isBypassed = true;
713 foreach (string proxy in proxies) {
714 isBypassed = string.IsNullOrEmpty(proxy);
716 if (checkFirstOnly || !isBypassed) {
717 break;
721 return isBypassed;
724 private static Uri ProxyUri(string proxyName) {
725 return proxyName==null || proxyName.Length==0 ? null : new Uri("http://" + proxyName);