github.com/teknogeek/dnscontrol/v2@v2.10.1-0.20200227202244-ae299b55ba42/pkg/transform/transform.go (about)

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