Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Package to setup system wide HTTP(S) Proxy on Desktop Platforms #190

Merged
merged 76 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
e793764
add dynamic config support
amircybersec Jan 16, 2024
bbd9f82
Merge branch 'Jigsaw-Code:main' into main
amircybersec Jan 21, 2024
ba98871
Revert "add dynamic config support"
amircybersec Jan 21, 2024
bcb602e
Merge branch 'Jigsaw-Code:main' into main
amircybersec Feb 14, 2024
4cb44eb
Merge branch 'Jigsaw-Code:main' into main
amircybersec Feb 24, 2024
c11ab04
add sys proxy for desktop platforms
amircybersec Feb 26, 2024
fdf398f
do nothing on unsupported platforms
amircybersec Feb 26, 2024
6a5680c
add proxyType and clean up code for Darwin
amircybersec Feb 29, 2024
f48a095
remove safeclose and clean up the package doc
amircybersec Feb 29, 2024
cc694c9
moved package docs to doc.go
amircybersec Feb 29, 2024
044d794
return an error if platform is not supported
amircybersec Feb 29, 2024
422ca10
refactored runCommad
amircybersec Feb 29, 2024
e2247d5
added docs and clean up
amircybersec Mar 1, 2024
f91cafc
linux code refactor and cleanup
amircybersec Mar 1, 2024
65db63e
Update x/sysproxy/doc.go
amircybersec Mar 6, 2024
f48f419
Update x/sysproxy/sysproxy_darwin.go
amircybersec Mar 6, 2024
ed4c93b
Update x/sysproxy/sysproxy_darwin.go
amircybersec Mar 6, 2024
ceec53f
Update x/sysproxy/sysproxy_windows.go
amircybersec Mar 6, 2024
5952a45
Update x/sysproxy/sysproxy_windows.go
amircybersec Mar 6, 2024
0faac88
Update x/sysproxy/sysproxy_windows.go
amircybersec Mar 6, 2024
7339b23
Update x/sysproxy/sysproxy_windows.go
amircybersec Mar 6, 2024
7b895a5
Update x/sysproxy/sysproxy_windows.go
amircybersec Mar 6, 2024
b6f3c97
Update x/sysproxy/sysproxy_darwin.go
amircybersec Mar 6, 2024
6c59ad8
Update x/sysproxy/sysproxy_windows.go
amircybersec Mar 6, 2024
a04317e
Update x/sysproxy/sysproxy_windows.go
amircybersec Mar 6, 2024
7dd57d0
Update x/sysproxy/sysproxy_linux.go
amircybersec Mar 6, 2024
75ea37c
windows: backup settings before change & revert
amircybersec Mar 16, 2024
bdc6d2d
linux: add backup settings + socks5
amircybersec Mar 16, 2024
f8906a1
darwin: backup settings + socks
amircybersec Mar 16, 2024
2826419
return error if socks5 is not supported
amircybersec Mar 16, 2024
d3020d3
updated readme.md
amircybersec Mar 17, 2024
34ce3c3
updated doc.go
amircybersec Mar 17, 2024
0522adb
removed backup settings from Darwin
amircybersec Mar 24, 2024
7cf4822
windows: add socks, remove backup
amircybersec Mar 24, 2024
1ef992a
linux: replaced backup with clearing
amircybersec Mar 24, 2024
029a9c4
windows: removed backup method
amircybersec Mar 24, 2024
3f736c9
windows: bug fix
amircybersec Mar 24, 2024
1baff92
darwin: added back getProxySetting for testing
amircybersec Mar 24, 2024
3c593f9
darwin: added back getHostandPort
amircybersec Mar 24, 2024
9d8c9c9
added back ProxySettings type
amircybersec Mar 24, 2024
b6558e2
add getWebProxy and getSocksProxy
amircybersec Mar 25, 2024
2594efb
linux: add getwebproxy and getsocksproxy
amircybersec Mar 25, 2024
984833e
darwin: added tests
amircybersec Mar 25, 2024
66658ac
windows: add getproxysetting
amircybersec Mar 25, 2024
1dedbf7
one test file for all platforms
amircybersec Mar 25, 2024
630fc56
updating tests
amircybersec Mar 25, 2024
4765b77
fixed getsocksproxy issue on windows
amircybersec Mar 25, 2024
3f15492
linux: getsocksproxy bug fix
amircybersec Mar 25, 2024
4700f68
test clean up
amircybersec Mar 25, 2024
8855f10
windows: made proxySettings private
amircybersec Mar 25, 2024
8d5f311
remove print line
amircybersec Mar 25, 2024
c7e805c
linux: removed proxysettings struct
amircybersec Mar 25, 2024
4405b78
darwin: proxySettings made private
amircybersec Mar 25, 2024
38e2535
update docs
amircybersec Mar 25, 2024
9cfad5f
update readme
amircybersec Mar 25, 2024
4fd18a6
typo fix
amircybersec Mar 25, 2024
428f291
Update x/sysproxy/README.md
amircybersec Mar 25, 2024
7490cd1
Update x/sysproxy/README.md
amircybersec Mar 25, 2024
00f414c
update go.mod file + delete readme
amircybersec Mar 26, 2024
17aeeb7
update go.doc file
amircybersec Mar 26, 2024
b3c6f61
doc polish
amircybersec Mar 26, 2024
e49e0da
Merge branch 'main' into sysproxy-amir
amircybersec Mar 26, 2024
136c2db
changed clear proxy to disable proxy
amircybersec Mar 27, 2024
3fbb4ed
test: check if proxy setting is cleared
amircybersec Mar 27, 2024
e5b3ad1
disable proxy instead of clearing it
amircybersec Mar 28, 2024
7d22038
linux: diable proxy instead of clearing it
amircybersec Mar 28, 2024
235ad1d
linux: get proxy mode bugfix
amircybersec Mar 28, 2024
7e4abd9
linux: fixed enabled comparison bug
amircybersec Mar 28, 2024
d9c10c8
Update x/sysproxy/doc.go
amircybersec Apr 1, 2024
3da2ff2
Update x/sysproxy/sysproxy_test.go
amircybersec Apr 1, 2024
05e3d14
Update x/sysproxy/sysproxy_windows.go
amircybersec Apr 1, 2024
d71b726
Update x/sysproxy/sysproxy_windows.go
amircybersec Apr 1, 2024
7cdec08
doc update
amircybersec Apr 1, 2024
77faf78
change unset to diable in other platforms
amircybersec Apr 1, 2024
b34db8e
exclude ios explictly
amircybersec Apr 1, 2024
35273de
return joined errors
amircybersec Apr 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions x/sysproxy/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2024 Jigsaw Operations LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/*
jyyi1 marked this conversation as resolved.
Show resolved Hide resolved
Package sysproxy provides a simple interface to set/unset system-wide proxy settings.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is cleaner:

Suggested change
/*
Package sysproxy provides a simple interface to set/unset system-wide proxy settings.
/*
Package sysproxy provides a simple interface to set or clear system-wide proxy settings.


# Platform Support

Currently this package supports desktop platforms only. The following platforms are supported:
amircybersec marked this conversation as resolved.
Show resolved Hide resolved
- macOS
amircybersec marked this conversation as resolved.
Show resolved Hide resolved
- Linux (Gnome)
- Windows

# Usage

To set up system-wide proxy settings, use the [SetProxy] function. This function takes two arguments: the IP address and the port of the proxy server.

To unset system-wide proxy settings, use the [UnsetProxy] function.
To ensure that the system-wide proxy settings are unset upon program termination, it is recommended to call:

defer UnsetProxy()

when the application starts.
amircybersec marked this conversation as resolved.
Show resolved Hide resolved
*/
package sysproxy
112 changes: 112 additions & 0 deletions x/sysproxy/sysproxy_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright 2024 Jigsaw Operations LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build darwin

