Skip to content

Commit

Permalink
server: resolve hostnames before use
Browse files Browse the repository at this point in the history
It seems that configurations where the OS-provided hostname doesn't
resolve from the local machine are quite common and can cause hard to
diagnose problems for users. Avoid using any hostname if we're not able
to resolve it locally.

Closes #12107.
Closes #16173.
  • Loading branch information
tamird committed Jun 19, 2017
1 parent cc6ba81 commit 92c483e
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 40 deletions.
47 changes: 22 additions & 25 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,12 +501,12 @@ func (s *Server) Start(ctx context.Context) error {
}
}
log.Eventf(ctx, "listening on port %s", s.cfg.Addr)
unresolvedListenAddr, err := officialAddr(s.cfg.Addr, ln.Addr())
unresolvedListenAddr, err := officialAddr(ctx, s.cfg.Addr, ln.Addr(), os.Hostname)
if err != nil {
return err
}
s.cfg.Addr = unresolvedListenAddr.String()
unresolvedAdvertAddr, err := officialAddr(s.cfg.AdvertiseAddr, ln.Addr())
unresolvedAdvertAddr, err := officialAddr(ctx, s.cfg.AdvertiseAddr, ln.Addr(), os.Hostname)
if err != nil {
return err
}
Expand All @@ -529,7 +529,7 @@ func (s *Server) Start(ctx context.Context) error {
Addr: s.cfg.HTTPAddr,
}
}
unresolvedHTTPAddr, err := officialAddr(s.cfg.HTTPAddr, httpLn.Addr())
unresolvedHTTPAddr, err := officialAddr(ctx, s.cfg.HTTPAddr, httpLn.Addr(), os.Hostname)
if err != nil {
return err
}
Expand Down Expand Up @@ -1035,38 +1035,35 @@ func (w *gzipResponseWriter) Close() {
}
}

func officialAddr(unresolvedAddr string, resolvedAddr net.Addr) (*util.UnresolvedAddr, error) {
unresolvedHost, unresolvedPort, err := net.SplitHostPort(unresolvedAddr)
func officialAddr(
ctx context.Context, cfgAddr string, lnAddr net.Addr, osHostname func() (string, error),
) (*util.UnresolvedAddr, error) {
cfgHost, _, err := net.SplitHostPort(cfgAddr)
if err != nil {
return nil, err
}

resolvedHost, resolvedPort, err := net.SplitHostPort(resolvedAddr.String())
lnHost, lnPort, err := net.SplitHostPort(lnAddr.String())
if err != nil {
return nil, err
}

var host string
if unresolvedHost != "" {
// A host was provided, use it.
host = unresolvedHost
} else {
// A host was not provided. Ask the system, and fall back to the listener.
if hostname, err := os.Hostname(); err == nil {
host = hostname
} else {
host = resolvedHost
host := cfgHost
if len(host) == 0 {
// A host was not provided. Ask the system.
name, err := osHostname()
if err != nil {
return nil, errors.Wrap(err, "unable to get hostname")
}
host = name
}

var port string
if unresolvedPort != "0" {
// A port was provided, use it.
port = unresolvedPort
} else {
// A port was not provided, but the system assigned one.
port = resolvedPort
addrs, err := net.DefaultResolver.LookupHost(ctx, host)
if err != nil {
return nil, errors.Wrapf(err, "unable to lookup hostname %q", host)
}
if len(addrs) == 0 {
return nil, errors.Errorf("hostname %q did not resolve to any addresses; listener address: %s", host, lnHost)
}

return util.NewUnresolvedAddr(resolvedAddr.Network(), net.JoinHostPort(host, port)), nil
return util.NewUnresolvedAddr(lnAddr.Network(), net.JoinHostPort(host, lnPort)), nil
}
68 changes: 53 additions & 15 deletions pkg/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package server
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"net"
Expand Down Expand Up @@ -433,30 +434,67 @@ func TestSystemConfigGossip(t *testing.T) {
})
}

func checkOfficialize(t *testing.T, network, oldAddrString, newAddrString, expAddrString string) {
resolvedAddr := util.NewUnresolvedAddr(network, newAddrString)

if unresolvedAddr, err := officialAddr(oldAddrString, resolvedAddr); err != nil {
t.Fatal(err)
} else if retAddrString := unresolvedAddr.String(); retAddrString != expAddrString {
t.Errorf("officialAddr(%s, %s) was %s; expected %s", oldAddrString, newAddrString, retAddrString, expAddrString)
}
}

func TestOfficializeAddr(t *testing.T) {
defer leaktest.AfterTest(t)()

hostname, err := os.Hostname()
host, err := os.Hostname()
if err != nil {
t.Fatal(err)
}
addrs, err := net.DefaultResolver.LookupHost(context.TODO(), host)
if err != nil {
t.Fatal(err)
}

for _, network := range []string{"tcp", "tcp4", "tcp6"} {
checkOfficialize(t, network, "hellow.world:0", "127.0.0.1:1234", "hellow.world:1234")
checkOfficialize(t, network, "hellow.world:1234", "127.0.0.1:2345", "hellow.world:1234")
checkOfficialize(t, network, ":1234", "127.0.0.1:2345", net.JoinHostPort(hostname, "1234"))
checkOfficialize(t, network, ":0", "127.0.0.1:2345", net.JoinHostPort(hostname, "2345"))
t.Run(fmt.Sprintf("network=%s", network), func(t *testing.T) {
for _, tc := range []struct {
cfgAddr, lnAddr, expAddr string
}{
{"localhost:0", "127.0.0.1:1234", "localhost:1234"},
{"localhost:1234", "127.0.0.1:2345", "localhost:2345"},
{":1234", net.JoinHostPort(addrs[0], "2345"), net.JoinHostPort(host, "2345")},
{":0", net.JoinHostPort(addrs[0], "2345"), net.JoinHostPort(host, "2345")},
} {
t.Run(tc.cfgAddr, func(t *testing.T) {
lnAddr := util.NewUnresolvedAddr(network, tc.lnAddr)

if unresolvedAddr, err := officialAddr(context.TODO(), tc.cfgAddr, lnAddr, os.Hostname); err != nil {
t.Fatal(err)
} else if retAddrString := unresolvedAddr.String(); retAddrString != tc.expAddr {
t.Errorf("officialAddr(%s, %s) was %s; expected %s", tc.cfgAddr, tc.lnAddr, retAddrString, tc.expAddr)
}
})
}
})
}

osHostnameError := errors.New("some error")

t.Run("osHostnameError", func(t *testing.T) {
if _, err := officialAddr(
context.TODO(),
":0",
util.NewUnresolvedAddr("tcp", "0.0.0.0:1234"),
func() (string, error) { return "", osHostnameError },
); errors.Cause(err) != osHostnameError {
t.Fatalf("unexpected error %v", err)
}
})

t.Run("LookupHostError", func(t *testing.T) {
if _, err := officialAddr(
context.TODO(),
"notarealhost:0",
util.NewUnresolvedAddr("tcp", "0.0.0.0:1234"),
os.Hostname,
); !testutils.IsError(err, "lookup notarealhost(?: on .+)?: no such host") {
// On Linux but not on macOS, the error returned from
// (*net.Resolver).LookupHost reports the DNS server used; permit
// both.
t.Fatalf("unexpected error %v", err)
}
})
}

func TestListenURLFileCreation(t *testing.T) {
Expand Down

0 comments on commit 92c483e

Please sign in to comment.