github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/dns/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 fullIPv6(ip net.IP) string { 15 16 dst := make([]byte, hex.EncodedLen(len(ip))) 17 _ = hex.Encode(dst, ip) 18 return string(dst[0:4]) + ":" + 19 string(dst[4:8]) + ":" + 20 string(dst[8:12]) + ":" + 21 string(dst[12:16]) + ":" + 22 string(dst[16:20]) + ":" + 23 string(dst[20:24]) + ":" + 24 string(dst[24:28]) + ":" + 25 string(dst[28:]) 26 } 27 28 func padValue(str string) string { 29 newStr := strings.Replace(str, "m", "", -1) 30 float, err := strconv.ParseFloat(newStr, 32) 31 if err != nil { 32 return "FAIL" 33 } 34 35 return fmt.Sprintf("%.2f", float) 36 } 37 38 func padCoordinates(str string) string { 39 s := strings.Split(str, " ") 40 if len(s) < 12 { 41 return "" 42 } 43 44 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] 45 46 return latd + " " + latm + " " + lats + " " + latDir + " " + longd + " " + longm + " " + longs + " " + longDir + " " + padValue(altitude) + "m " + padValue(size) + "m " + padValue(horizPrecision) + "m " + padValue(vertPrecision) + "m" 47 } 48 49 func (d *dns) GetRecord(ctx context.Context, zone, name, recordType string) (*RecordBody, error) { 50 logger := d.Log(ctx) 51 logger.Debug("GetRecord") 52 53 getURL := fmt.Sprintf("/config-dns/v2/zones/%s/names/%s/types/%s", zone, name, recordType) 54 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 55 if err != nil { 56 return nil, fmt.Errorf("failed to create GetRecord request: %w", err) 57 } 58 59 var result RecordBody 60 resp, err := d.Exec(req, &result) 61 if err != nil { 62 return nil, fmt.Errorf("GetRecord request failed: %w", err) 63 } 64 65 if resp.StatusCode != http.StatusOK { 66 return nil, d.Error(resp) 67 } 68 69 return &result, nil 70 } 71 72 func (d *dns) GetRecordList(ctx context.Context, zone, _, recordType string) (*RecordSetResponse, error) { 73 logger := d.Log(ctx) 74 logger.Debug("GetRecordList") 75 76 getURL := fmt.Sprintf("/config-dns/v2/zones/%s/recordsets?types=%s&showAll=true", zone, recordType) 77 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 78 if err != nil { 79 return nil, fmt.Errorf("failed to create GetRecordList request: %w", err) 80 } 81 82 var result RecordSetResponse 83 resp, err := d.Exec(req, &result) 84 if err != nil { 85 return nil, fmt.Errorf("GetRecordList request failed: %w", err) 86 } 87 88 if resp.StatusCode != http.StatusOK { 89 return nil, d.Error(resp) 90 } 91 92 return &result, nil 93 } 94 95 func (d *dns) GetRdata(ctx context.Context, zone, name, recordType string) ([]string, error) { 96 logger := d.Log(ctx) 97 logger.Debug("GetrData") 98 99 records, err := d.GetRecordList(ctx, zone, name, recordType) 100 if err != nil { 101 return nil, err 102 } 103 104 var rData []string 105 for _, r := range records.RecordSets { 106 if r.Name == name { 107 for _, i := range r.Rdata { 108 str := i 109 110 if recordType == "AAAA" { 111 addr := net.ParseIP(str) 112 result := fullIPv6(addr) 113 str = result 114 } else if recordType == "LOC" { 115 str = padCoordinates(str) 116 } 117 rData = append(rData, str) 118 } 119 } 120 } 121 return rData, nil 122 } 123 124 func (d *dns) ProcessRdata(ctx context.Context, rData []string, rType string) []string { 125 logger := d.Log(ctx) 126 logger.Debug("ProcessrData") 127 128 var newRData []string 129 for _, i := range rData { 130 str := i 131 if rType == "AAAA" { 132 addr := net.ParseIP(str) 133 result := fullIPv6(addr) 134 str = result 135 } else if rType == "LOC" { 136 str = padCoordinates(str) 137 } 138 newRData = append(newRData, str) 139 } 140 141 return newRData 142 } 143 144 func (d *dns) ParseRData(ctx context.Context, rType string, rData []string) map[string]interface{} { 145 logger := d.Log(ctx) 146 logger.Debug("ParserData") 147 148 fieldMap := make(map[string]interface{}, 0) 149 if len(rData) == 0 { 150 return fieldMap 151 } 152 newRData := make([]string, 0, len(rData)) 153 fieldMap["target"] = newRData 154 155 switch rType { 156 case "AFSDB": 157 resolveAFSDBType(rData, newRData, fieldMap) 158 159 case "DNSKEY": 160 resolveDNSKEYType(rData, fieldMap) 161 162 case "DS": 163 resolveDSType(rData, fieldMap) 164 165 case "HINFO": 166 resolveHINFOType(rData, fieldMap) 167 /* 168 // too many variations to calculate pri and increment 169 case "MX": 170 sort.Strings(rData) 171 parts := strings.Split(rData[0], " ") 172 fieldMap["priority"], _ = strconv.Atoi(parts[0]) 173 if len(rData) > 1 { 174 parts = strings.Split(rData[1], " ") 175 tpri, _ := strconv.Atoi(parts[0]) 176 fieldMap["priority_increment"] = tpri - fieldMap["priority"].(int) 177 } 178 for _, rContent := range rData { 179 parts := strings.Split(rContent, " ") 180 newrData = append(newrData, parts[1]) 181 } 182 fieldMap["target"] = newrData 183 */ 184 185 case "NAPTR": 186 resolveNAPTRType(rData, fieldMap) 187 188 case "NSEC3": 189 resolveNSEC3Type(rData, fieldMap) 190 191 case "NSEC3PARAM": 192 resolveNSEC3PARAMType(rData, fieldMap) 193 194 case "RP": 195 resolveRPType(rData, fieldMap) 196 197 case "RRSIG": 198 resolveRRSIGType(rData, fieldMap) 199 200 case "SRV": 201 resolveSRVType(rData, newRData, fieldMap) 202 203 case "SSHFP": 204 resolveSSHFPType(rData, fieldMap) 205 206 case "SOA": 207 resolveSOAType(rData, fieldMap) 208 209 case "AKAMAITLC": 210 resolveAKAMAITLCType(rData, fieldMap) 211 212 case "SPF": 213 resolveSPFType(rData, newRData, fieldMap) 214 215 case "TXT": 216 resolveTXTType(rData, newRData, fieldMap) 217 218 case "AAAA": 219 resolveAAAAType(rData, newRData, fieldMap) 220 221 case "LOC": 222 resolveLOCType(rData, newRData, fieldMap) 223 224 case "CERT": 225 resolveCERTType(rData, fieldMap) 226 227 case "TLSA": 228 resolveTLSAType(rData, fieldMap) 229 230 case "SVCB": 231 resolveSVCBType(rData, fieldMap) 232 233 case "HTTPS": 234 resolveHTTPSType(rData, fieldMap) 235 236 default: 237 for _, rContent := range rData { 238 newRData = append(newRData, rContent) 239 } 240 fieldMap["target"] = newRData 241 } 242 243 return fieldMap 244 } 245 246 func resolveAFSDBType(rData, newRData []string, fieldMap map[string]interface{}) { 247 parts := strings.Split(rData[0], " ") 248 fieldMap["subtype"], _ = strconv.Atoi(parts[0]) 249 for _, rContent := range rData { 250 parts = strings.Split(rContent, " ") 251 newRData = append(newRData, parts[1]) 252 } 253 fieldMap["target"] = newRData 254 } 255 256 func resolveDNSKEYType(rData []string, fieldMap map[string]interface{}) { 257 for _, rContent := range rData { 258 parts := strings.Split(rContent, " ") 259 fieldMap["flags"], _ = strconv.Atoi(parts[0]) 260 fieldMap["protocol"], _ = strconv.Atoi(parts[1]) 261 fieldMap["algorithm"], _ = strconv.Atoi(parts[2]) 262 key := parts[3] 263 // key can have whitespace 264 if len(parts) > 4 { 265 i := 4 266 for i < len(parts) { 267 key += " " + parts[i] 268 } 269 } 270 fieldMap["key"] = key 271 break 272 } 273 } 274 275 func resolveSVCBType(rData []string, fieldMap map[string]interface{}) { 276 for _, rContent := range rData { 277 parts := strings.SplitN(rContent, " ", 3) 278 // has to be at least two fields. 279 if len(parts) < 2 { 280 break 281 } 282 fieldMap["svc_priority"], _ = strconv.Atoi(parts[0]) 283 fieldMap["target_name"] = parts[1] 284 if len(parts) > 2 { 285 fieldMap["svc_params"] = parts[2] 286 } 287 break 288 } 289 } 290 291 func resolveDSType(rData []string, fieldMap map[string]interface{}) { 292 for _, rContent := range rData { 293 parts := strings.Split(rContent, " ") 294 fieldMap["keytag"], _ = strconv.Atoi(parts[0]) 295 fieldMap["digest_type"], _ = strconv.Atoi(parts[2]) 296 fieldMap["algorithm"], _ = strconv.Atoi(parts[1]) 297 dig := parts[3] 298 // digest can have whitespace 299 if len(parts) > 4 { 300 i := 4 301 for i < len(parts) { 302 dig += " " + parts[i] 303 } 304 } 305 fieldMap["digest"] = dig 306 break 307 } 308 } 309 310 func resolveHINFOType(rData []string, fieldMap map[string]interface{}) { 311 for _, rContent := range rData { 312 parts := strings.Split(rContent, " ") 313 fieldMap["hardware"] = parts[0] 314 fieldMap["software"] = parts[1] 315 break 316 } 317 } 318 319 func resolveNAPTRType(rData []string, fieldMap map[string]interface{}) { 320 for _, rContent := range rData { 321 parts := strings.Split(rContent, " ") 322 fieldMap["order"], _ = strconv.Atoi(parts[0]) 323 fieldMap["preference"], _ = strconv.Atoi(parts[1]) 324 fieldMap["flagsnaptr"] = parts[2] 325 fieldMap["service"] = parts[3] 326 fieldMap["regexp"] = parts[4] 327 fieldMap["replacement"] = parts[5] 328 break 329 } 330 } 331 332 func resolveNSEC3Type(rData []string, fieldMap map[string]interface{}) { 333 for _, rContent := range rData { 334 parts := strings.Split(rContent, " ") 335 fieldMap["flags"], _ = strconv.Atoi(parts[1]) 336 fieldMap["algorithm"], _ = strconv.Atoi(parts[0]) 337 fieldMap["iterations"], _ = strconv.Atoi(parts[2]) 338 fieldMap["salt"] = parts[3] 339 fieldMap["next_hashed_owner_name"] = parts[4] 340 fieldMap["type_bitmaps"] = parts[5] 341 break 342 } 343 } 344 345 func resolveNSEC3PARAMType(rData []string, fieldMap map[string]interface{}) { 346 for _, rContent := range rData { 347 parts := strings.Split(rContent, " ") 348 fieldMap["flags"], _ = strconv.Atoi(parts[1]) 349 fieldMap["algorithm"], _ = strconv.Atoi(parts[0]) 350 fieldMap["iterations"], _ = strconv.Atoi(parts[2]) 351 fieldMap["salt"] = parts[3] 352 break 353 } 354 } 355 356 func resolveRPType(rData []string, fieldMap map[string]interface{}) { 357 for _, rContent := range rData { 358 parts := strings.Split(rContent, " ") 359 fieldMap["mailbox"] = parts[0] 360 fieldMap["txt"] = parts[1] 361 break 362 } 363 } 364 365 func resolveRRSIGType(rData []string, fieldMap map[string]interface{}) { 366 for _, rContent := range rData { 367 parts := strings.Split(rContent, " ") 368 fieldMap["type_covered"] = parts[0] 369 fieldMap["algorithm"], _ = strconv.Atoi(parts[1]) 370 fieldMap["labels"], _ = strconv.Atoi(parts[2]) 371 fieldMap["original_ttl"], _ = strconv.Atoi(parts[3]) 372 fieldMap["expiration"] = parts[4] 373 fieldMap["inception"] = parts[5] 374 fieldMap["signer"] = parts[7] 375 fieldMap["keytag"], _ = strconv.Atoi(parts[6]) 376 sig := parts[8] 377 // sig can have whitespace 378 if len(parts) > 9 { 379 i := 9 380 for i < len(parts) { 381 sig += " " + parts[i] 382 } 383 } 384 fieldMap["signature"] = sig 385 break 386 } 387 } 388 389 func resolveSRVType(rData, newRData []string, fieldMap map[string]interface{}) { 390 // if all targets have the same priority, weight and port, process it in the old way 391 priorityMap := make(map[int]struct{}) 392 weightMap := make(map[int]struct{}) 393 portMap := make(map[int]struct{}) 394 for _, rContent := range rData { 395 parts := strings.Split(rContent, " ") 396 priority, _ := strconv.Atoi(parts[0]) 397 weight, _ := strconv.Atoi(parts[1]) 398 port, _ := strconv.Atoi(parts[2]) 399 priorityMap[priority] = struct{}{} 400 weightMap[weight] = struct{}{} 401 portMap[port] = struct{}{} 402 } 403 // all values are the same, so process in the old way 404 if len(priorityMap) == 1 && len(weightMap) == 1 && len(portMap) == 1 { 405 // pull out some fields 406 parts := strings.Split(rData[0], " ") 407 fieldMap["priority"], _ = strconv.Atoi(parts[0]) 408 fieldMap["weight"], _ = strconv.Atoi(parts[1]) 409 fieldMap["port"], _ = strconv.Atoi(parts[2]) 410 // populate target 411 for _, rContent := range rData { 412 parts = strings.Split(rContent, " ") 413 newRData = append(newRData, parts[3]) 414 } 415 } else { 416 delete(fieldMap, "priority") 417 delete(fieldMap, "weight") 418 delete(fieldMap, "port") 419 // populate target 420 for _, rContent := range rData { 421 newRData = append(newRData, rContent) 422 } 423 } 424 fieldMap["target"] = newRData 425 } 426 427 func resolveSSHFPType(rData []string, fieldMap map[string]interface{}) { 428 for _, rContent := range rData { 429 parts := strings.Split(rContent, " ") 430 fieldMap["algorithm"], _ = strconv.Atoi(parts[0]) 431 fieldMap["fingerprint_type"], _ = strconv.Atoi(parts[1]) 432 fieldMap["fingerprint"] = parts[2] 433 break 434 } 435 } 436 437 func resolveSOAType(rData []string, fieldMap map[string]interface{}) { 438 for _, rContent := range rData { 439 parts := strings.Split(rContent, " ") 440 fieldMap["name_server"] = parts[0] 441 fieldMap["email_address"] = parts[1] 442 fieldMap["serial"], _ = strconv.Atoi(parts[2]) 443 fieldMap["refresh"], _ = strconv.Atoi(parts[3]) 444 fieldMap["retry"], _ = strconv.Atoi(parts[4]) 445 fieldMap["expiry"], _ = strconv.Atoi(parts[5]) 446 fieldMap["nxdomain_ttl"], _ = strconv.Atoi(parts[6]) 447 break 448 } 449 } 450 451 func resolveAKAMAITLCType(rData []string, fieldMap map[string]interface{}) { 452 parts := strings.Split(rData[0], " ") 453 fieldMap["answer_type"] = parts[0] 454 fieldMap["dns_name"] = parts[1] 455 } 456 457 func resolveSPFType(rData, newRData []string, fieldMap map[string]interface{}) { 458 for _, rContent := range rData { 459 newRData = append(newRData, rContent) 460 } 461 fieldMap["target"] = newRData 462 } 463 464 func resolveTXTType(rData, newRData []string, fieldMap map[string]interface{}) { 465 for _, rContent := range rData { 466 newRData = append(newRData, rContent) 467 } 468 fieldMap["target"] = newRData 469 } 470 471 func resolveAAAAType(rData, newRData []string, fieldMap map[string]interface{}) { 472 for _, i := range rData { 473 str := i 474 addr := net.ParseIP(str) 475 result := fullIPv6(addr) 476 str = result 477 newRData = append(newRData, str) 478 } 479 fieldMap["target"] = newRData 480 } 481 482 func resolveLOCType(rData, newRData []string, fieldMap map[string]interface{}) { 483 for _, i := range rData { 484 str := i 485 str = padCoordinates(str) 486 newRData = append(newRData, str) 487 } 488 fieldMap["target"] = newRData 489 } 490 491 func resolveCERTType(rData []string, fieldMap map[string]interface{}) { 492 for _, rContent := range rData { 493 parts := strings.Split(rContent, " ") 494 val, err := strconv.Atoi(parts[0]) 495 if err == nil { 496 fieldMap["type_value"] = val 497 } else { 498 fieldMap["type_mnemonic"] = parts[0] 499 } 500 fieldMap["keytag"], _ = strconv.Atoi(parts[1]) 501 fieldMap["algorithm"], _ = strconv.Atoi(parts[2]) 502 fieldMap["certificate"] = parts[3] 503 break 504 } 505 } 506 507 func resolveTLSAType(rData []string, fieldMap map[string]interface{}) { 508 for _, rContent := range rData { 509 parts := strings.Split(rContent, " ") 510 fieldMap["usage"], _ = strconv.Atoi(parts[0]) 511 fieldMap["selector"], _ = strconv.Atoi(parts[1]) 512 fieldMap["match_type"], _ = strconv.Atoi(parts[2]) 513 fieldMap["certificate"] = parts[3] 514 break 515 } 516 } 517 518 func resolveHTTPSType(rData []string, fieldMap map[string]interface{}) { 519 for _, rContent := range rData { 520 parts := strings.SplitN(rContent, " ", 3) 521 // has to be at least two fields. 522 if len(parts) < 2 { 523 break 524 } 525 fieldMap["svc_priority"], _ = strconv.Atoi(parts[0]) 526 fieldMap["target_name"] = parts[1] 527 if len(parts) > 2 { 528 fieldMap["svc_params"] = parts[2] 529 } 530 break 531 } 532 }