package sysproxy

import (
"bytes"
"fmt"
"os/exec"
"strings"
)

type ProxyType string

const (
httpProxyType ProxyType = "web"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in Go it's more typical to share prefixes, instead of suffixes, for related identifiers. That way they are grouped when sorted. So prefer proxyTypeHTTP, proxyTypeHTTPS.

httpsProxyType ProxyType = "secureweb"
)

func SetProxy(ip string, port string) error {
amircybersec marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't set the SOCKS proxy. We need to specify what type of proxy we are setting somehow.

Two options:

  • Different method names: SetWebProxy, SetSecureWebProxy, SetSOCKSProxy...
  • Type parameter: SetProxy(ProxyTypeWeb, host, port)

Perhaps use HTTP/HTTPS instead of Web/SecureWeb.

I tend to prefer the first option because it allows us to potentially pass different options to different types, but passing the types is easier to implement. Up to you.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fortuna I agree it would be helpful to export different functions to set different types of proxies and perhaps one method that sets them all at once such as SetProxyAll(). This way developers can choose one method if they just want to set a specific type of proxy.

  • Also, I realized that HTTP proxy also allows for setting username/password for authentication. Do you thing we should expose that in the API?

  • Regarding socks proxy, I am researching on how to do it on Windows but if I can't get it working, we can just return platform not supported error for SetSocksProxy on Windows.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fortuna I did some further research on this. The table below summarizes system-wide support for various types of proxies on desktop platforms.

Proxy Type Windows MacOS Linux
HTTP Yes (No Auth)1 Yes Yes
HTTPS Yes (No Auth)1 Yes Yes (No Auth)3
FTP No No Yes (No Auth)
SOCKS No Yes (No Auth)2 Yes (No Auth)
  1. Windows does not explicitly distinguish between HTTP and HTTPS proxy. Also, username/password authentication is not supported.

  2. MacOS SOCKS does not seems to correctly support authentication even though it accepts credentials [ref].

  3. Username/Pass authentication is not currently supported for HTTPS proxy [ref].

Given the poor support for username/password authentication, do you think it makes sense to add authentication option to the sysproxy package?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably return not supported on iOS. I think you can check GOOS.

// Get the active network interface
activeInterface, err := getActiveNetworkInterface()
if err != nil {
return err
}

// Set the web proxy and secure web proxy
if err := setProxyCommand(httpProxyType, activeInterface, ip, port); err != nil {
return err
}
if err := setProxyCommand(httpsProxyType, activeInterface, ip, port); err != nil {
return err
}

return nil
}

