github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/pkg/scanners/terraform/parser/funcs/cidr.go (about) 1 // Copied from github.com/hashicorp/terraform/internal/lang/funcs 2 package funcs 3 4 import ( 5 "fmt" 6 "math/big" 7 "net" 8 9 "github.com/apparentlymart/go-cidr/cidr" 10 "github.com/zclconf/go-cty/cty" 11 "github.com/zclconf/go-cty/cty/function" 12 "github.com/zclconf/go-cty/cty/gocty" 13 ) 14 15 // CidrHostFunc constructs a function that calculates a full host IP address 16 // within a given IP network address prefix. 17 var CidrHostFunc = function.New(&function.Spec{ 18 Params: []function.Parameter{ 19 { 20 Name: "prefix", 21 Type: cty.String, 22 }, 23 { 24 Name: "hostnum", 25 Type: cty.Number, 26 }, 27 }, 28 Type: function.StaticReturnType(cty.String), 29 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 30 var hostNum *big.Int 31 if err := gocty.FromCtyValue(args[1], &hostNum); err != nil { 32 return cty.UnknownVal(cty.String), err 33 } 34 _, network, err := net.ParseCIDR(args[0].AsString()) 35 if err != nil { 36 return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err) 37 } 38 39 ip, err := cidr.HostBig(network, hostNum) 40 if err != nil { 41 return cty.UnknownVal(cty.String), err 42 } 43 44 return cty.StringVal(ip.String()), nil 45 }, 46 }) 47 48 // CidrNetmaskFunc constructs a function that converts an IPv4 address prefix given 49 // in CIDR notation into a subnet mask address. 50 var CidrNetmaskFunc = function.New(&function.Spec{ 51 Params: []function.Parameter{ 52 { 53 Name: "prefix", 54 Type: cty.String, 55 }, 56 }, 57 Type: function.StaticReturnType(cty.String), 58 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 59 _, network, err := net.ParseCIDR(args[0].AsString()) 60 if err != nil { 61 return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err) 62 } 63 64 return cty.StringVal(net.IP(network.Mask).String()), nil 65 }, 66 }) 67 68 // CidrSubnetFunc constructs a function that calculates a subnet address within 69 // a given IP network address prefix. 70 var CidrSubnetFunc = function.New(&function.Spec{ 71 Params: []function.Parameter{ 72 { 73 Name: "prefix", 74 Type: cty.String, 75 }, 76 { 77 Name: "newbits", 78 Type: cty.Number, 79 }, 80 { 81 Name: "netnum", 82 Type: cty.Number, 83 }, 84 }, 85 Type: function.StaticReturnType(cty.String), 86 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 87 var newbits int 88 if err := gocty.FromCtyValue(args[1], &newbits); err != nil { 89 return cty.UnknownVal(cty.String), err 90 } 91 var netnum *big.Int 92 if err := gocty.FromCtyValue(args[2], &netnum); err != nil { 93 return cty.UnknownVal(cty.String), err 94 } 95 96 _, network, err := net.ParseCIDR(args[0].AsString()) 97 if err != nil { 98 return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err) 99 } 100 101 newNetwork, err := cidr.SubnetBig(network, newbits, netnum) 102 if err != nil { 103 return cty.UnknownVal(cty.String), err 104 } 105 106 return cty.StringVal(newNetwork.String()), nil 107 }, 108 }) 109 110 // CidrSubnetsFunc is similar to CidrSubnetFunc but calculates many consecutive 111 // subnet addresses at once, rather than just a single subnet extension. 112 var CidrSubnetsFunc = function.New(&function.Spec{ 113 Params: []function.Parameter{ 114 { 115 Name: "prefix", 116 Type: cty.String, 117 }, 118 }, 119 VarParam: &function.Parameter{ 120 Name: "newbits", 121 Type: cty.Number, 122 }, 123 Type: function.StaticReturnType(cty.List(cty.String)), 124 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 125 _, network, err := net.ParseCIDR(args[0].AsString()) 126 if err != nil { 127 return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "invalid CIDR expression: %s", err) 128 } 129 startPrefixLen, _ := network.Mask.Size() 130 131 prefixLengthArgs := args[1:] 132 if len(prefixLengthArgs) == 0 { 133 return cty.ListValEmpty(cty.String), nil 134 } 135 136 var firstLength int 137 if err := gocty.FromCtyValue(prefixLengthArgs[0], &firstLength); err != nil { 138 return cty.UnknownVal(cty.String), function.NewArgError(1, err) 139 } 140 firstLength += startPrefixLen 141 142 retVals := make([]cty.Value, len(prefixLengthArgs)) 143 144 current, _ := cidr.PreviousSubnet(network, firstLength) 145 for i, lengthArg := range prefixLengthArgs { 146 var length int 147 if err := gocty.FromCtyValue(lengthArg, &length); err != nil { 148 return cty.UnknownVal(cty.String), function.NewArgError(i+1, err) 149 } 150 151 if length < 1 { 152 return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "must extend prefix by at least one bit") 153 } 154 // For portability with 32-bit systems where the subnet number 155 // will be a 32-bit int, we only allow extension of 32 bits in 156 // one call even if we're running on a 64-bit machine. 157 // (Of course, this is significant only for IPv6.) 158 if length > 32 { 159 return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "may not extend prefix by more than 32 bits") 160 } 161 length += startPrefixLen 162 if length > (len(network.IP) * 8) { 163 protocol := "IP" 164 switch len(network.IP) * 8 { 165 case 32: 166 protocol = "IPv4" 167 case 128: 168 protocol = "IPv6" 169 } 170 return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "would extend prefix to %d bits, which is too long for an %s address", length, protocol) 171 } 172 173 next, rollover := cidr.NextSubnet(current, length) 174 if rollover || !network.Contains(next.IP) { 175 // If we run out of suffix bits in the base CIDR prefix then 176 // NextSubnet will start incrementing the prefix bits, which 177 // we don't allow because it would then allocate addresses 178 // outside of the caller's given prefix. 179 return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "not enough remaining address space for a subnet with a prefix of %d bits after %s", length, current.String()) 180 } 181 182 current = next 183 retVals[i] = cty.StringVal(current.String()) 184 } 185 186 return cty.ListVal(retVals), nil 187 }, 188 }) 189 190 // CidrHost calculates a full host IP address within a given IP network address prefix. 191 func CidrHost(prefix, hostnum cty.Value) (cty.Value, error) { 192 return CidrHostFunc.Call([]cty.Value{prefix, hostnum}) 193 } 194 195 // CidrNetmask converts an IPv4 address prefix given in CIDR notation into a subnet mask address. 196 func CidrNetmask(prefix cty.Value) (cty.Value, error) { 197 return CidrNetmaskFunc.Call([]cty.Value{prefix}) 198 } 199 200 // CidrSubnet calculates a subnet address within a given IP network address prefix. 201 func CidrSubnet(prefix, newbits, netnum cty.Value) (cty.Value, error) { 202 return CidrSubnetFunc.Call([]cty.Value{prefix, newbits, netnum}) 203 } 204 205 // CidrSubnets calculates a sequence of consecutive subnet prefixes that may 206 // be of different prefix lengths under a common base prefix. 207 func CidrSubnets(prefix cty.Value, newbits ...cty.Value) (cty.Value, error) { 208 args := make([]cty.Value, len(newbits)+1) 209 args[0] = prefix 210 copy(args[1:], newbits) 211 return CidrSubnetsFunc.Call(args) 212 }