├── .github └── workflows │ └── go.yml ├── .gitignore ├── README.md ├── binaries ├── darwin │ └── sysproxy ├── linux_386 │ └── sysproxy ├── linux_amd64 │ └── sysproxy ├── linux_arm │ └── sysproxy └── windows │ ├── sysproxy_386.exe │ └── sysproxy_amd64.exe ├── example ├── example.go └── icon.png ├── go.mod ├── go.sum ├── sysproxy.go ├── sysproxy2go.sh ├── sysproxy_darwin.go ├── sysproxy_linux_386.go ├── sysproxy_linux_amd64.go ├── sysproxy_linux_arm64.go ├── sysproxy_test.go ├── sysproxy_windows_386.go └── sysproxy_windows_amd64.go /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v3 18 | with: 19 | go-version: 1.18 20 | 21 | - name: Build 22 | run: go build -v ./... 23 | 24 | - name: Test 25 | run: go test -v ./... 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | example/example* 2 | example/pac* 3 | *~ 4 | *.swp 5 | */*.exe 6 | sysproxy.test.exe 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sysproxy 2 | 3 | [sysproxy](https://github.com/getlantern/sysproxy) is a simple Go library to 4 | toggle the system proxy on and off for Windows and MacOS. It will 5 | extract a helper tool and use it to actually change the system proxy settings. 6 | 7 | ```go 8 | sysproxy.EnsureHelperToolPresent(fullPath, prompt, iconFullPath) 9 | sysproxy.On(proxyAddr string) 10 | sysproxy.Off() 11 | ``` 12 | 13 | See 'example/main.go' for detailed usage. 14 | 15 | ### Embedding sysproxy-cmd 16 | 17 | sysproxy uses binaries from the 18 | [sysproxy-cmd](https://github.com/getlantern/sysproxy-cmd) project and from 19 | [sysproxy-cmd-darwin](https://github.com/getlantern/sysproxy-cmd-darwin). 20 | 21 | To embed the binaries for different platforms, use the `sysproxy2go.sh` script. 22 | This script takes care of code signing the Windows and MacOS executables. 23 | 24 | This script signs the Windows executable, which requires that 25 | [osslsigncode](http://sourceforge.net/projects/osslsigncode/) utility be 26 | installed. On OS X with homebrew, you can do this with 27 | `brew install osslsigncode`. 28 | 29 | You will also need to set the environment variables BNS_CERT and BNS_CERT_PASS 30 | to point to [bns-cert.p12](https://github.com/getlantern/too-many-secrets/blob/master/bns_cert.p12) 31 | and its [password](https://github.com/getlantern/too-many-secrets/blob/master/build-installers/env-vars.txt#L3) 32 | so that the script can sign the Windows executable. 33 | 34 | This script also signs the MacOS executable, which requires you to install a MacOS signing certificate. 35 | -------------------------------------------------------------------------------- /binaries/darwin/sysproxy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getlantern/sysproxy/384834c7b4cb15ca6756e89b8a90b9970dad9822/binaries/darwin/sysproxy -------------------------------------------------------------------------------- /binaries/linux_386/sysproxy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getlantern/sysproxy/384834c7b4cb15ca6756e89b8a90b9970dad9822/binaries/linux_386/sysproxy -------------------------------------------------------------------------------- /binaries/linux_amd64/sysproxy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getlantern/sysproxy/384834c7b4cb15ca6756e89b8a90b9970dad9822/binaries/linux_amd64/sysproxy -------------------------------------------------------------------------------- /binaries/linux_arm/sysproxy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getlantern/sysproxy/384834c7b4cb15ca6756e89b8a90b9970dad9822/binaries/linux_arm/sysproxy -------------------------------------------------------------------------------- /binaries/windows/sysproxy_386.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getlantern/sysproxy/384834c7b4cb15ca6756e89b8a90b9970dad9822/binaries/windows/sysproxy_386.exe -------------------------------------------------------------------------------- /binaries/windows/sysproxy_amd64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getlantern/sysproxy/384834c7b4cb15ca6756e89b8a90b9970dad9822/binaries/windows/sysproxy_amd64.exe -------------------------------------------------------------------------------- /example/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | 7 | "github.com/getlantern/golog" 8 | "github.com/getlantern/sysproxy" 9 | ) 10 | 11 | var log = golog.LoggerFor("example") 12 | 13 | func main() { 14 | helperFullPath := "sysproxy-cmd" 15 | iconFullPath, _ := filepath.Abs("./icon.png") 16 | log.Debugf("Using icon at %v", iconFullPath) 17 | err := sysproxy.EnsureHelperToolPresent(helperFullPath, "Input your password and save the world!", iconFullPath) 18 | if err != nil { 19 | fmt.Printf("Error EnsureHelperToolPresent: %s\n", err) 20 | return 21 | } 22 | off, err := sysproxy.On("localhost:12345") 23 | if err != nil { 24 | fmt.Printf("Error set proxy: %s\n", err) 25 | return 26 | } 27 | fmt.Println("proxy set, hit enter to continue (or kill the parent process)...") 28 | var i int 29 | fmt.Scanf("%d\n", &i) 30 | off() 31 | } 32 | -------------------------------------------------------------------------------- /example/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getlantern/sysproxy/384834c7b4cb15ca6756e89b8a90b9970dad9822/example/icon.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/getlantern/sysproxy 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/getlantern/byteexec v0.0.0-20220903141943-7db46f110fbc 7 | github.com/getlantern/elevate v0.0.0-20220903142053-479ab992b264 8 | github.com/getlantern/golog v0.0.0-20211223150227-d4d95a44d873 9 | github.com/stretchr/testify v1.8.0 10 | ) 11 | 12 | require ( 13 | github.com/davecgh/go-spew v1.1.1 // indirect 14 | github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect 15 | github.com/getlantern/errors v1.0.1 // indirect 16 | github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c // indirect 17 | github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect 18 | github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect 19 | github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect 20 | github.com/getlantern/sysproxy-cmd v0.0.0-20231221103535-ebb0c581dff3 // indirect 21 | github.com/go-stack/stack v1.8.0 // indirect 22 | github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect 23 | github.com/pmezard/go-difflib v1.0.0 // indirect 24 | go.uber.org/atomic v1.7.0 // indirect 25 | go.uber.org/multierr v1.6.0 // indirect 26 | go.uber.org/zap v1.19.1 // indirect 27 | gopkg.in/yaml.v3 v3.0.1 // indirect 28 | ) 29 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= 2 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/getlantern/byteexec v0.0.0-20220903141943-7db46f110fbc h1:npKLx1Gx+m6MaKnS+QVvw6wtvtf9ado42/mn8KYjD54= 7 | github.com/getlantern/byteexec v0.0.0-20220903141943-7db46f110fbc/go.mod h1:oD9q9NB1LNBLHk3WAwza4tivxV7tm7jKFlCNCAv3+M8= 8 | github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4= 9 | github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY= 10 | github.com/getlantern/elevate v0.0.0-20220903142053-479ab992b264 h1:q50MSzoIIKotG7apUYaDME/bNGhOJMjG33Fpfc7KPWM= 11 | github.com/getlantern/elevate v0.0.0-20220903142053-479ab992b264/go.mod h1:2VB8zy/kMNX347i5fdusJbPNAZE26u8qoHJDy7CWP9A= 12 | github.com/getlantern/errors v1.0.1 h1:XukU2whlh7OdpxnkXhNH9VTLVz0EVPGKDV5K0oWhvzw= 13 | github.com/getlantern/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A= 14 | github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c h1:mcz27xtAkb1OuOLBct/uFfL1p3XxAIcFct82GbT+UZM= 15 | github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c/go.mod h1:8DGAx0LNUfXNnEH+fXI0s3OCBA/351kZCiz/8YSK3i8= 16 | github.com/getlantern/golog v0.0.0-20211223150227-d4d95a44d873 h1:nnod94N4hMKb7pyJmnXDk+HR23o1S2CbZ4oMKzHbp9A= 17 | github.com/getlantern/golog v0.0.0-20211223150227-d4d95a44d873/go.mod h1:+ZU1h+iOVqWReBpky6d5Y2WL0sF2Llxu+QcxJFs2+OU= 18 | github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 h1:micT5vkcr9tOVk1FiH8SWKID8ultN44Z+yzd2y/Vyb0= 19 | github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o= 20 | github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 h1:XYzSdCbkzOC0FDNrgJqGRo8PCMFOBFL9py72DRs7bmc= 21 | github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA= 22 | github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA= 23 | github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA= 24 | github.com/getlantern/sysproxy-cmd v0.0.0-20231221103535-ebb0c581dff3 h1:MRlX6j1b/QJUHT2lqiczOnoghCzDL34yLOdJmhhNxlc= 25 | github.com/getlantern/sysproxy-cmd v0.0.0-20231221103535-ebb0c581dff3/go.mod h1:THyQA0TgXZDjLT2LufPYEA8D5oO+nyCNdFEeF5Ui/oM= 26 | github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= 27 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 28 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 29 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 30 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 31 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 32 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 33 | github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= 34 | github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= 35 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 36 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 37 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 38 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 39 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 40 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 41 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 42 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 43 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 44 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 45 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 46 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 47 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 48 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 49 | go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4= 50 | go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 51 | go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= 52 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 53 | go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= 54 | go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= 55 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 56 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 57 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 58 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 59 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 60 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 61 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 62 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 63 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 64 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 65 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 66 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 67 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 68 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 69 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 70 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 71 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 72 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 73 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 74 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 75 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 76 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 77 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 78 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 79 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 80 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 81 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 82 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 83 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 84 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 85 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 86 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 87 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 88 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 89 | -------------------------------------------------------------------------------- /sysproxy.go: -------------------------------------------------------------------------------- 1 | package sysproxy 2 | 3 | import ( 4 | _ "embed" 5 | "fmt" 6 | "net" 7 | "os/exec" 8 | "strings" 9 | "sync" 10 | 11 | "github.com/getlantern/byteexec" 12 | "github.com/getlantern/golog" 13 | ) 14 | 15 | var ( 16 | log = golog.LoggerFor("sysproxy") 17 | 18 | mu sync.Mutex 19 | be *byteexec.Exec 20 | ) 21 | 22 | // EnsureHelperToolPresent checks if helper tool exists and extracts it if not. 23 | // On Mac OS, it also checks and set the file's owner to root:wheel and the setuid bit, 24 | // it will request user to input password through a dialog to gain the rights to do so. 25 | // path: absolute or relative path of the file to be checked and generated if 26 | // not exists. Note - relative paths are resolved relative to the system- 27 | // specific folder for aplication resources. 28 | // prompt: the message to be shown on the dialog. 29 | // iconPath: the full path of the icon to be shown on the dialog. 30 | func EnsureHelperToolPresent(path string, prompt string, iconFullPath string) (err error) { 31 | mu.Lock() 32 | defer mu.Unlock() 33 | if len(sysproxy) == 0 { 34 | return fmt.Errorf("unable to find binary: %v", sysproxy) 35 | } 36 | be, err = byteexec.New(sysproxy, path) 37 | if err != nil { 38 | return fmt.Errorf("unable to extract helper tool: %v", err) 39 | } 40 | return ensureElevatedOnDarwin(be, prompt, iconFullPath) 41 | } 42 | 43 | // On tells OS to configure proxy through `addr` as host:port. It always returns 44 | // a function that can be used to clear the system proxy setting. If the current 45 | // process terminates before the clear function is called, the system proxy 46 | // setting will be cleared anyway. 47 | func On(addr string) (func() error, error) { 48 | host, port, err := net.SplitHostPort(addr) 49 | if err != nil { 50 | return nil, fmt.Errorf("unable to parse address %v: %v", addr, err) 51 | } 52 | ip := net.ParseIP(host) 53 | if ip != nil && ip.To4() == nil { 54 | host = "[" + host + "]" 55 | } 56 | 57 | mu.Lock() 58 | defer mu.Unlock() 59 | if be == nil { 60 | return nil, fmt.Errorf("call EnsureHelperToolPresent() first") 61 | } 62 | 63 | cmd := be.Command("on", host, port) 64 | onErr := run(cmd) 65 | off, offErr := waitAndCleanup(host, port) 66 | if offErr != nil { 67 | log.Errorf("Unable to prepare waitAndCleanup job: %v", offErr) 68 | } 69 | if onErr != nil { 70 | return off, onErr 71 | } 72 | verifyErr := verify(addr) 73 | return off, verifyErr 74 | } 75 | 76 | // Off immediately unsets the proxy at addr as the system proxy. 77 | func Off(addr string) error { 78 | host, port, err := net.SplitHostPort(addr) 79 | if err != nil { 80 | return fmt.Errorf("unable to parse address %v: %v", addr, err) 81 | } 82 | 83 | mu.Lock() 84 | defer mu.Unlock() 85 | if be == nil { 86 | return fmt.Errorf("call EnsureHelperToolPresent() first") 87 | } 88 | 89 | cmd := be.Command("off", host, port) 90 | if err := run(cmd); err != nil { 91 | return err 92 | } 93 | return verify("") 94 | } 95 | 96 | type resultType struct { 97 | out []byte 98 | err error 99 | } 100 | 101 | func waitAndCleanup(host string, port string) (func() error, error) { 102 | cmd := be.Command("wait-and-cleanup", host, port) 103 | stdin, err := cmd.StdinPipe() 104 | if err != nil { 105 | return nil, err 106 | } 107 | // Set up the command to run as a detached process 108 | detach(cmd) 109 | resultCh := make(chan *resultType) 110 | go func() { 111 | out, err := cmd.CombinedOutput() 112 | resultCh <- &resultType{ 113 | out: out, 114 | err: err, 115 | } 116 | }() 117 | return func() error { 118 | stdin.Close() 119 | result := <-resultCh 120 | if result.err != nil { 121 | return fmt.Errorf("unable to finish %v: %s\n%s", cmd.Path, result.err, string(result.out)) 122 | } 123 | return verify("") 124 | }, nil 125 | } 126 | 127 | func run(cmd *exec.Cmd) error { 128 | out, err := cmd.CombinedOutput() 129 | if err != nil { 130 | return fmt.Errorf("unable to execute %v: %s\n%s", cmd.Path, err, string(out)) 131 | } 132 | log.Debugf("Command %v output %v", cmd.Path, string(out)) 133 | return nil 134 | } 135 | 136 | func verify(expected string) error { 137 | cmd := be.Command("show") 138 | out, err := cmd.Output() 139 | if err != nil { 140 | return err 141 | } 142 | actual := string(out) 143 | log.Debugf("Command %v output %v", cmd.Path, actual) 144 | if !allEquals(expected, actual) { 145 | return fmt.Errorf("unexpected output: expect '%s', got '%s'", expected, actual) 146 | } 147 | return nil 148 | } 149 | 150 | func allEquals(expected string, actual string) bool { 151 | if (expected == "") != (strings.TrimSpace(actual) == "") { // XOR 152 | return false 153 | } 154 | lines := strings.Split(actual, "\n") 155 | for _, l := range lines { 156 | trimmed := strings.TrimSpace(l) 157 | if trimmed != "" && trimmed != expected { 158 | return false 159 | } 160 | } 161 | return true 162 | } 163 | -------------------------------------------------------------------------------- /sysproxy2go.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################### 4 | # 5 | # This script regenerates the source files that embed the sysproxy-cmd executable. 6 | # 7 | ############################################################################### 8 | 9 | set -euo pipefail 10 | function die() { 11 | echo "$*" 12 | exit 1 13 | } 14 | 15 | if [ -z "$BNS_CERT" ] || [ -z "$BNS_CERT_PASS" ] 16 | then 17 | die "$0: Please set BNS_CERT and BNS_CERT_PASS to the bns_cert.p12 signing key and the password for that key" 18 | fi 19 | 20 | BINPATH=../sysproxy-cmd/binaries 21 | 22 | # Check for a recent version of osslsigncode that can handle 32-bit Windows 23 | # binaries. 24 | osslsigncode_version=$(osslsigncode --version 2>&1 | grep "using:" | cut -d " " -f 2 | cut -d "," -f 1) 25 | if [[ "$osslsigncode_version" < "1.7.1" ]] 26 | then 27 | die "$0: Please upgrade osslsigncode to at least version 1.7.1" 28 | fi 29 | 30 | osslsigncode sign -pkcs12 "$BNS_CERT" -pass "$BNS_CERT_PASS" -in $BINPATH/windows/sysproxy_386.exe -out $BINPATH/windows/sysproxy_386.exe || die "Could not sign windows 386" 31 | osslsigncode sign -pkcs12 "$BNS_CERT" -pass "$BNS_CERT_PASS" -in $BINPATH/windows/sysproxy_amd64.exe -out $BINPATH/windows/sysproxy_amd64.exe || die "Could not sign windows 386" 32 | cp $BINPATH/windows/sysproxy_386.exe binaries/windows 33 | cp $BINPATH/windows/sysproxy_amd64.exe binaries/windows 34 | 35 | codesign --options runtime --strict --timestamp --force --deep -r="designated => anchor trusted and identifier com.getlantern.lantern" -s "Developer ID Application: Innovate Labs LLC (4FYC28AXA2)" -v $BINPATH/darwin 36 | cp $BINPATH/darwin binaries 37 | -------------------------------------------------------------------------------- /sysproxy_darwin.go: -------------------------------------------------------------------------------- 1 | package sysproxy 2 | 3 | import ( 4 | _ "embed" 5 | "fmt" 6 | "os/exec" 7 | "syscall" 8 | 9 | "github.com/getlantern/byteexec" 10 | "github.com/getlantern/elevate" 11 | ) 12 | 13 | // Note this is a universal binary that runs on amd64 and arm64 14 | //go:embed binaries/darwin/sysproxy 15 | var sysproxy []byte 16 | 17 | func ensureElevatedOnDarwin(be *byteexec.Exec, prompt string, iconFullPath string) (err error) { 18 | var s syscall.Stat_t 19 | // we just checked its existence, not bother checking specific error again 20 | if err = syscall.Stat(be.Filename, &s); err != nil { 21 | return fmt.Errorf("error starting helper tool %s: %v", be.Filename, err) 22 | } 23 | if s.Mode&syscall.S_ISUID > 0 && s.Uid == 0 && s.Gid == 0 { 24 | log.Tracef("%v is already owned by root:wheel and has setuid bit on", be.Filename) 25 | return 26 | } 27 | cmd := elevate.WithPrompt(prompt).WithIcon(iconFullPath).Command(be.Filename, "setuid") 28 | return run(cmd) 29 | } 30 | 31 | func detach(cmd *exec.Cmd) { 32 | cmd.SysProcAttr = &syscall.SysProcAttr{ 33 | Setpgid: true, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sysproxy_linux_386.go: -------------------------------------------------------------------------------- 1 | package sysproxy 2 | 3 | import ( 4 | _ "embed" 5 | "os/exec" 6 | "syscall" 7 | 8 | "github.com/getlantern/byteexec" 9 | ) 10 | 11 | //go:embed binaries/linux_386/sysproxy 12 | var sysproxy []byte 13 | 14 | func ensureElevatedOnDarwin(be *byteexec.Exec, prompt string, iconFullPath string) (err error) { 15 | return nil 16 | } 17 | 18 | func detach(cmd *exec.Cmd) { 19 | cmd.SysProcAttr = &syscall.SysProcAttr{ 20 | Setpgid: true, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sysproxy_linux_amd64.go: -------------------------------------------------------------------------------- 1 | package sysproxy 2 | 3 | import ( 4 | _ "embed" 5 | "os/exec" 6 | "syscall" 7 | 8 | "github.com/getlantern/byteexec" 9 | ) 10 | 11 | //go:embed binaries/linux_amd64/sysproxy 12 | var sysproxy []byte 13 | 14 | func ensureElevatedOnDarwin(be *byteexec.Exec, prompt string, iconFullPath string) (err error) { 15 | return nil 16 | } 17 | 18 | func detach(cmd *exec.Cmd) { 19 | cmd.SysProcAttr = &syscall.SysProcAttr{ 20 | Setpgid: true, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sysproxy_linux_arm64.go: -------------------------------------------------------------------------------- 1 | package sysproxy 2 | 3 | import ( 4 | _ "embed" 5 | "os/exec" 6 | "syscall" 7 | 8 | "github.com/getlantern/byteexec" 9 | ) 10 | 11 | //go:embed binaries/linux_arm/sysproxy 12 | var sysproxy []byte 13 | 14 | func ensureElevatedOnDarwin(be *byteexec.Exec, prompt string, iconFullPath string) (err error) { 15 | return nil 16 | } 17 | 18 | func detach(cmd *exec.Cmd) { 19 | cmd.SysProcAttr = &syscall.SysProcAttr{ 20 | Setpgid: true, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sysproxy_test.go: -------------------------------------------------------------------------------- 1 | package sysproxy 2 | 3 | import ( 4 | "os" 5 | "path" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestAllEquals(t *testing.T) { 12 | assert.True(t, allEquals("127.0.0.1:8888", "127.0.0.1:8888\n127.0.0.1:8888")) 13 | assert.True(t, allEquals("127.0.0.1:8888", "127.0.0.1:8888\n127.0.0.1:8888\n")) 14 | assert.False(t, allEquals("127.0.0.1:8888", "127.0.0.1:8888\n127.0.0.1:8887")) 15 | assert.True(t, allEquals("", "\n\n")) 16 | assert.True(t, allEquals("", "\r\n")) 17 | assert.False(t, allEquals("", "127.0.0.1:8888")) 18 | assert.False(t, allEquals("127.0.0.1:8888", "")) 19 | } 20 | 21 | func TestGetOutput(t *testing.T) { 22 | path := path.Join(os.TempDir(), "sysproxy") 23 | err := EnsureHelperToolPresent(path, "For test purpose", "") 24 | assert.NoError(t, err, "should install helper tool") 25 | off, err := On("localhost:8888") 26 | assert.NoError(t, err, "should set system proxy on") 27 | err = off() 28 | assert.NoError(t, err, "should set system proxy off") 29 | } 30 | -------------------------------------------------------------------------------- /sysproxy_windows_386.go: -------------------------------------------------------------------------------- 1 | package sysproxy 2 | 3 | import ( 4 | _ "embed" 5 | "os/exec" 6 | "syscall" 7 | 8 | "github.com/getlantern/byteexec" 9 | ) 10 | 11 | //go:embed binaries/windows/sysproxy_386.exe 12 | var sysproxy []byte 13 | 14 | func ensureElevatedOnDarwin(be *byteexec.Exec, prompt string, iconFullPath string) (err error) { 15 | return nil 16 | } 17 | 18 | func detach(cmd *exec.Cmd) { 19 | cmd.SysProcAttr = &syscall.SysProcAttr{ 20 | CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sysproxy_windows_amd64.go: -------------------------------------------------------------------------------- 1 | package sysproxy 2 | 3 | import ( 4 | _ "embed" 5 | "os/exec" 6 | "syscall" 7 | 8 | "github.com/getlantern/byteexec" 9 | ) 10 | 11 | //go:embed binaries/windows/sysproxy_amd64.exe 12 | var sysproxy []byte 13 | 14 | func ensureElevatedOnDarwin(be *byteexec.Exec, prompt string, iconFullPath string) (err error) { 15 | return nil 16 | } 17 | 18 | func detach(cmd *exec.Cmd) { 19 | cmd.SysProcAttr = &syscall.SysProcAttr{ 20 | CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, 21 | } 22 | } 23 | --------------------------------------------------------------------------------