# How v2rayN Implements System Proxy Handling Across Windows, Linux, and macOS

> Discover how v2rayN handles system proxy settings on Windows, Linux, and macOS. Learn about native WinAPI calls, Bash scripts, gsettings, gsettings, kwriteconfig, and networksetup integration.

- Repository: [2dust/v2rayN](https://github.com/2dust/v2rayN)
- Tags: how-to-guide
- Published: 2026-02-27

---

**v2rayN routes system proxy configuration through a platform-specific abstraction layer where Windows uses native WinAPI calls, while Linux and macOS execute bundled Bash scripts that interface with gsettings, kwriteconfig, and networksetup respectively.**

The open-source proxy client v2rayN (2dust/v2rayN) implements a unified cross-platform system for managing OS-level proxy settings directly from its .NET-based ServiceLib architecture. Rather than relying on external dependencies or platform-specific libraries, the application embeds OS-specific logic that enables seamless switching between global proxy, PAC (Proxy Auto-Config), and direct connection modes across all supported operating systems.

## The Orchestration Layer: SysProxyHandler

The static class `SysProxyHandler` serves as the central dispatcher for all system proxy operations in [`v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs). When the user toggles system proxy settings or the application starts, the `UpdateSysProxy` method reads the desired configuration from `Config.SystemProxyItem.SysProxyType` and routes execution to the appropriate platform implementation.

```csharp
public static async Task<bool> UpdateSysProxy(Config config, bool forceDisable)
{
    var type = config.SystemProxyItem.SysProxyType;
    if (forceDisable && type != ESysProxyType.Unchanged) 
        type = ESysProxyType.ForcedClear;

    var port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
    var exceptions = config.SystemProxyItem.SystemProxyExceptions.Replace(" ", "");

    switch (type)
    {
        case ESysProxyType.ForcedChange when Utils.IsWindows():
            GetWindowsProxyString(config, port, out var strProxy, out var strExceptions);
            ProxySettingWindows.SetProxy(strProxy, strExceptions, 2);
            break;
        case ESysProxyType.ForcedChange when Utils.IsLinux():
            await ProxySettingLinux.SetProxy(Global.Loopback, port, exceptions);
            break;
        case ESysProxyType.ForcedChange when Utils.IsMacOS():
            await ProxySettingOSX.SetProxy(Global.Loopback, port, exceptions);
            break;
        case ESysProxyType.ForcedClear when Utils.IsWindows():
            ProxySettingWindows.UnsetProxy();
            break;
        case ESysProxyType.ForcedClear when Utils.IsLinux():
            await ProxySettingLinux.UnsetProxy();
            break;
        case ESysProxyType.ForcedClear when Utils.IsMacOS():
            await ProxySettingOSX.UnsetProxy();
            break;
        case ESysProxyType.Pac when Utils.IsWindows():
            await SetWindowsProxyPac(port);
            break;
    }
    return true;
}

```

The method uses the `ESysProxyType` enum (ForcedChange, ForcedClear, Pac, Unchanged) combined with runtime platform detection via `Utils.IsWindows()`, `Utils.IsLinux()`, and `Utils.IsMacOS()` to determine which helper class receives the command.

## Windows Implementation: Native WinAPI and Registry

On Windows, v2rayN interfaces directly with the operating system through the **`InternetSetOption`** WinAPI function rather than modifying the registry manually. The `ProxySettingWindows` class in [`v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs) constructs `InternetPerConnOption` structures to configure both LAN and dial-up (RAS) connections.

```csharp
public static bool SetProxy(string? strProxy, string? exceptions, int type)
{
    // Set LAN connection
    var result = SetConnectionProxy(null, strProxy, exceptions, type);
    
    // Set all RAS (dial-up) connections
    foreach (var connection in EnumerateRasEntries())
        result |= SetConnectionProxy(connection, strProxy, exceptions, type);
    return result;
}

```

The native invocation passes the configuration directly to Windows' internet settings:

```csharp
var isSuccess = NativeMethods.InternetSetOption(
    nint.Zero,
    InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,
    ipcoListPtr, list.dwSize);

```

For **PAC mode** on Windows, v2rayN starts a local HTTP server via `PacManager` and writes the PAC URL into the `AutoConfigURL` registry value using type `4` in the proxy configuration structure. This enables automatic proxy script updates without requiring manual file path configuration.

## Linux Implementation: GNOME/KDE Script Integration

Linux systems do not expose a unified system proxy API, so v2rayN ships with an embedded Bash script `proxy_set_linux_sh` located in `v2rayN/ServiceLib/Sample/`. The `ProxySettingLinux` class extracts this script to a temporary location and executes it with arguments specifying the proxy mode, loopback address (`127.0.0.1`), port, and exception list.

```csharp
public static async Task SetProxy(string host, int port, string exceptions)
{
    List<string> args = ["manual", host, port.ToString(), exceptions];
    await ExecCmd(args);
}

```

The execution logic checks for a user-provided custom script before falling back to the embedded resource:

```csharp
var fileName = (customSystemProxyScriptPath.IsNotEmpty() && File.Exists(customSystemProxyScriptPath))
    ? customSystemProxyScriptPath
    : await FileUtils.CreateLinuxShellFile(_proxySetFileName,
        EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName), false);
await Utils.GetCliWrapOutput(fileName, args);

```

The bundled script configures **GNOME** via `gsettings` and **KDE** via `kwriteconfig5`, setting the proxy mode to manual and applying the SOCKS host and port to both HTTP and HTTPS proxy settings while preserving user-defined bypass domains.

## macOS Implementation: Networksetup Script Execution

macOS follows a similar shell-script approach but utilizes the **`networksetup`** command-line tool through the `proxy_set_osx_sh` script. The `ProxySettingOSX` class in [`v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs) builds argument lists for "set" or "clear" operations and passes them to the script executor.

```csharp
public static async Task SetProxy(string host, int port, string exceptions)
{
    List<string> args = ["set", host, port.ToString()];
    if (exceptions.IsNotEmpty())
        args.AddRange(exceptions.Split(','));
    await ExecCmd(args);
}

```

The macOS script iterates through all network services reported by `networksetup -listallnetworkservices` and applies the proxy configuration to each active interface:

```bash
networksetup -setwebproxy "$SERVICE" "$PROXY_IP" "$PROXY_PORT"
networksetup -setsecurewebproxy "$SERVICE" "$PROXY_IP" "$PROXY_PORT"
networksetup -setsocksfirewallproxy "$SERVICE" "$PROXY_IP" "$PROXY_PORT"
networksetup -setproxybypassdomains "$SERVICE" "${BYPASS_DOMAINS[@]}"

```

This ensures that HTTP, HTTPS, and SOCKS traffic are all routed through the local v2rayN listener regardless of which network adapter is active.

## UI Integration and ViewModel State

User interactions flow through `StatusBarViewModel` in [`v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs), where commands like `SystemProxySetCmd` and `SystemProxyClearCmd` trigger `ChangeSystemProxyAsync`. This method updates the `BlSystemProxySet` and `BlSystemProxyClear` boolean flags that control the visual state of the system tray menu, then delegates to `SysProxyHandler`.

```csharp
private async Task ChangeSystemProxyAsync(ESysProxyType type, bool blChange)
{
    await SysProxyHandler.UpdateSysProxy(_config, false);
    BlSystemProxyClear = type == ESysProxyType.ForcedClear;
    BlSystemProxySet   = type == ESysProxyType.ForcedChange;
}

```

On application startup, the view model automatically applies the last saved proxy configuration by calling `ChangeSystemProxyAsync(_config.SystemProxyItem.SysProxyType, true)`, ensuring the system state matches the persisted user preferences.

## Practical Implementation Examples

### Manually Triggering System Proxy Changes

Developers integrating with v2rayN's core library can programmatically toggle the system proxy:

```csharp
// Retrieve current configuration from AppManager
var config = AppManager.Instance.Config;

// Force enable system proxy on current platform
await SysProxyHandler.UpdateSysProxy(config, forceDisable: false);

// Force disable system proxy
config.SystemProxyItem.SysProxyType = ESysProxyType.ForcedClear;
await SysProxyHandler.UpdateSysProxy(config, forceDisable: true);

```

### Using Custom Proxy Scripts on Linux/macOS

Advanced users can bypass the bundled scripts by specifying a custom executable path in the configuration:

```csharp
// Configure custom script path
config.SystemProxyItem.CustomSystemProxyScriptPath = "/home/user/custom_proxy.sh";

// The handler will execute this script instead of the embedded resource
await SysProxyHandler.UpdateSysProxy(config, false);

```

The custom script must accept the same command-line arguments: `manual <host> <port> [exceptions]` for setting, or `clear` for disabling the proxy.

## Summary

- **Centralized Dispatch**: `SysProxyHandler.UpdateSysProxy` orchestrates all platform-specific proxy operations based on the `ESysProxyType` enum and runtime OS detection.
- **Windows Native API**: Uses `InternetSetOption` with `INTERNET_PER_CONN_OPTION` structures for direct system configuration, supporting both LAN and dial-up connections.
- **Linux Shell Integration**: Executes embedded Bash scripts that configure GNOME (`gsettings`) and KDE (`kwriteconfig5`) desktop environments.
- **macOS Network Setup**: Leverages `networksetup` commands applied to all network services for HTTP, HTTPS, and SOCKS proxy configuration.
- **Customization Support**: Both Linux and macOS implementations support user-provided custom scripts via `CustomSystemProxyScriptPath`.
- **UI State Synchronization**: `StatusBarViewModel` maintains boolean flags (`BlSystemProxySet`, `BlSystemProxyClear`) that reflect the current system proxy state in the user interface.

## Frequently Asked Questions

### How does v2rayN handle system proxy on Windows without requiring administrative privileges?

v2rayN uses the **WinAPI `InternetSetOption`** function to modify per-connection internet settings rather than writing directly to protected registry keys. This approach modifies user-specific internet settings that reside in the user profile hive (HKEY_CURRENT_USER), which does not require elevated privileges for modifications, unlike system-wide registry changes.

### Can users customize the proxy configuration scripts on Linux and macOS?

Yes, users can specify a **custom system proxy script** by setting the `CustomSystemProxyScriptPath` property in the configuration. When this path points to an existing executable file, `ProxySettingLinux` and `ProxySettingOSX` will execute this user-provided script instead of extracting and running the bundled `proxy_set_linux_sh` or `proxy_set_osx_sh` scripts.

### Why does v2rayN use shell scripts instead of native APIs on Linux and macOS?

Linux desktop environments lack a unified system proxy API—**GNOME** uses `gsettings`, **KDE** uses `kwriteconfig`, and other distributions may use different mechanisms. Similarly, **macOS** requires different approaches for each network service. Shell scripts provide a flexible abstraction that can detect the current environment and apply the appropriate commands without requiring v2rayN to link against platform-specific libraries or frameworks.

### How does v2rayN manage PAC (Proxy Auto-Config) mode differently on Windows compared to Linux and macOS?

On **Windows**, v2rayN implements **PAC mode** by starting a local HTTP server (`PacManager`) that serves the PAC file, then registers this local URL (`http://127.0.0.1:<port>/pac`) in the Windows internet settings as the `AutoConfigURL`. On **Linux and macOS**, the current implementation focuses on manual proxy configuration via shell scripts, and users requiring PAC functionality typically configure the PAC URL manually in their system settings or use the global proxy mode with bypass lists.