// Function to mock a domain object func newDomain() model.Domain { var domain model.Domain domain.FQDN = "rafael.net.br" domain.Nameservers = []model.Nameserver{ { Host: "ns1.rafael.net.br", IPv4: net.ParseIP("127.0.0.1"), IPv6: net.ParseIP("::1"), }, { Host: "ns2.rafael.net.br", IPv4: net.ParseIP("127.0.0.2"), }, } domain.DSSet = []model.DS{ { Keytag: 1234, Algorithm: model.DSAlgorithmRSASHA1, Digest: "A790A11EA430A85DA77245F091891F73AA740483", }, } owner, _ := mail.ParseAddress("*****@*****.**") domain.Owners = []model.Owner{ { Email: owner, Language: "pt-BR", }, } return domain }
// Merge is used to merge a domain request object sent by the user into a domain object of // the database. It can return errors related to merge problems that are problem caused by // data format of the user input func Merge(domain model.Domain, domainRequest DomainRequest) (model.Domain, error) { var err error if domainRequest.FQDN, err = model.NormalizeDomainName(domainRequest.FQDN); err != nil { return domain, err } // Detect when the domain object is empty, that is the case when we are creating a new // domain in the Shelter project if len(domain.FQDN) == 0 { domain.FQDN = domainRequest.FQDN } else { // Cannot merge domains with different FQDNs if domain.FQDN != domainRequest.FQDN { return domain, ErrDomainsFQDNDontMatch } } nameservers, err := toNameserversModel(domainRequest.Nameservers) if err != nil { return domain, err } for index, userNameserver := range nameservers { for _, nameserver := range domain.Nameservers { if nameserver.Host == userNameserver.Host { // Found the same nameserver in the user domain object, maybe the user updated the // IP addresses nameserver.IPv4 = userNameserver.IPv4 nameserver.IPv6 = userNameserver.IPv6 nameservers[index] = nameserver break } } } domain.Nameservers = nameservers dsSet, err := toDSSetModel(domainRequest.DSSet) if err != nil { return domain, err } dnskeysDSSet, err := dnskeysRequestsToDSSetModel(domain.FQDN, domainRequest.DNSKEYS) if err != nil { return domain, err } dsSet = append(dsSet, dnskeysDSSet...) for index, userDS := range dsSet { for _, ds := range domain.DSSet { if ds.Keytag == userDS.Keytag { // Found the same DS in the user domain object ds.Algorithm = userDS.Algorithm ds.Digest = userDS.Digest ds.DigestType = userDS.DigestType dsSet[index] = ds break } } } domain.DSSet = dsSet // We can replace the whole structure of the e-mail every time that a new UPDATE arrives // because there's no extra information in server side that we need to keep domain.Owners, err = toOwnersModel(domainRequest.Owners) if err != nil { return domain, err } return domain, nil }
// Send DNS requests to fill a domain object from the information found on the DNS authoritative // nameservers. This is very usefull to make it easier for the user to fill forms with the domain // information. The domain must be already delegated by a registry to this function works, because // it uses a recursive DNS func QueryDomain(fqdn string) (model.Domain, error) { domain := model.Domain{ FQDN: fqdn, } querier := newQuerier( config.ShelterConfig.Scan.UDPMaxSize, time.Duration(config.ShelterConfig.Scan.Timeouts.DialSeconds)*time.Second, time.Duration(config.ShelterConfig.Scan.Timeouts.ReadSeconds)*time.Second, time.Duration(config.ShelterConfig.Scan.Timeouts.WriteSeconds)*time.Second, config.ShelterConfig.Scan.ConnectionRetries, ) resolver := fmt.Sprintf("%s:%d", config.ShelterConfig.Scan.Resolver.Address, config.ShelterConfig.Scan.Resolver.Port, ) var dnsRequestMessage dns.Msg dnsRequestMessage.SetQuestion(fqdn, dns.TypeNS) dnsRequestMessage.RecursionDesired = true // Allow retrieving domain information when there's a DNSSEC problem in the chain-of-trust dnsRequestMessage.CheckingDisabled = true dnsResponseMsg, err := querier.sendDNSRequest(resolver, &dnsRequestMessage) if err != nil { return domain, err } for _, answer := range dnsResponseMsg.Answer { nsRecord, ok := answer.(*dns.NS) if !ok { continue } domain.Nameservers = append(domain.Nameservers, model.Nameserver{ Host: nsRecord.Ns, }) } for index, nameserver := range domain.Nameservers { // Don't need to retrieve glue records if not necessary if !strings.HasSuffix(nameserver.Host, domain.FQDN) { continue } dnsRequestMessage.SetQuestion(nameserver.Host, dns.TypeA) dnsResponseMsg, err = querier.sendDNSRequest(resolver, &dnsRequestMessage) if err != nil { return domain, err } for _, answer := range dnsResponseMsg.Answer { ipv4Record, ok := answer.(*dns.A) if !ok { continue } domain.Nameservers[index].IPv4 = ipv4Record.A } dnsRequestMessage.SetQuestion(nameserver.Host, dns.TypeAAAA) dnsResponseMsg, err = querier.sendDNSRequest(resolver, &dnsRequestMessage) if err != nil { return domain, err } for _, answer := range dnsResponseMsg.Answer { ipv6Record, ok := answer.(*dns.AAAA) if !ok { continue } domain.Nameservers[index].IPv6 = ipv6Record.AAAA } } // We are going to retrieve the DNSKEYs from the user zone, and generate the DS records from it. // This is good if the user wants to use the Shelter project as a easy-to-fill domain registration // form dnsRequestMessage.SetQuestion(fqdn, dns.TypeDNSKEY) dnsResponseMsg, err = querier.sendDNSRequest(resolver, &dnsRequestMessage) if err != nil { return domain, err } for _, answer := range dnsResponseMsg.Answer { dnskeyRecord, ok := answer.(*dns.DNSKEY) if !ok { continue } // Only add DNSKEYs with bit SEP on if (dnskeyRecord.Flags & dns.SEP) == 0 { continue } dsRecord := dnskeyRecord.ToDS(uint8(DefaultDigestType)) domain.DSSet = append(domain.DSSet, model.DS{ Keytag: dsRecord.KeyTag, Algorithm: model.DSAlgorithm(dsRecord.Algorithm), DigestType: model.DSDigestType(dsRecord.DigestType), Digest: dsRecord.Digest, }) } return domain, nil }