Skip to content

Commit

Permalink
Add a commandline argument to support returning a list of proxy when … (
Browse files Browse the repository at this point in the history
#21)

* Add a commandline argument to support returning a list of proxy when proxy auto-config returns a list

* Fix arguments in provider_windows and help doc corrections

* Handle nil proxy when calling GetProxies

* Fix incorrect provider type in provider_linux

* Minor clean up
  • Loading branch information
bli-r7 authored Nov 16, 2021
1 parent ebe75b1 commit dcc9028
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 52 deletions.
37 changes: 30 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,16 @@ func main() {
targetP := flag.String("t", "", "Optional. Target URL which the proxy will be used for. Default: *")
jsonP := flag.Bool("j", false, "Optional. If a proxy is found, write it as JSON instead of a URL.")
verboseP := flag.Bool("v", false, "Optional. If set, log content will be sent to stderr.")
useListP := flag.Bool("l", false, "Optional. If set, a list of proxy will be returned.")

flag.Parse()
var (
protocol string
config string
target string
jsonOut bool
verbose bool
useList bool
)
if protocolP != nil {
protocol = *protocolP
Expand All @@ -57,17 +60,37 @@ func main() {
} else {
log.SetOutput(ioutil.Discard)
}
p := proxy.NewProvider(config).GetProxy(protocol, target)
if useListP != nil {
useList = *useListP
}
var exit int
if p != nil {
if jsonOut {
b, _ := json.MarshalIndent(p, "", " ")
fmt.Println(string(b))

if useList {
ps := proxy.NewProvider(config).GetProxies(protocol, target)
if ps != nil {
if jsonOut {
b, _ := json.MarshalIndent(ps, "", " ")
fmt.Println(string(b))
} else {
for _, p := range ps {
println(p.URL().String())
}
}
} else {
println(p.URL().String())
exit = 1
}
} else {
exit = 1
p := proxy.NewProvider(config).GetProxy(protocol, target)
if p != nil {
if jsonOut {
b, _ := json.MarshalIndent(p, "", " ")
fmt.Println(string(b))
} else {
println(p.URL().String())
}
} else {
exit = 1
}
}
os.Exit(exit)
}
1 change: 1 addition & 0 deletions proxy/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ type Provider interface {
receive: Time in milliseconds to receive a response to a request. Provider default is 20000.
*/
SetTimeouts(resolve int, connect int, send int, receive int)
GetProxies(protocol string, targetUrl string) []Proxy
}

const (
Expand Down
8 changes: 8 additions & 0 deletions proxy/provider_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,14 @@ func (p *providerDarwin) GetSOCKSProxy(targetUrl string) Proxy {
return p.GetProxy(protocolSOCKS, targetUrl)
}

func (p *providerDarwin) GetProxies(protocol string, targetUrl string) []Proxy {
proxy := p.GetProxy(protocol, targetUrl)
if proxy != nil {
return []Proxy{proxy}
}
return []Proxy{}
}

const (
scUtilBinary = "scutil"
scUtilBinaryArgument = "--proxy"
Expand Down
8 changes: 8 additions & 0 deletions proxy/provider_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,11 @@ Returns:
func (p *providerLinux) GetSOCKSProxy(targetUrl string) Proxy {
return p.GetProxy(protocolSOCKS, targetUrl)
}

func (p *providerLinux) GetProxies(protocol string, targetUrl string) []Proxy {
proxy := p.GetProxy(protocol, targetUrl)
if proxy != nil {
return []Proxy{proxy}
}
return []Proxy{}
}
104 changes: 59 additions & 45 deletions proxy/provider_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ func (p *providerWindows) GetProxy(protocol string, targetUrlStr string) Proxy {
if proxy != nil {
return proxy
}
return p.readWinHttpProxy(protocol, targetUrl)
proxies := p.readWinHttpProxy(protocol, targetUrl)
return proxies[len(proxies) - 1]
}

/*
Expand Down Expand Up @@ -109,6 +110,16 @@ func (p *providerWindows) GetSOCKSProxy(targetUrl string) Proxy {
return p.GetProxy(protocolSOCKS, targetUrl)
}

func (p *providerWindows) GetProxies(protocol string, targetUrlStr string) []Proxy {
targetUrl := ParseTargetURL(targetUrlStr, protocol)
proxy := p.provider.get(protocol, targetUrl)
if proxy != nil {
return []Proxy{proxy}
}
proxies := p.readWinHttpProxy(protocol, targetUrl)
return proxies
}

const (
userAgent = "ir_agent"
srcAutoDetect = "WinHTTP:AutoDetect"
Expand All @@ -122,7 +133,7 @@ type providerWindows struct {
}

//noinspection SpellCheckingInspection
func (p *providerWindows) readWinHttpProxy(protocol string, targetUrl *url.URL) Proxy {
func (p *providerWindows) readWinHttpProxy(protocol string, targetUrl *url.URL) []Proxy {
// Internet Options
ieProxyConfig, err := p.getIeProxyConfigCurrentUser()
if err != nil {
Expand All @@ -138,24 +149,25 @@ func (p *providerWindows) readWinHttpProxy(protocol string, targetUrl *url.URL)
}
}
if autoConfigUrl := winhttp.LpwstrToString(ieProxyConfig.LpszAutoConfigUrl); autoConfigUrl != "" {
proxy, err := p.getProxyAutoConfigUrl(protocol, targetUrl, autoConfigUrl)
proxies, err := p.getProxyAutoConfigUrl(protocol, targetUrl, autoConfigUrl)
if err == nil {
return proxy
return proxies
} else if !isNotFound(err) {
log.Printf("[proxy.Provider.readWinHttpProxy] No proxy discovered via AutoConfigUrl, %s: %s\n", autoConfigUrl, err)
}
}
proxy, err := p.parseProxyInfo(srcNamedProxy, protocol, targetUrl, ieProxyConfig.LpszProxy, ieProxyConfig.LpszProxyBypass)
// LpszProxy may contain multiple proxies which needs to be parsed into a list of Proxy
proxies, err := p.parseProxyInfo(srcNamedProxy, protocol, targetUrl, ieProxyConfig.LpszProxy, ieProxyConfig.LpszProxyBypass)
if err == nil {
return proxy
return proxies
} else if !isNotFound(err) {
log.Printf("[proxy.Provider.readWinHttpProxy] Failed to parse named proxy: %s\n", err)
}
}
// netsh winhttp
proxy, err := p.getProxyWinHttpDefault(protocol, targetUrl)
proxies, err := p.getProxyWinHttpDefault(protocol, targetUrl)
if err == nil {
return proxy
return proxies
} else if !isNotFound(err) {
log.Printf("[proxy.Provider.readWinHttpProxy] Failed to parse WinHttp default proxy info: %s\n", err)
}
Expand Down Expand Up @@ -187,7 +199,7 @@ Returns:
nil, notFoundError: No proxy was found
nil, error: An error occurred
*/
func (p *providerWindows) getProxyAutoDetect(protocol string, targetUrl *url.URL) (Proxy, error) {
func (p *providerWindows) getProxyAutoDetect(protocol string, targetUrl *url.URL) ([]Proxy, error) {
return p.getProxyForUrl(srcAutoDetect, protocol, targetUrl,
&winhttp.AutoProxyOptions{
DwFlags: winhttp.WINHTTP_AUTOPROXY_AUTO_DETECT,
Expand All @@ -207,7 +219,7 @@ Returns:
nil, notFoundError: No proxy was found
nil, error: An error occurred
*/
func (p *providerWindows) getProxyAutoConfigUrl(protocol string, targetUrl *url.URL, autoConfigUrl string) (Proxy, error) {
func (p *providerWindows) getProxyAutoConfigUrl(protocol string, targetUrl *url.URL, autoConfigUrl string) ([]Proxy, error) {
return p.getProxyForUrl(srcAutoConfigUrl, protocol, targetUrl,
&winhttp.AutoProxyOptions{
DwFlags: winhttp.WINHTTP_AUTOPROXY_CONFIG_URL,
Expand All @@ -227,7 +239,7 @@ Returns:
nil, notFoundError: No proxy was found
nil, error: An error occurred
*/
func (p *providerWindows) getProxyWinHttpDefault(protocol string, targetUrl *url.URL) (Proxy, error) {
func (p *providerWindows) getProxyWinHttpDefault(protocol string, targetUrl *url.URL) ([]Proxy, error) {
pInfo, err := winhttp.GetDefaultProxyConfiguration()
if err != nil {
return nil, err
Expand All @@ -237,18 +249,18 @@ func (p *providerWindows) getProxyWinHttpDefault(protocol string, targetUrl *url
}

/*
Returns the Proxy found through either automatic detection or a automatic configuration URL.
Returns the Proxies found through either automatic detection or a automatic configuration URL.
Params:
src: If a proxy is constructed, the human readable source to associated it with.
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)
autoProxyOptions: Use this to inform WinHTTP what route to take when doing the lookup (automatic detection, or automatic configuration URL)
Returns:
Proxy, nil: A proxy was found
Proxy, nil: A list of proxy was found
nil, notFoundError: No proxy was found
nil, error: An error occurred
*/
func (p *providerWindows) getProxyForUrl(src string, protocol string, targetUrl *url.URL, autoProxyOptions *winhttp.AutoProxyOptions) (Proxy, error) {
func (p *providerWindows) getProxyForUrl(src string, protocol string, targetUrl *url.URL, autoProxyOptions *winhttp.AutoProxyOptions) ([]Proxy, error) {
pInfo, err := p.getProxyInfoForUrl(targetUrl, autoProxyOptions)
if err != nil {
return nil, err
Expand Down Expand Up @@ -297,59 +309,61 @@ Params:
lpszProxy: The Lpwstr which represents the proxy value (if any). This value can be optionally separated by protocol.
lpszProxyBypass: The Lpwstr which represents the proxy bypass value (if any).
Returns:
Proxy, nil: A proxy was found
Proxy, nil: A list of proxies matching the lookup criteria
nil, notFoundError: No proxy was found or was bypassed
nil, error: An error occurred
*/
//noinspection SpellCheckingInspection
func (p *providerWindows) parseProxyInfo(src string, protocol string, targetUrl *url.URL, lpszProxy winhttp.Lpwstr, lpszProxyBypass winhttp.Lpwstr) (Proxy, error) {
proxyUrlStr := p.parseLpszProxy(protocol, winhttp.LpwstrToString(lpszProxy))
if proxyUrlStr == "" {
return nil, new(notFoundError)
}
proxyUrl, err := ParseURL(proxyUrlStr, "")
if err != nil {
return nil, err
}
func (p *providerWindows) parseProxyInfo(src string, protocol string, targetUrl *url.URL, lpszProxy winhttp.Lpwstr, lpszProxyBypass winhttp.Lpwstr) ([]Proxy, error) {
proxies := []Proxy{}
proxyBypass := winhttp.LpwstrToString(lpszProxyBypass)
if proxyBypass != "" {
bypass := p.isLpszProxyBypass(targetUrl, proxyBypass)
log.Printf("[proxy.Provider.parseProxyInfo]: lpszProxyBypass=\"%s\", targetUrl=%s, bypass=%t", proxyBypass, targetUrl, bypass)
if bypass {
return nil, new(notFoundError)
if p.isLpszProxyBypass(targetUrl, proxyBypass) {
return proxies, nil
}
}

proxyUrlStrList := p.parseLpszProxy(protocol, winhttp.LpwstrToString(lpszProxy))
if len(proxyUrlStrList) == 0 {
return nil, new(notFoundError)
}
for _, proxyUrlStr := range proxyUrlStrList {
proxyUrl, err := ParseURL(proxyUrlStr, "")
if err != nil {
log.Printf("Failed to parse proxy URL\"$\"", proxyUrlStr)
continue
}
pr, _ := NewProxy(proxyUrl, src)
proxies = append(proxies, pr)
}
return NewProxy(proxyUrl, src)
return proxies, nil
}

/*
Parse the lpszProxy into a single proxy URL, represented as a string.
Parse the lpszProxy into a list of proxy URL, represented as a list of string.
For example:
("https", "1.2.3.4") -> "1.2.3.4"
("https", "https=1.2.3.4;http=4.5.6.7") -> "1.2.3.4"
("https", "") -> ""
("https", "http=4.5.6.7") -> ""
("https", "1.2.3.4") -> ["1.2.3.4"]
("https", "https=1.2.3.4;http=4.5.6.7") -> ["1.2.3.4"]
("https", "https=1.2.3.4;https=4.5.6.7") -> ["1.2.3.4","4.5.6.7"]
("https", "") -> []
("https", "http=4.5.6.7") -> []
Params:
protocol: The protocol of traffic the proxy is to be used for. (i.e. http, https, ftp, socks)
lpszProxy: The Lpwstr which represents the proxy value (if any). This value can be optionally separated by protocol.
Returns:
string: The proxy URL (if any) from the lpszProxy value.
string: The list of proxy URL (if any) from the lpszProxy value.
*/
//noinspection SpellCheckingInspection
func (p *providerWindows) parseLpszProxy(protocol string, lpszProxy string) string {
m := ""
func (p *providerWindows) parseLpszProxy(protocol string, lpszProxy string) []string {
proxies := []string{}
for _, s := range strings.Split(lpszProxy, ";") {
parts := strings.SplitN(s, "=", 2)
// No protocol?
if len(parts) < 2 {
// Assign a match, but keep looking in case we have a protocol specific match
m = s
} else if strings.TrimSpace(parts[0]) == protocol {
m = parts[1]
break
// Include the proxy if protocol matches or if no protocol is specified
if len(parts) < 2 || strings.TrimSpace(parts[0]) == protocol {
proxies = append(proxies, s)
}
}
return m
return proxies
}

/*
Expand Down

0 comments on commit dcc9028

Please sign in to comment.