监听系统代理变更

mac

//
//  main.swift
//  sptest
//  格式化输出部分的代码来源: https://github.com/janvogt/export-proxies/blob/master/export-proxies/ProxySettings.swift
//  API Document: https://developer.apple.com/documentation/systemconfiguration/1437818-scdynamicstorecreatewithoptions
//  Created by henry on 2024/4/26.
//

import SystemConfiguration
import CoreFoundation
import Foundation

private protocol ValueGetter {
    var type: ProxySetting { get }
    func getValueFromDict(_ dict: NSDictionary) -> String?
}

fileprivate enum ProxySetting {
    case http, https, ftp, socks, exceptions
    var envVariableName: String {
        get {
            var envVar: String
            switch self {
            case .http: envVar = "http"
            case .https: envVar = "https"
            case .ftp: envVar = "ftp"
            case .socks: envVar = "all"
            case .exceptions: envVar = "no"
            }
            return suffixProxy(envVar)
        }
    }
    var protocolName: String? {
        get {
            var httpsProxyProtocol: String = "https"
            if CommandLine.argc == 2 {
                if CommandLine.arguments[1] == "--use-http-protocol-for-https-proxy" {
                    httpsProxyProtocol = "http"
                }
            }
            var proto: String?
            switch self {
            case .http: proto = "http"
            case .https: proto = httpsProxyProtocol
            case .ftp: proto = "ftp"
            case .socks: proto = "socks"
            default: break
            }
            return proto
        }
    }
    fileprivate func suffixProxy(_ label: String) -> String {
        return "\(label)_proxy"
    }
}

fileprivate struct ProxyProtocol: ValueGetter {
    let type: ProxySetting
    let kHost, kPort, kEnabled: String
    func getValueFromDict(_ dict: NSDictionary) -> String? {
        var value: String?
        switch (type.protocolName, dict[kHost] as! NSString?, dict[kPort] as! NSNumber?, dict[kEnabled] as! NSNumber?) {
        case (.some(let proto), .some(let host), .some(let port), let enabled) where (enabled != nil && enabled! == 1):
            value = "\(proto)://\(host):\(port)"
        default: break
        }
        return value
    }
}
fileprivate struct Exception: ValueGetter {
    let type = ProxySetting.exceptions
    let kExceptions: NSString = kSCPropNetProxiesExceptionsList
    func getValueFromDict(_ dict: NSDictionary) -> String? {
        var value: String?
        switch (type, dict[kExceptions] as? [String]) {
        case (.exceptions, .some(let exceptions)):
            value = exceptions.map {
                $0.replacingOccurrences(of: "*.", with: ".")
            }.joined(separator: ",")
        default: break
        }
        return value
    }
}

fileprivate let protocols: [ValueGetter] = [
    ProxyProtocol(type: .http,
        kHost: kSCPropNetProxiesHTTPProxy as String,
        kPort: kSCPropNetProxiesHTTPPort as String,
        kEnabled: kSCPropNetProxiesHTTPEnable as String),
    ProxyProtocol(type: .https,
        kHost: kSCPropNetProxiesHTTPSProxy as String,
        kPort: kSCPropNetProxiesHTTPSPort as String,
        kEnabled: kSCPropNetProxiesHTTPSEnable as String),
    ProxyProtocol(type: .ftp,
        kHost: kSCPropNetProxiesFTPProxy as String,
        kPort: kSCPropNetProxiesFTPPort as String,
        kEnabled: kSCPropNetProxiesFTPEnable as String),
    ProxyProtocol(type: .socks,
        kHost: kSCPropNetProxiesSOCKSProxy as String,
        kPort: kSCPropNetProxiesSOCKSPort as String,
        kEnabled: kSCPropNetProxiesSOCKSEnable as String),
    Exception()
]


let changed: SCDynamicStoreCallBack = {store, keys, pointer in
    let swiftArray = keys as! Array<String>
    let includeGlobalProxy = swiftArray.contains(where: {$0 == "State:/Network/Global/Proxies"})
    if (includeGlobalProxy) {
        print("proxy has been changed!")
        if let osxProxySettings: NSDictionary = SCDynamicStoreCopyProxies(store) {
            for proto in protocols {
                switch (proto.type.envVariableName, proto.getValueFromDict(osxProxySettings)) {
                case (let name, .some(let value)):
                    print("name: \(name), value: \(value)")
                default: break
                }
            }
        }
    } else {
        print("other settings has been changed... \(keys)")
    }
}
if let store = SCDynamicStoreCreateWithOptions(nil, "app" as CFString, nil, changed, nil) {
    let keys = ["State:/Network/Global/Proxies" as CFString] as CFArray
    SCDynamicStoreSetNotificationKeys(store, keys, nil)
    
    let loop = SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault, store, 0)
    CFRunLoopAddSource(CFRunLoopGetCurrent(), loop, CFRunLoopMode.defaultMode)
    CFRunLoopRun()
}

windows

// 原理:监听注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NlaSvc\Parameters\Internet\ManualProxies
// 依赖Advapi32.dll
#include <windows.h>
#include <tchar.h>
#include <stdio.h>

//void main(int argc, char *argv[])
void __cdecl _tmain(int argc, TCHAR* argv[])
{
    DWORD  dwFilter = REG_NOTIFY_CHANGE_NAME |
        REG_NOTIFY_CHANGE_ATTRIBUTES |
        REG_NOTIFY_CHANGE_LAST_SET |
        REG_NOTIFY_CHANGE_SECURITY;

    HANDLE hEvent;
    HKEY   hMainKey;
    HKEY   hKey;
    LONG   lErrorCode;

    hMainKey = HKEY_LOCAL_MACHINE;

    // Open a key.
    auto subKey = TEXT("SYSTEM\\CurrentControlSet\\Services\\NlaSvc\\Parameters\\Internet\\ManualProxies");
    lErrorCode = RegOpenKeyEx(hMainKey, subKey, 0, KEY_NOTIFY, &hKey);
    if (lErrorCode != ERROR_SUCCESS)
    {
        _tprintf(TEXT("Error in RegOpenKeyEx (%d).\n"), lErrorCode);
        return;
    }

    // Create an event.
    hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (hEvent == NULL)
    {
        _tprintf(TEXT("Error in CreateEvent (%d).\n"), GetLastError());
        return;
    }

    // Watch the registry key for a change of value.
    lErrorCode = RegNotifyChangeKeyValue(hKey,
        TRUE,
        dwFilter,
        hEvent,
        TRUE);
    if (lErrorCode != ERROR_SUCCESS)
    {
        _tprintf(TEXT("Error in RegNotifyChangeKeyValue (%d).\n"), lErrorCode);
        return;
    }

    // Wait for an event to occur.
    _tprintf(TEXT("Waiting for a change in the specified key...\n"));
    if (WaitForSingleObject(hEvent, INFINITE) == WAIT_FAILED)
    {
        _tprintf(TEXT("Error in WaitForSingleObject (%d).\n"), GetLastError());
        return;
    }
    else _tprintf(TEXT("\nChange has occurred.\n"));

    // Close the key.
    lErrorCode = RegCloseKey(hKey);
    if (lErrorCode != ERROR_SUCCESS)
    {
        _tprintf(TEXT("Error in RegCloseKey (%d).\n"), GetLastError());
        return;
    }

    // Close the handle.
    if (!CloseHandle(hEvent))
    {
        _tprintf(TEXT("Error in CloseHandle.\n"));
        return;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容