How to check if I can bind to IP address? - go

I have an IP address and want to check if I can send requests to the internet from it.
I want to check this both for IPv4 addresses and IPv6 addresses.
The check should work for any IP address (local, public, docker's, lo, etc.).
From another angle, I want to see if I can bind to the address. For example, I have IPs set on my machine to which I can't bind (Tested via curl --interface) and want to filter those from my available addresses.
Usage: user inputs an IP address to send (http get) requests from, and I want to check if the IP is valid/usable.
EDIT on trying to bind and seeing if I can:
I've tried this:
tcpAddr, _ := net.ResolveTCPAddr("tcp", ipString)
client := &http.Client{
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: 10 * time.Second,
LocalAddr: tcpAddr,
}).DialContext,
},
}
_, err = client.Get("http://www.google.com/")
if err != nil {
fmt.Println("Unusable")
} else {
fmt.Println("Usable")
}
It shows all available addresses (even loopback) as usable (Even checked response bodies; All were valid). Many of those aren't bindable through curl --interface.

Try binding it and see if you get an error:
l,err := net.Listen("tcp", address)
if err != nil {
// address is not bindable
} else {
l.Close()
}

Related

How do you retrieve the NATed IP address of an incoming HTTP request in golang?

I have a GCP cloud run service that posts data to external URLs. These outgoing requests are routed thru a NAT to ensure requests originate from a static IP address. When I post the data to https://curlmyip.org, CurlmyIP correctly identifies the static IP address.
I have another Cloud Run service that can receive the same data. However, this service is unable to retrieve the static IP address. I am using this code to try and retrieve the static IP address of the incoming request
func getIP(r *http.Request) (string, error) {
//Get IP from the X-REAL-IP header
ip := r.Header.Get("X-REAL-IP")
log.Printf("X-REAL-IP: %s", ip)
netIP := net.ParseIP(ip)
if netIP != nil {
return ip, nil
}
//Get IP from X-FORWARDED-FOR header
ips := r.Header.Get("X-FORWARDED-FOR")
log.Printf("X-FORWARDED-FOR: %s", ips)
splitIps := strings.Split(ips, ",")
for _, ip := range splitIps {
netIP := net.ParseIP(ip)
if netIP != nil {
return ip, nil
}
}
//Get IP from RemoteAddr
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
return "", err
}
log.Printf("r.RemoteAddr: %s", ip)
netIP = net.ParseIP(ip)
if netIP != nil {
return ip, nil
}
return "", fmt.Errorf("no valid ip found")
}
The above always returns 0.0.0.0 which is in r.RemoteAddr and not the configured static IP address. I added logs to print all the headers in the incoming request and the configured IP address is nowhere to be found.
Depending on what routers/proxies/load balancers/content delivery networks/etc. the request passes through before it gets to you (and how those boxen are configured), the header could be found in any of
True-Client-IP
X-Forwarded-For
X-Real-IP
Others?
Or it could simply not be there at all.
In the past we use the header x-forwarded-for but we skip the gcp ip ranges via nginx module.
There are some ip blocks that we found in the web but you must include the load balancer ip too. Then extract the first non-private ip from header.
You can do with pure go. Just need to find the ip ranges to skip

Any way to tell DNS resolver in go to lookup IPV4 address only?

