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