Added win_http_proxy and win_inet_proxy (#54631)

* Added win_http_proxy and win_inet_proxy

* Fix up docs sanity issues

* removed duplicate doc entry

* Fix docs issues and fix for empty proxy

* Removed <-loopback> for win_http_proxy

* doc changes from review
This commit is contained in:
Jordan Borean
2019-04-05 11:19:30 +10:00
committed by GitHub
parent a39051067b
commit 10a9cf59dd
12 changed files with 2439 additions and 0 deletions

View File

@@ -0,0 +1,267 @@
#!powershell
# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#AnsibleRequires -CSharpUtil Ansible.Basic
#Requires -Module Ansible.ModuleUtils.AddType
$spec = @{
options = @{
bypass = @{ type = "list" }
proxy = @{ type = "raw" }
source = @{ type = "str"; choices = @("ie") }
}
mutually_exclusive = @(
@("proxy", "source"),
@("bypass", "source")
)
required_by = @{
bypass = @("proxy")
}
supports_check_mode = $true
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$proxy = $module.Params.proxy
$bypass = $module.Params.bypass
$source = $module.Params.source
# Parse the raw value, it should be a Dictionary or String
if ($proxy -is [System.Collections.IDictionary]) {
$valid_keys = [System.Collections.Generic.List`1[String]]@("http", "https", "ftp", "socks")
# Check to make sure we don't have any invalid keys in the dict
$invalid_keys = [System.Collections.Generic.List`1[String]]@()
foreach ($k in $proxy.Keys) {
if ($k -notin $valid_keys) {
$invalid_keys.Add($k)
}
}
if ($invalid_keys.Count -gt 0) {
$invalid_keys = $invalid_keys | Sort-Object # So our test assertion doesn't fail due to random ordering
$module.FailJson("Invalid keys found in proxy: $($invalid_keys -join ', '). Valid keys are $($valid_keys -join ', ').")
}
# Build the proxy string in the form 'protocol=host;', the order of valid_keys is also important
$proxy_list = [System.Collections.Generic.List`1[String]]@()
foreach ($k in $valid_keys) {
if ($proxy.ContainsKey($k)) {
$proxy_list.Add("$k=$($proxy.$k)")
}
}
$proxy = $proxy_list -join ";"
} elseif ($null -ne $proxy) {
$proxy = $proxy.ToString()
}
if ($bypass) {
if ([System.String]::IsNullOrEmpty($proxy)) {
$module.FailJson("missing parameter(s) required by ''bypass'': proxy")
}
$bypass = $bypass -join ';'
}
$win_http_invoke = @'
using System;
using System.Runtime.InteropServices;
namespace Ansible.WinHttpProxy
{
internal class NativeHelpers
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class WINHTTP_CURRENT_USER_IE_PROXY_CONFIG : IDisposable
{
public bool fAutoDetect;
public IntPtr lpszAutoConfigUrl;
public IntPtr lpszProxy;
public IntPtr lpszProxyBypass;
public void Dispose()
{
if (lpszAutoConfigUrl != IntPtr.Zero)
Marshal.FreeHGlobal(lpszAutoConfigUrl);
if (lpszProxy != IntPtr.Zero)
Marshal.FreeHGlobal(lpszProxy);
if (lpszProxyBypass != IntPtr.Zero)
Marshal.FreeHGlobal(lpszProxyBypass);
GC.SuppressFinalize(this);
}
~WINHTTP_CURRENT_USER_IE_PROXY_CONFIG() { this.Dispose(); }
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class WINHTTP_PROXY_INFO : IDisposable
{
public UInt32 dwAccessType;
public IntPtr lpszProxy;
public IntPtr lpszProxyBypass;
public void Dispose()
{
if (lpszProxy != IntPtr.Zero)
Marshal.FreeHGlobal(lpszProxy);
if (lpszProxyBypass != IntPtr.Zero)
Marshal.FreeHGlobal(lpszProxyBypass);
GC.SuppressFinalize(this);
}
~WINHTTP_PROXY_INFO() { this.Dispose(); }
}
}
internal class NativeMethods
{
[DllImport("Winhttp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool WinHttpGetDefaultProxyConfiguration(
[Out] NativeHelpers.WINHTTP_PROXY_INFO pProxyInfo);
[DllImport("Winhttp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool WinHttpGetIEProxyConfigForCurrentUser(
[Out] NativeHelpers.WINHTTP_CURRENT_USER_IE_PROXY_CONFIG pProxyConfig);
[DllImport("Winhttp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool WinHttpSetDefaultProxyConfiguration(
NativeHelpers.WINHTTP_PROXY_INFO pProxyInfo);
}
public class Win32Exception : System.ComponentModel.Win32Exception
{
private string _msg;
public Win32Exception(string message) : this(Marshal.GetLastWin32Error(), message) { }
public Win32Exception(int errorCode, string message) : base(errorCode)
{
_msg = String.Format("{0} ({1}, Win32ErrorCode {2})", message, base.Message, errorCode);
}
public override string Message { get { return _msg; } }
public static explicit operator Win32Exception(string message) { return new Win32Exception(message); }
}
public class WinINetProxy
{
public bool AutoDetect;
public string AutoConfigUrl;
public string Proxy;
public string ProxyBypass;
}
public class WinHttpProxy
{
public string Proxy;
public string ProxyBypass;
public WinHttpProxy()
{
Refresh();
}
public void Set()
{
using (NativeHelpers.WINHTTP_PROXY_INFO proxyInfo = new NativeHelpers.WINHTTP_PROXY_INFO())
{
if (String.IsNullOrEmpty(Proxy))
proxyInfo.dwAccessType = 1; // WINHTTP_ACCESS_TYPE_NO_PROXY
else
{
proxyInfo.dwAccessType = 3; // WINHTTP_ACCESS_TYPE_NAMED_PROXY
proxyInfo.lpszProxy = Marshal.StringToHGlobalUni(Proxy);
if (!String.IsNullOrEmpty(ProxyBypass))
proxyInfo.lpszProxyBypass = Marshal.StringToHGlobalUni(ProxyBypass);
}
if (!NativeMethods.WinHttpSetDefaultProxyConfiguration(proxyInfo))
throw new Win32Exception("WinHttpSetDefaultProxyConfiguration() failed");
}
}
public void Refresh()
{
using (NativeHelpers.WINHTTP_PROXY_INFO proxyInfo = new NativeHelpers.WINHTTP_PROXY_INFO())
{
if (!NativeMethods.WinHttpGetDefaultProxyConfiguration(proxyInfo))
throw new Win32Exception("WinHttpGetDefaultProxyConfiguration() failed");
Proxy = Marshal.PtrToStringUni(proxyInfo.lpszProxy);
ProxyBypass = Marshal.PtrToStringUni(proxyInfo.lpszProxyBypass);
}
}
public static WinINetProxy GetIEProxyConfig()
{
using (NativeHelpers.WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxy = new NativeHelpers.WINHTTP_CURRENT_USER_IE_PROXY_CONFIG())
{
if (!NativeMethods.WinHttpGetIEProxyConfigForCurrentUser(ieProxy))
throw new Win32Exception("WinHttpGetIEProxyConfigForCurrentUser() failed");
return new WinINetProxy
{
AutoDetect = ieProxy.fAutoDetect,
AutoConfigUrl = Marshal.PtrToStringUni(ieProxy.lpszAutoConfigUrl),
Proxy = Marshal.PtrToStringUni(ieProxy.lpszProxy),
ProxyBypass = Marshal.PtrToStringUni(ieProxy.lpszProxyBypass),
};
}
}
}
}
'@
Add-CSharpType -References $win_http_invoke -AnsibleModule $module
$actual_proxy = New-Object -TypeName Ansible.WinHttpProxy.WinHttpProxy
$module.Diff.before = @{
proxy = $actual_proxy.Proxy
bypass = $actual_proxy.ProxyBypass
}
if ($source -eq "ie") {
# If source=ie we need to get the server and bypass values from the IE configuration
$ie_proxy = [Ansible.WinHttpProxy.WinHttpProxy]::GetIEProxyConfig()
$proxy = $ie_proxy.Proxy
$bypass = $ie_proxy.ProxyBypass
}
$previous_proxy = $actual_proxy.Proxy
$previous_bypass = $actual_proxy.ProxyBypass
# Make sure an empty string is converted to $null for easier comparisons
if ([String]::IsNullOrEmpty($proxy)) {
$proxy = $null
}
if ([String]::IsNullOrEmpty($bypass)) {
$bypass = $null
}
if ($previous_proxy -ne $proxy -or $previous_bypass -ne $bypass) {
$actual_proxy.Proxy = $proxy
$actual_proxy.ProxyBypass = $bypass
if (-not $module.CheckMode) {
$actual_proxy.Set()
# Validate that the change was made correctly and revert if it wasn't. The Set() method won't fail on invalid
# values so we need to check again to make sure all was good.
$actual_proxy.Refresh()
if ($actual_proxy.Proxy -ne $proxy -or $actual_proxy.ProxyBypass -ne $bypass) {
$actual_proxy.Proxy = $previous_proxy
$actual_proxy.ProxyBypass = $previous_bypass
$actual_proxy.Set()
$module.FailJson("Unknown error when trying to set proxy '$proxy' or bypass '$bypass'")
}
}
$module.Result.changed = $true
}
$module.Diff.after = @{
proxy = $proxy
bypass = $bypass
}
$module.ExitJson()

View File

@@ -0,0 +1,103 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: win_http_proxy
version_added: '2.8'
short_description: Manages proxy settings for WinHTTP
description:
- Used to set, remove, or import proxy settings for Windows HTTP Services
C(WinHTTP).
- WinHTTP is a framework used by applications or services, typically .NET
applications or non-interactive services, to make web requests.
options:
bypass:
description:
- A list of hosts that will bypass the set proxy when being accessed.
- Use C(<local>) to match hostnames that are not fully qualified domain
names. This is useful when needing to connect to intranet sites using
just the hostname.
- Omit, set to null or an empty string/list to remove the bypass list.
- If this is set then I(proxy) must also be set.
type: list
proxy:
description:
- A string or dict that specifies the proxy to be set.
- If setting a string, should be in the form C(hostname), C(hostname:port),
or C(protocol=hostname:port).
- If the port is undefined, the default port for the protocol in use is
used.
- If setting a dict, the keys should be the protocol and the values should
be the hostname and/or port for that protocol.
- Valid protocols are C(http), C(https), C(ftp), and C(socks).
- Omit, set to null or an empty string to remove the proxy settings.
source:
description:
- Instead of manually specifying the I(proxy) and/or I(bypass), set this to
import the proxy from a set source like Internet Explorer.
- Using C(ie) will import the Internet Explorer proxy settings for the
current active network connection of the current user.
- Only IE's proxy URL and bypass list will be imported into WinHTTP.
- This is like running C(netsh winhttp import proxy source=ie).
- The value is imported when the module runs and will not automatically
be updated if the IE configuration changes in the future. The module will
have to be run again to sync the latest changes.
choices:
- ie
type: str
notes:
- This is not the same as the proxy settings set in Internet Explorer, also
known as C(WinINet); use the M(win_inet_proxy) module to manage that instead.
- These settings are set system wide and not per user, it will require
Administrative privileges to run.
seealso:
- module: win_inet_proxy
author:
- Jordan Borean (@jborean93)
'''
EXAMPLES = r'''
- name: Set a proxy to use for all protocols
win_http_proxy:
proxy: hostname
- name: Set a proxy with a specific port with a bypass list
win_http_proxy:
proxy: hostname:8080
bypass:
- server1
- server2
- <local>
- name: Set the proxy based on the IE proxy settings
win_http_proxy:
source: ie
- name: Set a proxy for specific protocols
win_http_proxy:
proxy:
http: hostname:8080
https: hostname:8443
- name: Set a proxy for specific protocols using a string
win_http_proxy:
proxy: http=hostname:8080;https=hostname:8443
bypass: server1,server2,<local>
- name: Remove any proxy settings
win_http_proxy:
proxy: ''
bypass: ''
'''
RETURN = r'''
#
'''

View File

@@ -0,0 +1,495 @@
#!powershell
# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#AnsibleRequires -CSharpUtil Ansible.Basic
#Requires -Module Ansible.ModuleUtils.AddType
$spec = @{
options = @{
auto_detect = @{ type = "bool"; default = $true }
auto_config_url = @{ type = "str" }
proxy = @{ type = "raw" }
bypass = @{ type = "list" }
connection = @{ type = "str" }
}
required_by = @{
bypass = @("proxy")
}
supports_check_mode = $true
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$auto_detect = $module.Params.auto_detect
$auto_config_url = $module.Params.auto_config_url
$proxy = $module.Params.proxy
$bypass = $module.Params.bypass
$connection = $module.Params.connection
# Parse the raw value, it should be a Dictionary or String
if ($proxy -is [System.Collections.IDictionary]) {
$valid_keys = [System.Collections.Generic.List`1[String]]@("http", "https", "ftp", "socks")
# Check to make sure we don't have any invalid keys in the dict
$invalid_keys = [System.Collections.Generic.List`1[String]]@()
foreach ($k in $proxy.Keys) {
if ($k -notin $valid_keys) {
$invalid_keys.Add($k)
}
}
if ($invalid_keys.Count -gt 0) {
$invalid_keys = $invalid_keys | Sort-Object # So our test assertion doesn't fail due to random ordering
$module.FailJson("Invalid keys found in proxy: $($invalid_keys -join ', '). Valid keys are $($valid_keys -join ', ').")
}
# Build the proxy string in the form 'protocol=host;', the order of valid_keys is also important
$proxy_list = [System.Collections.Generic.List`1[String]]@()
foreach ($k in $valid_keys) {
if ($proxy.ContainsKey($k)) {
$proxy_list.Add("$k=$($proxy.$k)")
}
}
$proxy = $proxy_list -join ";"
} elseif ($null -ne $proxy) {
$proxy = $proxy.ToString()
}
if ($bypass) {
if ([System.String]::IsNullOrEmpty($proxy)) {
$module.FailJson("missing parameter(s) required by ''bypass'': proxy")
}
$bypass = $bypass -join ';'
}
$win_inet_invoke = @'
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
namespace Ansible.WinINetProxy
{
internal class NativeHelpers
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class INTERNET_PER_CONN_OPTION_LISTW : IDisposable
{
public UInt32 dwSize;
public IntPtr pszConnection;
public UInt32 dwOptionCount;
public UInt32 dwOptionError;
public IntPtr pOptions;
public INTERNET_PER_CONN_OPTION_LISTW()
{
dwSize = (UInt32)Marshal.SizeOf(this);
}
public void Dispose()
{
if (pszConnection != IntPtr.Zero)
Marshal.FreeHGlobal(pszConnection);
if (pOptions != IntPtr.Zero)
Marshal.FreeHGlobal(pOptions);
GC.SuppressFinalize(this);
}
~INTERNET_PER_CONN_OPTION_LISTW() { this.Dispose(); }
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class INTERNET_PER_CONN_OPTIONW : IDisposable
{
public INTERNET_PER_CONN_OPTION dwOption;
public ValueUnion Value;
[StructLayout(LayoutKind.Explicit)]
public class ValueUnion
{
[FieldOffset(0)]
public UInt32 dwValue;
[FieldOffset(0)]
public IntPtr pszValue;
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME ftValue;
}
public void Dispose()
{
// We can't just check if Value.pszValue is not IntPtr.Zero as the union means it could be set even
// when the value is a UInt32 or FILETIME. We check against a known string option type and only free
// the value in those cases.
List<INTERNET_PER_CONN_OPTION> stringOptions = new List<INTERNET_PER_CONN_OPTION>
{
{ INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_AUTOCONFIG_URL },
{ INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_BYPASS },
{ INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_SERVER }
};
if (Value != null && Value.pszValue != IntPtr.Zero && stringOptions.Contains(dwOption))
Marshal.FreeHGlobal(Value.pszValue);
GC.SuppressFinalize(this);
}
~INTERNET_PER_CONN_OPTIONW() { this.Dispose(); }
}
public enum INTERNET_OPTION : uint
{
INTERNET_OPTION_PER_CONNECTION_OPTION = 75,
INTERNET_OPTION_PROXY_SETTINGS_CHANGED = 95,
}
public enum INTERNET_PER_CONN_OPTION : uint
{
INTERNET_PER_CONN_FLAGS = 1,
INTERNET_PER_CONN_PROXY_SERVER = 2,
INTERNET_PER_CONN_PROXY_BYPASS = 3,
INTERNET_PER_CONN_AUTOCONFIG_URL = 4,
INTERNET_PER_CONN_AUTODISCOVERY_FLAGS = 5,
INTERNET_PER_CONN_FLAGS_UI = 10, // IE8+ - Included with Windows 7 and Server 2008 R2
}
[Flags]
public enum PER_CONN_FLAGS : uint
{
PROXY_TYPE_DIRECT = 0x00000001,
PROXY_TYPE_PROXY = 0x00000002,
PROXY_TYPE_AUTO_PROXY_URL = 0x00000004,
PROXY_TYPE_AUTO_DETECT = 0x00000008,
}
}
internal class NativeMethods
{
[DllImport("Wininet.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool InternetQueryOptionW(
IntPtr hInternet,
NativeHelpers.INTERNET_OPTION dwOption,
SafeMemoryBuffer lpBuffer,
ref UInt32 lpdwBufferLength);
[DllImport("Wininet.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool InternetSetOptionW(
IntPtr hInternet,
NativeHelpers.INTERNET_OPTION dwOption,
SafeMemoryBuffer lpBuffer,
UInt32 dwBufferLength);
[DllImport("Rasapi32.dll", CharSet = CharSet.Unicode)]
public static extern UInt32 RasValidateEntryNameW(
string lpszPhonebook,
string lpszEntry);
}
internal class SafeMemoryBuffer : SafeHandleZeroOrMinusOneIsInvalid
{
public SafeMemoryBuffer() : base(true) { }
public SafeMemoryBuffer(int cb) : base(true)
{
base.SetHandle(Marshal.AllocHGlobal(cb));
}
public SafeMemoryBuffer(IntPtr handle) : base(true)
{
base.SetHandle(handle);
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected override bool ReleaseHandle()
{
Marshal.FreeHGlobal(handle);
return true;
}
}
public class Win32Exception : System.ComponentModel.Win32Exception
{
private string _msg;
public Win32Exception(string message) : this(Marshal.GetLastWin32Error(), message) { }
public Win32Exception(int errorCode, string message) : base(errorCode)
{
_msg = String.Format("{0} ({1}, Win32ErrorCode {2})", message, base.Message, errorCode);
}
public override string Message { get { return _msg; } }
public static explicit operator Win32Exception(string message) { return new Win32Exception(message); }
}
public class WinINetProxy
{
private string Connection;
public string AutoConfigUrl;
public bool AutoDetect;
public string Proxy;
public string ProxyBypass;
public WinINetProxy(string connection)
{
Connection = connection;
Refresh();
}
public static bool IsValidConnection(string name)
{
// RasValidateEntryName is used to verify is a name can be a valid phonebook entry. It returns 0 if no
// entry exists and 183 if it already exists. We just need to check if it returns 183 to verify the
// connection name.
return NativeMethods.RasValidateEntryNameW(null, name) == 183;
}
public void Refresh()
{
using (var connFlags = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_FLAGS_UI))
using (var autoConfigUrl = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_AUTOCONFIG_URL))
using (var server = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_SERVER))
using (var bypass = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_BYPASS))
{
NativeHelpers.INTERNET_PER_CONN_OPTIONW[] options = new NativeHelpers.INTERNET_PER_CONN_OPTIONW[]
{
connFlags, autoConfigUrl, server, bypass
};
try
{
QueryOption(options, Connection);
}
catch (Win32Exception e)
{
if (e.NativeErrorCode == 87) // ERROR_INVALID_PARAMETER
{
// INTERNET_PER_CONN_FLAGS_UI only works for IE8+, try the fallback in case we are still working
// with an ancient version.
connFlags.dwOption = NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_FLAGS;
QueryOption(options, Connection);
}
else
throw;
}
NativeHelpers.PER_CONN_FLAGS flags = (NativeHelpers.PER_CONN_FLAGS)connFlags.Value.dwValue;
AutoConfigUrl = flags.HasFlag(NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_AUTO_PROXY_URL)
? Marshal.PtrToStringUni(autoConfigUrl.Value.pszValue) : null;
AutoDetect = flags.HasFlag(NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_AUTO_DETECT);
if (flags.HasFlag(NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_PROXY))
{
Proxy = Marshal.PtrToStringUni(server.Value.pszValue);
ProxyBypass = Marshal.PtrToStringUni(bypass.Value.pszValue);
}
else
{
Proxy = null;
ProxyBypass = null;
}
}
}
public void Set()
{
using (var connFlags = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_FLAGS_UI))
using (var autoConfigUrl = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_AUTOCONFIG_URL))
using (var server = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_SERVER))
using (var bypass = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_BYPASS))
{
List<NativeHelpers.INTERNET_PER_CONN_OPTIONW> options = new List<NativeHelpers.INTERNET_PER_CONN_OPTIONW>();
// PROXY_TYPE_DIRECT seems to always be set, need to verify
NativeHelpers.PER_CONN_FLAGS flags = NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_DIRECT;
if (AutoDetect)
flags |= NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_AUTO_DETECT;
if (!String.IsNullOrEmpty(AutoConfigUrl))
{
flags |= NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_AUTO_PROXY_URL;
autoConfigUrl.Value.pszValue = Marshal.StringToHGlobalUni(AutoConfigUrl);
}
options.Add(autoConfigUrl);
if (!String.IsNullOrEmpty(Proxy))
{
flags |= NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_PROXY;
server.Value.pszValue = Marshal.StringToHGlobalUni(Proxy);
}
options.Add(server);
if (!String.IsNullOrEmpty(ProxyBypass))
bypass.Value.pszValue = Marshal.StringToHGlobalUni(ProxyBypass);
options.Add(bypass);
connFlags.Value.dwValue = (UInt32)flags;
options.Add(connFlags);
SetOption(options.ToArray(), Connection);
// Tell IE that the proxy settings have been changed.
if (!NativeMethods.InternetSetOptionW(
IntPtr.Zero,
NativeHelpers.INTERNET_OPTION.INTERNET_OPTION_PROXY_SETTINGS_CHANGED,
new SafeMemoryBuffer(IntPtr.Zero),
0))
{
throw new Win32Exception("InternetSetOptionW(INTERNET_OPTION_PROXY_SETTINGS_CHANGED) failed");
}
}
}
internal static NativeHelpers.INTERNET_PER_CONN_OPTIONW CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION option)
{
return new NativeHelpers.INTERNET_PER_CONN_OPTIONW
{
dwOption = option,
Value = new NativeHelpers.INTERNET_PER_CONN_OPTIONW.ValueUnion(),
};
}
internal static void QueryOption(NativeHelpers.INTERNET_PER_CONN_OPTIONW[] options, string connection = null)
{
using (NativeHelpers.INTERNET_PER_CONN_OPTION_LISTW optionList = new NativeHelpers.INTERNET_PER_CONN_OPTION_LISTW())
using (SafeMemoryBuffer optionListPtr = MarshalOptionList(optionList, options, connection))
{
UInt32 bufferSize = optionList.dwSize;
if (!NativeMethods.InternetQueryOptionW(
IntPtr.Zero,
NativeHelpers.INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION,
optionListPtr,
ref bufferSize))
{
throw new Win32Exception("InternetQueryOptionW(INTERNET_OPTION_PER_CONNECTION_OPTION) failed");
}
for (int i = 0; i < options.Length; i++)
{
IntPtr opt = IntPtr.Add(optionList.pOptions, i * Marshal.SizeOf(typeof(NativeHelpers.INTERNET_PER_CONN_OPTIONW)));
NativeHelpers.INTERNET_PER_CONN_OPTIONW option = (NativeHelpers.INTERNET_PER_CONN_OPTIONW)Marshal.PtrToStructure(opt,
typeof(NativeHelpers.INTERNET_PER_CONN_OPTIONW));
options[i].Value = option.Value;
option.Value = null; // Stops the GC from freeing the same memory twice
}
}
}
internal static void SetOption(NativeHelpers.INTERNET_PER_CONN_OPTIONW[] options, string connection = null)
{
using (NativeHelpers.INTERNET_PER_CONN_OPTION_LISTW optionList = new NativeHelpers.INTERNET_PER_CONN_OPTION_LISTW())
using (SafeMemoryBuffer optionListPtr = MarshalOptionList(optionList, options, connection))
{
if (!NativeMethods.InternetSetOptionW(
IntPtr.Zero,
NativeHelpers.INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION,
optionListPtr,
optionList.dwSize))
{
throw new Win32Exception("InternetSetOptionW(INTERNET_OPTION_PER_CONNECTION_OPTION) failed");
}
}
}
internal static SafeMemoryBuffer MarshalOptionList(NativeHelpers.INTERNET_PER_CONN_OPTION_LISTW optionList,
NativeHelpers.INTERNET_PER_CONN_OPTIONW[] options, string connection)
{
optionList.pszConnection = Marshal.StringToHGlobalUni(connection);
optionList.dwOptionCount = (UInt32)options.Length;
int optionSize = Marshal.SizeOf(typeof(NativeHelpers.INTERNET_PER_CONN_OPTIONW));
optionList.pOptions = Marshal.AllocHGlobal(optionSize * options.Length);
for (int i = 0; i < options.Length; i++)
{
IntPtr option = IntPtr.Add(optionList.pOptions, i * optionSize);
Marshal.StructureToPtr(options[i], option, false);
}
SafeMemoryBuffer optionListPtr = new SafeMemoryBuffer((int)optionList.dwSize);
Marshal.StructureToPtr(optionList, optionListPtr.DangerousGetHandle(), false);
return optionListPtr;
}
}
}
'@
Add-CSharpType -References $win_inet_invoke -AnsibleModule $module
# We need to validate the connection because WinINet will just silently continue even if the connection does not
# already exist.
if ($null -ne $connection -and -not [Ansible.WinINetProxy.WinINetProxy]::IsValidConnection($connection)) {
$module.FailJson("The connection '$connection' does not exist.")
}
$actual_proxy = New-Object -TypeName Ansible.WinINetProxy.WinINetProxy -ArgumentList @(,$connection)
$module.Diff.before = @{
auto_config_url = $actual_proxy.AutoConfigUrl
auto_detect = $actual_proxy.AutoDetect
bypass = $actual_proxy.ProxyBypass
server = $actual_proxy.Proxy
}
# Make sure an empty string is converted to $null for easier comparisons
if ([String]::IsNullOrEmpty($auto_config_url)) {
$auto_config_url = $null
}
if ([String]::IsNullOrEmpty($proxy)) {
$proxy = $null
}
if ([String]::IsNullOrEmpty($bypass)) {
$bypass = $null
}
# Record the original values in case we need to revert on a failure
$previous_auto_config_url = $actual_proxy.AutoConfigUrl
$previous_auto_detect = $actual_proxy.AutoDetect
$previous_proxy = $actual_proxy.Proxy
$previous_bypass = $actual_proxy.ProxyBypass
$changed = $false
if ($auto_config_url -ne $previous_auto_config_url) {
$actual_proxy.AutoConfigUrl = $auto_config_url
$changed = $true
}
if ($auto_detect -ne $previous_auto_detect) {
$actual_proxy.AutoDetect = $auto_detect
$changed = $true
}
if ($proxy -ne $previous_proxy) {
$actual_proxy.Proxy = $proxy
$changed = $true
}
if ($bypass -ne $previous_bypass) {
$actual_proxy.ProxyBypass = $bypass
$changed = $true
}
if ($changed -and -not $module.CheckMode) {
$actual_proxy.Set()
# Validate that the change was made correctly and revert if it wasn't. THe Set() method won't fail on invalid
# values so we need to check again to make sure all was good
$actual_proxy.Refresh()
if ($actual_proxy.AutoConfigUrl -ne $auto_config_url -or
$actual_proxy.AutoDetect -ne $auto_detect -or
$actual_proxy.Proxy -ne $proxy -or
$actual_proxy.ProxyBypass -ne $bypass) {
$actual_proxy.AutoConfigUrl = $previous_auto_config_url
$actual_proxy.AutoDetect = $previous_auto_detect
$actual_proxy.Proxy = $previous_proxy
$actual_proxy.ProxyBypass = $previous_bypass
$actual_proxy.Set()
$module.FailJson("Unknown error when trying to set auto_config_url '$auto_config_url', proxy '$proxy', or bypass '$bypass'")
}
}
$module.Result.changed = $changed
$module.Diff.after = @{
auto_config_url = $auto_config_url
auto_detect = $auto_detect
bypass = $bypass
proxy = $proxy
}
$module.ExitJson()

