Skip to content

Commit

Permalink
Load proxy info from environment variables rather than storing in config
Browse files Browse the repository at this point in the history
  • Loading branch information
t1m0thyj committed May 6, 2020
1 parent 3f44639 commit fb71d7d
Show file tree
Hide file tree
Showing 9 changed files with 395 additions and 101 deletions.
2 changes: 1 addition & 1 deletion src/DownloadDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public DownloadDialog()
this.FormClosing += OnFormClosing;
ThemeLoader.taskbarHandle = this.Handle;

ProxyServer.ApplyProxyToClient(wc);
ProxyWrapper.ApplyProxyToClient(wc);
wc.DownloadProgressChanged += OnDownloadProgressChanged;
wc.DownloadFileCompleted += OnDownloadFileCompleted;
}
Expand Down
339 changes: 339 additions & 0 deletions src/HttpEnvironmentProxy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,339 @@
// 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.
// https://github.com/dotnet/runtime/blob/master/LICENSE.TXT

using System;
using System.Net.Http;
using System.Net;
using System.Collections.Generic;

namespace WinDynamicDesktop
{
internal sealed class HttpEnvironmentProxyCredentials : ICredentials
{
// Wrapper class for cases when http and https has different authentication.
private readonly NetworkCredential _httpCred;
private readonly NetworkCredential _httpsCred;
private readonly Uri _httpProxy;
private readonly Uri _httpsProxy;

public HttpEnvironmentProxyCredentials(Uri httpProxy, NetworkCredential httpCred,
Uri httpsProxy, NetworkCredential httpsCred)
{
_httpCred = httpCred;
_httpsCred = httpsCred;
_httpProxy = httpProxy;
_httpsProxy = httpsProxy;
}

public NetworkCredential GetCredential(Uri uri, string authType)
{
if (uri == null)
{
return null;
}
return uri.Equals(_httpProxy) ? _httpCred :
uri.Equals(_httpsProxy) ? _httpsCred : null;
}

public static HttpEnvironmentProxyCredentials TryCreate(Uri httpProxy, Uri httpsProxy)
{
NetworkCredential httpCred = null;
NetworkCredential httpsCred = null;

if (httpProxy != null)
{
httpCred = GetCredentialsFromString(httpProxy.UserInfo);
}
if (httpsProxy != null)
{
httpsCred = GetCredentialsFromString(httpsProxy.UserInfo);
}
if (httpCred == null && httpsCred == null)
{
return null;
}
return new HttpEnvironmentProxyCredentials(httpProxy, httpCred, httpsProxy, httpsCred);
}

/// <summary>
/// Converts string containing user:password to NetworkCredential object
/// </summary>
private static NetworkCredential GetCredentialsFromString(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return null;
}

value = Uri.UnescapeDataString(value);

string password = "";
string domain = null;
int idx = value.IndexOf(':');
if (idx != -1)
{
password = value.Substring(idx + 1);
value = value.Substring(0, idx);
}

idx = value.IndexOf('\\');
if (idx != -1)
{
domain = value.Substring(0, idx);
value = value.Substring(idx + 1);
}

return new NetworkCredential(value, password, domain);
}
}

