go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/network/resources/dns.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package resources 5 6 import ( 7 "crypto/sha256" 8 "encoding/hex" 9 "errors" 10 "strconv" 11 "strings" 12 13 "github.com/miekg/dns" 14 "go.mondoo.com/cnquery/llx" 15 "go.mondoo.com/cnquery/providers-sdk/v1/plugin" 16 "go.mondoo.com/cnquery/providers-sdk/v1/util/convert" 17 "go.mondoo.com/cnquery/providers/network/connection" 18 "go.mondoo.com/cnquery/providers/network/resources/dnsshake" 19 "go.mondoo.com/cnquery/providers/network/resources/domain" 20 "go.mondoo.com/cnquery/types" 21 "go.mondoo.com/cnquery/utils/sortx" 22 ) 23 24 func (d *mqlDomainName) id() (string, error) { 25 return "domainName/" + d.Fqdn.Data, nil 26 } 27 28 func initDomainName(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) { 29 fqdn, ok := args["fqdn"] 30 if !ok { 31 conn := runtime.Connection.(*connection.HostConnection) 32 fqdn = llx.StringData(conn.FQDN()) 33 args["fqdn"] = fqdn 34 } 35 36 if fqdn == nil { 37 return nil, nil, errors.New("domainName resource requires fqdn argument") 38 } 39 40 dn, err := domain.Parse(fqdn.Value.(string)) 41 if err != nil { 42 return nil, nil, err 43 } 44 45 args["effectiveTLDPlusOne"] = llx.StringData(dn.EffectiveTLDPlusOne) 46 args["tld"] = llx.StringData(dn.TLD) 47 args["tldIcannManaged"] = llx.BoolData(dn.IcannManagedTLD) 48 args["labels"] = llx.ArrayData(llx.TArr2Raw[string](dn.Labels), types.String) 49 50 return args, nil, nil 51 } 52 53 func (d *mqlDns) id() (string, error) { 54 return "dns/" + d.Fqdn.Data, nil 55 } 56 57 func initDns(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) { 58 _, ok := args["fqdn"] 59 if !ok { 60 conn := runtime.Connection.(*connection.HostConnection) 61 args["fqdn"] = llx.StringData(conn.FQDN()) 62 } 63 64 return args, nil, nil 65 } 66 67 func (d *mqlDns) params(fqdn string) (interface{}, error) { 68 dnsShaker, err := dnsshake.New(fqdn) 69 if err != nil { 70 return nil, err 71 } 72 73 records, err := dnsShaker.Query() 74 if err != nil { 75 return nil, err 76 } 77 78 return convert.JsonToDict(records) 79 } 80 81 func (d *mqlDns) records(params interface{}) ([]interface{}, error) { 82 // NOTE: mql does not cache the results of GetRecords since it has an input argument 83 // Iterations over map keys are not deterministic and therefore we need to sort the keys 84 85 paramsM, ok := params.(map[string]interface{}) 86 if !ok { 87 return nil, errors.New("incorrect structure of params received") 88 } 89 90 // convert responses to dns types 91 resultMap := make(map[string]*mqlDnsRecord) 92 for k := range paramsM { 93 r, ok := paramsM[k].(map[string]interface{}) 94 if !ok { 95 return nil, errors.New("incorrect structure of params entries received") 96 } 97 98 // filter by successful dns records 99 if r["rCode"] != dns.RcodeToString[dns.RcodeSuccess] { 100 continue 101 } 102 103 var ttl *llx.RawData 104 if r["TTL"] == nil { 105 ttl = llx.NilData 106 } else { 107 ttl = llx.IntData(r["TTL"].(int64)) 108 } 109 o, err := CreateResource(d.MqlRuntime, "dns.record", map[string]*llx.RawData{ 110 "name": llx.StringData(r["name"].(string)), 111 "ttl": ttl, 112 "class": llx.StringData(r["class"].(string)), 113 "type": llx.StringData(r["type"].(string)), 114 "rdata": llx.ArrayData(llx.TArr2Raw(r["rData"].([]interface{})), types.String), 115 }) 116 if err != nil { 117 return nil, err 118 } 119 120 record := o.(*mqlDnsRecord) 121 resultMap[record.__id] = record 122 } 123 124 keys := sortx.Keys(resultMap) 125 res := []interface{}{} 126 for i := range keys { 127 res = append(res, resultMap[keys[i]]) 128 } 129 130 return res, nil 131 } 132 133 func (d *mqlDnsRecord) id() (string, error) { 134 return "dns.record/" + d.Name.Data + "/" + d.Class.Data + "/" + d.Type.Data, nil 135 } 136 137 func (d *mqlDns) mx(params interface{}) ([]interface{}, error) { 138 paramsM, ok := params.(map[string]interface{}) 139 if !ok { 140 return []interface{}{}, nil 141 } 142 143 mxEntries := []interface{}{} 144 record, ok := paramsM["MX"] 145 if !ok { 146 return mxEntries, nil 147 } 148 149 r := record.(map[string]interface{}) 150 151 var name, c, t string 152 var ttl int64 153 var rdata []interface{} 154 155 if r["name"] != nil { 156 name = r["name"].(string) 157 } 158 159 if r["class"] != nil { 160 c = r["class"].(string) 161 } 162 163 if r["type"] != nil { 164 t = r["type"].(string) 165 } 166 167 if r["TTL"] != nil { 168 ttl = r["TTL"].(int64) 169 } 170 171 if r["rData"] != nil { 172 rdata = r["rData"].([]interface{}) 173 } 174 175 for j := range rdata { 176 entry := rdata[j].(string) 177 178 // use dns package to parse mx entry 179 s := name + "\t" + strconv.FormatInt(ttl, 10) + "\t" + c + "\t" + t + "\t" + entry 180 got, err := dns.NewRR(s) 181 if err != nil { 182 return nil, err 183 } 184 185 switch v := got.(type) { 186 case *dns.MX: 187 mxEntry, err := CreateResource(d.MqlRuntime, "dns.mxRecord", map[string]*llx.RawData{ 188 "name": llx.StringData(name), 189 "preference": llx.IntData(int64(v.Preference)), 190 "domainName": llx.StringData(v.Mx), 191 }) 192 if err != nil { 193 return nil, err 194 } 195 mxEntries = append(mxEntries, mxEntry) 196 } 197 } 198 199 return mxEntries, nil 200 } 201 202 func (d *mqlDnsMxRecord) id() (string, error) { 203 return "dns.mx/" + d.Name.Data + "+" + d.DomainName.Data, nil 204 } 205 206 func (d *mqlDns) dkim(params interface{}) ([]interface{}, error) { 207 paramsM, ok := params.(map[string]interface{}) 208 if !ok { 209 return []interface{}{}, nil 210 } 211 212 dkimEntries := []interface{}{} 213 214 record, ok := paramsM["TXT"] 215 if !ok { 216 return dkimEntries, nil 217 } 218 219 r := record.(map[string]interface{}) 220 221 var name string 222 var rdata []interface{} 223 224 if r["name"] != nil { 225 name = r["name"].(string) 226 } 227 228 if r["rData"] != nil { 229 rdata = r["rData"].([]interface{}) 230 } 231 232 for j := range rdata { 233 entry := rdata[j].(string) 234 entry = strings.TrimSpace(entry) 235 236 if !strings.HasPrefix(entry, "v=DKIM1;") { 237 continue 238 } 239 240 dkimRepr, err := dnsshake.NewDkimPublicKeyRepresentation(entry) 241 if err != nil { 242 return nil, err 243 } 244 245 o, err := CreateResource(d.MqlRuntime, "dns.dkimRecord", map[string]*llx.RawData{ 246 "domain": llx.StringData(name), 247 "dnsTxt": llx.StringData(entry), 248 "version": llx.StringData(dkimRepr.Version), 249 "hashAlgorithms": llx.ArrayData(llx.TArr2Raw(dkimRepr.HashAlgorithms), types.String), 250 "keyType": llx.StringData(dkimRepr.KeyType), 251 "notes": llx.StringData(dkimRepr.Notes), 252 "publicKeyData": llx.StringData(dkimRepr.PublicKeyData), 253 "serviceTypes": llx.ArrayData(llx.TArr2Raw(dkimRepr.ServiceType), types.String), 254 "flags": llx.ArrayData(llx.TArr2Raw(dkimRepr.Flags), types.String), 255 }) 256 if err != nil { 257 return nil, err 258 } 259 record := o.(*mqlDnsDkimRecord) 260 record.dkim = dkimRepr 261 dkimEntries = append(dkimEntries, record) 262 } 263 264 return dkimEntries, nil 265 } 266 267 type mqlDnsDkimRecordInternal struct { 268 dkim *dnsshake.DkimPublicKeyRepresentation 269 } 270 271 func (d *mqlDnsDkimRecord) id() (string, error) { 272 hasher := sha256.New() 273 hasher.Write([]byte(d.DnsTxt.Data)) 274 sha256 := hex.EncodeToString(hasher.Sum(nil)) 275 return "dns.dkim/" + d.Domain.Data + "/" + sha256, nil 276 } 277 278 func (d *mqlDnsDkimRecord) valid() (bool, error) { 279 if d.dkim == nil { 280 return false, errors.New("could not load dkim data") 281 } 282 283 ok, _, _ := d.dkim.Valid() 284 return ok, nil 285 }