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  }