update... work pending
This commit is contained in:
79
tplCmd/consts.go
Normal file
79
tplCmd/consts.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package tplCmd
|
||||
|
||||
import (
|
||||
`context`
|
||||
`net`
|
||||
`net/netip`
|
||||
`text/template`
|
||||
|
||||
`go4.org/netipx`
|
||||
)
|
||||
|
||||
var (
|
||||
// Funcs added externally to CombinedTplFuncMap will override any of the funcs defined here or in sprig.
|
||||
CombinedTplFuncMap template.FuncMap = make(template.FuncMap)
|
||||
// TODO: github.com/vishvananda/netlink funcs?
|
||||
TplFuncs = template.FuncMap{
|
||||
"GetCtx": context.Background,
|
||||
// stdlib net funcs; everything missing you can get from a net.Resolver (see GetResolver/TplGetResolver)
|
||||
"CIDRMask": net.CIDRMask,
|
||||
"InterfaceAddrs": net.InterfaceAddrs,
|
||||
"InterfaceByIndex": net.InterfaceByIndex,
|
||||
"InterfaceByName": net.InterfaceByName,
|
||||
"IPv4": net.IPv4,
|
||||
"IPv4Mask": net.IPv4Mask,
|
||||
"JoinHostPort": net.JoinHostPort,
|
||||
"ParseIP": net.ParseIP,
|
||||
"ParseMAC": net.ParseMAC,
|
||||
"ResolveIPAddr": net.ResolveIPAddr,
|
||||
"ResolveTCPAddr": net.ResolveTCPAddr,
|
||||
"ResolveUDPAddr": net.ResolveUDPAddr,
|
||||
"ResolveUnixAddr": net.ResolveUnixAddr,
|
||||
"TCPAddrFromAddrPort": net.TCPAddrFromAddrPort,
|
||||
"UDPAddrFromAddrPort": net.UDPAddrFromAddrPort,
|
||||
// stdlib net/netip funcs
|
||||
"AddrFrom16": netip.AddrFrom16,
|
||||
"AddrFrom4": netip.AddrFrom4,
|
||||
"AddrFromSlice": netip.AddrFromSlice,
|
||||
"AddrPortFrom": netip.AddrPortFrom,
|
||||
"IPv4Unspecified": netip.IPv4Unspecified,
|
||||
"IPv6LinkLocalAllNodes": netip.IPv6LinkLocalAllNodes,
|
||||
"IPv6LinkLocalAllRouters": netip.IPv6LinkLocalAllRouters,
|
||||
"IPv6Loopback": netip.IPv6Loopback,
|
||||
"IPv6Unspecified": netip.IPv6Unspecified,
|
||||
"ParseAddr": netip.ParseAddr,
|
||||
"ParseAddrPort": netip.ParseAddrPort,
|
||||
"ParsePrefix": netip.ParsePrefix,
|
||||
"PrefixFrom": netip.PrefixFrom,
|
||||
// go4.org/netipx
|
||||
"AddrIPNet": netipx.AddrIPNet,
|
||||
"ComparePrefix": netipx.ComparePrefix,
|
||||
"FromStdAddr": netipx.FromStdAddr,
|
||||
"FromStdIP": netipx.FromStdIP,
|
||||
"FromStdIPNet": netipx.FromStdIPNet,
|
||||
"IPRangeFrom": netipx.IPRangeFrom,
|
||||
"ParseIPRange": netipx.ParseIPRange,
|
||||
"ParsePrefixOrAddr": netipx.ParsePrefixOrAddr,
|
||||
"PrefixIPNet": netipx.PrefixIPNet,
|
||||
"PrefixLastIP": netipx.PrefixLastIP,
|
||||
"RangeOfPrefix": netipx.RangeOfPrefix,
|
||||
// Custom-defined/compat wrappers
|
||||
"GetIPSetBuilder": TplGetIPSetBuilder,
|
||||
"GetResolver": TplGetResolver,
|
||||
"SplitHostPortHost": TplSplitHostPortHost, // net.SplitHostPort
|
||||
"SplitHostPortPort": TplSplitHostPortPort, // net.SplitHostPort
|
||||
"ToCidrHost": TplToCidrHost, // net.ParseCIDR
|
||||
"ToCidrNet": TplToCidrNet, // net.ParseCIDR
|
||||
// Weak coercers and other funcs.
|
||||
"IsNil": TplIsNil,
|
||||
"ToBool": TplToBool,
|
||||
"ToFloat": TplToFloat,
|
||||
"ToInt": TplToInt,
|
||||
"ToMap": TplToMap,
|
||||
"ToString": TplToString,
|
||||
"ToUint": TplToUint,
|
||||
// Host information
|
||||
"GetDefaultIface": GetDefaultIface,
|
||||
"GetSITIface": GetSITIface,
|
||||
}
|
||||
)
|
||||
20
tplCmd/funcs.go
Normal file
20
tplCmd/funcs.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package tplCmd
|
||||
|
||||
import (
|
||||
`text/template`
|
||||
|
||||
`github.com/Masterminds/sprig/v3`
|
||||
)
|
||||
|
||||
// GetTpl returns a generic text/template.Template with the customization/FuncMap logic applied.
|
||||
func GetTpl() (tpl *template.Template) {
|
||||
|
||||
tpl = template.New("")
|
||||
|
||||
tpl.Funcs(sprig.TxtFuncMap()).Funcs(TplFuncs)
|
||||
if CombinedTplFuncMap != nil && len(CombinedTplFuncMap) > 0 {
|
||||
tpl.Funcs(CombinedTplFuncMap)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -7,7 +7,10 @@ import (
|
||||
`text/template`
|
||||
)
|
||||
|
||||
// ToCmd returns an (os/)exec.Cmd from a TemplateCmd. t should be a tunnelbroker.FetchedTunnel, generally.
|
||||
/*
|
||||
ToCmd returns an (os/)exec.Cmd from a TemplateCmd.
|
||||
t should be a runner.TunnelResult.
|
||||
*/
|
||||
func (c *TemplateCmd) ToCmd(t any) (cmd *exec.Cmd, err error) {
|
||||
|
||||
var progName string
|
||||
@@ -16,8 +19,15 @@ func (c *TemplateCmd) ToCmd(t any) (cmd *exec.Cmd, err error) {
|
||||
var args []string
|
||||
var buf *bytes.Buffer = new(bytes.Buffer)
|
||||
|
||||
if !c.IsTemplate {
|
||||
cmd, err = c.Cmd.ToCmd()
|
||||
return
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
if tpl, err = template.New("").Parse(c.Program); err != nil {
|
||||
|
||||
tpl = GetTpl()
|
||||
if _, err = tpl.Parse(c.Program); err != nil {
|
||||
return
|
||||
}
|
||||
if err = tpl.Execute(buf, t); err != nil {
|
||||
@@ -29,7 +39,8 @@ func (c *TemplateCmd) ToCmd(t any) (cmd *exec.Cmd, err error) {
|
||||
args = make([]string, len(c.Args))
|
||||
for idx, arg := range c.Args {
|
||||
buf.Reset()
|
||||
if tpl, err = template.New("").Parse(arg); err != nil {
|
||||
tpl = GetTpl()
|
||||
if _, err = tpl.Parse(arg); err != nil {
|
||||
return
|
||||
}
|
||||
if err = tpl.Execute(buf, t); err != nil {
|
||||
@@ -44,7 +55,8 @@ func (c *TemplateCmd) ToCmd(t any) (cmd *exec.Cmd, err error) {
|
||||
envs = make([]string, len(c.EnvVars))
|
||||
for idx, env := range c.EnvVars {
|
||||
buf.Reset()
|
||||
if tpl, err = template.New("").Parse(env); err != nil {
|
||||
tpl = GetTpl()
|
||||
if _, err = tpl.Parse(env); err != nil {
|
||||
return
|
||||
}
|
||||
if err = tpl.Execute(buf, t); err != nil {
|
||||
|
||||
310
tplCmd/funcs_tpl.go
Normal file
310
tplCmd/funcs_tpl.go
Normal file
@@ -0,0 +1,310 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user