Browse Source

backport WinINet from shadowsocksrr

pull/2874/head
Student Main 5 years ago
parent
commit
592ecd9702
2 changed files with 340 additions and 0 deletions
  1. +92
    -0
      shadowsocks-csharp/Util/SystemProxy/RAS.cs
  2. +248
    -0
      shadowsocks-csharp/Util/SystemProxy/WinINet.cs

+ 92
- 0
shadowsocks-csharp/Util/SystemProxy/RAS.cs View File

@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace Shadowsocks.Util.SystemProxy
{

enum RasFieldSizeConst
{
MaxEntryName = 256,
MaxPath = 260,
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct RasEntryName
{
public int dwSize;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS.MaxEntryName + 1)]
public string szEntryName;

public int dwFlags;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS.MaxPath + 1)]
public string szPhonebookPath;
}
class RAS
{
public const int MaxEntryName = 256;
public const int MaxPath = 260;

const int ESuccess = 0;
const int RasBase = 600;
const int EBufferTooSmall = 603;

[DllImport("rasapi32.dll", CharSet = CharSet.Auto)]
private static extern uint RasEnumEntries(
// reserved, must be NULL
string reserved,
// pointer to full path and file name of phone-book file
string lpszPhonebook,
// buffer to receive phone-book entries
[In, Out] RasEntryName[] lprasentryname,
// size in bytes of buffer
ref int lpcb,
// number of entries written to buffer
out int lpcEntries
);

public static string[] GetAllConnections()
{
int lpNames = 0;
int entryNameSize = 0;
int lpSize = 0;
uint retval = ESuccess;
RasEntryName[] names = null;

entryNameSize = Marshal.SizeOf(typeof(RasEntryName));

// Windows Vista or later: To determine the required buffer size, call RasEnumEntries
// with lprasentryname set to NULL. The variable pointed to by lpcb should be set to zero.
// The function will return the required buffer size in lpcb and an error code of ERROR_BUFFER_TOO_SMALL.
retval = RasEnumEntries(null, null, null, ref lpSize, out lpNames);
if (retval == EBufferTooSmall)
{
names = new RasEntryName[lpNames];
for (int i = 0; i < names.Length; i++)
{
names[i].dwSize = entryNameSize;
}

retval = RasEnumEntries(null, null, names, ref lpSize, out lpNames);
}

if (retval == ESuccess)
{
if (lpNames == 0)
{
// no entries found.
return Array.Empty<string>();
}
return names.Select(n => n.szEntryName).ToArray();
}
else
{
throw new Exception();
}
}
}
}

+ 248
- 0
shadowsocks-csharp/Util/SystemProxy/WinINet.cs View File

