Why does http.Get("http://[::]:1234") work? - go

I was writing a test where I wanted an HTTP server to listen on a random port and then connect to this port. I wrote:
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Println("foo")
})
listener, err := net.Listen("tcp", ":0")
fmt.Println(err)
httpServer := &http.Server{Handler: mux}
go httpServer.Serve(listener)
fmt.Println("http://" + listener.Addr().String())
r, err := http.Get("http://" + listener.Addr().String())
fmt.Println(r)
fmt.Println(err)
I meant to write net.Listen("tcp", "127.0.0.1:0") but accidentally wrote net.Listen("tcp", ":0").
For "http://" + listener.Addr().String() it prints:
http://[::]:1709
where as far as I understand the "bracket colon colon bracket" means "all interfaces".
To my surprise, the http.Get("http://[::]:1709") works, it connects to the webserver and "foo" is printed.
How is "http://[::]:1709" a valid address?

At least on a Linux system, this results in a connection being made to localhost.
The address :: is IN6ADDR_ANY, typically used when listening to listen for connections to any IPv6 address on the system. It's analogous to INADDR_ANY, also known as 0.0.0.0 in IPv4.
Occasionally someone attempts to use one of these addresses as the destination address for an outgoing connection. When this happens:
When making an outgoing connection to 0.0.0.0, Linux actually connects from 127.0.0.1 to 127.0.0.1.
Similarly, when making an outgoing connection to ::, Linux actually connects from ::1 to ::1. Here is an example, taken from one of my websites (which happens to be an IP address lookup tool):
[error#murloc ~]$ curl -k -H "Host: myip.addr.space" https://[::]:8443/
::1
For completeness, here is the IPv4 version:
[error#murloc ~]$ curl -k -H "Host: myip.addr.space" https://0.0.0.0:8443/
127.0.0.1
Note that this is OS-specific. You would have received an error on Windows.

Related

How can I get HTTP RPC Server and client to work?

I am trying to send messages from client to server and back using the exact HTTP RPC server/client code given here.
However, when I run the server, my command line becomes blank because the server starts listening using:
err := http.ListenAndServe(":1234", nil)
if err != nil {
fmt.Println(err.Error())
}
In the client code, I need to get an argument from the command line for client to run:
serverAddress := os.Args[1]
However, this argument is not available because the server code makes my command line blank.
How can I get the server and client to work on the same command line window?

TCP Listener is not shut down completely

I have a TCP Listener that initialized as next:
myListener := net.Listen("tcp", addr)
Then am able to receive connections and process them. Then I need to close the server in order that I can reuse the same port but this is not happening, this is how am closing the tcp server:
myListener.Close()
In the client side am closing all the existent TCP connections to that server and then from the terminal I see that those connections are being close but the port is still in use by the server and listening (even when is not accepting new connections which is right according to documentation). This is how I check in the terminal:
netstat -an | grep 8080
And after close the client side connections I get this and cannot reuse the port:
tcp46 0 0 *.8080 *.* LISTEN
After doing myListener.Close() I waited some time but in the terminal the port is still in use.
In addition to checking the error from the net.Listener as stated in https://stackoverflow.com/a/65638937/1435495
You will also want to add a defer to your myListener.Close() will help ensure that the close does actually execute even if something would cause the app to exit prematurely.
defer myListener.Close()
The net.Listen function returns two parameters (Listener, error), in your example above you appear to only be capturing the Listener and not the error.
Assuming you're actually capturing it, you should check if the error is empty before you begin using the listener.
package main
import "net"
func main() {
myListener, err := net.Listen("tcp", ":8080")
if err != nil {
panic(err)
}
myListener.Close()
}
Something similar to the snippet above should work. Now if you're not getting an error (I presume you will get an error), the problem you likely have is that something else is already using that port.
Try running your netstat as root so you can see all processes which will give you a better idea of what is holding on to that port.
sudo netstat -apn | grep -i listen | grep 8080

Go net.DialUDP auto-selecting loopback interface

I have a need to send some UDP packets from eth0 on a host to itself, also on eth0.
I have some working Go code, but it appears to be using the loopback interface instead of eth0 as expected.
package main
import (
"net"
)
func main() {
ip := net.ParseIP("192.168.1.158")
src := net.UDPAddr{IP: ip, Port: 8888,}
dest := net.UDPAddr{IP: ip, Port: 88}
conn, _ := net.DialUDP("udp", &src, &dest)
conn.Write([]byte("hi"))
}
192.168.1.158 is the primary and only IP address for the interface on my machine.
Running the following command shows me the traffic:
sudo tcpdump udp -v -i lo
However, I would expect the traffic be to/from the system interface with the IP address I've defined.
Am I missing a step here?
I have also tried this slightly different approach, but with the same results:
src, _ := net.ResolveUDPAddr("udp", "192.168.1.158:8888")
dest, _ := net.ResolveUDPAddr("udp", "192.168.1.158:88")
However, I would expect the traffic be to/from the system interface with the IP address I've defined.
This is simply the wrong expectation and unrelated to Go.
Just try ping 192.168.1.158 and check with tcpdump -i lo -n icmp vs. tcpdump -i eth0 -n icmp where the data actually gets transferred. You will see that the packets are transferred on the lo interface. Similar a ip route get 192.168.1.158 will show you that the route to your local address goes through the lo interface.

go-ping library for unprivileged ICMP ping in golang

I have been using go-ping library for the unprivileged ping and calculate various statistics of network in golang.
code snippet is as->
func (p *Ping) doPing() (latency, jitter, packetLoss float64, err error) {
timeout := time.Second*1000
interval := time.Second
count := 5
host := p.ipAddr
pinger, cmdErr := ping.NewPinger(host)
if cmdErr != nil {
glog.Error("Failed to ping " + p.ipAddr)
err = cmdErr
return
}
pinger.Count = count
pinger.Interval = interval
pinger.Timeout = timeout
pinger.SetPrivileged(false)
pinger.Run()
stats := pinger.Statistics()
latency = float64(stats.AvgRtt)
jitter = float64(stats.StdDevRtt)
packetLoss = stats.PacketLoss
return
}
It was working fine but now it has started throwing :-
"Error listening for ICMP packets: socket: permission denied" error.
Anyone knows the reason behind this? Go version I am using is go1.7.4.
This is in the README.md of the library you're using :
This library attempts to send an "unprivileged" ping via UDP. On linux, this must be enabled by setting
sudo sysctl -w net.ipv4.ping_group_range="0 2147483647"
If you do not wish to do this, you can set pinger.SetPrivileged(true) and use setcap to allow your binary using go-ping to bind to raw sockets (or just run as super-user):
setcap cap_net_raw=+ep /bin/goping-binary
See this blog and the Go icmp library for more details.
Hope it helps !
Make sure your setting haven't changed in any way. Using ping from the package still works for me on a 32-bit Ubuntu 16.04 with Go 1.7.4 (linux/386) if I previousely set the net.ipv4.ping_group_range according to the instructions on Github.
Note on Linux Support:
This library attempts to send an "unprivileged" ping via UDP. On linux, this must be enabled by setting
sudo sysctl -w net.ipv4.ping_group_range="0 2147483647"
If you do not wish to do this, you can set pinger.SetPrivileged(true) and use setcap to allow your binary
using go-ping to bind to raw sockets (or just run as super-user):
setcap cap_net_raw=+ep /bin/goping-binary
See this blog
and the Go icmp library for
more details.

Why are request.URL.Host and Scheme blank in the development server?

I'm very new to Go. Tried this first hello, world from the documentation, and wanted to read the Host and Scheme from the request:
package hello
import (
"fmt"
"http"
)
func init() {
http.HandleFunc("/", handler)
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Host: " + r.URL.Host + " Scheme: " + r.URL.Scheme)
}
But their values are both blank. Why?
Basically, since you're accessing the HTTP server not from an HTTP proxy, a browser can issue a relative HTTP request, like so:
GET / HTTP/1.1
Host: localhost:8080
(Given that, of course, the server is listening on localhost port 8080).
Now, if you were accessing said server using a proxy, the proxy may use an absolute URL:
GET http://localhost:8080/ HTTP/1.1
Host: localhost:8080
In both cases, what you get from Go's http.Request.URL is the raw URL (as parsed by the library). In the case you're getting, you're accessing the URL from a relative path, hence the lack of a Host or Scheme in the URL object.
If you do want to get the HTTP host, you may want to access the Host attribute of the http.Request struct. See http://golang.org/pkg/http/#Request
You can validate that by using netcat and an appropriately formatted HTTP request (you can copy the above blocks, make sure there's a trailing blank line after in your file). To try it out:
cat my-http-request-file | nc localhost 8080
Additionally, you could check in the server/handler whether you get a relative or absolute URL in the request by calling the IsAbs() method:
isAbsoluteURL := r.URL.IsAbs()

Resources