311 lines
6.3 KiB
Go
311 lines
6.3 KiB
Go
![]() |
package tplCmd
|
||
|
|
||
|
import (
|
||
|
`fmt`
|
||
|
`net`
|
||
|
`strconv`
|
||
|
`strings`
|
||
|
|
||
|
`github.com/vishvananda/netlink`
|
||
|
`go4.org/netipx`
|
||
|
)
|
||
|
|
||
|
/*
|
||
|
This file contains functions strictly for use in templates.
|
||
|
*/
|
||
|
|
||
|
// Host functionality
|
||
|
// TODO: How would I do this on non-Linux?
|
||
|
|
||
|
/*
|
||
|
GetDefaultIface returns the interface name for the default route using netlink.
|
||
|
IPv4 by default, IPv6 if ipv6 is true.
|
||
|
If multiple routes match the default route for the inet family,
|
||
|
the lowest metric route's interface will be returned.
|
||
|
*/
|
||
|
func GetDefaultIface(ipv6 bool) (iface string, err error) {
|
||
|
|
||
|
var inetFamily int
|
||
|
var defNet *net.IPNet
|
||
|
var routes []netlink.Route
|
||
|
var defRt *netlink.Route
|
||
|
var defIface *net.Interface
|
||
|
var curPrio int = -1
|
||
|
|
||
|
// This can even be netlink.FAMILY_ALL, but that's silly.
|
||
|
if !ipv6 {
|
||
|
inetFamily = netlink.FAMILY_V4
|
||
|
defNet = &net.IPNet{
|
||
|
IP: net.ParseIP("0.0.0.0"),
|
||
|
Mask: net.CIDRMask(0, 32),
|
||
|
}
|
||
|
} else {
|
||
|
inetFamily = netlink.FAMILY_V6
|
||
|
defNet = &net.IPNet{
|
||
|
IP: net.ParseIP("::"),
|
||
|
Mask: net.CIDRMask(0, 128),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if routes, err = netlink.RouteList(nil, inetFamily); err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
for _, route := range routes {
|
||
|
if !(route.Dst != nil || route.Dst.String() == defNet.String()) {
|
||
|
continue
|
||
|
}
|
||
|
if curPrio == -1 {
|
||
|
curPrio = route.Priority
|
||
|
defRt = &route
|
||
|
} else {
|
||
|
if route.Priority < curPrio {
|
||
|
curPrio = route.Priority
|
||
|
defRt = &route
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if defRt != nil {
|
||
|
// There's also defRt.ILinkIndex, which is used for VLANs, tunnels, etc.
|
||
|
if defIface, err = net.InterfaceByIndex(defRt.LinkIndex); err != nil {
|
||
|
return
|
||
|
}
|
||
|
if defIface != nil {
|
||
|
iface = defIface.Name
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func GetSITIface() () {
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Conversions/assertions/coercions.
|
||
|
|
||
|
/*
|
||
|
TplIsNil returns true if v is nil. This lets you determine if e.g. a map or slice is empty or nil.
|
||
|
It currently only really works for a []interface{}, map[interface{}]interface{}, or map[string]interface{}.
|
||
|
*/
|
||
|
func TplIsNil(v interface{}) (isNil bool) {
|
||
|
|
||
|
switch t := v.(type) {
|
||
|
case []interface{}:
|
||
|
isNil = t == nil
|
||
|
case map[interface{}]interface{}:
|
||
|
isNil = t == nil
|
||
|
case map[string]interface{}:
|
||
|
isNil = t == nil
|
||
|
case nil:
|
||
|
isNil = true
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// TplToBool attempts to determine a boolean from v. Note that for numbers, b is true if v is *NOT* 0.
|
||
|
func TplToBool(v interface{}) (b bool, err error) {
|
||
|
|
||
|
switch t := v.(type) {
|
||
|
case bool:
|
||
|
b = t
|
||
|
case string:
|
||
|
switch s := strings.ToLower(t); s {
|
||
|
case "true", "y", "yes", "on", "1":
|
||
|
b = true
|
||
|
}
|
||
|
case []byte:
|
||
|
switch s := strings.ToLower(string(t)); s {
|
||
|
case "true", "y", "yes", "on", "1":
|
||
|
b = true
|
||
|
}
|
||
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
|
||
|
b = t != 0
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
TplToFloat returns a float64 from v.
|
||
|
Strings will be run through strconv.ParseFloat with 64 bitness.
|
||
|
Mind overflows.
|
||
|
*/
|
||
|
func TplToFloat(v interface{}) (i float64, err error) {
|
||
|
|
||
|
switch t := v.(type) {
|
||
|
case uint, uint8, uint16, uint32, uint64:
|
||
|
i = float64(t.(uint64))
|
||
|
case int, int8, int16, int32, int64:
|
||
|
i = float64(t.(int64))
|
||
|
case float64:
|
||
|
i = t
|
||
|
case float32:
|
||
|
i = float64(t)
|
||
|
case string:
|
||
|
if i, err = strconv.ParseFloat(t, 64); err != nil {
|
||
|
return
|
||
|
}
|
||
|
case []byte:
|
||
|
if i, err = strconv.ParseFloat(string(t), 64); err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
TplToInt returns a signed 64-bit integer from primitives.
|
||
|
Strings will be run through strconv.ParseInt with base 10 and 64 bitness.
|
||
|
Mind overflows.
|
||
|
*/
|
||
|
func TplToInt(v interface{}) (i int64, err error) {
|
||
|
|
||
|
switch t := v.(type) {
|
||
|
case uint, uint8, uint16, uint32, uint64:
|
||
|
i = int64(t.(uint64)) // If an overflow happens anywhere, it's either here or the string/[]bytes.
|
||
|
case int64:
|
||
|
i = t
|
||
|
case int, int8, int16, int32:
|
||
|
i = t.(int64)
|
||
|
case float32, float64:
|
||
|
i = int64(t.(float64))
|
||
|
case string:
|
||
|
if i, err = strconv.ParseInt(t, 10, 64); err != nil {
|
||
|
return
|
||
|
}
|
||
|
case []byte:
|
||
|
if i, err = strconv.ParseInt(string(t), 10, 64); err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
TplToMap attempts to return a map[string]interface{} from v.
|
||
|
Values can then be used with other TplTo* functions further.
|
||
|
m will be nil if it can't be asserted.
|
||
|
*/
|
||
|
func TplToMap(v interface{}) (m map[string]interface{}, err error) {
|
||
|
|
||
|
switch t := v.(type) {
|
||
|
case map[string]interface{}:
|
||
|
m = t
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// TplToString returns a string representation of primitives.
|
||
|
func TplToString(v interface{}) (s string, err error) {
|
||
|
|
||
|
switch t := v.(type) {
|
||
|
case string:
|
||
|
s = t
|
||
|
case []byte:
|
||
|
s = string(t)
|
||
|
default:
|
||
|
s = fmt.Sprintf("%v", v)
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
TplToUint returns an unsigned 64-bit integer from primitives.
|
||
|
Strings will be run through strconv.ParseUint with base 10 and 64 bitness.
|
||
|
Mind overflows.
|
||
|
*/
|
||
|
func TplToUint(v interface{}) (i uint64, err error) {
|
||
|
|
||
|
switch t := v.(type) {
|
||
|
case uint64:
|
||
|
i = t
|
||
|
case uint, uint8, uint16, uint32:
|
||
|
i = t.(uint64)
|
||
|
case int, int8, int16, int32, int64:
|
||
|
i = uint64(t.(int64))
|
||
|
case float32, float64:
|
||
|
i = uint64(t.(float64))
|
||
|
case string:
|
||
|
if i, err = strconv.ParseUint(t, 10, 64); err != nil {
|
||
|
return
|
||
|
}
|
||
|
case []byte:
|
||
|
if i, err = strconv.ParseUint(string(t), 10, 64); err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Wrappers
|
||
|
|
||
|
// TplGetIPSetBuilder returns a netipx.IPSetBuilder.
|
||
|
func TplGetIPSetBuilder() (ipsb *netipx.IPSetBuilder) {
|
||
|
|
||
|
ipsb = new(netipx.IPSetBuilder)
|
||
|
*ipsb = netipx.IPSetBuilder{}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// TplGetResolver returns a net.Resolver from the given options.
|
||
|
func TplGetResolver(useGo, strictErr bool) (resolver *net.Resolver) {
|
||
|
|
||
|
resolver = &net.Resolver{
|
||
|
PreferGo: useGo,
|
||
|
StrictErrors: strictErr,
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// TplSplitHostPortHost wraps net.SplitHostPort and returns the host.
|
||
|
func TplSplitHostPortHost(s string) (host string, err error) {
|
||
|
|
||
|
if host, _, err = net.SplitHostPort(s); err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// TplSplitHostPortPort wraps net.SplitHostPort and returns the port.
|
||
|
func TplSplitHostPortPort(s string) (port string, err error) {
|
||
|
|
||
|
if _, port, err = net.SplitHostPort(s); err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// TplToCidrHost wraps net.ParseCIDR and returns the host net.IP and any error.
|
||
|
func TplToCidrHost(s string) (host net.IP, err error) {
|
||
|
|
||
|
if host, _, err = net.ParseCIDR(s); err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// TplToCidrNet wraps net.ParseCIDR and returns the network *net.IPNet and any error.
|
||
|
func TplToCidrNet(s string) (netwk *net.IPNet, err error) {
|
||
|
|
||
|
if _, netwk, err = net.ParseCIDR(s); err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|