func UnsetProxy() error {
// Get the active network interface
activeInterface, err := getActiveNetworkInterface()
if err != nil {
return err
}

// Unset the web proxy and secure web proxy
if err := removeProxyCommand(httpProxyType, activeInterface); err != nil {
return err
}
if err := removeProxyCommand(httpsProxyType, activeInterface); err != nil {
return err
}

return nil
}

// getActiveNetworkInterface finds the active network interface using shell commands.
// https://keith.github.io/xcode-man-pages/networksetup.8.html#listnetworkserviceorder
func getActiveNetworkInterface() (string, error) {
cmd := "networksetup -listnetworkserviceorder | grep `route -n get 0.0.0.0 | grep 'interface' | cut -d ':' -f2` -B 1 | head -n 1 | cut -d ' ' -f2"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer $() over backticks: https://google.github.io/styleguide/shellguide.html#s6.2-command-substitution

Suggested change
cmd := "networksetup -listnetworkserviceorder | grep `route -n get 0.0.0.0 | grep 'interface' | cut -d ':' -f2` -B 1 | head -n 1 | cut -d ' ' -f2"
cmd := "networksetup -listnetworkserviceorder | grep "$(route -n get 0.0.0.0 | grep 'interface' | cut -d ':' -f2)" -B 1 | head -n 1 | cut -d ' ' -f2"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, can you put that on a separate getDefaultInterface()?

And then provide a getNetworkServiceForInterface(interfaceName string) that does the networksetup -listnetworkserviceorder call?

That will be a lot more readable.

out, err := exec.Command("bash", "-c", cmd).Output()
if err != nil {
return "", err
}
return strings.TrimSpace(string(out)), nil
}

