go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/networkinterface/hostip.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package networkinterface 5 6 import ( 7 "fmt" 8 "net" 9 "sort" 10 11 "github.com/cockroachdb/errors" 12 "github.com/rs/zerolog/log" 13 ) 14 15 func filterNetworkInterface(interfaces []Interface, flagFilter func(flags net.Flags) bool) []Interface { 16 i := []Interface{} 17 for _, v := range interfaces { 18 if flagFilter(v.Flags) { 19 i = append(i, v) 20 } 21 } 22 return i 23 } 24 25 // byIfaceIndex Interface by its index 26 type byIfaceIndex []Interface 27 28 func (iface byIfaceIndex) Len() int { return len(iface) } 29 func (iface byIfaceIndex) Less(i, j int) bool { return iface[i].Index < iface[j].Index } 30 func (iface byIfaceIndex) Swap(i, j int) { iface[i], iface[j] = iface[j], iface[i] } 31 32 // HostIP extracts the best-guess for the IP of the host 33 // It will search ip v4 first and fallback to v6 34 func HostIP(interfaces []Interface) (ip string, err error) { 35 log.Debug().Int("interfaces", len(interfaces)).Msg("search ip") 36 // filter interfaces that are not up or a loopback/p2p interface 37 interfaces = filterNetworkInterface(interfaces, func(flags net.Flags) bool { 38 if (flags&net.FlagUp != 0) && 39 (flags&net.FlagLoopback == 0) && 40 (flags&net.FlagPointToPoint == 0) { 41 return true 42 } 43 return false 44 }) 45 46 // sort interfaces by its index 47 sort.Sort(byIfaceIndex(interfaces)) 48 49 var foundIPv4 net.IP 50 foundIPsv6 := []net.IP{} 51 52 // search for IPv4 53 for _, i := range interfaces { 54 addrs := i.Addrs 55 for _, addr := range addrs { 56 var foundIPv6 net.IP 57 switch v := addr.(type) { 58 case *net.IPAddr: 59 foundIPv4 = v.IP.To4() 60 foundIPv6 = v.IP.To16() 61 case *net.IPNet: 62 foundIPv4 = v.IP.To4() 63 foundIPv6 = v.IP.To16() 64 case *ipAddr: 65 foundIPv4 = v.IP.To4() 66 foundIPv6 = v.IP.To16() 67 } 68 69 if foundIPv4 != nil { 70 return foundIPv4.String(), nil 71 } 72 if foundIPv6 != nil { 73 foundIPsv6 = append(foundIPsv6, foundIPv6) 74 } 75 } 76 } 77 78 // search for IPv6 79 if len(foundIPsv6) > 0 { 80 return foundIPsv6[0].String(), nil 81 } 82 83 return "", fmt.Errorf("no IP address found") 84 } 85 86 // GetOutboundIP returns the local IP that is used for outbound connections 87 // It does not establish a real connection and the destination does not need to valid. 88 // Since its using udp protocol (unlike TCP) a handshake nor connection is required, 89 // / then it gets the local up address if it would connect to that target 90 // conn.LocalAddr().String() returns the local ip and port 91 // 92 // # NOTE be aware that this code does not work on remote targets 93 // 94 // @see this approach is derived from https://stackoverflow.com/a/37382208 95 func GetOutboundIP() (net.IP, error) { 96 conn, err := net.Dial("udp", "1.1.1.1:80") 97 if err != nil { 98 return nil, errors.Wrap(err, "could not determine outbound ip") 99 } 100 defer conn.Close() 101 102 localAddr := conn.LocalAddr().(*net.UDPAddr) 103 104 if localAddr == nil { 105 return nil, errors.New("could not determine outbound ip") 106 } 107 108 return localAddr.IP, nil 109 }