View File

@@ -0,0 +1,173 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: win_inet_proxy
version_added: '2.8'
short_description: Manages proxy settings for WinINet and Internet Explorer
description:
- Used to set or remove proxy settings for Windows INet which includes Internet
Explorer.
- WinINet is a framework used by interactive applications to submit web
requests through.
- The proxy settings can also be used by other applications like Firefox,
Chrome, and others but there is no definitive list.
options:
auto_detect:
description:
- Whether to configure WinINet to automatically detect proxy settings
through Web Proxy Auto-Detection C(WPAD).
- This corresponds to the checkbox I(Automatically detect settings) in the
connection settings window.
default: yes
type: bool
auto_config_url:
description:
- The URL of a proxy configuration script.
- Proxy configuration scripts are typically JavaScript files with the
C(.pac) extension that implement the C(FindProxyForURL(url, host)
function.
- Omit, set to null or an empty string to remove the auto config URL.
- This corresponds to the checkbox I(Use automatic configuration script) in
the connection settings window.
type: str
bypass:
description:
- A list of hosts that will bypass the set proxy when being accessed.
- Use C(<local>) to match hostnames that are not fully qualified domain
names. This is useful when needing to connect to intranet sites using
just the hostname. If defined, this should be the last entry in the
bypass list.
- Use C(<-loopback>) to stop automatically bypassing the proxy when
connecting through any loopback address like C(127.0.0.1), C(localhost),
or the local hostname.
- Omit, set to null or an empty string/list to remove the bypass list.
- If this is set then I(proxy) must also be set.
type: list
connection:
description:
- The name of the IE connection to set the proxy settings for.
- These are the connections under the I(Dial-up and Virtual Private Network)
header in the IE settings.
- When ommited, the default LAN connection is used.
type: str
proxy:
description:
- A string or dict that specifies the proxy to be set.
- If setting a string, should be in the form C(hostname), C(hostname:port),
or C(protocol=hostname:port).
- If the port is undefined, the default port for the protocol in use is
used.
- If setting a dict, the keys should be the protocol and the values should
be the hostname and/or port for that protocol.
- Valid protocols are C(http), C(https), C(ftp), and C(socks).
- Omit, set to null or an empty string to remove the proxy settings.
notes:
- This is not the same as the proxy settings set in WinHTTP through the
C(netsh) command. Use the M(win_http_proxy) module to manage that instead.
- These settings are by default set per user and not system wide. A registry
property must be set independently from this module if you wish to apply the
proxy for all users. See examples for more detail.
- If per user proxy settings are desired, use I(become) to become any local
user on the host. No password is needed to be set for this to work.
- If the proxy requires authentication, set the credentials using the
M(win_credential) module. This requires I(become) to be used so the
credential store can be accessed.
seealso:
- module: win_http_proxy
- module: win_credential
author:
- Jordan Borean (@jborean93)
'''
EXAMPLES = r'''
# This should be set before running the win_inet_proxy module
- name: Configure IE proxy settings to apply to all users
win_regedit:
path: HKLM:\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings
name: ProxySettingsPerUser
data: 0
type: dword
state: present
# This should be set before running the win_inet_proxy module
- name: Configure IE proxy settings to apply per user
win_regedit:
path: HKLM:\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings
name: ProxySettingsPerUser
data: 1
type: dword
state: present
- name: Configure IE proxy to use auto detected settings without an explicit proxy
win_inet_proxy:
auto_detect: yes
- name: Configure IE proxy to use auto detected settings with a configuration script
win_inet_proxy:
auto_detect: yes
auto_config_url: http://proxy.ansible.com/proxy.pac
- name: Configure IE to use explicit proxy host
win_inet_proxy:
auto_detect: yes
proxy: ansible.proxy
- name: Configure IE to use explicit proxy host with port and without auto detection
win_inet_proxy:
auto_detect: no
proxy: ansible.proxy:8080
- name: Configure IE to use a specific proxy per protocol
win_inet_proxy:
proxy:
http: ansible.proxy:8080
https: ansible.proxy:8443
- name: Configure IE to use a specific proxy per protocol using a string
win_inet_proxy:
proxy: http=ansible.proxy:8080;https=ansible.proxy:8443
- name: Set a proxy with a bypass list
win_inet_proxy:
proxy: ansible.proxy
bypass:
- server1
- server2
- <-loopback>
- <local>
- name: Remove any explicit proxies that are set
win_inet_proxy:
proxy: ''
bypass: ''
# This should be done after setting the IE proxy with win_inet_proxy
- name: Import IE proxy configuration to WinHTTP
win_http_proxy:
source: ie
# Explicit credentials can only be set per user and require become to work
- name: Set credential to use for proxy auth
win_credential:
name: ansible.proxy # The name should be the FQDN of the proxy host
type: generic_password
username: proxyuser
secret: proxypass
state: present
become: yes
become_user: '{{ ansible_user }}'
become_method: runas
'''
RETURN = r'''
#
'''