Manager: improve the "my own URLs" construction
Improve the "my own URLs" construction, such that: - IPv6 link-local addresses are always skipped. They require a "zone index" string, typically the interface name, so something like `[fe80::cafe:f00d%eth0]`. This is not supported by web browsers, so the URLs would be of limited use. Furthermore, they require the interface name of the side initiating the connection, whereas this code is used to answer the question "how can this machine be reached as a server?" - IPv4 addresses are sorted before IPv6 addresses. Even though I like IPv6 a lot, IPv4 is still more familiar to people. - Loopback addresses (::1, 127.0.0.1) are sorted last, so that the First- Time Wizard is most likely to use the bigger-scoped address.
This commit is contained in:
parent
bf5bf86f03
commit
43e8f3f623
@ -23,9 +23,11 @@ package own_url
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
@ -83,7 +85,7 @@ func networkInterfaces() ([]net.IP, error) {
|
|||||||
case ip.IsUnspecified():
|
case ip.IsUnspecified():
|
||||||
logger.Trace().Msg(" - skipping unspecified")
|
logger.Trace().Msg(" - skipping unspecified")
|
||||||
default:
|
default:
|
||||||
logger.Trace().Msg(" - usable")
|
logger.Trace().Msg(" - potentially usable")
|
||||||
ifaceAddresses = append(ifaceAddresses, ip)
|
ifaceAddresses = append(ifaceAddresses, ip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,35 +97,56 @@ func networkInterfaces() ([]net.IP, error) {
|
|||||||
return usableAddresses, ErrNoInterface
|
return usableAddresses, ErrNoInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sort.Slice(usableAddresses, func(i, j int) bool {
|
||||||
|
// Sort loopback addresses after others.
|
||||||
|
if usableAddresses[i].IsLoopback() != usableAddresses[j].IsLoopback() {
|
||||||
|
return usableAddresses[j].IsLoopback()
|
||||||
|
}
|
||||||
|
// Sort IPv4 before IPv6, because people are likely to be more familiar with
|
||||||
|
// them.
|
||||||
|
if isIPv4(usableAddresses[i]) != isIPv4(usableAddresses[j]) {
|
||||||
|
return isIPv4(usableAddresses[i])
|
||||||
|
}
|
||||||
|
// Otherwise just order lexicographically.
|
||||||
|
return bytes.Compare(usableAddresses[i], usableAddresses[j]) < 0
|
||||||
|
})
|
||||||
|
|
||||||
return usableAddresses, nil
|
return usableAddresses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// filterAddresses reduces the number of IPv6 addresses.
|
// filterAddresses reduces the number of IP addresses.
|
||||||
// It prefers link-local addresses; if these are in the list, all the other IPv6
|
//
|
||||||
// addresses will be removed. Link-local addresses are stable and meant for
|
// The function prefers non-link-local addresses over link-local ones.
|
||||||
// same-network connections, which is exactly what Flamenco needs.
|
// Link-local addresses are stable and meant for same-network connections, but
|
||||||
// Loopback addresses (localhost) are always filtered out, unless they're the only addresses available.
|
// they require a "zone index", typically the interface name, so something like
|
||||||
|
// `[fe80::cafe:f00d%eth0]`. This is not supported by webbrowsers. Furthermore,
|
||||||
|
// they require the interface name of the side initiating the connection,
|
||||||
|
// whereas this code is used to answer the question "how can this machine be
|
||||||
|
// reached?".
|
||||||
|
//
|
||||||
|
// Source: https://stackoverflow.com/a/52972417/875379
|
||||||
|
//
|
||||||
|
// Loopback addresses (localhost) are always filtered out, unless they're the
|
||||||
|
// only addresses available.
|
||||||
func filterAddresses(addrs []net.IP) []net.IP {
|
func filterAddresses(addrs []net.IP) []net.IP {
|
||||||
keepAddrs := make([]net.IP, 0)
|
keepAddrs := make([]net.IP, 0)
|
||||||
|
keepLinkLocalv4 := hasOnlyLinkLocalv4(addrs)
|
||||||
|
|
||||||
if hasOnlyLoopback(addrs) {
|
|
||||||
return addrs
|
|
||||||
}
|
|
||||||
|
|
||||||
var keepLinkLocalv6 = hasLinkLocalv6(addrs)
|
|
||||||
var keepLinkLocalv4 = hasLinkLocalv4(addrs)
|
|
||||||
|
|
||||||
var keep bool
|
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
if addr.IsLoopback() {
|
if addr.IsLoopback() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
isv4 := isIPv4(addr)
|
isv4 := isIPv4(addr)
|
||||||
|
|
||||||
|
var keep bool
|
||||||
if isv4 {
|
if isv4 {
|
||||||
keep = keepLinkLocalv4 == addr.IsLinkLocalUnicast()
|
keep = keepLinkLocalv4 == addr.IsLinkLocalUnicast()
|
||||||
} else {
|
} else {
|
||||||
keep = keepLinkLocalv6 == addr.IsLinkLocalUnicast()
|
// Never keep IPv6 link-local addresses. They need a "zone index" to work,
|
||||||
|
// and those can only be determined on the connecting side. Furthermore,
|
||||||
|
// they're incompatible with most webbrowsers.
|
||||||
|
keep = !addr.IsLinkLocalUnicast()
|
||||||
}
|
}
|
||||||
|
|
||||||
if keep {
|
if keep {
|
||||||
@ -131,6 +154,19 @@ func filterAddresses(addrs []net.IP) []net.IP {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only when after the filtering there is nothing left, add the loopback
|
||||||
|
// addresses. This is likely a bit of a strange test, because either this is a
|
||||||
|
// loopback device (and should only have loopback addresses) or it is not (and
|
||||||
|
// should only have non-loopback addresses). It does make the code reliable
|
||||||
|
// even when things are mixed, which is nice.
|
||||||
|
if len(keepAddrs) == 0 {
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if addr.IsLoopback() {
|
||||||
|
keepAddrs = append(keepAddrs, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return keepAddrs
|
return keepAddrs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,29 +174,17 @@ func isIPv4(addr net.IP) bool {
|
|||||||
return addr.To4() != nil
|
return addr.To4() != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasLinkLocalv6(addrs []net.IP) bool {
|
func hasOnlyLinkLocalv4(addrs []net.IP) bool {
|
||||||
|
hasLinkLocalv4 := false
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
if !isIPv4(addr) && addr.IsLinkLocalUnicast() {
|
// Only consider non-loopback IPv4 addresses.
|
||||||
return true
|
if addr.IsLoopback() || !isIPv4(addr) {
|
||||||
}
|
continue
|
||||||
}
|
}
|
||||||
|
if !addr.IsLinkLocalUnicast() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
hasLinkLocalv4 = true
|
||||||
func hasLinkLocalv4(addrs []net.IP) bool {
|
|
||||||
for _, addr := range addrs {
|
|
||||||
if isIPv4(addr) && addr.IsLinkLocalUnicast() {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
return hasLinkLocalv4
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasOnlyLoopback(addrs []net.IP) bool {
|
|
||||||
for _, addr := range addrs {
|
|
||||||
if !addr.IsLoopback() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
@ -32,10 +32,14 @@ func Test_filterAddresses(t *testing.T) {
|
|||||||
{"IPv6 without link-local",
|
{"IPv6 without link-local",
|
||||||
[]net.IP{globalIPv6, lanIPv6},
|
[]net.IP{globalIPv6, lanIPv6},
|
||||||
[]net.IP{globalIPv6, lanIPv6, localhostIPv6}},
|
[]net.IP{globalIPv6, lanIPv6, localhostIPv6}},
|
||||||
// Link-local address present, just use that one.
|
// In a mix, only the global address should be used.
|
||||||
{"IPv6 with link-local",
|
{"IPv6 with link-local",
|
||||||
[]net.IP{linkLocalIPv6},
|
[]net.IP{globalIPv6},
|
||||||
[]net.IP{linkLocalIPv6, lanIPv6, localhostIPv6}},
|
[]net.IP{linkLocalIPv6, globalIPv6, localhostIPv6}},
|
||||||
|
// Only loopback and link-local.
|
||||||
|
{"IPv6 with link-local + loopback",
|
||||||
|
[]net.IP{localhostIPv6},
|
||||||
|
[]net.IP{localhostIPv6, linkLocalIPv6}},
|
||||||
// Only loopback
|
// Only loopback
|
||||||
{"IPv6 with only loopback",
|
{"IPv6 with only loopback",
|
||||||
[]net.IP{localhostIPv6},
|
[]net.IP{localhostIPv6},
|
||||||
@ -46,10 +50,14 @@ func Test_filterAddresses(t *testing.T) {
|
|||||||
{"IPv4 without link-local",
|
{"IPv4 without link-local",
|
||||||
[]net.IP{globalIPv4, lanIPv4},
|
[]net.IP{globalIPv4, lanIPv4},
|
||||||
[]net.IP{globalIPv4, lanIPv4, localhostIPv4}},
|
[]net.IP{globalIPv4, lanIPv4, localhostIPv4}},
|
||||||
// Link-local address present, just use that one.
|
// In a mix, only the global and lan addresses should be used.
|
||||||
{"IPv4 with link-local",
|
{"IPv4 with link-local",
|
||||||
|
[]net.IP{globalIPv4, lanIPv4},
|
||||||
|
[]net.IP{globalIPv4, linkLocalIPv4, lanIPv4, localhostIPv4}},
|
||||||
|
// Only loopback and link-local.
|
||||||
|
{"IPv4 with link-local + loopback",
|
||||||
[]net.IP{linkLocalIPv4},
|
[]net.IP{linkLocalIPv4},
|
||||||
[]net.IP{linkLocalIPv4, lanIPv4, localhostIPv4}},
|
[]net.IP{localhostIPv4, linkLocalIPv4}},
|
||||||
// Only loopback
|
// Only loopback
|
||||||
{"IPv4 with only loopback",
|
{"IPv4 with only loopback",
|
||||||
[]net.IP{localhostIPv4},
|
[]net.IP{localhostIPv4},
|
||||||
@ -58,11 +66,11 @@ func Test_filterAddresses(t *testing.T) {
|
|||||||
// Mixed IPv4/IPv6 tests:
|
// Mixed IPv4/IPv6 tests:
|
||||||
// IPv4 no link-local, but IPv6 with link-local:
|
// IPv4 no link-local, but IPv6 with link-local:
|
||||||
{"IPv4 w/o, IPv6 w/ link-local",
|
{"IPv4 w/o, IPv6 w/ link-local",
|
||||||
[]net.IP{lanIPv4, linkLocalIPv6},
|
[]net.IP{lanIPv4, lanIPv6},
|
||||||
[]net.IP{lanIPv4, localhostIPv4, lanIPv6, linkLocalIPv6}},
|
[]net.IP{lanIPv4, localhostIPv4, lanIPv6, linkLocalIPv6}},
|
||||||
// IPv4 link-local, IPv6 without:
|
// IPv4 link-local, IPv6 without:
|
||||||
{"IPv4 w/, IPv4 w/o link-local",
|
{"IPv4 w/, IPv4 w/o link-local",
|
||||||
[]net.IP{linkLocalIPv4, lanIPv6},
|
[]net.IP{lanIPv4, lanIPv6},
|
||||||
[]net.IP{linkLocalIPv4, lanIPv4, lanIPv6}},
|
[]net.IP{linkLocalIPv4, lanIPv4, lanIPv6}},
|
||||||
// Only loopback
|
// Only loopback
|
||||||
{"IPv4 + IPv6 with only loopback",
|
{"IPv4 + IPv6 with only loopback",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user