go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/network/resources/dnsshake/dnsshake.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package dnsshake 5 6 import ( 7 "net" 8 "os" 9 "strconv" 10 "strings" 11 "sync" 12 13 "github.com/cockroachdb/errors" 14 "github.com/miekg/dns" 15 "go.mondoo.com/cnquery/utils/multierr" 16 ) 17 18 type DnsClient struct { 19 config *dns.ClientConfig 20 fqdn string 21 sync sync.Mutex 22 } 23 24 type DnsRecord struct { 25 // DNS name 26 Name string `json:"name"` 27 // Time-To-Live (TTL) in seconds 28 TTL int64 `json:"ttl"` 29 // DNS class 30 Class string `json:"class"` 31 // DNS type 32 Type string `json:"type"` 33 // Resource Data 34 RData []string `json:"rData"` 35 // DNS Response Code 36 RCode string `json:"rCode"` 37 // Error during dns request 38 Error error `json:"error"` 39 } 40 41 func New(fqdn string) (*DnsClient, error) { 42 // use Google DNS for now 43 config := &dns.ClientConfig{} 44 config.Servers = []string{"8.8.8.8", "8.8.4.4"} 45 config.Search = []string{} 46 config.Port = "53" 47 config.Ndots = 1 48 config.Timeout = 5 49 config.Attempts = 2 50 51 // try to load unix dns server 52 // TODO: this does not work on windows https://github.com/go-acme/lego/issues/1015 53 resolveFile := "/etc/resolv.conf" 54 _, err := os.Stat(resolveFile) 55 if err == nil { 56 rConfig, err := dns.ClientConfigFromFile(resolveFile) 57 if err == nil { 58 config = rConfig 59 } 60 } 61 62 return &DnsClient{ 63 fqdn: fqdn, 64 config: config, 65 }, nil 66 } 67 68 // stringToType is a map of strings to each RR type. 69 var stringToType = map[string]uint16{ 70 "A": dns.TypeA, 71 "AAAA": dns.TypeAAAA, 72 "AFSDB": dns.TypeAFSDB, 73 "ANY": dns.TypeANY, 74 "APL": dns.TypeAPL, 75 "ATMA": dns.TypeATMA, 76 "AVC": dns.TypeAVC, 77 "AXFR": dns.TypeAXFR, 78 "CAA": dns.TypeCAA, 79 "CDNSKEY": dns.TypeCDNSKEY, 80 "CDS": dns.TypeCDS, 81 "CERT": dns.TypeCERT, 82 "CNAME": dns.TypeCNAME, 83 "CSYNC": dns.TypeCSYNC, 84 "DHCID": dns.TypeDHCID, 85 "DLV": dns.TypeDLV, 86 "DNAME": dns.TypeDNAME, 87 "DNSKEY": dns.TypeDNSKEY, 88 "DS": dns.TypeDS, 89 "EID": dns.TypeEID, 90 "EUI48": dns.TypeEUI48, 91 "EUI64": dns.TypeEUI64, 92 "GID": dns.TypeGID, 93 "GPOS": dns.TypeGPOS, 94 "HINFO": dns.TypeHINFO, 95 "HIP": dns.TypeHIP, 96 "HTTPS": dns.TypeHTTPS, 97 "ISDN": dns.TypeISDN, 98 "IXFR": dns.TypeIXFR, 99 "KEY": dns.TypeKEY, 100 "KX": dns.TypeKX, 101 "L32": dns.TypeL32, 102 "L64": dns.TypeL64, 103 "LOC": dns.TypeLOC, 104 "LP": dns.TypeLP, 105 "MAILA": dns.TypeMAILA, 106 "MAILB": dns.TypeMAILB, 107 "MB": dns.TypeMB, 108 "MD": dns.TypeMD, 109 "MF": dns.TypeMF, 110 "MG": dns.TypeMG, 111 "MINFO": dns.TypeMINFO, 112 "MR": dns.TypeMR, 113 "MX": dns.TypeMX, 114 "NAPTR": dns.TypeNAPTR, 115 "NID": dns.TypeNID, 116 "NIMLOC": dns.TypeNIMLOC, 117 "NINFO": dns.TypeNINFO, 118 "NS": dns.TypeNS, 119 "NSEC": dns.TypeNSEC, 120 "NSEC3": dns.TypeNSEC3, 121 "NSEC3PARAM": dns.TypeNSEC3PARAM, 122 "NULL": dns.TypeNULL, 123 "NXT": dns.TypeNXT, 124 "None": dns.TypeNone, 125 "OPENPGPKEY": dns.TypeOPENPGPKEY, 126 "OPT": dns.TypeOPT, 127 "PTR": dns.TypePTR, 128 "PX": dns.TypePX, 129 "RKEY": dns.TypeRKEY, 130 "RP": dns.TypeRP, 131 "RRSIG": dns.TypeRRSIG, 132 "RT": dns.TypeRT, 133 "Reserved": dns.TypeReserved, 134 "SIG": dns.TypeSIG, 135 "SMIMEA": dns.TypeSMIMEA, 136 "SOA": dns.TypeSOA, 137 "SPF": dns.TypeSPF, 138 "SRV": dns.TypeSRV, 139 "SSHFP": dns.TypeSSHFP, 140 "SVCB": dns.TypeSVCB, 141 "TA": dns.TypeTA, 142 "TALINK": dns.TypeTALINK, 143 "TKEY": dns.TypeTKEY, 144 "TLSA": dns.TypeTLSA, 145 "TSIG": dns.TypeTSIG, 146 "TXT": dns.TypeTXT, 147 "UID": dns.TypeUID, 148 "UINFO": dns.TypeUINFO, 149 "UNSPEC": dns.TypeUNSPEC, 150 "URI": dns.TypeURI, 151 "X25": dns.TypeX25, 152 "ZONEMD": dns.TypeZONEMD, 153 "NSAP-PTR": dns.TypeNSAPPTR, 154 } 155 156 func (d *DnsClient) Query(dnsTypes ...string) (map[string]DnsRecord, error) { 157 if len(dnsTypes) == 0 { 158 for k := range stringToType { 159 dnsTypes = append(dnsTypes, k) 160 } 161 } 162 163 workers := sync.WaitGroup{} 164 var errs multierr.Errors 165 166 res := map[string]DnsRecord{} 167 for i := range dnsTypes { 168 dnsType := dnsTypes[i] 169 170 workers.Add(1) 171 go func() { 172 defer workers.Done() 173 174 records, err := d.queryDnsType(d.fqdn, dnsType) 175 if err != nil { 176 d.sync.Lock() 177 errs.Add(err) 178 d.sync.Unlock() 179 return 180 } 181 182 d.sync.Lock() 183 for k := range records { 184 res[k] = records[k] 185 } 186 d.sync.Unlock() 187 }() 188 } 189 190 workers.Wait() 191 return res, errs.Deduplicate() 192 } 193 194 func (d *DnsClient) queryDnsType(fqdn string, t string) (map[string]DnsRecord, error) { 195 dnsType, ok := stringToType[t] 196 if !ok { 197 return nil, errors.New("unknown dns type") 198 } 199 dnsTypText := dns.Type(dnsType).String() 200 201 res := map[string]DnsRecord{} 202 203 c := &dns.Client{} 204 m := &dns.Msg{} 205 m.SetEdns0(4096, false) 206 m.SetQuestion(dns.Fqdn(fqdn), dnsType) 207 m.RecursionDesired = true 208 209 r, _, err := c.Exchange(m, net.JoinHostPort(d.config.Servers[0], d.config.Port)) 210 if err != nil { 211 res[dnsTypText] = DnsRecord{ 212 Type: dnsTypText, 213 Error: err, 214 } 215 return res, nil 216 } 217 218 if r.Rcode != dns.RcodeSuccess { 219 res[dnsTypText] = DnsRecord{ 220 Type: dnsTypText, 221 RCode: dns.RcodeToString[r.Rcode], 222 } 223 return res, nil 224 } 225 226 // extract more information if dns request was successful 227 for i := range r.Answer { 228 a := r.Answer[i] 229 230 typ := dns.Type(a.Header().Rrtype).String() 231 232 var rec DnsRecord 233 234 rec, ok := res[typ] 235 if !ok { 236 rec = DnsRecord{ 237 Name: a.Header().Name, 238 Type: typ, 239 Class: dns.Class(a.Header().Class).String(), 240 TTL: int64(a.Header().Ttl), 241 RData: []string{}, 242 RCode: dns.RcodeToString[r.Rcode], 243 } 244 } 245 246 switch v := a.(type) { 247 case *dns.A: 248 rec.RData = append(rec.RData, v.A.String()) 249 case *dns.NS: 250 rec.RData = append(rec.RData, v.Ns) 251 case *dns.MD: 252 rec.RData = append(rec.RData, v.Md) 253 case *dns.MF: 254 rec.RData = append(rec.RData, v.Mf) 255 case *dns.CNAME: 256 rec.RData = append(rec.RData, v.Target) 257 case *dns.MB: 258 rec.RData = append(rec.RData, v.Mb) 259 case *dns.MG: 260 rec.RData = append(rec.RData, v.Mg) 261 case *dns.MR: 262 rec.RData = append(rec.RData, v.Mr) 263 case *dns.NULL: 264 rec.RData = append(rec.RData, v.Data) 265 case *dns.PTR: 266 rec.RData = append(rec.RData, v.Ptr) 267 case *dns.TXT: 268 rec.RData = append(rec.RData, strings.Join(v.Txt, "")) 269 case *dns.EID: 270 rec.RData = append(rec.RData, v.Endpoint) 271 case *dns.NIMLOC: 272 rec.RData = append(rec.RData, v.Locator) 273 case *dns.SPF: 274 rec.RData = append(rec.RData, strings.Join(v.Txt, "")) 275 case *dns.UINFO: 276 rec.RData = append(rec.RData, v.Uinfo) 277 case *dns.UID: 278 rec.RData = append(rec.RData, strconv.FormatInt(int64(v.Uid), 10)) 279 case *dns.GID: 280 rec.RData = append(rec.RData, strconv.FormatInt(int64(v.Gid), 10)) 281 case *dns.EUI48: 282 strconv.FormatInt(int64(v.Address), 10) 283 case *dns.EUI64: 284 strconv.FormatInt(int64(v.Address), 10) 285 case *dns.AVC: 286 rec.RData = append(rec.RData, strings.Join(v.Txt, "")) 287 default: 288 rec.RData = append(rec.RData, strings.TrimPrefix(v.String(), v.Header().String())) 289 } 290 291 res[typ] = rec 292 } 293 return res, nil 294 }