// setProxyCommand sets the specified type of proxy on the given network interface.
// https://keith.github.io/xcode-man-pages/networksetup.8.html#getsecurewebproxy
func setProxyCommand(ptype ProxyType, interfaceName string, ip string, port string) error {
cmdStr := fmt.Sprintf("networksetup -set%sproxy \"%s\" %s %s", ptype, interfaceName, ip, port)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you do a switch based on the type, instead of interpolating the command?
Like

switch proxyType {
  case ProxyTypeHTTP:
    cmd = exec.Command("networksetup", "-setwebproxy", interfaceName, host, port)
...
}

That will be a lot cleaner to read.

cmd := exec.Command("bash", "-c", cmdStr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call networksetup directly instead. There's no need for bash here.

var stderr bytes.Buffer
cmd.Stderr = &stderr
amircybersec marked this conversation as resolved.
Show resolved Hide resolved

return runCommand(cmdStr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop runCommand

}

// removeProxyCommand unsets the specified type of proxy from the given network interface.
// https://keith.github.io/xcode-man-pages/networksetup.8.html#setwebproxystate
func removeProxyCommand(ptype ProxyType, interfaceName string) error {
cmdStr := fmt.Sprintf("networksetup -set%sproxystate \"%s\" off", ptype, interfaceName)
cmd := exec.Command("bash", "-c", cmdStr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

var stderr bytes.Buffer
cmd.Stderr = &stderr
amircybersec marked this conversation as resolved.
Show resolved Hide resolved

return runCommand(cmdStr)
}

func runCommand(cmdStr string) error {
cmd := exec.Command("bash", "-c", cmdStr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't recreate the command. You already created it in the caller. I don't think runCommand is needed., since you can call cmd.Run() in the caller.

var stderr bytes.Buffer
cmd.Stderr = &stderr

err := cmd.Run()
if err != nil {
return fmt.Errorf("%v: %s", err, stderr.String())
}
return nil
amircybersec marked this conversation as resolved.
Show resolved Hide resolved
}
76 changes: 76 additions & 0 deletions x/sysproxy/sysproxy_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright 2024 Jigsaw Operations LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build linux

package sysproxy

import (
"fmt"
"os/exec"
)

type ProxyType string

const (
httpProxyType ProxyType = "http"
httpsProxyType ProxyType = "https"
ftpProxyType ProxyType = "ftp"
)

func SetProxy(ip string, port string) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably return not supported on Android. I think you can check GOOS?

// Execute Linux specific commands to set proxy
if err := setManualMode(); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should enable manual mode after you set the proxies, so whoever observing this change can get the right values. Also in case one of them fails.

return err
}
if err := setWebProxy(httpProxyType, ip, port); err != nil {
return err
}
if err := setWebProxy(httpsProxyType, ip, port); err != nil {
return err
}
if err := setWebProxy(ftpProxyType, ip, port); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a web proxy. Rename to setProxy?

return err
}
return nil
}

func setManualMode() error {
return execCommand("gsettings", "set", "org.gnome.system.proxy", "mode", "manual")
}

func setWebProxy(pType ProxyType, ip string, port string) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't have the type in the parameter and function name.

Suggested change
func setWebProxy(pType ProxyType, ip string, port string) error {
func setProxy(pType ProxyType, ip string, port string) error {

p := fmt.Sprintf("org.gnome.system.proxy.%s", pType)
if err := execCommand("gsettings", "set", p, "host", ip); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For readability, can you define a gnomeSettingsSetString(settings, key, value) method, akin to
https://docs.gtk.org/gio/method.Settings.set_string.html?

Then do calls like:

gnomeSettingsSetString("org.gnome.system.proxy.http", "host", host)

return err
}
if err := execCommand("gsettings", "set", p, "port", port); err != nil {
return err
}
return nil
}

func UnsetProxy() error {
// Execute Linux specific commands to unset proxy
return execCommand("gsettings", "set", "org.gnome.system.proxy", "mode", "none")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it support other modes? If so, do we need to backup the previous settings and restore it here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jyyi1 The other modes are auto, and manual. Choosing manual, the system uses the last used proxy settings that can be manually overwritten. I can potentially backup the previous state before setting the proxy to the new settings and restore the system settings back to the previous state when UnsetProxy is called.

auto uses a link to .pac file that contains the proxy settings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

amircybersec marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also erase the individual proxy entries we added, in case someone else enables the manual mode later.

}

func execCommand(name string, arg ...string) error {
cmd := exec.Command(name, arg...)
err := cmd.Run()
if err != nil {
return fmt.Errorf("failed to execute command: %w", err)
}
return nil
}
29 changes: 29 additions & 0 deletions x/sysproxy/sysproxy_other.go
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove this file so we get a build error instead of run time error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fortuna this approach was suggested by @jyyi1 which does what you have in mind (we get a build error instead of run time error). The functions in this file gets called if the platform we are building on is not darwin or linux, or windows.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, I believe I need to explicitly exclude ios and android in the build constraints in this form: //go:build darwin && !ios in sysproxy_darwin.go file and //go:build linux && !android in sysproxy_linux.go

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2024 Jigsaw Operations LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build !linux && !windows && !darwin

package sysproxy

import "errors"

// SetProxy does nothing on unsupported platforms.
func SetProxy(ip string, port string) error {
return errors.New("unsupported platform")
}

// SetProxy does nothing on unsupported platforms.
func UnsetProxy() error {
return errors.New("unsupported platform")
}
132 changes: 132 additions & 0 deletions x/sysproxy/sysproxy_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Copyright 2024 Jigsaw Operations LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build windows

package sysproxy

import (
"errors"
"fmt"

"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)

var (
modwininet = windows.NewLazySystemDLL("wininet.dll")
procInternetSetOption = modwininet.NewProc("InternetSetOptionW")
)

// https://learn.microsoft.com/en-us/windows/win32/wininet/option-flags
// INTERNET_OPTION_SETTINGS_CHANGED: 39
// Notifies the system that the registry settings have been changed so that it verifies the settings on the next call to InternetConnect.
// INTERNET_OPTION_REFRESH: 37
// Causes the proxy data to be reread from the registry for a handle. No buffer is required.
// This option can be used on the HINTERNET handle returned by InternetOpen.
// This is used by InternetSetOption.
const (
amircybersec marked this conversation as resolved.
Show resolved Hide resolved
INTERNET_OPTION_SETTINGS_CHANGED = 39
INTERNET_OPTION_REFRESH = 37
)

// https://learn.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-internetsetoptionw
// internetSetOption sets an Internet option.
func internetSetOption(hInternet uintptr, dwOption int, lpBuffer uintptr, dwBufferLength uint32) bool {
ret, _, _ := procInternetSetOption.Call(
hInternet,
uintptr(dwOption),
lpBuffer,
uintptr(dwBufferLength),
)
return ret != 0
}
amircybersec marked this conversation as resolved.
Show resolved Hide resolved

func resetWininetProxySettings() error {
amircybersec marked this conversation as resolved.
Show resolved Hide resolved

amircybersec marked this conversation as resolved.
Show resolved Hide resolved
result1 := internetSetOption(0, INTERNET_OPTION_SETTINGS_CHANGED, 0, 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to error, and return early if failed.

Suggested change
result1 := internetSetOption(0, INTERNET_OPTION_SETTINGS_CHANGED, 0, 0)
if err := internetSetOption(0, INTERNET_OPTION_SETTINGS_CHANGED, 0, 0); err != nil {
return fmt.Errorf("call to InternetSetOptionW(INTERNET_OPTION_SETTINGS_CHANGED, 0, 0) failed: %w", err)
}

result2 := internetSetOption(0, INTERNET_OPTION_REFRESH, 0, 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to error and return if error.


if result1 && result2 {
fmt.Println("Operation successful")
amircybersec marked this conversation as resolved.
Show resolved Hide resolved
return nil
} else {
fmt.Println("Operation failed")
amircybersec marked this conversation as resolved.
Show resolved Hide resolved
return errors.New("Wininet setting change operation failed")
}
}

func SetProxy(ip string, port string) error {
key, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.SET_VALUE)
if err != nil {
return err
}
defer key.Close()

values := map[string]interface{}{
"MigrateProxy": 1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this option mean? Do we need to unset it in our UnsetProxy function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jyyi1 Documentation for MigrateProxy DWORD was sparse. From what I understood by reading up one some resources I found (1, 2), It appears that this is related to how Internet Explore / Edge migrate their proxy settings.

I am a bit conflicted now since the docs say that IE sets this value to 1 after migrating the settings. BTW, I followed the way GreenTunnel was setting up Windows proxy params and tested the code on Windows successfully. But there could be corner and untested cases that I missed.

If you recall in the office, we looked at Chrome proxy settings was in sync with system proxy settings and we could see the settings switching in the Browser on Windows. I did not however test this with IE. I am looking further into this...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the information. I think we can ignore IE for now because Microsoft has already ended its support on June 15, 2022.

For the Edge, since it's using Chromium, I believe it should behave the same as Chrome.

Let's remove this setting and make sure everything works fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jyyi1 It seems like "ProxyHttp1.1": 0, setting is also related to IE according to this. I am going to remove this one too and test the changes on Windows.

"ProxyEnable": 1,
"ProxyHttp1.1": 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar question to the MigrationProxy configuration, do we need to unset it?

"ProxyServer": fmt.Sprintf("%s:%s", ip, port),
amircybersec marked this conversation as resolved.
Show resolved Hide resolved
"ProxyOverride": "*.local;<local>",
}

for name, value := range values {
switch v := value.(type) {
case int:
err = key.SetDWordValue(name, uint32(v))
case string:
err = key.SetStringValue(name, v)
default:
return fmt.Errorf("unsupported value type")
}
if err != nil {
return err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to unset the registry here, so we don't leave it in an unusable state?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. That's definitely a much safer approach. I will make a copy of settings param before making changes and revert then back to previous state at UnsetProxy.

}
}

// Refresh the settings
err = resetWininetProxySettings()
if err != nil {
return err
}

return nil
amircybersec marked this conversation as resolved.
Show resolved Hide resolved
}

func UnsetProxy() error {
key, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.SET_VALUE)
if err != nil {
return err
}
defer key.Close()

// Set ProxyEnable to 0 and ProxyServer to an empty string
err = key.SetDWordValue("ProxyEnable", 0)
if err != nil {
return err
}
err = key.SetStringValue("ProxyServer", "")
if err != nil {
return err
}

// Refresh the settings
err = resetWininetProxySettings()
if err != nil {
return err
}

return nil
amircybersec marked this conversation as resolved.
Show resolved Hide resolved
}
Loading