netutil additions

Adds ListenOnLocalRandomPort and make Localhost more robust.

Change-Id: Id6416f7ae5c35fb247035ecb0021f3978ff3480e
This commit is contained in:
Antonin Amand 2014-09-15 15:15:59 +02:00
parent 3374899d40
commit 87e6815d62
3 changed files with 119 additions and 13 deletions

View File

@ -273,16 +273,3 @@ func uidFromProcReader(lip net.IP, lport int, rip net.IP, rport int, r io.Reader
} }
panic("unreachable") panic("unreachable")
} }
// Localhost returns the first address found when
// doing a lookup of "localhost".
func Localhost() (net.IP, error) {
ips, err := net.LookupIP("localhost")
if err != nil {
return nil, err
}
if len(ips) < 1 {
return nil, errors.New("IP lookup for localhost returned no result")
}
return ips[0], nil
}

View File

@ -17,6 +17,7 @@ limitations under the License.
package netutil package netutil
import ( import (
"errors"
"fmt" "fmt"
"net" "net"
"net/url" "net/url"
@ -67,3 +68,62 @@ func HostPort(urlStr string) (string, error) {
} }
return hostPort, nil return hostPort, nil
} }
// ListenOnLocalRandomPort returns a tcp listener on a local (see LoopbackIP) random port.
func ListenOnLocalRandomPort() (net.Listener, error) {
ip, err := Localhost()
if err != nil {
return nil, err
}
l, err := net.ListenTCP("tcp", &net.TCPAddr{IP: ip, Port: 0})
if err != nil {
return nil, err
}
return l, nil
}
// Localhost returns the first address found when
// doing a lookup of "localhost". If not successful,
// it looks for an ip on the loopback interfaces.
func Localhost() (net.IP, error) {
if ip := localhostLookup(); ip != nil {
return ip, nil
}
if ip := loopbackIP(); ip != nil {
return ip, nil
}
return nil, errors.New("No loopback ip found.")
}
// localhostLookup looks for a loopback IP by resolving localhost.
func localhostLookup() net.IP {
if ips, err := net.LookupIP("localhost"); err == nil && len(ips) > 0 {
return ips[0]
}
return nil
}
const flagUpLoopback = net.FlagUp | net.FlagLoopback
// loopbackIP finds the first loopback IP address sniffing network interfaces.
func loopbackIP() net.IP {
interfaces, err := net.Interfaces()
if err != nil {
return nil
}
for _, inf := range interfaces {
if inf.Flags&flagUpLoopback == flagUpLoopback {
addrs, err := inf.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
ip, _, err := net.ParseCIDR(addr.String())
if err == nil && ip.IsLoopback() {
return ip
}
}
}
}
return nil
}

View File

@ -17,6 +17,8 @@ limitations under the License.
package netutil package netutil
import ( import (
"net"
"strconv"
"testing" "testing"
) )
@ -120,3 +122,60 @@ func TestHostPort(t *testing.T) {
} }
} }
} }
func testLocalhostResolver(t *testing.T, resolve func() net.IP) {
ip := resolve()
if ip == nil {
t.Fatal("no ip found.")
}
if !ip.IsLoopback() {
t.Errorf("expected a loopback address: %s", ip)
}
}
func testLocalhost(t *testing.T) {
testLocalhostResolver(t, localhostLookup)
}
func testLoopbackIp(t *testing.T) {
testLocalhostResolver(t, loopbackIP)
}
func TestLocalhost(t *testing.T) {
_, err := Localhost()
if err != nil {
t.Fatal(err)
}
}
func TestListenOnLocalRandomPort(t *testing.T) {
l, err := ListenOnLocalRandomPort()
if err != nil {
t.Fatalf("unexpected error %v", err)
}
defer l.Close()
_, port, err := net.SplitHostPort(l.Addr().String())
if err != nil {
t.Fatal(err)
}
if p, _ := strconv.Atoi(port); p < 1 {
t.Fatalf("expected port(%d) to be > 0", p)
}
}
func BenchmarkLocalhostLookup(b *testing.B) {
for i := 0; i < b.N; i++ {
if ip := localhostLookup(); ip == nil {
b.Fatal("no ip found.")
}
}
}
func BenchmarkLoopbackIP(b *testing.B) {
for i := 0; i < b.N; i++ {
if ip := loopbackIP(); ip == nil {
b.Fatal("no ip found.")
}
}
}