github.com/kurthockenbury/dnscontrol@v0.2.8/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  }