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