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