1package template
2
3import (
4	"bytes"
5	"fmt"
6	"text/template"
7
8	"github.com/hashicorp/errwrap"
9	sockaddr "github.com/hashicorp/go-sockaddr"
10)
11
12var (
13	// SourceFuncs is a map of all top-level functions that generate
14	// sockaddr data types.
15	SourceFuncs template.FuncMap
16
17	// SortFuncs is a map of all functions used in sorting
18	SortFuncs template.FuncMap
19
20	// FilterFuncs is a map of all functions used in sorting
21	FilterFuncs template.FuncMap
22
23	// HelperFuncs is a map of all functions used in sorting
24	HelperFuncs template.FuncMap
25)
26
27func init() {
28	SourceFuncs = template.FuncMap{
29		// GetAllInterfaces - Returns an exhaustive set of IfAddr
30		// structs available on the host.  `GetAllInterfaces` is the
31		// initial input and accessible as the initial "dot" in the
32		// pipeline.
33		"GetAllInterfaces": sockaddr.GetAllInterfaces,
34
35		// GetDefaultInterfaces - Returns one IfAddr for every IP that
36		// is on the interface containing the default route for the
37		// host.
38		"GetDefaultInterfaces": sockaddr.GetDefaultInterfaces,
39
40		// GetPrivateInterfaces - Returns one IfAddr for every IP that
41		// matches RFC 6890, are attached to the interface with the
42		// default route, and are forwardable IP addresses.  NOTE: RFC
43		// 6890 is a more exhaustive version of RFC1918 because it spans
44		// IPv4 and IPv6, however it doespermit the inclusion of likely
45		// undesired addresses such as multicast, therefore our
46		// definition of a "private" address also excludes
47		// non-forwardable IP addresses (as defined by the IETF).
48		"GetPrivateInterfaces": sockaddr.GetPrivateInterfaces,
49
50		// GetPublicInterfaces - Returns a list of IfAddr that do not
51		// match RFC 6890, are attached to the default route, and are
52		// forwardable.
53		"GetPublicInterfaces": sockaddr.GetPublicInterfaces,
54	}
55
56	SortFuncs = template.FuncMap{
57		"sort": sockaddr.SortIfBy,
58	}
59
60	FilterFuncs = template.FuncMap{
61		"exclude": sockaddr.ExcludeIfs,
62		"include": sockaddr.IncludeIfs,
63	}
64
65	HelperFuncs = template.FuncMap{
66		// Misc functions that operate on IfAddrs inputs
67		"attr":   Attr,
68		"join":   sockaddr.JoinIfAddrs,
69		"limit":  sockaddr.LimitIfAddrs,
70		"offset": sockaddr.OffsetIfAddrs,
71		"unique": sockaddr.UniqueIfAddrsBy,
72
73		// Misc math functions that operate on a single IfAddr input
74		"math": sockaddr.IfAddrsMath,
75
76		// Return a Private RFC 6890 IP address string that is attached
77		// to the default route and a forwardable address.
78		"GetPrivateIP": sockaddr.GetPrivateIP,
79
80		// Return all Private RFC 6890 IP addresses as a space-delimited string of
81		// IP addresses.  Addresses returned do not have to be on the interface with
82		// a default route.
83		"GetPrivateIPs": sockaddr.GetPrivateIPs,
84
85		// Return a Public RFC 6890 IP address string that is attached
86		// to the default route and a forwardable address.
87		"GetPublicIP": sockaddr.GetPublicIP,
88
89		// Return allPublic RFC 6890 IP addresses as a space-delimited string of IP
90		// addresses.  Addresses returned do not have to be on the interface with a
91		// default route.
92		"GetPublicIPs": sockaddr.GetPublicIPs,
93
94		// Return the first IP address of the named interface, sorted by
95		// the largest network size.
96		"GetInterfaceIP": sockaddr.GetInterfaceIP,
97
98		// Return all IP addresses on the named interface, sorted by the largest
99		// network size.
100		"GetInterfaceIPs": sockaddr.GetInterfaceIPs,
101	}
102}
103
104// Attr returns the attribute from the ifAddrRaw argument.  If the argument is
105// an IfAddrs, only the first element will be evaluated for resolution.
106func Attr(selectorName string, ifAddrsRaw interface{}) (string, error) {
107	switch v := ifAddrsRaw.(type) {
108	case sockaddr.IfAddr:
109		return sockaddr.IfAttr(selectorName, v)
110	case sockaddr.IfAddrs:
111		return sockaddr.IfAttrs(selectorName, v)
112	default:
113		return "", fmt.Errorf("unable to obtain attribute %s from type %T (%v)", selectorName, ifAddrsRaw, ifAddrsRaw)
114	}
115}
116
117// Parse parses input as template input using the addresses available on the
118// host, then returns the string output if there are no errors.
119func Parse(input string) (string, error) {
120	addrs, err := sockaddr.GetAllInterfaces()
121	if err != nil {
122		return "", errwrap.Wrapf("unable to query interface addresses: {{err}}", err)
123	}
124
125	return ParseIfAddrs(input, addrs)
126}
127
128// ParseIfAddrs parses input as template input using the IfAddrs inputs, then
129// returns the string output if there are no errors.
130func ParseIfAddrs(input string, ifAddrs sockaddr.IfAddrs) (string, error) {
131	return ParseIfAddrsTemplate(input, ifAddrs, template.New("sockaddr.Parse"))
132}
133
134// ParseIfAddrsTemplate parses input as template input using the IfAddrs inputs,
135// then returns the string output if there are no errors.
136func ParseIfAddrsTemplate(input string, ifAddrs sockaddr.IfAddrs, tmplIn *template.Template) (string, error) {
137	// Create a template, add the function map, and parse the text.
138	tmpl, err := tmplIn.Option("missingkey=error").
139		Funcs(SourceFuncs).
140		Funcs(SortFuncs).
141		Funcs(FilterFuncs).
142		Funcs(HelperFuncs).
143		Parse(input)
144	if err != nil {
145		return "", errwrap.Wrapf(fmt.Sprintf("unable to parse template %+q: {{err}}", input), err)
146	}
147
148	var outWriter bytes.Buffer
149	err = tmpl.Execute(&outWriter, ifAddrs)
150	if err != nil {
151		return "", errwrap.Wrapf(fmt.Sprintf("unable to execute sockaddr input %+q: {{err}}", input), err)
152	}
153
154	return outWriter.String(), nil
155}
156