@@ -0,0 +1,248 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace Shadowsocks.Util.SystemProxy
{
public enum InternetOptions
{
Refresh = 37,
SettingsChanged = 39,
PerConnectionOption = 75,
ProxySettingChanged = 95,
}

public enum InternetPerConnectionOptionEnum
{
Flags = 1,
ProxyServer = 2,
ProxyBypass = 3,
AutoConfigUrl = 4,
AutoDiscovery = 5,
AutoConfigSecondaryUrl = 6,
AutoConfigReloadDelay = 7,
AutoConfigLastDetectTime = 8,
AutoConfigLastDetectUrl = 9,
FlagsUI = 10,
}

[Flags]
public enum InternetPerConnectionFlags
{
Direct = 0x01,
Proxy = 0x02,
AutoProxyUrl = 0x04,
AutoDetect = 0x08,
}

[StructLayout(LayoutKind.Explicit)]
public struct InternetPerConnectionOptionUnion : IDisposable
{
[FieldOffset(0)]
public int dwValue;

[FieldOffset(0)]
public IntPtr pszValue;

[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME ftValue;

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

private void Dispose(bool disposing)
{
if (disposing)
{
if (pszValue != IntPtr.Zero)
{
Marshal.FreeHGlobal(pszValue);
pszValue = IntPtr.Zero;
}
}
}
}



[StructLayout(LayoutKind.Sequential)]
public struct InternetPerConnectionOption
{
public int dwOption;
public InternetPerConnectionOptionUnion Value;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct InternetPerConnectionOptionList : IDisposable
{
public int Size;

// The connection to be set. NULL means LAN.
public IntPtr Connection;

public int OptionCount;
public int OptionError;

// List of INTERNET_PER_CONN_OPTIONs.
public System.IntPtr pOptions;

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposing)
{
if (Connection != IntPtr.Zero)
{
Marshal.FreeHGlobal(Connection);
Connection = IntPtr.Zero;
}

if (pOptions != IntPtr.Zero)
{
Marshal.FreeHGlobal(pOptions);
pOptions = IntPtr.Zero;
}
}
}
}

public class WinINet
{
// TODO: Save, Restore,
// TODO: Query, Set,

public static void ProxyGlobal(string server, string bypass)
{
List<InternetPerConnectionOption> options = new List<InternetPerConnectionOption>
{
GetOption(InternetPerConnectionOptionEnum.FlagsUI,InternetPerConnectionFlags.Proxy),
GetOption(InternetPerConnectionOptionEnum.ProxyServer,server),
GetOption(InternetPerConnectionOptionEnum.ProxyBypass,bypass),
};
Exec(options);
}
public static void ProxyPAC(string url)
{
List<InternetPerConnectionOption> options = new List<InternetPerConnectionOption>
{
GetOption(InternetPerConnectionOptionEnum.FlagsUI,InternetPerConnectionFlags.AutoProxyUrl),
GetOption(InternetPerConnectionOptionEnum.ProxyServer,url),
};
Exec(options);
}
public static void Direct()
{
List<InternetPerConnectionOption> options = new List<InternetPerConnectionOption>
{
GetOption(InternetPerConnectionOptionEnum.FlagsUI,InternetPerConnectionFlags.Direct),
};
Exec(options);
}

private static InternetPerConnectionOption GetOption(
InternetPerConnectionOptionEnum option,
InternetPerConnectionFlags flag
)
{
return new InternetPerConnectionOption
{
dwOption = (int)option,
Value =
{
dwValue = (int)flag,
}
};
}

private static InternetPerConnectionOption GetOption(
InternetPerConnectionOptionEnum option,
string param
)
{
return new InternetPerConnectionOption
{
dwOption = (int)option,
Value =
{
pszValue = Marshal.StringToCoTaskMemAuto(param),
}
};
}

private static void Exec(List<InternetPerConnectionOption> options)
{
Exec(options, null);
foreach (var conn in RAS.GetAllConnections())
{
Exec(options, conn);
}
}

private static void Exec(List<InternetPerConnectionOption> options, string connName)
{
int len = options.Sum(o => Marshal.SizeOf(o));

IntPtr buf = Marshal.AllocCoTaskMem(len);
IntPtr cur = buf;

foreach (var o in options)
{
Marshal.StructureToPtr(o, cur, false);
cur += Marshal.SizeOf(o);
}
InternetPerConnectionOptionList optionList = new InternetPerConnectionOptionList
{
pOptions = buf,
Connection = string.IsNullOrEmpty(connName)
? IntPtr.Zero
: Marshal.StringToHGlobalAuto(connName),
OptionCount = options.Count,
OptionError = 0,
};
int listSize = Marshal.SizeOf(optionList);
optionList.Size = listSize;

IntPtr unmanagedList = Marshal.AllocCoTaskMem(listSize);
Marshal.StructureToPtr(optionList, unmanagedList, true);

bool ok = InternetSetOption(
IntPtr.Zero,
(int)InternetOptions.PerConnectionOption,
unmanagedList,
listSize
);

Marshal.FreeCoTaskMem(buf);
Marshal.FreeCoTaskMem(unmanagedList);

if (!ok) throw new Exception();
ok = InternetSetOption(
IntPtr.Zero,
(int)InternetOptions.ProxySettingChanged,
IntPtr.Zero,
0
);
if (!ok) throw new Exception();
ok = InternetSetOption(
IntPtr.Zero,
(int)InternetOptions.Refresh,
IntPtr.Zero,
0
);
if (!ok) throw new Exception();

}

[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength);
}

}

Loading…
Cancel
Save