internal sealed class HttpEnvironmentProxy : IWebProxy
{
private const string EnvAllProxyUC = "ALL_PROXY";
private const string EnvAllProxyLC = "all_proxy";
private const string EnvHttpProxyLC = "http_proxy";
private const string EnvHttpProxyUC = "HTTP_PROXY";
private const string EnvHttpsProxyLC = "https_proxy";
private const string EnvHttpsProxyUC = "HTTPS_PROXY";
private const string EnvNoProxyLC = "no_proxy";
private const string EnvNoProxyUC = "NO_PROXY";
private const string EnvCGI = "GATEWAY_INTERFACE"; // Running in a CGI environment.

private Uri _httpProxyUri; // String URI for HTTP requests
private Uri _httpsProxyUri; // String URI for HTTPS requests
private string[] _bypass = null;// list of domains not to proxy
private ICredentials _credentials;

public static bool TryCreate(out IWebProxy proxy)
{
// Get environmental variables. Protocol specific take precedence over
// general all_*, lower case variable has precedence over upper case.
// Note that curl uses HTTPS_PROXY but not HTTP_PROXY.

Uri httpProxy = GetUriFromString(Environment.GetEnvironmentVariable(EnvHttpProxyLC));
if (httpProxy == null && Environment.GetEnvironmentVariable(EnvCGI) == null)
{
httpProxy = GetUriFromString(Environment.GetEnvironmentVariable(EnvHttpProxyUC));
}

Uri httpsProxy = GetUriFromString(Environment.GetEnvironmentVariable(EnvHttpsProxyLC)) ??
GetUriFromString(Environment.GetEnvironmentVariable(EnvHttpsProxyUC));

if (httpProxy == null || httpsProxy == null)
{
Uri allProxy = GetUriFromString(Environment.GetEnvironmentVariable(EnvAllProxyLC)) ??
GetUriFromString(Environment.GetEnvironmentVariable(EnvAllProxyUC));

if (httpProxy == null)
{
httpProxy = allProxy;
}
if (httpsProxy == null)
{
httpsProxy = allProxy;
}
}

// Do not instantiate if nothing is set.
// Caller may pick some other proxy type.
if (httpProxy == null && httpsProxy == null)
{
proxy = null;
return false;
}

string noProxy = Environment.GetEnvironmentVariable(EnvNoProxyLC) ??
Environment.GetEnvironmentVariable(EnvNoProxyUC);
proxy = new HttpEnvironmentProxy(httpProxy, httpsProxy, noProxy);

return true;
}

private HttpEnvironmentProxy(Uri httpProxy, Uri httpsProxy, string bypassList)
{
_httpProxyUri = httpProxy;
_httpsProxyUri = httpsProxy;

_credentials = HttpEnvironmentProxyCredentials.TryCreate(httpProxy, httpsProxy);

if (!string.IsNullOrWhiteSpace(bypassList))
{
string[] list = bypassList.Split(',');
List<string> tmpList = new List<string>(list.Length);

foreach (string value in list)
{
string tmp = value.Trim();
if (tmp.Length > 0)
{
tmpList.Add(tmp);
}
}
if (tmpList.Count > 0)
{
_bypass = tmpList.ToArray();
}
}
}

/// <summary>
/// This function will evaluate given string and it will try to convert
/// it to Uri object. The string could contain URI fragment, IP address and port
/// tuple or just IP address or name. It will return null if parsing fails.
/// </summary>
private static Uri GetUriFromString(string value)
{
if (string.IsNullOrEmpty(value))
{
return null;
}
if (value.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
{
value = value.Substring(7);
}

string user = null;
string password = null;
ushort port = 80;
string host = null;

// Check if there is authentication part with user and possibly password.
// Curl accepts raw text and that may break URI parser.
int separatorIndex = value.LastIndexOf('@');
if (separatorIndex != -1)
{
string auth = value.Substring(0, separatorIndex);

// The User and password may or may not be URL encoded.
// Curl seems to accept both. To match that,
// we do opportunistic decode and we use original string if it fails.
try
{
auth = Uri.UnescapeDataString(auth);
}
catch { };

value = value.Substring(separatorIndex + 1);
separatorIndex = auth.IndexOf(':');
if (separatorIndex == -1)
{
user = auth;
}
else
{
user = auth.Substring(0, separatorIndex);
password = auth.Substring(separatorIndex + 1);
}
}

int ipV6AddressEnd = value.IndexOf(']');
separatorIndex = value.LastIndexOf(':');
// No ':' or it is part of IPv6 address.
if (separatorIndex == -1 || (ipV6AddressEnd != -1 && separatorIndex < ipV6AddressEnd))
{
host = value;
}
else
{
host = value.Substring(0, separatorIndex);
int endIndex = separatorIndex + 1;
// Strip any trailing characters after port number.
while (endIndex < value.Length)
{
if (!char.IsDigit(value[endIndex]))
{
break;
}
endIndex += 1;
}

if (!ushort.TryParse(value.Substring(separatorIndex + 1, endIndex - separatorIndex - 1), out port))
{
return null;
}
}

try
{
UriBuilder ub = new UriBuilder("http", host, port);
if (user != null)
{
ub.UserName = Uri.EscapeDataString(user);
}

if (password != null)
{
ub.Password = Uri.EscapeDataString(password);
}

return ub.Uri;
}
catch { };
return null;
}

/// <summary>
/// This function returns true if given Host match bypass list.
/// Note, that the list is common for http and https.
/// </summary>
private bool IsMatchInBypassList(Uri input)
{
if (_bypass != null)
{
foreach (string s in _bypass)
{
if (s[0] == '.')
{
// This should match either domain it self or any subdomain or host
// .foo.com will match foo.com it self or *.foo.com
if ((s.Length - 1) == input.Host.Length &&
string.Compare(s, 1, input.Host, 0, input.Host.Length, StringComparison.OrdinalIgnoreCase) == 0)
{
return true;
}
else if (input.Host.EndsWith(s, StringComparison.OrdinalIgnoreCase))
{
return true;
}

}
else
{
if (string.Equals(s, input.Host, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
}
}
return false;
}

/// <summary>
/// Gets the proxy URI. (iWebProxy interface)
/// </summary>
public Uri GetProxy(Uri uri)
{
return uri.Scheme == Uri.UriSchemeHttp ? _httpProxyUri : _httpsProxyUri;
}

/// <summary>
/// Checks if URI is subject to proxy or not.
/// </summary>
public bool IsBypassed(Uri uri)
{
return GetProxy(uri) == null ? true : IsMatchInBypassList(uri);
}

public ICredentials Credentials
{
get
{
return _credentials;
}
set { throw new NotSupportedException(); }
}
}
}
1 change: 0 additions & 1 deletion src/JsonConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ public class AppConfig : INotifyPropertyChanged
public int sunriseSunsetDuration { get; set; }
public bool fullScreenPause { get; set; }
public bool enableScripts { get; set; }
public string webProxy { get; set; }
}

public class ThemeConfig
Expand Down
4 changes: 2 additions & 2 deletions src/Localization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public static void LoadLocaleFromFile()
private static void LoadLocaleFromWeb()
{
var client = new RestClient("https://api.poeditor.com");
ProxyServer.ApplyProxyToClient(client);
ProxyWrapper.ApplyProxyToClient(client);

var request = new RestRequest("/v2/projects/export", Method.POST);
request.AddParameter("api_token", JsonConfig.settings.poeditorApiToken);
Expand All @@ -126,7 +126,7 @@ private static void LoadLocaleFromWeb()

using (WebClient wc = new WebClient())
{
ProxyServer.ApplyProxyToClient(client);
ProxyWrapper.ApplyProxyToClient(client);
byte[] moBinary = wc.DownloadData(response.Data.result.url);

using (Stream stream = new MemoryStream(moBinary))
Expand Down
2 changes: 1 addition & 1 deletion src/LocationIQ.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ private static void HandleLocationSuccess(LocationIQData data, ScheduleDialog di
public static void GetLocationData(string locationStr, ScheduleDialog dialog)
{
var client = new RestClient("https://us1.locationiq.org");
ProxyServer.ApplyProxyToClient(client);
ProxyWrapper.ApplyProxyToClient(client);

var request = new RestRequest("v1/search.php");
request.AddParameter("key", apiKey);
Expand Down
Loading

0 comments on commit fb71d7d

Please sign in to comment.