使用Node修改系统代理--兼容Windows、MacOS

1,037 阅读3分钟

前段时间在用electron开发一个跨平台抓包工具,需要设置系统代理到我自己的服务,但是没有找到合适的跨平台修改代理的node工具,于是对不同平台的设置做了一些探索。

封装了一个工具cross-os-proxy ,支持Windows和Mac,源码地址

const osProxy = require('cross-os-proxy');

(async () => {
    await osProxy.setProxy('127.0.0.1', 9999); // set http and https proxy
    console.log('done');
})();

(async () => {
    await osProxy.closeProxy(); // close http and https proxy
    console.log('done');
})();

windows

windows的代理设置写在注册表里,路径是HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings

通过npm包regedit,修改注册表即可。

const regedit = require('regedit').promisified
const internetSettingsPath = 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings'

const valuesToPut = {
    [internetSettingsPath]: {
      'ProxyServer': {
        value: `127.0.0.1:9999`, // 代理地址
        type: 'REG_SZ'
      },
      'ProxyEnable': {
        value: 1, // 1-开启 0-关闭
        type: 'REG_DWORD'
      }
    }
  }
  
regedit.putValue(valuesToPut)

MacOS

mac系统下需要借助shell去操作系统的网络设置。

mac下网络设备不止一个,如USB网卡、wifi... 需要找到是哪个设备在联网,然后对其进行设置。

设置流程:

graph LR
获取所有网络设备 --> 获取活跃的网络设备 --> 设置活跃设备的代理地址和端口 --> 开启代理

networksetup

networksetup是用于设置/获取客户端的网络设置的命令行工具。
-listnetworkserviceorder 获取所有网络服务
-setwebproxy <networkservice> <host> <port> 设置HTTP代理地址
-setsecurewebproxy <networkservice> <host> <port> 设置HTTPS代理地址
-setwebproxystate <networkservice> <on off> 打开/关闭HTTP代理
-setsecurewebproxystate <networkservice> <on off> 打开/关闭HTTPS代理

要设置代理,需要先知道当前活跃的网络服务(networkservice)

获取所有网络服务与设备名称

networksetup -listnetworkserviceorder

image.png

获取网络服务开启状态

使用ifconfig [设备名称]命令查看当前网络设备状态

ifconfig # 获取所有网络设备状态

image.png

因此先使用networksetup -listnetworkserviceorder拿到所有的网络设备名称,进行遍历,使用ifconfig判断开启状态,找到开启的networkservice

新建proxy.sh

# 遍历设备信息
while read -r line; do
    # awk用来处理每一行的字符串,这里用来获取networkservice(sname)和设备名(sdev)
    sname=$(echo "$line" | awk -F  "(, )|(: )|[)]" '{print $2}')
    sdev=$(echo "$line" | awk -F  "(, )|(: )|[)]" '{print $4}')

    #echo "Current service: $sname, $sdev, $currentservice"
    
    # 找出处于活跃状态的networkservice
    if [ -n "$sdev" ]; then
        ifout="$(ifconfig "$sdev" 2>/dev/null)"
        echo "$ifout" | grep 'status: active' > /dev/null 2>&1
        rc="$?"
        if [ "$rc" -eq 0 ]; then
            currentservice="$sname"
            echo "$currentservice"
        fi
    fi
done <<< "$(networksetup -listnetworkserviceorder | grep 'Hardware Port')" # 查询所有网络设备|过滤"Hardware Port"行

# 无活跃设备(没有联网)
if [ -z "$currentservice" ]; then
    >&2 echo "Could not find current service"
    exit 1
fi

# 拿到处于活跃状态的networkservice -> $currentservice
echo  Open web proxy  for  currentservice: $currentservice # Open web proxy for currentservice: Wi-Fi (当前用wifi连接的网络)
sh proxy.sh

image.png

设置代理

现在已经拿到了处于活跃状态的networkservice,使用networksetup设置代理

proxy.sh追加代码

#...

echo $1 # host
echo $2 # port

if  [  "$2"  !=  ""  ];  then
    networksetup -setwebproxy $currentservice $1 $2    #设置HTTP代理
    networksetup -setsecurewebproxy $currentservice $1 $2    #设置HTTPS代理
fi
networksetup -setwebproxystate $currentservice on    #打开HTTP代理
networksetup -setsecurewebproxystate $currentservice on   #打开HTTPS代理
echo  Done

执行proxy.sh image.png

查看网络设置,可以看到代理已经被修改了 image.png

关闭代理

关闭的过程与开启一样,只需要将最后的设置状态改为off就行了

networksetup -setwebproxystate $currentservice off    # 关闭HTTP代理
networksetup -setsecurewebproxystate $currentservice off   # 关闭HTTPS代理

node中操作shell

使用child_process下的execFile执行shell脚本

const { execFile } = require("child_process")
const path = require("path")

/**
 * start http & https proxy
 * @param {string} host proxy host eg: 127.0.0.1
 * @param {number} port port eg: 8001
 * @returns Promise<string>
 */
function setProxy(host, port) {
  const proxyOnShellPath = path.resolve(__dirname, "./proxyon.sh")
  return new Promise((resolve, reject) => {
    execFile(proxyOnShellPath, [host, port], (error, stdout, stderr) => {
      if (error) {
        reject(error)
        throw error
      } else {
        resolve(stdout)
      }
    })
  })
}

兼容

使用process.platform获取当前系统,使用不同的方式设置代理即可。

const platform = process.platform.toLowerCase() // darwin-MacOS  win32-windows
const platformsPath = "./platforms"
const currentOsProxy = require(`${platformsPath}/${platform}`)

/**
 * set system proxy, includes http & https
 * @param {string} host proxy host eg: '127.0.0.1'
 * @param {number} port eg: 8001
 * @returns Promise
 */
async function setProxy(host, port) {
  return currentOsProxy.setProxy(host, port)
}

/**
 * close system proxy, includes http & https
 * @returns Promise
 */
async function closeProxy() {
  return currentOsProxy.closeProxy()
}

module.exports = {
  setProxy,
  closeProxy
}