chore: import upstream snapshot with attribution
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
// addr provides functions to retrieve local IP addresses from device interfaces.
|
||||
package addr
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrIPNotFound no IP address found, and explicit IP not provided.
|
||||
ErrIPNotFound = errors.New("no IP address found, and explicit IP not provided")
|
||||
)
|
||||
|
||||
// IsLocal checks whether an IP belongs to one of the device's interfaces.
|
||||
func IsLocal(addr string) bool {
|
||||
// Extract the host
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err == nil {
|
||||
addr = host
|
||||
}
|
||||
|
||||
if addr == "localhost" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check against all local ips
|
||||
for _, ip := range IPs() {
|
||||
if addr == ip {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Extract returns a valid IP address. If the address provided is a valid
|
||||
// address, it will be returned directly. Otherwise, the available interfaces
|
||||
// will be iterated over to find an IP address, preferably private.
|
||||
func Extract(addr string) (string, error) {
|
||||
// if addr is already specified then it's directly returned
|
||||
if len(addr) > 0 && (addr != "0.0.0.0" && addr != "[::]" && addr != "::") {
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
var (
|
||||
addrs []net.Addr
|
||||
loAddrs []net.Addr
|
||||
)
|
||||
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to get interfaces")
|
||||
}
|
||||
|
||||
for _, iface := range ifaces {
|
||||
ifaceAddrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
// ignore error, interface can disappear from system
|
||||
continue
|
||||
}
|
||||
|
||||
if iface.Flags&net.FlagLoopback != 0 {
|
||||
loAddrs = append(loAddrs, ifaceAddrs...)
|
||||
continue
|
||||
}
|
||||
|
||||
addrs = append(addrs, ifaceAddrs...)
|
||||
}
|
||||
|
||||
// Add loopback addresses to the end of the list
|
||||
addrs = append(addrs, loAddrs...)
|
||||
|
||||
// Try to find private IP in list, public IP otherwise
|
||||
ip, err := findIP(addrs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return ip.String(), nil
|
||||
}
|
||||
|
||||
// IPs returns all available interface IP addresses.
|
||||
func IPs() []string {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var ipAddrs []string
|
||||
|
||||
for _, i := range ifaces {
|
||||
addrs, err := i.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
}
|
||||
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
ipAddrs = append(ipAddrs, ip.String())
|
||||
}
|
||||
}
|
||||
|
||||
return ipAddrs
|
||||
}
|
||||
|
||||
// findIP will return the first private IP available in the list.
|
||||
// If no private IP is available it will return the first public IP, if present.
|
||||
// If no public IP is available, it will return the first loopback IP, if present.
|
||||
func findIP(addresses []net.Addr) (net.IP, error) {
|
||||
var publicIP net.IP
|
||||
var localIP net.IP
|
||||
|
||||
for _, rawAddr := range addresses {
|
||||
var ip net.IP
|
||||
switch addr := rawAddr.(type) {
|
||||
case *net.IPAddr:
|
||||
ip = addr.IP
|
||||
case *net.IPNet:
|
||||
ip = addr.IP
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
if ip.IsLoopback() {
|
||||
if localIP == nil {
|
||||
localIP = ip
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !ip.IsPrivate() {
|
||||
if publicIP == nil {
|
||||
publicIP = ip
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Return private IP if available
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// Return public or virtual IP
|
||||
if len(publicIP) > 0 {
|
||||
return publicIP, nil
|
||||
}
|
||||
|
||||
// Return local IP
|
||||
if len(localIP) > 0 {
|
||||
return localIP, nil
|
||||
}
|
||||
|
||||
return nil, ErrIPNotFound
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package addr
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsLocal(t *testing.T) {
|
||||
testData := []struct {
|
||||
addr string
|
||||
expect bool
|
||||
}{
|
||||
{"localhost", true},
|
||||
{"localhost:8080", true},
|
||||
{"127.0.0.1", true},
|
||||
{"127.0.0.1:1001", true},
|
||||
{"80.1.1.1", false},
|
||||
}
|
||||
|
||||
for _, d := range testData {
|
||||
res := IsLocal(d.addr)
|
||||
if res != d.expect {
|
||||
t.Fatalf("expected %t got %t", d.expect, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractor(t *testing.T) {
|
||||
testData := []struct {
|
||||
addr string
|
||||
expect string
|
||||
parse bool
|
||||
}{
|
||||
{"127.0.0.1", "127.0.0.1", false},
|
||||
{"10.0.0.1", "10.0.0.1", false},
|
||||
{"", "", true},
|
||||
{"0.0.0.0", "", true},
|
||||
{"[::]", "", true},
|
||||
}
|
||||
|
||||
for _, d := range testData {
|
||||
addr, err := Extract(d.addr)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error %v", err)
|
||||
}
|
||||
|
||||
if d.parse {
|
||||
ip := net.ParseIP(addr)
|
||||
if ip == nil {
|
||||
t.Error("Unexpected nil IP")
|
||||
}
|
||||
} else if addr != d.expect {
|
||||
t.Errorf("Expected %s got %s", d.expect, addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindIP(t *testing.T) {
|
||||
localhost, _ := net.ResolveIPAddr("ip", "127.0.0.1")
|
||||
localhostIPv6, _ := net.ResolveIPAddr("ip", "::1")
|
||||
privateIP, _ := net.ResolveIPAddr("ip", "10.0.0.1")
|
||||
publicIP, _ := net.ResolveIPAddr("ip", "100.0.0.1")
|
||||
publicIPv6, _ := net.ResolveIPAddr("ip", "2001:0db8:85a3:0000:0000:8a2e:0370:7334")
|
||||
|
||||
testCases := []struct {
|
||||
addrs []net.Addr
|
||||
ip net.IP
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
addrs: []net.Addr{},
|
||||
ip: nil,
|
||||
errMsg: ErrIPNotFound.Error(),
|
||||
},
|
||||
{
|
||||
addrs: []net.Addr{localhost},
|
||||
ip: localhost.IP,
|
||||
},
|
||||
{
|
||||
addrs: []net.Addr{localhost, localhostIPv6},
|
||||
ip: localhost.IP,
|
||||
},
|
||||
{
|
||||
addrs: []net.Addr{localhostIPv6},
|
||||
ip: localhostIPv6.IP,
|
||||
},
|
||||
{
|
||||
addrs: []net.Addr{privateIP, localhost},
|
||||
ip: privateIP.IP,
|
||||
},
|
||||
{
|
||||
addrs: []net.Addr{privateIP, publicIP, localhost},
|
||||
ip: privateIP.IP,
|
||||
},
|
||||
{
|
||||
addrs: []net.Addr{publicIP, privateIP, localhost},
|
||||
ip: privateIP.IP,
|
||||
},
|
||||
{
|
||||
addrs: []net.Addr{publicIP, localhost},
|
||||
ip: publicIP.IP,
|
||||
},
|
||||
{
|
||||
addrs: []net.Addr{publicIP, localhostIPv6},
|
||||
ip: publicIP.IP,
|
||||
},
|
||||
{
|
||||
addrs: []net.Addr{localhostIPv6, publicIP},
|
||||
ip: publicIP.IP,
|
||||
},
|
||||
{
|
||||
addrs: []net.Addr{localhostIPv6, publicIPv6, publicIP},
|
||||
ip: publicIPv6.IP,
|
||||
},
|
||||
{
|
||||
addrs: []net.Addr{publicIP, publicIPv6},
|
||||
ip: publicIP.IP,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
ip, err := findIP(tc.addrs)
|
||||
if tc.errMsg == "" {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, tc.ip.String(), ip.String())
|
||||
} else {
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, tc.errMsg, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user