answer.go raw

   1  package dns
   2  
   3  import (
   4  	"encoding/json"
   5  	"errors"
   6  	"fmt"
   7  	"strconv"
   8  	"strings"
   9  
  10  	"gopkg.in/ns1/ns1-go.v2/rest/model/data"
  11  )
  12  
  13  // AnswerFeed wraps the values of an Answer's "feed" attribute
  14  type AnswerFeed struct {
  15  	FeedID   string `json:"feed"`
  16  	SourceID string `json:"source"`
  17  }
  18  
  19  // Answer wraps the values of a Record's "filters" attribute
  20  type Answer struct {
  21  	ID string `json:"id,omitempty"`
  22  
  23  	Meta  *data.Meta   `json:"meta,omitempty"`
  24  	Feeds []AnswerFeed `json:"feeds,omitempty"`
  25  
  26  	// Answer response data. eg:
  27  	// Av4: ["1.1.1.1"]
  28  	// Av6: ["2001:db8:85a3::8a2e:370:7334"]
  29  	// MX:  [10, "2.2.2.2"]
  30  	Rdata []string `json:"answer"`
  31  
  32  	// Region(grouping) that answer belongs to.
  33  	RegionName string `json:"region,omitempty"`
  34  }
  35  
  36  // Alias is used as an alias for an answer so that the custom marshaler isn't used.
  37  type Alias struct {
  38  	Rdata []interface{} `json:"answer"`
  39  	*AliasAnswer
  40  }
  41  
  42  // AliasAnswer is a duplicate of Answer.
  43  type AliasAnswer Answer
  44  
  45  // UnmarshalJSON parses responses to Answer and attempts to convert Rdata
  46  // elements to string.
  47  func (a *Answer) UnmarshalJSON(data []byte) error {
  48  	aux := &Alias{
  49  		AliasAnswer: (*AliasAnswer)(a),
  50  	}
  51  	if err := json.Unmarshal(data, &aux); err != nil {
  52  		return err
  53  	}
  54  
  55  	var rdata []string
  56  	for _, record := range aux.Rdata {
  57  		switch v := record.(type) {
  58  		case string:
  59  			rdata = append(rdata, v)
  60  		case float64:
  61  			rdata = append(rdata, strconv.Itoa(int(v)))
  62  		default:
  63  			return fmt.Errorf(
  64  				"Could not unmarshal Rdata value %[1]v (type %[1]T) as type string", v,
  65  			)
  66  		}
  67  	}
  68  	a.Rdata = rdata
  69  
  70  	return nil
  71  }
  72  
  73  func (a Answer) String() string {
  74  	return strings.Trim(fmt.Sprint(a.Rdata), "[]")
  75  }
  76  
  77  // SetRegion associates a region with this answer.
  78  func (a *Answer) SetRegion(name string) {
  79  	a.RegionName = name
  80  }
  81  
  82  // NewAnswer creates a generic Answer with given rdata.
  83  func NewAnswer(rdata []string) *Answer {
  84  	return &Answer{
  85  		Meta:  &data.Meta{},
  86  		Rdata: rdata,
  87  	}
  88  }
  89  
  90  // NewAv4Answer creates an Answer for A record.
  91  func NewAv4Answer(host string) *Answer {
  92  	return &Answer{
  93  		Meta:  &data.Meta{},
  94  		Rdata: []string{host},
  95  	}
  96  }
  97  
  98  // NewAv6Answer creates an Answer for AAAA record.
  99  func NewAv6Answer(host string) *Answer {
 100  	return &Answer{
 101  		Meta:  &data.Meta{},
 102  		Rdata: []string{host},
 103  	}
 104  }
 105  
 106  // NewALIASAnswer creates an Answer for ALIAS record.
 107  func NewALIASAnswer(host string) *Answer {
 108  	return &Answer{
 109  		Meta:  &data.Meta{},
 110  		Rdata: []string{host},
 111  	}
 112  }
 113  
 114  // NewCNAMEAnswer creates an Answer for CNAME record.
 115  func NewCNAMEAnswer(name string) *Answer {
 116  	return &Answer{
 117  		Meta:  &data.Meta{},
 118  		Rdata: []string{name},
 119  	}
 120  }
 121  
 122  // NewTXTAnswer creates an Answer for TXT record.
 123  func NewTXTAnswer(text string) *Answer {
 124  	return &Answer{
 125  		Meta:  &data.Meta{},
 126  		Rdata: []string{text},
 127  	}
 128  }
 129  
 130  // NewMXAnswer creates an Answer for MX record.
 131  func NewMXAnswer(pri int, host string) *Answer {
 132  	return &Answer{
 133  		Meta:  &data.Meta{},
 134  		Rdata: []string{strconv.Itoa(pri), host},
 135  	}
 136  }
 137  
 138  // NewSRVAnswer creates an Answer for SRV record.
 139  func NewSRVAnswer(priority, weight, port int, target string) *Answer {
 140  	return &Answer{
 141  		Meta: &data.Meta{},
 142  		Rdata: []string{
 143  			strconv.Itoa(priority),
 144  			strconv.Itoa(weight),
 145  			strconv.Itoa(port),
 146  			target,
 147  		},
 148  	}
 149  }
 150  
 151  // NewDSAnswer creates an Answer for DS record.
 152  func NewDSAnswer(key string, algorithm string, t string, digest string) *Answer {
 153  	return &Answer{
 154  		Meta: &data.Meta{},
 155  		Rdata: []string{
 156  			key,
 157  			algorithm,
 158  			t,
 159  			digest,
 160  		},
 161  	}
 162  }
 163  
 164  // NewCAAAnswer creates an Answer for a CAA record.
 165  func NewCAAAnswer(flag int, tag, value string) *Answer {
 166  	return &Answer{
 167  		Meta:  &data.Meta{},
 168  		Rdata: []string{strconv.Itoa(flag), tag, value},
 169  	}
 170  }
 171  
 172  // NewURLFWDAnswer creates an Answer for URLFWD record.
 173  func NewURLFWDAnswer(from, to string, redirectType, pathForwardingMode, queryForwarding int) *Answer {
 174  	return &Answer{
 175  		Meta: &data.Meta{},
 176  		Rdata: []string{
 177  			from,
 178  			to,
 179  			strconv.Itoa(redirectType),
 180  			strconv.Itoa(pathForwardingMode),
 181  			strconv.Itoa(queryForwarding),
 182  		},
 183  	}
 184  }
 185  
 186  // return Answer with Rdata as list of interface, with elements of the correct
 187  // type for API.
 188  func prepareURLFWDAnswer(a *Answer) (interface{}, error) {
 189  	if len(a.Rdata) < 5 {
 190  		return nil, errors.New("invalid number of arguments for Rdata")
 191  	}
 192  
 193  	redirectType, err := strconv.Atoi(a.Rdata[2])
 194  	if err != nil {
 195  		return nil, err
 196  	}
 197  	pathForwardingMode, err := strconv.Atoi(a.Rdata[3])
 198  	if err != nil {
 199  		return nil, err
 200  	}
 201  	queryForwarding, err := strconv.Atoi(a.Rdata[4])
 202  	if err != nil {
 203  		return nil, err
 204  	}
 205  
 206  	prepared := &Alias{
 207  		Rdata: []interface{}{
 208  			a.Rdata[0],
 209  			a.Rdata[1],
 210  			redirectType,
 211  			pathForwardingMode,
 212  			queryForwarding,
 213  		},
 214  		AliasAnswer: (*AliasAnswer)(a),
 215  	}
 216  
 217  	return prepared, nil
 218  }
 219