github.com/philhug/dnscontrol@v0.2.4-0.20180625181521-921fa9849001/pkg/transform/transform.go (about) 1 package transform 2 3 import ( 4 "net" 5 "strings" 6 7 "github.com/pkg/errors" 8 ) 9 10 // IpConversion describes an IP conversion. 11 type IpConversion struct { 12 Low, High net.IP 13 NewBases []net.IP 14 NewIPs []net.IP 15 } 16 17 func ipToUint(i net.IP) (uint32, error) { 18 parts := i.To4() 19 if parts == nil || len(parts) != 4 { 20 return 0, errors.Errorf("%s is not an ipv4 address", parts.String()) 21 } 22 r := uint32(parts[0])<<24 | uint32(parts[1])<<16 | uint32(parts[2])<<8 | uint32(parts[3]) 23 return r, nil 24 } 25 26 // UintToIP convert a 32-bit into into a net.IP. 27 func UintToIP(u uint32) net.IP { 28 return net.IPv4( 29 byte((u>>24)&255), 30 byte((u>>16)&255), 31 byte((u>>8)&255), 32 byte((u)&255)) 33 } 34 35 // DecodeTransformTable turns a string-encoded table into a list of conversions. 36 func DecodeTransformTable(transforms string) ([]IpConversion, error) { 37 result := []IpConversion{} 38 rows := strings.Split(transforms, ";") 39 for ri, row := range rows { 40 items := strings.Split(row, "~") 41 if len(items) != 4 { 42 return nil, errors.Errorf("transform_table rows should have 4 elements. (%v) found in row (%v) of %#v", len(items), ri, transforms) 43 } 44 for i, item := range items { 45 items[i] = strings.TrimSpace(item) 46 } 47 48 con := IpConversion{ 49 Low: net.ParseIP(items[0]), 50 High: net.ParseIP(items[1]), 51 } 52 parseList := func(s string) ([]net.IP, error) { 53 ips := []net.IP{} 54 for _, ip := range strings.Split(s, ",") { 55 56 if ip == "" { 57 continue 58 } 59 addr := net.ParseIP(ip) 60 if addr == nil { 61 return nil, errors.Errorf("%s is not a valid ip address", ip) 62 } 63 ips = append(ips, addr) 64 } 65 return ips, nil 66 } 67 var err error 68 if con.NewBases, err = parseList(items[2]); err != nil { 69 return nil, err 70 } 71 if con.NewIPs, err = parseList(items[3]); err != nil { 72 return nil, err 73 } 74 75 low, _ := ipToUint(con.Low) 76 high, _ := ipToUint(con.High) 77 if low > high { 78 return nil, errors.Errorf("transform_table Low should be less than High. row (%v) %v>%v (%v)", ri, con.Low, con.High, transforms) 79 } 80 if len(con.NewBases) > 0 && len(con.NewIPs) > 0 { 81 return nil, errors.Errorf("transform_table_rows should only specify one of NewBases or NewIPs, Not both") 82 } 83 result = append(result, con) 84 } 85 86 return result, nil 87 } 88 89 // TransformIP transforms a single ip address. If the transform results in multiple new targets, an error will be returned. 90 func TransformIP(address net.IP, transforms []IpConversion) (net.IP, error) { 91 ips, err := TransformIPToList(address, transforms) 92 if err != nil { 93 return nil, err 94 } 95 if len(ips) != 1 { 96 return nil, errors.Errorf("Expect exactly one ip for TransformIP result. Got: %s", ips) 97 } 98 return ips[0], err 99 } 100 101 // TransformIPToList manipulates an net.IP based on a list of IpConversions. It can potentially expand one ip address into multiple addresses. 102 func TransformIPToList(address net.IP, transforms []IpConversion) ([]net.IP, error) { 103 thisIP, err := ipToUint(address) 104 if err != nil { 105 return nil, err 106 } 107 for _, conv := range transforms { 108 min, err := ipToUint(conv.Low) 109 if err != nil { 110 return nil, err 111 } 112 max, err := ipToUint(conv.High) 113 if err != nil { 114 return nil, err 115 } 116 if (thisIP >= min) && (thisIP <= max) { 117 if len(conv.NewIPs) > 0 { 118 return conv.NewIPs, nil 119 } 120 list := []net.IP{} 121 for _, nb := range conv.NewBases { 122 newbase, err := ipToUint(nb) 123 if err != nil { 124 return nil, err 125 } 126 list = append(list, UintToIP(newbase+(thisIP-min))) 127 } 128 return list, nil 129 } 130 } 131 return []net.IP{address}, nil 132 }