WinInetProxy.psm1

#########################################################################################
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# WinInetProxy Module
#
#########################################################################################

#requires -runasadministrator

Function Set-WinInetProxy
{
    <#
    .DESCRIPTION
        Update WinINet proxy settings to control outbound communication from the configured server.
        Note: This function requires LocalAdmin privileges to be able to run on the machine.
 
    .PARAMETER ProxyServer
        Required: $false
        The proxy server end point in the format [Address]:[Port].
        If not provided, the environment variable ProxyServer will be used instead, if present.
 
    .PARAMETER ProxyBypass
        List of Host URLs that bypass proxy servers set by -ProxyServer key.
        Set "-ProxyBypass <local>" to bypass local Intranet URLs.
        If not provided, the environment variable ProxyBypass will be used instead, if present.
 
    .PARAMETER PACUrl
        Sets URL for downloading PAC script
        If not provided, the environment variable PACUrl will be used instead, if present.
 
    .PARAMETER AutoDetect
        1 - Proxy Auto detect (WPAD) is enabled.
        0 or default - Proxy Auto detect (WPAD) is disabled.
        If not provided, the environment variable AutoDetect will be used instead, if present.
 
    .PARAMETER ProxySettingsPerUser
        0 - Proxy settings are per machine.
        1 or default - Proxy settings are per user.
        If not provided, the environment variable ProxySettingsPerUser will be used instead, if present.
 
    .EXAMPLE
        To configure proxy settings:
        Set-WinInetProxy -ProxySettingsPerUser 0 -ProxyServer http://itg.contoso.com:3128 -ProxyBypass "<local>" -PACUrl htpp://corp.contoso.com/wpad.dat -AutoDetect 1
 
    .EXAMPLE
        To remove all proxy settings:
        Set-WinInetProxy
    #>


    param (
        [Parameter()]
        [String] $ProxyServer = $env:ProxyServer,
        
        [Parameter()]
        [String] $ProxyBypass = $env:ProxyBypass,
        
        [Parameter()]
        [String] $PACUrl = $env:PACUrl,
        
        [Parameter()]
        [Int]$AutoDetect = $env:AutoDetect,
        
        [Parameter()]
        [Int]$ProxySettingsPerUser = $env:ProxySettingsPerUser
    )

    $TypeSystemAsmemblies = @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
 
public static class WinInetHelper
{
 
"@


    $TypeWinInetHelper32 = @"
 
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct INTERNET_PER_CONN_OPTION
{
    [FieldOffset(0)] public UInt32 dwOption;
    [FieldOffset(0x4)] public IntPtr dwValue;
    [FieldOffset(0x8)] public UInt32 dwPadding;
}
 
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct INTERNET_PER_CONN_OPTION_LIST
{
    [FieldOffset(0)] public Int32 dwSize; // size of the INTERNET_PER_CONN_OPTION_LIST struct
    [FieldOffset(0x4)] [MarshalAs(UnmanagedType.LPWStr)] public string pszConnection; // connection name to set/query options
    [FieldOffset(0x8)] public Int32 dwOptionCount; // number of options to set/query
    [FieldOffset(0xc)] public Int32 dwOptionError; // on error, which option failed
    [FieldOffset(0x10)] public IntPtr pOptions;
}
 
"@


    $TypeWinInetHelper64 = @"
 
[StructLayout(LayoutKind.Explicit)]
public struct INTERNET_PER_CONN_OPTION
{
    [FieldOffset(0)] public UInt32 dwOption;
    [FieldOffset(0x8)] public IntPtr dwValue;
}
 
[StructLayout(LayoutKind.Explicit)]
public struct INTERNET_PER_CONN_OPTION_LIST
{
    [FieldOffset(0)] public Int32 dwSize; // size of the INTERNET_PER_CONN_OPTION_LIST struct
    [FieldOffset(0x8)] [MarshalAs(UnmanagedType.LPWStr)] public string pszConnection; // connection name to set/query options
    [FieldOffset(0x10)] public Int32 dwOptionCount; // number of options to set/query
    [FieldOffset(0x14)] public Int32 dwOptionError; // on error, which option failed
    [FieldOffset(0x18)] public IntPtr pOptions;
}
 
"@


    $TypeWinInetHelper = @"
 
    public static UInt32 INTERNET_PER_CONN_FLAGS = 1;
    public static UInt32 INTERNET_PER_CONN_PROXY_SERVER = 2;
    public static UInt32 INTERNET_PER_CONN_PROXY_BYPASS = 3;
    public static UInt32 INTERNET_PER_CONN_AUTOCONFIG_URL = 4;
    public static UInt32 INTERNET_PER_CONN_AUTODISCOVERY_FLAGS = 5;
    public static UInt32 INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL = 6;
    public static UInt32 INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS = 7;
    public static UInt32 INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME = 8;
    public static UInt32 INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL = 9;
    public static UInt32 INTERNET_PER_CONN_FLAGS_UI = 10;
 
    public static UInt32 PROXY_TYPE_DIRECT = 0x00000001; // direct to net
    public static UInt32 PROXY_TYPE_PROXY = 0x00000002; // via named proxy
    public static UInt32 PROXY_TYPE_AUTO_PROXY_URL = 0x00000004; // autoproxy URL
    public static UInt32 PROXY_TYPE_AUTO_DETECT = 0x00000008; // use autoproxy detection
 
    public static UInt32 INTERNET_OPTION_REFRESH = 37;
    public static UInt32 INTERNET_OPTION_SETTINGS_CHANGED = 39;
    public static UInt32 INTERNET_OPTION_PER_CONNECTION_OPTION = 75;
     
    [DllImport("WinInet.dll", SetLastError = true)]
    public static extern int InternetSetOptionW(
            [In, Optional] IntPtr hHinternet,
            UInt32 dwOption,
            [In, Optional] ref INTERNET_PER_CONN_OPTION_LIST pBuffer,
            Int32 dwSize);
 
    [DllImport("WinInet.dll", SetLastError = true)]
    public static extern int InternetSetOptionW(
            [In, Optional] IntPtr hHinternet,
            UInt32 dwOption,
            [In, Optional] IntPtr pBuffer,
            Int32 dwSize);
 
    [DllImport("WinInet.dll", SetLastError = true)]
    public static extern int InternetQueryOptionW(
            [Optional, MarshalAs(UnmanagedType.LPWStr)] string hHinternet,
            UInt32 dwOption,
            [In, Out] ref INTERNET_PER_CONN_OPTION_LIST pBuffer,
            [In, Out] ref Int32 pBufferSize);
 
    public static int WriteProxySettingsHelper(
            int AutoDetect,
            [Optional] string pwszPacUrl,
            [Optional] string pwszProxy,
            [Optional] string pwszProxyBypassList)
    {
        int WinError = 0;
        INTERNET_PER_CONN_OPTION_LIST list = new INTERNET_PER_CONN_OPTION_LIST();
        Int32 dwBufSize = Marshal.SizeOf<INTERNET_PER_CONN_OPTION_LIST>();
        IntPtr nativePacUrl;
        IntPtr nativeProxyBypassList;
        IntPtr nativeProxyPtr;
 
        INTERNET_PER_CONN_OPTION[] options = new INTERNET_PER_CONN_OPTION[4];
 
        System.Console.WriteLine("Entered WriteProxySettingsHelper");
 
        list.pszConnection = null;
        list.dwSize = Marshal.SizeOf<INTERNET_PER_CONN_OPTION_LIST>();
        list.dwOptionCount = 4;
        list.dwOptionError = 0;
 
        for (int i = 0; i < options.Length; i++)
        {
            options[i].dwOption = 0;
            options[i].dwValue = IntPtr.Zero;
        }
 
        UInt32 OptionsValue_0 = 0;
 
        //
        // save off all other options
        //
        options[0].dwOption = INTERNET_PER_CONN_FLAGS_UI;
        options[0].dwValue = IntPtr.Zero;
 
        OptionsValue_0 = PROXY_TYPE_DIRECT;
 
        options[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
        if (pwszProxy != null &&
            pwszProxy.Length != 0)
        {
            OptionsValue_0 |= PROXY_TYPE_PROXY;
            nativeProxyPtr = Marshal.StringToHGlobalUni(pwszProxy);
            options[1].dwValue = nativeProxyPtr;
        }
        else
        {
            options[1].dwValue = IntPtr.Zero;
        }
 
        options[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
        if (pwszProxyBypassList != null &&
            pwszProxyBypassList.Length != 0)
        {
            nativeProxyBypassList = Marshal.StringToHGlobalUni(pwszProxyBypassList);
            options[2].dwValue = nativeProxyBypassList;
        }
        else
        {
            options[2].dwValue = IntPtr.Zero;
        }
 
        //
        // save autodetect
        //
        if (AutoDetect == 1)
        {
            OptionsValue_0 |= PROXY_TYPE_AUTO_DETECT;
            System.Console.WriteLine("AutoDetect == 1");
        }
 
        //
        // save autoconfig
        //
 
        options[3].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL;
        if (pwszPacUrl != null &&
            pwszPacUrl.Length != 0)
        {
            OptionsValue_0 |= PROXY_TYPE_AUTO_PROXY_URL;
            nativePacUrl = Marshal.StringToHGlobalUni(pwszPacUrl);
            options[3].dwValue = nativePacUrl;
        }
        else
        {
            options[3].dwValue = IntPtr.Zero;
        }
 
        options[0].dwValue = new IntPtr(OptionsValue_0);
 
        int iOptionSize = Marshal.SizeOf<INTERNET_PER_CONN_OPTION>();
        var allocatedMemory = new System.Collections.Generic.List<IntPtr>();
        IntPtr nativeOptionsArray = Marshal.AllocHGlobal(iOptionSize * options.Length);
        for (int i = 0; i < options.Length; i++)
        {
            Marshal.StructureToPtr(options[i], nativeOptionsArray + i * iOptionSize, false);
        }
 
        list.pOptions = nativeOptionsArray;
 
        if (0 == InternetSetOptionW(IntPtr.Zero,
                                    INTERNET_OPTION_PER_CONNECTION_OPTION,
                                    ref list,
                                    dwBufSize))
        {
            WinError = Marshal.GetLastWin32Error();
            System.Console.WriteLine("InternetSetOptionW failed with {0}", WinError);
            return WinError;
        }
 
        if (0 == InternetSetOptionW(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0))
        {
            WinError = Marshal.GetLastWin32Error();
            System.Console.WriteLine("InternetSetOptionA(INTERNET_OPTION_SETTINGS_CHANGED) failed with {0}", WinError);
            return WinError;
        }
 
        if(0 == InternetSetOptionW(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0))
        {
            WinError = Marshal.GetLastWin32Error();
            System.Console.WriteLine("InternetSetOptionA(INTERNET_OPTION_REFRESH) failed with {0}", WinError);
            return WinError;
        }
 
        return WinError;
    }
}
"@


    $HKLM_Wow6432InternetSettingsRegKey = "HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Internet Settings"
    $HKLM_internetSettingsRegKey = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Internet Settings"

    $HKCU_Wow6432InternetSettingsRegKey = "HKCU:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Internet Settings"
    $HKCU_internetSettingsRegKey = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings"

    $internetSettingsPolicyRegKey = "HKLM:\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings"

    if ($env:EMULATED -eq $true)
    {
        Write-Output "No proxy changes will be made"
        return
    }

    Write-Output "Start proxy Configuration"

    try
    {
        $internetSettingsRegKey = $HKCU_internetSettingsRegKey
        $internetSettingsRegKeyWow6432 = $HKCU_Wow6432InternetSettingsRegKey

        if ($PSBoundParameters.ContainsKey('ProxySettingsPerUser') -and 
            $ProxySettingsPerUser -eq 0)
        {
            Set-ItemProperty -path $internetSettingsPolicyRegKey ProxySettingsPerUser -value 0;

            $internetSettingsRegKey = $HKLM_internetSettingsRegKey
            $internetSettingsRegKeyWow6432 = $HKLM_Wow6432InternetSettingsRegKey

            Write-Output "Proxy is machine wide"
        }
        else
        {
            Remove-ItemProperty -path $internetSettingsPolicyRegKey ProxySettingsPerUser -ErrorAction SilentlyContinue;

            Write-Output "Proxy is Per User"

        }
        
        Set-ItemProperty -path $internetSettingsRegKey MigrateProxy -value 1 -ErrorAction SilentlyContinue;
        Set-ItemProperty -path $internetSettingsRegKeyWow6432 MigrateProxy -value 1 -ErrorAction SilentlyContinue;

        Write-Output "AutoDetect is $AutoDetect"
        Write-Output "PACUrl is $PACUrl"
        Write-Output "ProxyServer is $ProxyServer"
        Write-Output "ProxyBypass is $ProxyBypass"

        if ([System.IntPtr]::Size -eq 4) 
        {
            $TmpWinInetHelper32 = $TypeSystemAsmemblies + $TypeWinInetHelper32 + $TypeWinInetHelper
            Add-Type -TypeDefinition $TmpWinInetHelper32
            #$error[0].Exception.GetBaseException().LoaderExceptions
            [WinInetHelper]::WriteProxySettingsHelper( 0, "", "", "" ) | Out-Null
            [WinInetHelper]::WriteProxySettingsHelper( $AutoDetect, $PACUrl, $ProxyServer, $ProxyBypass ) | Out-Null
        }
        else 
        {
            $TmpWinInetHelper64 = $TypeSystemAsmemblies + $TypeWinInetHelper64 + $TypeWinInetHelper
            Add-Type -TypeDefinition $TmpWinInetHelper64
            #$error[0].Exception.GetBaseException().LoaderExceptions
            [WinInetHelper]::WriteProxySettingsHelper( 0, "", "", "" ) | Out-Null
            [WinInetHelper]::WriteProxySettingsHelper( $AutoDetect, $PACUrl, $ProxyServer, $ProxyBypass ) | Out-Null
        }
        
        if ($error -ne 0)
        {
            Write-Output "WriteProxySettingsHelper failed with $error"
        }
        else
        {
            Write-Output "`nSuccessfully set proxy"
        }
    }
    catch
    {
        throw "Exception when resetting proxy settings: $($_.ToString())"
    }
}