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

Feature request: bind to specific system interface or IP address #444

Closed
mzpqnxow opened this issue Jul 31, 2020 · 1 comment
Closed

Feature request: bind to specific system interface or IP address #444

mzpqnxow opened this issue Jul 31, 2020 · 1 comment

Comments

@mzpqnxow
Copy link
Contributor

mzpqnxow commented Jul 31, 2020

For systems with multiple interfaces but only one interface set as the default route, it would be nice if it was possible to specify a specific interface to bind to, either by the interface name or by using the IP address. This allows cycling the interfaces across runs without making invasive changes to the routing table

This is a real use-case for me that I've been working around this using a simple hack- here is an example of what I'm doing in the HTTP dialer:

func getenv(key, fallback string) string {
    value := os.Getenv(key)
    if len(value) == 0 {
        return fallback
    }
    return value
}

func init() {
    lAddrStr := getenv("BIND_ADDRESS", "0.0.0.0")
    fmt.Printf("Binding to interface with address %s for all HTTP requests...\n", lAddrStr)
    lAddr, _ := net.ResolveTCPAddr("tcp", lAddrStr+":0")

    jar, _ := cookiejar.New(nil)
    defaultClient = &http.Client{
        Timeout: 30 * time.Second,
        Transport: &http.Transport{
            DialContext: (&net.Dialer{
                Timeout:   30 * time.Second,
                KeepAlive: 30 * time.Second,
                DualStack: true,
                LocalAddr: lAddr,
            }).DialContext,
            MaxIdleConns:          200,
            IdleConnTimeout:       90 * time.Second,
            TLSHandshakeTimeout:   20 * time.Second,
            ExpectContinueTimeout: 20 * time.Second,
            TLSClientConfig:       &tls.Config{InsecureSkipVerify: true},
        },
        Jar: jar,
    }
}

I would like to implement a "proper" solution, by having the interface name or address be part of the options and taken from the context, though it's a little tricky because there is more than one place where various network requests are made from- they aren't all HTTP, for starters

I'm a C and Python programmer, so I can read golang and can write golang with very basic proficiency. I'm not very well-versed in the amass architecture, so it has been a little slow for me trying to figure out the most appropriate way to add this functionality. Ideally there would be a single function that is used to get a socket, whether it's TCP or UDP- and it would have the context Config and be able to set the DialContext in the way that I do above. I'm not familiar enough with golang to know if there is a simpler way to do something that would more seamlessly and simply apply this DialContext globally. If there is a mechanism to do that it would obviously be the preferred way

I guess my questions are:

  1. Would this feature in general be something you consider worth of supporting?
  2. If so, is there a high-level approach you would recommend for accomplishing this? Inserting getenv() calls all over the place is certainly not the right way, it's just the easiest

Alternately, if you think this is a simple and you're willing to do it yourself because of your proficiency in golang and familiarity with the code, I would very happily defer the task to you :)

@mzpqnxow
Copy link
Contributor Author

mzpqnxow commented Aug 31, 2020

@caffix thanks for implementing this!

There's one little bug which is breaking it- it looks like you need an extra call on the string you're using for the interface- as it is, it's a CIDR notation representation, so instead of, e.g., 10.1.1.11 I'm getting 10.1.1.11/24 and it silently fails and continues on with the primary interface. The following takes care of it (I didn't do error handling)

diff --git a/go.mod b/go.mod
index 2ce8ae4..b20926c 100644
--- a/go.mod
+++ b/go.mod
@@ -52,3 +52,5 @@ require (
        honnef.co/go/tools v0.0.1-2020.1.3 // indirect
        layeh.com/gopher-json v0.0.0-20190114024228-97fed8db8427
 )
+
+replace github.com/OWASP/Amass/v3 => ./
diff --git a/net/network.go b/net/network.go
index 54d4d29..cbffb9e 100644
--- a/net/network.go
+++ b/net/network.go
@@ -4,6 +4,7 @@
 package net
 
 import (
+        "fmt"
        "bytes"
        "context"
        "math/big"
@@ -58,11 +59,12 @@ func init() {
 // DialContext performs the dial using global variables (e.g. LocalAddr).
 func DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
        d := &net.Dialer{DualStack: true}
-
+        ip, _, _ := net.ParseCIDR(LocalAddr.String())
+        fmt.Printf("LocalAddr: %s\n", ip.String())
        if LocalAddr != nil && strings.HasPrefix(network, "tcp") {
                d.Timeout = 30 * time.Second
                d.LocalAddr = &net.TCPAddr{
-                       IP:   net.ParseIP(LocalAddr.String()),
+                       IP:   net.ParseIP(ip.String()),
                        Port: 0,
                }
        } else if LocalAddr != nil && strings.HasPrefix(network, "udp") {

caffix added a commit that referenced this issue Sep 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant