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  }