前段时间在用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
获取网络服务开启状态
使用ifconfig [设备名称]
命令查看当前网络设备状态
ifconfig # 获取所有网络设备状态
因此先使用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
设置代理
现在已经拿到了处于活跃状态的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
查看网络设置,可以看到代理已经被修改了
关闭代理
关闭的过程与开启一样,只需要将最后的设置状态改为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
}