@ -1,37 +1,68 @@
using Microsoft.Win32 ;
using System.Runtime.InteropServices ;
using static v2rayN . Handler . ProxySetting . InternetConnectionOption ;
namespace v2rayN.Handler
{
internal class ProxySetting
{
/// <summary>
// set to use no proxy
/// </summary>
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
public static bool UnsetProxy ( )
{
return SetProxy ( null , null , 1 ) ;
}
/// <summary>
/// Set system proxy settings
/// </summary>
/// <param name="strProxy"> proxy address</param>
/// <param name="exceptions">exception addresses that do not use proxy</param>
/// <param name="type">type of proxy defined in PerConnFlags
/// PROXY_TYPE_DIRECT = 0x00000001, // direct connection (no proxy)
/// PROXY_TYPE_PROXY = 0x00000002, // via named proxy
/// PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy script URL
/// PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
/// </param>
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
/// <returns>true: one of connnection is successfully updated proxy settings</returns>
public static bool SetProxy ( string? strProxy , string? exceptions , int type )
{
// set proxy for LAN
bool result = SetConnectionProxy ( null , strProxy , exceptions , type ) ;
// set proxy for dial up connections
var connections = EnumerateRasEntries ( ) ;
foreach ( var connection in connections )
{
result | = SetConnectionProxy ( connection , strProxy , exceptions , type ) ;
}
return result ;
}
private static bool SetConnectionProxy ( string? connectionName , string? strProxy , string? exceptions , int type )
{
InternetPerConnOptionList list = new ( ) ;
int optionCount = 1 ;
if ( type = = 1 )
if ( type = = 1 ) // No proxy
{
optionCount = 1 ;
}
else if ( type is 2 or 4 )
else if ( type is 2 or 4 ) // named proxy or autoproxy script URL
{
optionCount = Utils . IsNullOrEmpty ( exceptions ) ? 2 : 3 ;
}
int m_Int = ( int ) PerConnFlags . PROXY_TYPE_DIRECT ;
PerConnOption m_Option = PerConnOption . INTERNET_PER_CONN_FLAGS ;
if ( type = = 2 )
if ( type = = 2 ) // named proxy
{
m_Int = ( int ) ( PerConnFlags . PROXY_TYPE_DIRECT | PerConnFlags . PROXY_TYPE_PROXY ) ;
m_Option = PerConnOption . INTERNET_PER_CONN_PROXY_SERVER ;
}
else if ( type = = 4 )
else if ( type = = 4 ) // autoproxy script url
{
m_Int = ( int ) ( PerConnFlags . PROXY_TYPE_DIRECT | PerConnFlags . PROXY_TYPE_AUTO_PROXY_URL ) ;
m_Option = PerConnOption . INTERNET_PER_CONN_AUTOCONFIG_URL ;
@ -47,24 +78,31 @@ namespace v2rayN.Handler
if ( optionCount > 1 )
{
options [ 1 ] . m_Option = m_Option ;
options [ 1 ] . m_Value . m_StringPtr = Marshal . StringToHGlobalAuto ( strProxy ) ;
options [ 1 ] . m_Value . m_StringPtr = Marshal . StringToHGlobalAuto ( strProxy ) ; // !! remember to deallocate memory 1
// except for these addresses ...
if ( optionCount > 2 )
{
options [ 2 ] . m_Option = PerConnOption . INTERNET_PER_CONN_PROXY_BYPASS ;
options [ 2 ] . m_Value . m_StringPtr = Marshal . StringToHGlobalAuto ( exceptions ) ;
options [ 2 ] . m_Value . m_StringPtr = Marshal . StringToHGlobalAuto ( exceptions ) ; // !! remember to deallocate memory 2
}
}
// default stuff
list . dwSize = Marshal . SizeOf ( list ) ;
list . szConnection = IntPtr . Zero ;
if ( connectionName ! = null )
{
list . szConnection = Marshal . StringToHGlobalAuto ( connectionName ) ; // !! remember to deallocate memory 3
}
else
{
list . szConnection = IntPtr . Zero ;
}
list . dwOptionCount = options . Length ;
list . dwOptionError = 0 ;
int optSize = Marshal . SizeOf ( typeof ( InternetConnectionOption ) ) ;
// make a pointer out of all that ...
IntPtr optionsPtr = Marshal . AllocCoTaskMem ( optSize * options . Length ) ;
IntPtr optionsPtr = Marshal . AllocCoTaskMem ( optSize * options . Length ) ; // !! remember to deallocate memory 4
// copy the array over into that spot in memory ...
for ( int i = 0 ; i < options . Length ; + + i )
{
@ -83,26 +121,82 @@ namespace v2rayN.Handler
list . options = optionsPtr ;
// and then make a pointer out of the whole list
IntPtr ipcoListPtr = Marshal . AllocCoTaskMem ( list . dwSize ) ;
IntPtr ipcoListPtr = Marshal . AllocCoTaskMem ( list . dwSize ) ; // !! remember to deallocate memory 5
Marshal . StructureToPtr ( list , ipcoListPtr , false ) ;
// and finally, call the API method!
int returnvalue = NativeMethods . InternetSetOption ( IntPtr . Zero ,
bool isSuccess = NativeMethods . InternetSetOption ( IntPtr . Zero ,
InternetOption . INTERNET_OPTION_PER_CONNECTION_OPTION ,
ipcoListPtr , list . dwSize ) ? - 1 : 0 ;
if ( returnvalue = = 0 )
ipcoListPtr , list . dwSize ) ;
int returnvalue = 0 ; // ERROR_SUCCESS
if ( ! isSuccess )
{ // get the error codes, they might be helpful
returnvalue = Marshal . GetLastWin32Error ( ) ;
returnvalue = Marshal . GetLastPInvokeError ( ) ;
}
else
{
// Notify the system that the registry settings have been changed and cause them to be refreshed
NativeMethods . InternetSetOption ( IntPtr . Zero , InternetOption . INTERNET_OPTION_SETTINGS_CHANGED , IntPtr . Zero , 0 ) ;
NativeMethods . InternetSetOption ( IntPtr . Zero , InternetOption . INTERNET_OPTION_REFRESH , IntPtr . Zero , 0 ) ;
}
// FREE the data ASAP
Marshal . FreeCoTaskMem ( optionsPtr ) ;
Marshal . FreeCoTaskMem ( ipcoListPtr ) ;
if ( returnvalue > 0 )
{ // throw the error codes, they might be helpful
//throw new Win32Exception(Marshal.GetLastWin32Error());
if ( list . szConnection ! = IntPtr . Zero ) Marshal . FreeHGlobal ( list . szConnection ) ; // release mem 3
if ( optionCount > 1 )
{
Marshal . FreeHGlobal ( options [ 1 ] . m_Value . m_StringPtr ) ; // release mem 1
if ( optionCount > 2 )
{
Marshal . FreeHGlobal ( options [ 2 ] . m_Value . m_StringPtr ) ; // release mem 2
}
}
Marshal . FreeCoTaskMem ( optionsPtr ) ; // release mem 4
Marshal . FreeCoTaskMem ( ipcoListPtr ) ; // release mem 5
if ( returnvalue ! = 0 )
{
// throw the error codes, they might be helpful
throw new ApplicationException ( $"Set Internet Proxy failed with error code: {Marshal.GetLastWin32Error()}" ) ;
}
return ( returnvalue < 0 ) ;
return true ;
}
/// <summary>
/// Retrieve list of connections including LAN and WAN to support PPPoE connection
/// </summary>
/// <returns>A list of RAS connection names. May be empty list if no dial up connection.</returns>
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
private static IEnumerable < string > EnumerateRasEntries ( )
{
int entries = 0 ;
// attempt to query with 1 entry buffer
RASENTRYNAME [ ] rasEntryNames = new RASENTRYNAME [ 1 ] ;
int bufferSize = Marshal . SizeOf ( typeof ( RASENTRYNAME ) ) ;
rasEntryNames [ 0 ] . dwSize = Marshal . SizeOf ( typeof ( RASENTRYNAME ) ) ;
uint result = NativeMethods . RasEnumEntries ( null , null , rasEntryNames , ref bufferSize , ref entries ) ;
// increase buffer if the buffer is not large enough
if ( result = = ( uint ) ErrorCode . ERROR_BUFFER_TOO_SMALL )
{
rasEntryNames = new RASENTRYNAME [ bufferSize / Marshal . SizeOf ( typeof ( RASENTRYNAME ) ) ] ;
for ( int i = 0 ; i < rasEntryNames . Length ; i + + )
{
rasEntryNames [ i ] . dwSize = Marshal . SizeOf ( typeof ( RASENTRYNAME ) ) ;
}
result = NativeMethods . RasEnumEntries ( null , null , rasEntryNames , ref bufferSize , ref entries ) ;
}
if ( result = = 0 )
{
var entryNames = new List < string > ( ) ;
for ( int i = 0 ; i < entries ; i + + )
{
entryNames . Add ( rasEntryNames [ i ] . szEntryName ) ;
}
return entryNames ;
}
throw new ApplicationException ( $"RasEnumEntries failed with error code: {result}" ) ;
}
#region WinInet structures
@ -145,6 +239,25 @@ namespace v2rayN.Handler
[FieldOffset(0)]
public IntPtr m_StringPtr ;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct RASENTRYNAME
{
public int dwSize ;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxEntryName + 1)]
public string szEntryName ;
public int dwFlags ;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH + 1)]
public string szPhonebookPath ;
}
// Constants
public const int RAS_MaxEntryName = 256 ;
public const int MAX_PATH = 260 ; // Standard MAX_PATH value in Windows
}
#endregion WinInet structures
@ -156,7 +269,9 @@ namespace v2rayN.Handler
/ /
public enum InternetOption : uint
{
INTERNET_OPTION_PER_CONNECTION_OPTION = 75
INTERNET_OPTION_PER_CONNECTION_OPTION = 75 ,
INTERNET_OPTION_REFRESH = 37 ,
INTERNET_OPTION_SETTINGS_CHANGED = 39
}
/ /
@ -182,6 +297,12 @@ namespace v2rayN.Handler
PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
}
public enum ErrorCode : uint
{
ERROR_BUFFER_TOO_SMALL = 603 ,
ERROR_INVALID_SIZE = 632
}
#endregion WinInet enums
internal static class NativeMethods
@ -189,6 +310,14 @@ namespace v2rayN.Handler
[DllImport("WinInet.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InternetSetOption ( IntPtr hInternet , InternetOption dwOption , IntPtr lpBuffer , int dwBufferLength ) ;
[DllImport("Rasapi32.dll", CharSet = CharSet.Auto)]
public static extern uint RasEnumEntries (
string? reserved , // Reserved, must be null
string? lpszPhonebook , // Pointer to full path and filename of phone-book file. If this parameter is NULL, the entries are enumerated from all the remote access phone-book files
[In, Out] RASENTRYNAME [ ] ? lprasentryname , // Buffer to receive RAS entry names
ref int lpcb , // Size of the buffer
ref int lpcEntries // Number of entries written to the buffer
) ;
}
//判断是否使用代理