format.go raw

   1  package dns
   2  
   3  import (
   4  	"net"
   5  	"reflect"
   6  	"strconv"
   7  )
   8  
   9  // NumField returns the number of rdata fields r has.
  10  func NumField(r RR) int {
  11  	return reflect.ValueOf(r).Elem().NumField() - 1 // Remove RR_Header
  12  }
  13  
  14  // Field returns the rdata field i as a string. Fields are indexed starting from 1.
  15  // RR types that holds slice data, for instance the NSEC type bitmap will return a single
  16  // string where the types are concatenated using a space.
  17  // Accessing non existing fields will cause a panic.
  18  func Field(r RR, i int) string {
  19  	if i == 0 {
  20  		return ""
  21  	}
  22  	d := reflect.ValueOf(r).Elem().Field(i)
  23  	switch d.Kind() {
  24  	case reflect.String:
  25  		return d.String()
  26  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  27  		return strconv.FormatInt(d.Int(), 10)
  28  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  29  		return strconv.FormatUint(d.Uint(), 10)
  30  	case reflect.Slice:
  31  		switch reflect.ValueOf(r).Elem().Type().Field(i).Tag {
  32  		case `dns:"a"`:
  33  			// TODO(miek): Hmm store this as 16 bytes
  34  			if d.Len() < net.IPv4len {
  35  				return ""
  36  			}
  37  			if d.Len() < net.IPv6len {
  38  				return net.IPv4(byte(d.Index(0).Uint()),
  39  					byte(d.Index(1).Uint()),
  40  					byte(d.Index(2).Uint()),
  41  					byte(d.Index(3).Uint())).String()
  42  			}
  43  			return net.IPv4(byte(d.Index(12).Uint()),
  44  				byte(d.Index(13).Uint()),
  45  				byte(d.Index(14).Uint()),
  46  				byte(d.Index(15).Uint())).String()
  47  		case `dns:"aaaa"`:
  48  			if d.Len() < net.IPv6len {
  49  				return ""
  50  			}
  51  			return net.IP{
  52  				byte(d.Index(0).Uint()),
  53  				byte(d.Index(1).Uint()),
  54  				byte(d.Index(2).Uint()),
  55  				byte(d.Index(3).Uint()),
  56  				byte(d.Index(4).Uint()),
  57  				byte(d.Index(5).Uint()),
  58  				byte(d.Index(6).Uint()),
  59  				byte(d.Index(7).Uint()),
  60  				byte(d.Index(8).Uint()),
  61  				byte(d.Index(9).Uint()),
  62  				byte(d.Index(10).Uint()),
  63  				byte(d.Index(11).Uint()),
  64  				byte(d.Index(12).Uint()),
  65  				byte(d.Index(13).Uint()),
  66  				byte(d.Index(14).Uint()),
  67  				byte(d.Index(15).Uint()),
  68  			}.String()
  69  		case `dns:"nsec"`:
  70  			if d.Len() == 0 {
  71  				return ""
  72  			}
  73  			s := Type(d.Index(0).Uint()).String()
  74  			for i := 1; i < d.Len(); i++ {
  75  				s += " " + Type(d.Index(i).Uint()).String()
  76  			}
  77  			return s
  78  		default:
  79  			// if it does not have a tag its a string slice
  80  			fallthrough
  81  		case `dns:"txt"`:
  82  			if d.Len() == 0 {
  83  				return ""
  84  			}
  85  			s := d.Index(0).String()
  86  			for i := 1; i < d.Len(); i++ {
  87  				s += " " + d.Index(i).String()
  88  			}
  89  			return s
  90  		}
  91  	}
  92  	return ""
  93  }
  94