I'm trying to configure a Go program (namely, Prometheus) to only look up IPv4 addresses, but seems the LookupIP function that the program uses for DNS resolution doesn't have an option for that.
Is there any way I can work around it, or am I getting something wrong?
the LookupIP function as in the src:
// LookupIP looks up a host using the local resolver.
// It returns a slice of that host's IPv4 and IPv6 addresses.
func LookupIP(host string) ([]IP, error) {
addrs, err := DefaultResolver.LookupIPAddr(context.Background(), host)
if err != nil {
return nil, err
}
ips := make([]IP, len(addrs))
for i, ia := range addrs {
ips[i] = ia.IP
}
return ips, nil
}
I think you're looking for LookupIP. It's the same function but your pass the ip network you are targeting.
// LookupIP looks up host for the given network using the local resolver.
// It returns a slice of that host's IP addresses of the type specified by
// network.
// network must be one of "ip", "ip4" or "ip6".
func (r *Resolver) LookupIP(ctx context.Context, network, host string) ([]IP, error) {
Usage:
net.DefaultResolver.LookupIP(context.Background(), "ip4", "host")

Parse a string into a net.Addr

How this can be done? I would like to know how to parse a string into a net.Addr in Go. As I see there are only parsers for CIDR, IP, and MAC.
Use the net.Resolve* functions to parse addresses. The functions return values that satisfy the net.Addr interface.
The net.ResolveIPAddr function parses the address for an IP endpoint:
ip, err := net.ResolveIPAddr("ip", "203.0.113.0")
if err != nil {
// handle error
}
Use net.ResolveUPDAddr to parse the address of a UDP endpoint (the scenario mentioned in the question comments). The addresss of a UDP endpoint is an IP endpoint and port.
ip, err := net.ResolveUDPAddr("udp", "203.0.113.0:53")
if err != nil {
// handle error
}
The variables ip in the snippets above are typed to the specific address type, but can be used anywhere a net.Addr is needed.
Use net.ResolveIPAddr
var ip net.Addr
ip, _ = net.ResolveIPAddr("ip", "1.1.1.1")

Make a program (written in Golang) use one of the two available IP addresses [duplicate]

I dont know if it's possible as the std lib does not state anything about the current address being used:
http://golang.org/pkg/net/http/
resp, err := http.Get("http://example.com/")
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
What I'm trying to do is set the source address for that http request, why? because I don't want to use my primary ip address for that kind of stuff...
You can set a custom Dialer in the Client's Transport.
// Create a transport like http.DefaultTransport, but with a specified localAddr
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
LocalAddr: localAddr,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
client := &http.Client{
Transport: transport,
}
Setting up a custom dialer with the specify IP works sometimes, but it is weird since sometimes it failed to work, while curl works normally.
After checking the source code of curl, I figured out why curl is able to specify what network interface to be used: SO_BINDTODEVICE. Unfortunately this is a Linux thing.
So compared to JimB's answer, my method has:
Pro: Behaves as stable as curl, which is irrelevant from routing tables
Con: Only support Linux. So you probably want to learn more about the build tags to write platform-specific code.
dialer := &net.Dialer{
Control: func(network, address string, conn syscall.RawConn) error {
var operr error
if err := conn.Control(func(fd uintptr) {
operr = unix.BindToDevice(int(fd), forceNetworkInterface)
}); err != nil {
return err
}
return operr
},
}
client = http.Client{
Transport = &http.Transport{
DialContext: dialer.DialContext,
},
}
In addition, curl performs SO_BINDTODEVICE which behaves like the above code. And for non-Linux platforms, or when SO_BINDTODEVICE fails, curl sets the local IP address just as JimB's answer does. So you can first try my code and then use JimB's answer as a fallback.
See the source code of curl for details.

Open URL on open port

I'm assuming I can run a service on port 3000 like many other code samples I've seen on Github.
Now I am trying to improve my code so that it looks for an open port in case 3000 is in use:
for port := 3000; port <= 3005; port++ {
fmt.Println(port)
err := http.ListenAndServe(":"+strconv.Itoa(port), nil)
if err == nil {
fmt.Println("lk is serving", dirPath, "from http://0.0.0.0:", string(port))
open.Start("http://0.0.0.0:" + string(port))
}
}
However it blocks on the http.ListenAndServe line and doesn't open.Start my browser. I'm told I should use goroutines but I am still a bit bewildered how to use them in this context.
This is a "client" Web app so I do need it to invoke my browser.
Instead of calling ListenAndServe, create the Listener in the application and then call Serve. When creating the listener, request a free port by specifying the listener address as ":0":
ln, err := net.Listen("tcp", ":0")
if err != nil {
// handle error
}
Once the listener is open, you can start the browser:
open.Start("http://" + ln.Addr().String())
and then start the server:
if err := http.Serve(ln, nil); err != nil {
// handle error
}
There's no need to use a goroutine.
The code above uses addr.String() to format the listener's address. If you do need to get the port number for some reason, use a type assertion:
if a, ok := ln.Addr().(*net.TCPAddr); ok {
fmt.Println("port", a.Port)
}

Resources