Skip to content

Commit

Permalink
No longer forcing proxy scheme to match expected message protocol (#13)
Browse files Browse the repository at this point in the history
The client is expected to handle their expectations of the proxy protocol, this library is only responsible for returning it.
  • Loading branch information
tosmun-r7 authored Dec 10, 2018
1 parent d016699 commit ffa73d4
Show file tree
Hide file tree
Showing 13 changed files with 384 additions and 190 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,27 +47,27 @@ Current WinHTTP proxy settings:
Proxy Server(s) : testProxy:8999
Bypass List : (none)
> ./go-get-proxied
https://testProxy:8999
//testProxy:8999
> ./go-get-proxied -j
{
"host": "testProxy",
"password": null,
"port": 8999,
"protocol": "https",
"protocol": "",
"src": "WinHTTP:WinHttpDefault",
"username": null
}
```
```bash
> echo '{"https":"testProxy:8999"}' > proxy.config
> echo '{"https":"http://testProxy:8999"}' > proxy.config
> ./go-get-proxied -c proxy.config
https://testProxy:8999
http://testProxy:8999
> ./go-get-proxied -c proxy.config -j
{
"host": "testProxy",
"password": null,
"port": 8999,
"protocol": "https",
"protocol": "http",
"src": "ConfigurationFile",
"username": null
}
Expand All @@ -78,15 +78,15 @@ https://testProxy:8999
The priority of retrieval is the following.
- **Windows**:
- Configuration File
- Environment Variable: `HTTPS_PROXY` and `NO_PROXY`
- Environment Variable: `HTTPS_PROXY`, `HTTP_PROXY`, `FTP_PROXY`, or `ALL_PROXY`. `NO_PROXY` is respected.
- Internet Options: Automatically detect settings (`WPAD`)
- Internet Options: Use automatic configuration script (`PAC`)
- Internet Options: Manual proxy server
- WINHTTP: (`netsh winhttp`)
- **Linux**:
- Configuration File
- Environment Variable: `HTTPS_PROXY` and `NO_PROXY`
- Environment Variable: `HTTPS_PROXY`, `HTTP_PROXY`, `FTP_PROXY`, or `ALL_PROXY`. `NO_PROXY` is respected.
- **MacOS**:
- Configuration File
- Environment Variable: `HTTPS_PROXY` and `NO_PROXY`
- Environment Variable: `HTTPS_PROXY`, `HTTP_PROXY`, `FTP_PROXY`, or `ALL_PROXY`. `NO_PROXY` is respected.
- Network Settings: `scutil`
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func main() {
} else {
log.SetOutput(ioutil.Discard)
}
p := proxy.NewProvider(config).Get(protocol, target)
p := proxy.NewProvider(config).GetProxy(protocol, target)
var exit int
if p != nil {
if jsonOut {
Expand Down
6 changes: 3 additions & 3 deletions proxy/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@
//
// Windows:
// Configuration File
// Environment Variable: HTTPS_PROXY and NO_PROXY
// Environment Variable: HTTPS_PROXY, HTTP_PROXY, FTP_PROXY, or ALL_PROXY. `NO_PROXY` is respected.
// Internet Options: Automatically detect settings (WPAD)
// Internet Options: Use automatic configuration script (PAC)
// Internet Options: Manual proxy server
// WINHTTP: (netsh winhttp)
//
// Linux:
// Configuration File
// Environment Variable: HTTPS_PROXY and NO_PROXY
// Environment Variable: HTTPS_PROXY, HTTP_PROXY, FTP_PROXY, or ALL_PROXY. `NO_PROXY` is respected.
//
// MacOS:
// Configuration File
// Environment Variable: HTTPS_PROXY and NO_PROXY
// Environment Variable: HTTPS_PROXY, HTTP_PROXY, FTP_PROXY, or ALL_PROXY. `NO_PROXY` is respected.
// Network Settings: scutil
//
// Example Usage
Expand Down
116 changes: 80 additions & 36 deletions proxy/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,36 +26,81 @@ import (
"strings"
)

const (
proxyKeyFormat = "%s_PROXY"
noProxyKeyUpper = "NO_PROXY"
noProxyKeyLower = "no_proxy"
)

type Provider interface {
/*
Returns the Proxy configuration for the given proxy protocol and targetUrl.
Returns the Proxy configuration for the given traffic protocol and targetUrl.
If none is found, or an error occurs, nil is returned.
Params:
protocol: The proxy's protocol (i.e. https)
protocol: The protocol of traffic the proxy is to be used for. (i.e. http, https, ftp, socks)
targetUrl: The URL the proxy is to be used for. (i.e. https://test.endpoint.rapid7.com)
Returns:
Proxy: A proxy was found.
nil: A proxy was not found, or an error occurred.
*/
Get(protocol string, targetUrl string) Proxy
GetProxy(protocol string, targetUrl string) Proxy

/*
Returns the Proxy configuration for HTTP traffic and the given targetUrl.
If none is found, or an error occurs, nil is returned.
Params:
targetUrl: The URL the proxy is to be used for. (i.e. http://test.endpoint.rapid7.com)
Returns:
Proxy: A proxy was found.
nil: A proxy was not found, or an error occurred.
*/
GetHTTPProxy(targetUrl string) Proxy

/*
Returns the HTTPS Proxy configuration for the given targetUrl.
Returns the Proxy configuration for HTTPS traffic and the given targetUrl.
If none is found, or an error occurs, nil is returned.
Params:
targetUrl: The URL the proxy is to be used for. (i.e. https://test.endpoint.rapid7.com)
Returns:
Proxy: A proxy was found.
nil: A proxy was not found, or an error occurred.
*/
GetHTTPS(targetUrl string) Proxy
GetHTTPSProxy(targetUrl string) Proxy

/*
Returns the Proxy configuration for FTP traffic and the given targetUrl.
If none is found, or an error occurs, nil is returned.
Params:
targetUrl: The URL the proxy is to be used for. (i.e. ftp://test.endpoint.rapid7.com)
Returns:
Proxy: A proxy was found.
nil: A proxy was not found, or an error occurred.
*/
GetFTPProxy(targetUrl string) Proxy

/*
Returns the Proxy configuration for generic TCP/UDP traffic and the given targetUrl.
If none is found, or an error occurs, nil is returned.
Params:
targetUrl: The URL the proxy is to be used for. (i.e. ftp://test.endpoint.rapid7.com)
Returns:
Proxy: A proxy was found.
nil: A proxy was not found, or an error occurred.
*/
GetSOCKSProxy(targetUrl string) Proxy
}

const (
protocolHTTP = "http"
protocolHTTPS = "https"
protocolFTP = "ftp"
protocolSOCKS = "socks"
proxyKeyFormat = "%s_PROXY"
noProxyKeyUpper = "NO_PROXY"
noProxyKeyLower = "no_proxy"
prefixSOCKS = protocolSOCKS
prefixAll = "all"
targetUrlWildcard = "*"
domainDelimiter = "."
bypassLocal = "<local>"
srcConfigurationFile = "ConfigurationFile"
srcEnvironmentFmt = "Environment[%s]"
)

type getEnvAdapter func(string) string

type commandAdapter func(context.Context, string, ...string) *exec.Cmd
Expand All @@ -73,13 +118,10 @@ func (p *provider) init(configFile string) {
}

/*
Returns the Proxy configuration for the given proxy protocol and targetUrl.
Returns the Proxy configuration for the given traffic protocol and targetUrl.
If none is found, or an error occurs, nil is returned.
This function searches the following locations in the following order:
* Configuration file: proxy.config
* Environment: HTTPS_PROXY, https_proxy, ...
Params:
protocol: The proxy's protocol (i.e. https)
protocol: The protocol of traffic the proxy is to be used for. (i.e. http, https, ftp, socks)
targetUrl: The URL the proxy is to be used for. (i.e. https://test.endpoint.rapid7.com)
Returns:
Proxy: A proxy was found.
Expand All @@ -97,7 +139,7 @@ func (p *provider) get(protocol string, targetUrl *url.URL) Proxy {
Unmarshal the proxy.config file, and return the first proxy matched for the given protocol.
If no proxy is found, or an error occurs reading the proxy.config file, nil is returned.
Params:
protocol: The proxy's protocol (i.e. https)
protocol: The protocol of traffic the proxy is to be used for. (i.e. http, https, ftp, socks)
Returns:
Proxy: A proxy is found in proxy.config for the given protocol.
nil: No proxy is found or an error occurs reading the proxy.config file.
Expand All @@ -112,10 +154,10 @@ func (p *provider) readConfigFileProxy(protocol string) Proxy {
if !exists {
return nil
}
uUrl, uErr := ParseURL(uStr, protocol)
uUrl, uErr := ParseURL(uStr, "")
var uProxy Proxy
if uErr == nil {
uProxy, uErr = NewProxy(protocol, uUrl, "ConfigurationFile")
uProxy, uErr = NewProxy(uUrl, srcConfigurationFile)
}
if uErr != nil {
log.Printf("[proxy.Provider.readConfigFileProxy]: invalid config file proxy, skipping \"%s\": \"%s\"\n", protocol, uStr)
Expand Down Expand Up @@ -166,26 +208,29 @@ func (p *provider) unmarshalProxyConfigFile() (map[string]string, error) {
}

/*
Find the proxy configured by environment variables for the given protocol and targetUrl.
Find the proxy configured by environment variables for the given traffic protocol and targetUrl.
If no proxy is found, or an error occurs, nil is returned.
Params:
protocol: The proxy's protocol (i.e. https)
protocol: The protocol of traffic the proxy is to be used for. (i.e. http, https, ftp, socks)
targetUrl: The URL the proxy is to be used for. (i.e. https://test.endpoint.rapid7.com)
Returns:
proxy: A proxy is found through environment variables for the given protocol.
proxy: A proxy is found through environment variables for the given traffic protocol.
nil: No proxy is found or an error occurs reading the environment variables.
*/
func (p *provider) readSystemEnvProxy(protocol string, targetUrl *url.URL) Proxy {
func (p *provider) readSystemEnvProxy(prefix string, targetUrl *url.URL) Proxy {
// SOCKS configuration is set as ALL_PROXY and all_proxy on Linux. Replace here for all OSs to keep consistent
if strings.HasPrefix(prefix, prefixSOCKS) {
prefix = prefixAll
}
keys := []string{
strings.ToUpper(fmt.Sprintf(proxyKeyFormat, protocol)),
strings.ToLower(fmt.Sprintf(proxyKeyFormat, protocol))}
// TODO windows is case insensitive, waste of cycles here
strings.ToUpper(fmt.Sprintf(proxyKeyFormat, prefix)),
strings.ToLower(fmt.Sprintf(proxyKeyFormat, prefix))}
noProxyValues := map[string]string{
noProxyKeyUpper: p.getEnv(noProxyKeyUpper),
noProxyKeyLower: p.getEnv(noProxyKeyLower)}
K:
for _, key := range keys {
proxy, err := p.parseEnvProxy(protocol, key)
proxy, err := p.parseEnvProxy(key)
if err != nil {
if !isNotFound(err) {
log.Printf("[proxy.Provider.readSystemEnvProxy]: failed to parse \"%s\" value: %s\n", key, err)
Expand Down Expand Up @@ -233,7 +278,7 @@ func (p *provider) isProxyBypass(targetUrl *url.URL, proxyBypass string, sep str
if s == "" {
// No value
continue
} else if s == "<local>" {
} else if s == bypassLocal {
// Windows uses <local> for local domains
if IsLoopbackHost(targetHost) {
return true
Expand All @@ -246,12 +291,12 @@ func (p *provider) isProxyBypass(targetUrl *url.URL, proxyBypass string, sep str
return true
}
// Prefix "* for wildcard matches (rapid7.com -> *.rapid7.com)
if strings.Index(s, "*") != 0 {
if strings.Index(s, targetUrlWildcard) != 0 {
// (rapid7.com -> .rapid7.com)
if strings.Index(s, ".") != 0 {
s = "." + s
if strings.Index(s, domainDelimiter) != 0 {
s = domainDelimiter + s
}
s = "*" + s
s = targetUrlWildcard + s
}
if m, err := filepath.Match(s, targetHost); err != nil {
return false
Expand All @@ -263,21 +308,20 @@ func (p *provider) isProxyBypass(targetUrl *url.URL, proxyBypass string, sep str
}

/*
Read the given environment variable by key and expected protocol, returning the proxy if it is valid.
Read the given environment variable by key, returning the proxy if it is valid.
Returns nil if no proxy is configured, or an error occurs.
Params:
protocol: The proxy's expected protocol (i.e. https)
key: The environment variable key
Returns:
proxy: A proxy was found for the given environment variable key and is valid.
false: Otherwise
*/
func (p *provider) parseEnvProxy(protocol string, key string) (Proxy, error) {
func (p *provider) parseEnvProxy(key string) (Proxy, error) {
proxyUrl, err := p.parseEnvURL(key)
if err != nil {
return nil, err
}
proxy, err := NewProxy(protocol, proxyUrl, fmt.Sprintf("Environment[%s]", key))
proxy, err := NewProxy(proxyUrl, fmt.Sprintf(srcEnvironmentFmt, key))
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit ffa73d4

Please sign in to comment.