github.com/akamai/AkamaiOPEN-edgegrid-golang/v2@v2.17.0/pkg/configdns/record_lookup.go (about) 1 package dns 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 8 "encoding/hex" 9 "net" 10 "strconv" 11 "strings" 12 ) 13 14 func (p *dns) FullIPv6(ctx context.Context, ip net.IP) string { 15 16 logger := p.Log(ctx) 17 logger.Debug("FullIPv6") 18 19 dst := make([]byte, hex.EncodedLen(len(ip))) 20 _ = hex.Encode(dst, ip) 21 return string(dst[0:4]) + ":" + 22 string(dst[4:8]) + ":" + 23 string(dst[8:12]) + ":" + 24 string(dst[12:16]) + ":" + 25 string(dst[16:20]) + ":" + 26 string(dst[20:24]) + ":" + 27 string(dst[24:28]) + ":" + 28 string(dst[28:]) 29 } 30 31 func padvalue(str string) string { 32 vstr := strings.Replace(str, "m", "", -1) 33 vfloat, err := strconv.ParseFloat(vstr, 32) 34 if err != nil { 35 return "FAIL" 36 } 37 38 return fmt.Sprintf("%.2f", vfloat) 39 } 40 41 // Used to pad coordinates to x.xxm format 42 func (p *dns) PadCoordinates(ctx context.Context, str string) string { 43 44 logger := p.Log(ctx) 45 logger.Debug("PadCoordinates") 46 47 s := strings.Split(str, " ") 48 if len(s) < 12 { 49 return "" 50 } 51 52 latd, latm, lats, latDir, longd, longm, longs, longDir, altitude, size, horizPrecision, vertPrecision := s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11] 53 54 return latd + " " + latm + " " + lats + " " + latDir + " " + longd + " " + longm + " " + longs + " " + longDir + " " + padvalue(altitude) + "m " + padvalue(size) + "m " + padvalue(horizPrecision) + "m " + padvalue(vertPrecision) + "m" 55 56 } 57 58 // Get single Recordset. Following convention for other single record CRUD operations, return a RecordBody. 59 func (p *dns) GetRecord(ctx context.Context, zone string, name string, recordType string) (*RecordBody, error) { 60 61 logger := p.Log(ctx) 62 logger.Debug("GetRecord") 63 64 var rec RecordBody 65 getURL := fmt.Sprintf("/config-dns/v2/zones/%s/names/%s/types/%s", zone, name, recordType) 66 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 67 if err != nil { 68 return nil, fmt.Errorf("failed to create GetRecord request: %w", err) 69 } 70 71 resp, err := p.Exec(req, &rec) 72 if err != nil { 73 return nil, fmt.Errorf("GetRecord request failed: %w", err) 74 } 75 76 if resp.StatusCode != http.StatusOK { 77 return nil, p.Error(resp) 78 } 79 80 return &rec, nil 81 } 82 83 func (p *dns) GetRecordList(ctx context.Context, zone string, _ string, recordType string) (*RecordSetResponse, error) { 84 85 logger := p.Log(ctx) 86 logger.Debug("GetRecordList") 87 88 var records RecordSetResponse 89 getURL := fmt.Sprintf("/config-dns/v2/zones/%s/recordsets?types=%s&showAll=true", zone, recordType) 90 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 91 if err != nil { 92 return nil, fmt.Errorf("failed to create GetRecordList request: %w", err) 93 } 94 95 resp, err := p.Exec(req, &records) 96 if err != nil { 97 return nil, fmt.Errorf("GetRecordList request failed: %w", err) 98 } 99 100 if resp.StatusCode != http.StatusOK { 101 return nil, p.Error(resp) 102 } 103 104 return &records, nil 105 } 106 107 func (p *dns) GetRdata(ctx context.Context, zone string, name string, recordType string) ([]string, error) { 108 109 logger := p.Log(ctx) 110 logger.Debug("GetRdata") 111 112 records, err := p.GetRecordList(ctx, zone, name, recordType) 113 if err != nil { 114 return nil, err 115 } 116 117 var arrLength int 118 for _, c := range records.Recordsets { 119 if c.Name == name { 120 arrLength = len(c.Rdata) 121 } 122 } 123 124 rdata := make([]string, 0, arrLength) 125 126 for _, r := range records.Recordsets { 127 if r.Name == name { 128 for _, i := range r.Rdata { 129 str := i 130 131 if recordType == "AAAA" { 132 addr := net.ParseIP(str) 133 result := p.FullIPv6(ctx, addr) 134 str = result 135 } else if recordType == "LOC" { 136 str = p.PadCoordinates(ctx, str) 137 } 138 rdata = append(rdata, str) 139 } 140 } 141 } 142 return rdata, nil 143 } 144 145 func (p *dns) ProcessRdata(ctx context.Context, rdata []string, rtype string) []string { 146 147 logger := p.Log(ctx) 148 logger.Debug("ProcessRdata") 149 150 newrdata := make([]string, 0, len(rdata)) 151 for _, i := range rdata { 152 str := i 153 if rtype == "AAAA" { 154 addr := net.ParseIP(str) 155 result := p.FullIPv6(ctx, addr) 156 str = result 157 } else if rtype == "LOC" { 158 str = p.PadCoordinates(ctx, str) 159 } 160 newrdata = append(newrdata, str) 161 } 162 return newrdata 163 164 } 165 166 // Utility method to parse RData in context of type. Return map of fields and values 167 func (p *dns) ParseRData(ctx context.Context, rtype string, rdata []string) map[string]interface{} { 168 169 logger := p.Log(ctx) 170 logger.Debug("ParseRData") 171 172 fieldMap := make(map[string]interface{}, 0) 173 if len(rdata) == 0 { 174 return fieldMap 175 } 176 newrdata := make([]string, 0, len(rdata)) 177 fieldMap["target"] = newrdata 178 179 switch rtype { 180 case "AFSDB": 181 parts := strings.Split(rdata[0], " ") 182 fieldMap["subtype"], _ = strconv.Atoi(parts[0]) 183 for _, rcontent := range rdata { 184 parts := strings.Split(rcontent, " ") 185 newrdata = append(newrdata, parts[1]) 186 } 187 fieldMap["target"] = newrdata 188 189 case "DNSKEY": 190 for _, rcontent := range rdata { 191 parts := strings.Split(rcontent, " ") 192 fieldMap["flags"], _ = strconv.Atoi(parts[0]) 193 fieldMap["protocol"], _ = strconv.Atoi(parts[1]) 194 fieldMap["algorithm"], _ = strconv.Atoi(parts[2]) 195 key := parts[3] 196 // key can have whitespace 197 if len(parts) > 4 { 198 i := 4 199 for i < len(parts) { 200 key += " " + parts[i] 201 } 202 } 203 fieldMap["key"] = key 204 break 205 } 206 207 case "DS": 208 for _, rcontent := range rdata { 209 parts := strings.Split(rcontent, " ") 210 fieldMap["keytag"], _ = strconv.Atoi(parts[0]) 211 fieldMap["digest_type"], _ = strconv.Atoi(parts[2]) 212 fieldMap["algorithm"], _ = strconv.Atoi(parts[1]) 213 dig := parts[3] 214 // digest can have whitespace 215 if len(parts) > 4 { 216 i := 4 217 for i < len(parts) { 218 dig += " " + parts[i] 219 } 220 } 221 fieldMap["digest"] = dig 222 break 223 } 224 225 case "HINFO": 226 for _, rcontent := range rdata { 227 parts := strings.Split(rcontent, " ") 228 fieldMap["hardware"] = parts[0] 229 fieldMap["software"] = parts[1] 230 break 231 } 232 /* 233 // too many variations to calculate pri and increment 234 case "MX": 235 sort.Strings(rdata) 236 parts := strings.Split(rdata[0], " ") 237 fieldMap["priority"], _ = strconv.Atoi(parts[0]) 238 if len(rdata) > 1 { 239 parts = strings.Split(rdata[1], " ") 240 tpri, _ := strconv.Atoi(parts[0]) 241 fieldMap["priority_increment"] = tpri - fieldMap["priority"].(int) 242 } 243 for _, rcontent := range rdata { 244 parts := strings.Split(rcontent, " ") 245 newrdata = append(newrdata, parts[1]) 246 } 247 fieldMap["target"] = newrdata 248 */ 249 250 case "NAPTR": 251 for _, rcontent := range rdata { 252 parts := strings.Split(rcontent, " ") 253 fieldMap["order"], _ = strconv.Atoi(parts[0]) 254 fieldMap["preference"], _ = strconv.Atoi(parts[1]) 255 fieldMap["flagsnaptr"] = parts[2] 256 fieldMap["service"] = parts[3] 257 fieldMap["regexp"] = parts[4] 258 fieldMap["replacement"] = parts[5] 259 break 260 } 261 262 case "NSEC3": 263 for _, rcontent := range rdata { 264 parts := strings.Split(rcontent, " ") 265 fieldMap["flags"], _ = strconv.Atoi(parts[1]) 266 fieldMap["algorithm"], _ = strconv.Atoi(parts[0]) 267 fieldMap["iterations"], _ = strconv.Atoi(parts[2]) 268 fieldMap["salt"] = parts[3] 269 fieldMap["next_hashed_owner_name"] = parts[4] 270 fieldMap["type_bitmaps"] = parts[5] 271 break 272 } 273 274 case "NSEC3PARAM": 275 for _, rcontent := range rdata { 276 parts := strings.Split(rcontent, " ") 277 fieldMap["flags"], _ = strconv.Atoi(parts[1]) 278 fieldMap["algorithm"], _ = strconv.Atoi(parts[0]) 279 fieldMap["iterations"], _ = strconv.Atoi(parts[2]) 280 fieldMap["salt"] = parts[3] 281 break 282 } 283 284 case "RP": 285 for _, rcontent := range rdata { 286 parts := strings.Split(rcontent, " ") 287 fieldMap["mailbox"] = parts[0] 288 fieldMap["txt"] = parts[1] 289 break 290 } 291 292 case "RRSIG": 293 for _, rcontent := range rdata { 294 parts := strings.Split(rcontent, " ") 295 fieldMap["type_covered"] = parts[0] 296 fieldMap["algorithm"], _ = strconv.Atoi(parts[1]) 297 fieldMap["labels"], _ = strconv.Atoi(parts[2]) 298 fieldMap["original_ttl"], _ = strconv.Atoi(parts[3]) 299 fieldMap["expiration"] = parts[4] 300 fieldMap["inception"] = parts[5] 301 fieldMap["signer"] = parts[7] 302 fieldMap["keytag"], _ = strconv.Atoi(parts[6]) 303 sig := parts[8] 304 // sig can have whitespace 305 if len(parts) > 9 { 306 i := 9 307 for i < len(parts) { 308 sig += " " + parts[i] 309 } 310 } 311 fieldMap["signature"] = sig 312 break 313 } 314 315 case "SRV": 316 // pull out some fields 317 parts := strings.Split(rdata[0], " ") 318 fieldMap["priority"], _ = strconv.Atoi(parts[0]) 319 fieldMap["weight"], _ = strconv.Atoi(parts[1]) 320 fieldMap["port"], _ = strconv.Atoi(parts[2]) 321 // populate target 322 for _, rcontent := range rdata { 323 parts := strings.Split(rcontent, " ") 324 newrdata = append(newrdata, parts[3]) 325 } 326 fieldMap["target"] = newrdata 327 328 case "SSHFP": 329 for _, rcontent := range rdata { 330 parts := strings.Split(rcontent, " ") 331 fieldMap["algorithm"], _ = strconv.Atoi(parts[0]) 332 fieldMap["fingerprint_type"], _ = strconv.Atoi(parts[1]) 333 fieldMap["fingerprint"] = parts[2] 334 break 335 } 336 337 case "SOA": 338 for _, rcontent := range rdata { 339 parts := strings.Split(rcontent, " ") 340 fieldMap["name_server"] = parts[0] 341 fieldMap["email_address"] = parts[1] 342 fieldMap["serial"], _ = strconv.Atoi(parts[2]) 343 fieldMap["refresh"], _ = strconv.Atoi(parts[3]) 344 fieldMap["retry"], _ = strconv.Atoi(parts[4]) 345 fieldMap["expiry"], _ = strconv.Atoi(parts[5]) 346 fieldMap["nxdomain_ttl"], _ = strconv.Atoi(parts[6]) 347 break 348 } 349 350 case "AKAMAITLC": 351 parts := strings.Split(rdata[0], " ") 352 fieldMap["answer_type"] = parts[0] 353 fieldMap["dns_name"] = parts[1] 354 355 case "SPF": 356 for _, rcontent := range rdata { 357 newrdata = append(newrdata, rcontent) 358 } 359 fieldMap["target"] = newrdata 360 361 case "TXT": 362 for _, rcontent := range rdata { 363 newrdata = append(newrdata, rcontent) 364 } 365 fieldMap["target"] = newrdata 366 367 case "AAAA": 368 for _, i := range rdata { 369 str := i 370 addr := net.ParseIP(str) 371 result := p.FullIPv6(ctx, addr) 372 str = result 373 newrdata = append(newrdata, str) 374 } 375 fieldMap["target"] = newrdata 376 377 case "LOC": 378 for _, i := range rdata { 379 str := i 380 str = p.PadCoordinates(ctx, str) 381 newrdata = append(newrdata, str) 382 } 383 fieldMap["target"] = newrdata 384 385 case "CERT": 386 for _, rcontent := range rdata { 387 parts := strings.Split(rcontent, " ") 388 val, err := strconv.Atoi(parts[0]) 389 if err == nil { 390 fieldMap["type_value"] = val 391 } else { 392 fieldMap["type_mnemonic"] = parts[0] 393 } 394 fieldMap["keytag"], _ = strconv.Atoi(parts[1]) 395 fieldMap["algorithm"], _ = strconv.Atoi(parts[2]) 396 fieldMap["certificate"] = parts[3] 397 break 398 } 399 400 case "TLSA": 401 for _, rcontent := range rdata { 402 parts := strings.Split(rcontent, " ") 403 fieldMap["usage"], _ = strconv.Atoi(parts[0]) 404 fieldMap["selector"], _ = strconv.Atoi(parts[1]) 405 fieldMap["match_type"], _ = strconv.Atoi(parts[2]) 406 fieldMap["certificate"] = parts[3] 407 break 408 } 409 410 case "SVCB": 411 for _, rcontent := range rdata { 412 parts := strings.SplitN(rcontent, " ", 3) 413 // has to be at least two fields. 414 if len(parts) < 2 { 415 break 416 } 417 fieldMap["svc_priority"], _ = strconv.Atoi(parts[0]) 418 fieldMap["target_name"] = parts[1] 419 if len(parts) > 2 { 420 fieldMap["svc_params"] = parts[2] 421 } 422 break 423 } 424 425 case "HTTPS": 426 for _, rcontent := range rdata { 427 parts := strings.SplitN(rcontent, " ", 3) 428 // has to be at least two fields. 429 if len(parts) < 2 { 430 break 431 } 432 fieldMap["svc_priority"], _ = strconv.Atoi(parts[0]) 433 fieldMap["target_name"] = parts[1] 434 if len(parts) > 2 { 435 fieldMap["svc_params"] = parts[2] 436 } 437 break 438 } 439 440 default: 441 for _, rcontent := range rdata { 442 newrdata = append(newrdata, rcontent) 443 } 444 fieldMap["target"] = newrdata 445 } 446 447 return fieldMap 448 }