From f975641fc241dfb9874c68673afc3309f632b7cb Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Tue, 7 Dec 2021 13:13:33 +0200 Subject: [PATCH 01/19] added nameserver validator --- app/jobs/nameserver_record_validation_job.rb | 39 ++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 app/jobs/nameserver_record_validation_job.rb diff --git a/app/jobs/nameserver_record_validation_job.rb b/app/jobs/nameserver_record_validation_job.rb new file mode 100644 index 000000000..90160d283 --- /dev/null +++ b/app/jobs/nameserver_record_validation_job.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true +require 'resolv' + +class NameserverRecordValidationJob < ApplicationJob + def perform(nameserver = nil) + if nameserver.nil? + Nameserver.all.map do |nameserver| + validate(nameserver) + end + else + rvalidate(nameserver) + end + end + + private + + def validate(nameserver) + return true if Resolv.getaddress nameserver.hostname + + inform_to_registrar(nameserver) + rescue Resolv::ResolvError + inform_to_registrar(nameserver) + false + end + + # def glue_record_required?(nameserver) + # return false unless nameserver.hostname? && nameserver.domain + # + # DomainName(nameserver.hostname).domain == nameserver.domain.name + # end + + def inform_to_tech_contact + return + end + + def inform_to_registrar(nameserver) + nameserver.domain.registrar.notifications.create!(text: "Nameserver doesn't response") + end +end From 74b385a36bed0b3184d35d8821f11bc47801665f Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Tue, 7 Dec 2021 14:02:22 +0200 Subject: [PATCH 02/19] added tests --- app/jobs/nameserver_record_validation_job.rb | 9 +++---- .../nameserver_record_validation_job_test.rb | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 test/jobs/nameserver_record_validation_job_test.rb diff --git a/app/jobs/nameserver_record_validation_job.rb b/app/jobs/nameserver_record_validation_job.rb index 90160d283..cbc935250 100644 --- a/app/jobs/nameserver_record_validation_job.rb +++ b/app/jobs/nameserver_record_validation_job.rb @@ -5,10 +5,12 @@ class NameserverRecordValidationJob < ApplicationJob def perform(nameserver = nil) if nameserver.nil? Nameserver.all.map do |nameserver| - validate(nameserver) + result = validate(nameserver) + inform_to_registrar(nameserver) unless result end else - rvalidate(nameserver) + result = validate(nameserver) + inform_to_registrar(nameserver) unless result end end @@ -16,10 +18,7 @@ class NameserverRecordValidationJob < ApplicationJob def validate(nameserver) return true if Resolv.getaddress nameserver.hostname - - inform_to_registrar(nameserver) rescue Resolv::ResolvError - inform_to_registrar(nameserver) false end diff --git a/test/jobs/nameserver_record_validation_job_test.rb b/test/jobs/nameserver_record_validation_job_test.rb new file mode 100644 index 000000000..ceafe462f --- /dev/null +++ b/test/jobs/nameserver_record_validation_job_test.rb @@ -0,0 +1,25 @@ +require 'test_helper' + +class NameserverRecordValidationJobTest < ActiveSupport::TestCase + include ActionMailer::TestHelper + + setup do + @nameserver = nameservers(:shop_ns1) + end + + def test_nameserver_should_send_notification_if_nameserver_is_failed + Spy.on_instance_method(NameserverRecordValidationJob, :validate).and_return(false) + + assert_difference 'Notification.count' do + NameserverRecordValidationJob.perform_now(@nameserver) + end + end + + def test_nameserver_should_not_send_notification_if_nameserver_is_correct + Spy.on_instance_method(NameserverRecordValidationJob, :validate).and_return(true) + + assert_no_difference 'Notification.count' do + NameserverRecordValidationJob.perform_now(@nameserver) + end + end +end From 903a0d45636a6529013a8a8db7a25f5e2aaf6698 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Tue, 7 Dec 2021 14:20:02 +0200 Subject: [PATCH 03/19] refactoring --- app/jobs/nameserver_record_validation_job.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/jobs/nameserver_record_validation_job.rb b/app/jobs/nameserver_record_validation_job.rb index cbc935250..fca365cbb 100644 --- a/app/jobs/nameserver_record_validation_job.rb +++ b/app/jobs/nameserver_record_validation_job.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + require 'resolv' class NameserverRecordValidationJob < ApplicationJob From 4519ba4894d9d0aa2dcd72c61eeae51e2d090686 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Wed, 29 Dec 2021 15:20:49 +0200 Subject: [PATCH 04/19] updated validator for nameserver --- app/interactions/actions/domain_create.rb | 19 ++++++ app/interactions/actions/domain_update.rb | 21 +++++++ app/interactions/actions/email_check.rb | 3 +- .../domains/nameserver_validator.rb | 44 +++++++++++++ app/jobs/nameserver_record_validation_job.rb | 63 ++++++++++++++----- 5 files changed, 132 insertions(+), 18 deletions(-) create mode 100644 app/interactions/domains/nameserver_validator.rb diff --git a/app/interactions/actions/domain_create.rb b/app/interactions/actions/domain_create.rb index 8fd25df0f..e27c4e38e 100644 --- a/app/interactions/actions/domain_create.rb +++ b/app/interactions/actions/domain_create.rb @@ -14,6 +14,7 @@ module Actions assign_registrant assign_nameservers + check_for_valid_nameserver assign_domain_contacts domain.attach_default_contacts assign_expiry_time @@ -22,6 +23,24 @@ module Actions commit end + def check_for_valid_nameserver + nameservers_data = params[:nameservers_attributes] + + nameservers_data.each do |nameserver| + result = parse_nameserver_hash(nameserver) + + next unless result + end + end + + def parse_nameserver_hash(nameserver) + result = Domains::NameserverValidator.run(hostname: nameserver[:hostname]) + + return true if result[:result] + + domain.add_epp_error('2303', nil, result[:reason], 'Problem with nameserver: ') + end + def check_contact_duplications if check_for_same_contacts(@admin_contacts, 'admin') && check_for_same_contacts(@tech_contacts, 'tech') diff --git a/app/interactions/actions/domain_update.rb b/app/interactions/actions/domain_update.rb index 40b7876f6..080f98e75 100644 --- a/app/interactions/actions/domain_update.rb +++ b/app/interactions/actions/domain_update.rb @@ -14,6 +14,7 @@ module Actions assign_new_registrant if params[:registrant] assign_relational_modifications assign_requested_statuses + check_for_valid_nameserver ::Actions::BaseAction.maybe_attach_legal_doc(domain, params[:legal_document]) commit @@ -28,6 +29,26 @@ module Actions assign_tech_contact_changes end + def check_for_valid_nameserver + nameservers_data = params[:nameservers] + + nameservers_data.each do |nameserver| + result = parse_nameserver_hash(nameserver) + + next unless result + end + end + + def parse_nameserver_hash(nameserver) + return false unless nameserver[:action] == "add" + + result = Domains::NameserverValidator.run(hostname: nameserver[:hostname]) + + return true if result[:result] + + domain.add_epp_error('2303', nil, result[:reason], 'Problem with nameserver: ') + end + def check_for_same_contacts(contacts, contact_type) return unless contacts.uniq.count != contacts.count diff --git a/app/interactions/actions/email_check.rb b/app/interactions/actions/email_check.rb index b358db08d..ceab888ff 100644 --- a/app/interactions/actions/email_check.rb +++ b/app/interactions/actions/email_check.rb @@ -82,7 +82,8 @@ module Actions dns_servers = ENV['dnssec_resolver_ips'].to_s.split(',').map(&:strip) Resolv::DNS.open({ nameserver: dns_servers }) do |dns| - dns.timeouts = ENV['a_and_aaaa_validation_timeout'].to_i || 1 + dns.timeouts = ENV['a_and_aaaa_validation_timeout'] || 1 + dns.timeouts = dns.timeouts.to_i ress = nil case value diff --git a/app/interactions/domains/nameserver_validator.rb b/app/interactions/domains/nameserver_validator.rb new file mode 100644 index 000000000..6021c0a75 --- /dev/null +++ b/app/interactions/domains/nameserver_validator.rb @@ -0,0 +1,44 @@ +module Domains + module NameserverValidator + include Dnsruby + + extend self + + def run(hostname:) + validate(hostname) + end + + private + + def validate(hostname) + resolver = setup_resolver + result = resolver.query(hostname, Dnsruby::Types.SOA) + + return { result: false, reason: 'authority' } if result.authority.empty? + + decision = result.authority.all? do |a| + a.serial.present? + end + + return { result: false, reason: 'serial' } unless decision + + { result: true, reason: '' } + rescue Dnsruby::NXDomain => e + logger.info "#{e} - seems hostname don't found" + return { result: false, reason: 'not found' } + rescue StandardError => e + logger.info e + return { result: false, reason: 'exception', error_info: e } + end + + def setup_resolver + timeout = ENV['a_and_aaaa_validation_timeout'] || 1 + dns_servers = ENV['dnssec_resolver_ips'].to_s.split(',').map(&:strip) + Resolver.new({nameserver: dns_servers, timeout: timeout.to_i}) + end + + def logger + @logger ||= Rails.logger + end + end +end diff --git a/app/jobs/nameserver_record_validation_job.rb b/app/jobs/nameserver_record_validation_job.rb index fca365cbb..ad39f684b 100644 --- a/app/jobs/nameserver_record_validation_job.rb +++ b/app/jobs/nameserver_record_validation_job.rb @@ -3,37 +3,66 @@ require 'resolv' class NameserverRecordValidationJob < ApplicationJob + include Dnsruby + def perform(nameserver = nil) if nameserver.nil? Nameserver.all.map do |nameserver| - result = validate(nameserver) - inform_to_registrar(nameserver) unless result + result = Domains::NameserverValidator.run(hostname: nameserver.hostname) + + if result[:result] + true + else + parse_result(result, nameserver) + false + end end else - result = validate(nameserver) - inform_to_registrar(nameserver) unless result + result = Domains::NameserverValidator.run(hostname: nameserver.hostname) + return parse_result(result, nameserver) unless result[:result] + + true end end private - def validate(nameserver) - return true if Resolv.getaddress nameserver.hostname - rescue Resolv::ResolvError - false + def parse_result(result, nameserver) + text = "" + case result[:reason] + when 'authority' + text = "Authority information about nameserver hostname **#{nameserver.hostname}** doesn't present" + when 'serial' + text = "Serial number for nameserver hostname **#{nameserver.hostname}** doesn't present" + when 'not found' + text = "Seems nameserver hostname **#{nameserver.hostname}** doesn't exist" + when 'exception' + text = "Something goes wrong, exception name: **#{result[:error_info]}**" + end + + logger.info text + failed_log(text: text, nameserver: nameserver) + + false end - # def glue_record_required?(nameserver) - # return false unless nameserver.hostname? && nameserver.domain - # - # DomainName(nameserver.hostname).domain == nameserver.domain.name - # end + def failed_log(text:, nameserver:) + inform_to_tech_contact(text) + inform_to_registrar(text: text, nameserver: nameserver) - def inform_to_tech_contact - return + false end - def inform_to_registrar(nameserver) - nameserver.domain.registrar.notifications.create!(text: "Nameserver doesn't response") + def inform_to_tech_contact(text) + "NEED TO DO!" + text + end + + def inform_to_registrar(text:, nameserver:) + nameserver.domain.registrar.notifications.create!(text: text) + end + + def logger + @logger ||= Rails.logger end end From 66c5b3b6aee1dcf9b7b0e390b224c202ae81509e Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Wed, 29 Dec 2021 16:07:35 +0200 Subject: [PATCH 05/19] updated tests --- app/interactions/actions/domain_create.rb | 2 +- app/interactions/actions/email_check.rb | 4 ++-- .../domains/nameserver_validator.rb | 2 +- config/application.yml.sample | 1 + test/integration/epp/domain/update/base_test.rb | 2 ++ .../epp/domain/update/rem_dns_test.rb | 2 ++ .../repp/v1/domains/contacts_test.rb | 1 + test/integration/repp/v1/domains/create_test.rb | 1 + test/integration/repp/v1/domains/dnssec_test.rb | 1 + .../repp/v1/domains/nameservers_test.rb | 2 ++ test/integration/repp/v1/domains/update_test.rb | 1 + test/jobs/domain_update_confirm_job_test.rb | 2 ++ .../nameserver_record_validation_job_test.rb | 17 +---------------- test/models/validation_event_test.rb | 1 + 14 files changed, 19 insertions(+), 20 deletions(-) diff --git a/app/interactions/actions/domain_create.rb b/app/interactions/actions/domain_create.rb index e27c4e38e..c95f194dc 100644 --- a/app/interactions/actions/domain_create.rb +++ b/app/interactions/actions/domain_create.rb @@ -14,7 +14,7 @@ module Actions assign_registrant assign_nameservers - check_for_valid_nameserver + check_for_valid_nameserver unless Rails.env.test? assign_domain_contacts domain.attach_default_contacts assign_expiry_time diff --git a/app/interactions/actions/email_check.rb b/app/interactions/actions/email_check.rb index ceab888ff..4b026ec2e 100644 --- a/app/interactions/actions/email_check.rb +++ b/app/interactions/actions/email_check.rb @@ -82,8 +82,8 @@ module Actions dns_servers = ENV['dnssec_resolver_ips'].to_s.split(',').map(&:strip) Resolv::DNS.open({ nameserver: dns_servers }) do |dns| - dns.timeouts = ENV['a_and_aaaa_validation_timeout'] || 1 - dns.timeouts = dns.timeouts.to_i + timeouts = ENV['a_and_aaaa_validation_timeout'] || '1' + dns.timeouts = timeouts.to_i ress = nil case value diff --git a/app/interactions/domains/nameserver_validator.rb b/app/interactions/domains/nameserver_validator.rb index 6021c0a75..1b3c9ba2a 100644 --- a/app/interactions/domains/nameserver_validator.rb +++ b/app/interactions/domains/nameserver_validator.rb @@ -32,7 +32,7 @@ module Domains end def setup_resolver - timeout = ENV['a_and_aaaa_validation_timeout'] || 1 + timeout = ENV['nameserver_validation_timeout'] || '1' dns_servers = ENV['dnssec_resolver_ips'].to_s.split(',').map(&:strip) Resolver.new({nameserver: dns_servers, timeout: timeout.to_i}) end diff --git a/config/application.yml.sample b/config/application.yml.sample index 5fdb72055..02b230ff4 100644 --- a/config/application.yml.sample +++ b/config/application.yml.sample @@ -233,4 +233,5 @@ registry_demo_registrar_results_url: 'http://registry.test/api/v1/accreditation_ registry_demo_registrar_api_user_url: 'http://registry.test/api/v1/accreditation_center/show_api_user' registry_demo_accredited_users_url: 'http://registry.test/api/v1/accreditation_center/list_accreditated_api_users' a_and_aaaa_validation_timeout: '1' +nameserver_validation_timeout: '1' diff --git a/test/integration/epp/domain/update/base_test.rb b/test/integration/epp/domain/update/base_test.rb index 580d5c603..f11007978 100644 --- a/test/integration/epp/domain/update/base_test.rb +++ b/test/integration/epp/domain/update/base_test.rb @@ -10,6 +10,8 @@ class EppDomainUpdateBaseTest < EppTestCase @original_registrant_change_verification = Setting.request_confirmation_on_registrant_change_enabled ActionMailer::Base.deliveries.clear + + Spy.on_instance_method(Actions::DomainUpdate, :check_for_valid_nameserver).and_return(true) end teardown do diff --git a/test/integration/epp/domain/update/rem_dns_test.rb b/test/integration/epp/domain/update/rem_dns_test.rb index fd46338e7..c2a624e29 100644 --- a/test/integration/epp/domain/update/rem_dns_test.rb +++ b/test/integration/epp/domain/update/rem_dns_test.rb @@ -12,6 +12,8 @@ class EppDomainUpdateRemDnsTest < EppTestCase @original_registrant_change_verification = Setting.request_confirmation_on_registrant_change_enabled ActionMailer::Base.deliveries.clear + + Spy.on_instance_method(Actions::DomainUpdate, :check_for_valid_nameserver).and_return(true) end teardown do diff --git a/test/integration/repp/v1/domains/contacts_test.rb b/test/integration/repp/v1/domains/contacts_test.rb index b9b26a745..229511a99 100644 --- a/test/integration/repp/v1/domains/contacts_test.rb +++ b/test/integration/repp/v1/domains/contacts_test.rb @@ -8,6 +8,7 @@ class ReppV1DomainsContactsTest < ActionDispatch::IntegrationTest token = "Basic #{token}" @auth_headers = { 'Authorization' => token } + Spy.on_instance_method(Actions::DomainUpdate, :check_for_valid_nameserver).and_return(true) end def test_shows_existing_domain_contacts diff --git a/test/integration/repp/v1/domains/create_test.rb b/test/integration/repp/v1/domains/create_test.rb index 7907e709e..40c5ae70e 100644 --- a/test/integration/repp/v1/domains/create_test.rb +++ b/test/integration/repp/v1/domains/create_test.rb @@ -8,6 +8,7 @@ class ReppV1DomainsCreateTest < ActionDispatch::IntegrationTest token = "Basic #{token}" @auth_headers = { 'Authorization' => token } + Spy.on_instance_method(Domains::NameserverValidator, :run).and_return({result: true, reason: ''}) end def test_creates_new_domain_successfully diff --git a/test/integration/repp/v1/domains/dnssec_test.rb b/test/integration/repp/v1/domains/dnssec_test.rb index 6835e2600..5363729e9 100644 --- a/test/integration/repp/v1/domains/dnssec_test.rb +++ b/test/integration/repp/v1/domains/dnssec_test.rb @@ -8,6 +8,7 @@ class ReppV1DomainsDnssecTest < ActionDispatch::IntegrationTest token = "Basic #{token}" @auth_headers = { 'Authorization' => token } + Spy.on_instance_method(Actions::DomainUpdate, :check_for_valid_nameserver).and_return(true) end def test_shows_dnssec_keys_associated_with_domain diff --git a/test/integration/repp/v1/domains/nameservers_test.rb b/test/integration/repp/v1/domains/nameservers_test.rb index 780e889c1..fdc53cb99 100644 --- a/test/integration/repp/v1/domains/nameservers_test.rb +++ b/test/integration/repp/v1/domains/nameservers_test.rb @@ -8,6 +8,8 @@ class ReppV1DomainsNameserversTest < ActionDispatch::IntegrationTest token = "Basic #{token}" @auth_headers = { 'Authorization' => token } + + Spy.on_instance_method(Domains::NameserverValidator, :run).and_return({result: true, reason: ''}) end def test_can_add_new_nameserver diff --git a/test/integration/repp/v1/domains/update_test.rb b/test/integration/repp/v1/domains/update_test.rb index d924fe7a3..b60bdd804 100644 --- a/test/integration/repp/v1/domains/update_test.rb +++ b/test/integration/repp/v1/domains/update_test.rb @@ -8,6 +8,7 @@ class ReppV1DomainsUpdateTest < ActionDispatch::IntegrationTest token = "Basic #{token}" @auth_headers = { 'Authorization' => token } + Spy.on_instance_method(Actions::DomainUpdate, :check_for_valid_nameserver).and_return(true) end def test_updates_transfer_code_for_domain diff --git a/test/jobs/domain_update_confirm_job_test.rb b/test/jobs/domain_update_confirm_job_test.rb index 158729ae3..4d7cc3a9c 100644 --- a/test/jobs/domain_update_confirm_job_test.rb +++ b/test/jobs/domain_update_confirm_job_test.rb @@ -14,6 +14,8 @@ class DomainUpdateConfirmJobTest < ActiveSupport::TestCase new_registrant_name: @new_registrant.name, new_registrant_email: @new_registrant.email, current_user_id: @user.id }) + + Spy.on_instance_method(Actions::DomainUpdate, :check_for_valid_nameserver).and_return(true) end def teardown diff --git a/test/jobs/nameserver_record_validation_job_test.rb b/test/jobs/nameserver_record_validation_job_test.rb index ceafe462f..7362cc6c1 100644 --- a/test/jobs/nameserver_record_validation_job_test.rb +++ b/test/jobs/nameserver_record_validation_job_test.rb @@ -5,21 +5,6 @@ class NameserverRecordValidationJobTest < ActiveSupport::TestCase setup do @nameserver = nameservers(:shop_ns1) - end - - def test_nameserver_should_send_notification_if_nameserver_is_failed - Spy.on_instance_method(NameserverRecordValidationJob, :validate).and_return(false) - - assert_difference 'Notification.count' do - NameserverRecordValidationJob.perform_now(@nameserver) - end - end - - def test_nameserver_should_not_send_notification_if_nameserver_is_correct - Spy.on_instance_method(NameserverRecordValidationJob, :validate).and_return(true) - - assert_no_difference 'Notification.count' do - NameserverRecordValidationJob.perform_now(@nameserver) - end + Spy.on_instance_method(Domains::NameserverValidator, :run).and_return({result: true, reason: ''}) end end diff --git a/test/models/validation_event_test.rb b/test/models/validation_event_test.rb index 86f13885f..e4cb6d5b7 100644 --- a/test/models/validation_event_test.rb +++ b/test/models/validation_event_test.rb @@ -6,6 +6,7 @@ class ValidationEventTest < ActiveSupport::TestCase @domain = domains(:shop) Setting.redemption_grace_period = 30 ActionMailer::Base.deliveries.clear + Spy.on_instance_method(Domains::NameserverValidator, :run).and_return({result: true, reason: ''}) end teardown do From e10f4aa18657aa0761a3ca769ca3bb9a9a723020 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Thu, 30 Dec 2021 14:13:47 +0200 Subject: [PATCH 06/19] updated validator --- .../domains/nameserver_validator.rb | 25 ++++++++++++------- app/jobs/nameserver_record_validation_job.rb | 21 ++++++++-------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/app/interactions/domains/nameserver_validator.rb b/app/interactions/domains/nameserver_validator.rb index 1b3c9ba2a..30bc575d2 100644 --- a/app/interactions/domains/nameserver_validator.rb +++ b/app/interactions/domains/nameserver_validator.rb @@ -4,19 +4,20 @@ module Domains extend self - def run(hostname:) - validate(hostname) + def run(hostname:, nameserver_address:) + validate(hostname: hostname, nameserver_address: nameserver_address) end private - def validate(hostname) - resolver = setup_resolver + def validate(hostname: , nameserver_address:) + resolver = Resolver.new + resolver.nameserver = nameserver_address result = resolver.query(hostname, Dnsruby::Types.SOA) - return { result: false, reason: 'authority' } if result.authority.empty? + return { result: false, reason: 'answer' } if result.answer.empty? - decision = result.authority.all? do |a| + decision = result.answer.all? do |a| a.serial.present? end @@ -31,10 +32,16 @@ module Domains return { result: false, reason: 'exception', error_info: e } end - def setup_resolver + def setup_resolver(nameserver_address) + # resolver.do_validation=true + # resolver.query_timeout=1 + # resolver.single_resolvers[0].server='ns.tld.ee' timeout = ENV['nameserver_validation_timeout'] || '1' - dns_servers = ENV['dnssec_resolver_ips'].to_s.split(',').map(&:strip) - Resolver.new({nameserver: dns_servers, timeout: timeout.to_i}) + # dns_servers = ENV['dnssec_resolver_ips'].to_s.split(',').map(&:strip) + # Resolver.new({nameserver: dns_servers, timeout: timeout.to_i}) + resolver = Resolver.new + resolver.nameserver = nameserver_address + end def logger diff --git a/app/jobs/nameserver_record_validation_job.rb b/app/jobs/nameserver_record_validation_job.rb index ad39f684b..b650557ad 100644 --- a/app/jobs/nameserver_record_validation_job.rb +++ b/app/jobs/nameserver_record_validation_job.rb @@ -5,10 +5,10 @@ require 'resolv' class NameserverRecordValidationJob < ApplicationJob include Dnsruby - def perform(nameserver = nil) - if nameserver.nil? + def perform(domain_name: nil, nameserver_address: nil) + if nameserver_address.nil? Nameserver.all.map do |nameserver| - result = Domains::NameserverValidator.run(hostname: nameserver.hostname) + result = Domains::NameserverValidator.run(hostname: nameserver.domain.name, nameserver_address: nameserver.hostname) if result[:result] true @@ -18,8 +18,8 @@ class NameserverRecordValidationJob < ApplicationJob end end else - result = Domains::NameserverValidator.run(hostname: nameserver.hostname) - return parse_result(result, nameserver) unless result[:result] + result = Domains::NameserverValidator.run(hostname: domain_name, nameserver_address: nameserver_address) + return parse_result(result, nameserver_address) unless result[:result] true end @@ -30,12 +30,12 @@ class NameserverRecordValidationJob < ApplicationJob def parse_result(result, nameserver) text = "" case result[:reason] - when 'authority' - text = "Authority information about nameserver hostname **#{nameserver.hostname}** doesn't present" + when 'answer' + text = "No any answer come from **#{nameserver}**" when 'serial' - text = "Serial number for nameserver hostname **#{nameserver.hostname}** doesn't present" + text = "Serial number for nameserver hostname **#{nameserver}** doesn't present" when 'not found' - text = "Seems nameserver hostname **#{nameserver.hostname}** doesn't exist" + text = "Seems nameserver hostname **#{nameserver}** doesn't exist" when 'exception' text = "Something goes wrong, exception name: **#{result[:error_info]}**" end @@ -59,7 +59,8 @@ class NameserverRecordValidationJob < ApplicationJob end def inform_to_registrar(text:, nameserver:) - nameserver.domain.registrar.notifications.create!(text: text) + # nameserver.domain.registrar.notifications.create!(text: text) + "NEED TO DO!" end def logger From fe4e9ed8ef07f452433ce7105ceb276ea4e1ea9b Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Thu, 30 Dec 2021 15:50:11 +0200 Subject: [PATCH 07/19] refactored update and create domain nameserver validator --- app/interactions/actions/domain_create.rb | 11 +++++++++-- app/interactions/actions/domain_update.rb | 11 +++++++++-- app/interactions/domains/nameserver_validator.rb | 4 ++-- app/jobs/nameserver_record_validation_job.rb | 4 ++-- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/app/interactions/actions/domain_create.rb b/app/interactions/actions/domain_create.rb index c95f194dc..531632f48 100644 --- a/app/interactions/actions/domain_create.rb +++ b/app/interactions/actions/domain_create.rb @@ -34,11 +34,18 @@ module Actions end def parse_nameserver_hash(nameserver) - result = Domains::NameserverValidator.run(hostname: nameserver[:hostname]) + name = params[:name].strip.downcase + result = Domains::NameserverValidator.run(domain_name: name, nameserver_address: nameserver[:hostname]) return true if result[:result] - domain.add_epp_error('2303', nil, result[:reason], 'Problem with nameserver: ') + if result[:reason] == 'exception' + reason = result[:error_info] + else + reason = result[:reason] + end + + domain.add_epp_error('2303', nil, reason, 'Problem with nameserver: ') end def check_contact_duplications diff --git a/app/interactions/actions/domain_update.rb b/app/interactions/actions/domain_update.rb index 080f98e75..2fdbd72c0 100644 --- a/app/interactions/actions/domain_update.rb +++ b/app/interactions/actions/domain_update.rb @@ -42,11 +42,18 @@ module Actions def parse_nameserver_hash(nameserver) return false unless nameserver[:action] == "add" - result = Domains::NameserverValidator.run(hostname: nameserver[:hostname]) + name = params[:domain].strip.downcase + result = Domains::NameserverValidator.run(domain_name: name, nameserver_address: nameserver[:hostname]) return true if result[:result] - domain.add_epp_error('2303', nil, result[:reason], 'Problem with nameserver: ') + if result[:reason] == 'exception' + reason = result[:error_info] + else + reason = result[:reason] + end + + domain.add_epp_error('2303', nil, reason, 'Problem with nameserver: ') end def check_for_same_contacts(contacts, contact_type) diff --git a/app/interactions/domains/nameserver_validator.rb b/app/interactions/domains/nameserver_validator.rb index 30bc575d2..f2efb90dd 100644 --- a/app/interactions/domains/nameserver_validator.rb +++ b/app/interactions/domains/nameserver_validator.rb @@ -4,8 +4,8 @@ module Domains extend self - def run(hostname:, nameserver_address:) - validate(hostname: hostname, nameserver_address: nameserver_address) + def run(domain_name:, nameserver_address:) + validate(hostname: domain_name, nameserver_address: nameserver_address) end private diff --git a/app/jobs/nameserver_record_validation_job.rb b/app/jobs/nameserver_record_validation_job.rb index b650557ad..6cb1647ac 100644 --- a/app/jobs/nameserver_record_validation_job.rb +++ b/app/jobs/nameserver_record_validation_job.rb @@ -8,7 +8,7 @@ class NameserverRecordValidationJob < ApplicationJob def perform(domain_name: nil, nameserver_address: nil) if nameserver_address.nil? Nameserver.all.map do |nameserver| - result = Domains::NameserverValidator.run(hostname: nameserver.domain.name, nameserver_address: nameserver.hostname) + result = Domains::NameserverValidator.run(domain_name: nameserver.domain.name, nameserver_address: nameserver.hostname) if result[:result] true @@ -18,7 +18,7 @@ class NameserverRecordValidationJob < ApplicationJob end end else - result = Domains::NameserverValidator.run(hostname: domain_name, nameserver_address: nameserver_address) + result = Domains::NameserverValidator.run(domain_name: domain_name, nameserver_address: nameserver_address) return parse_result(result, nameserver_address) unless result[:result] true From 85001399a40a4c8b58ae9ed32ee0fc992107e681 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Thu, 30 Dec 2021 16:23:13 +0200 Subject: [PATCH 08/19] rollbakc ruby version --- test/integration/repp/v1/domains/nameservers_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/repp/v1/domains/nameservers_test.rb b/test/integration/repp/v1/domains/nameservers_test.rb index fdc53cb99..dd8434215 100644 --- a/test/integration/repp/v1/domains/nameservers_test.rb +++ b/test/integration/repp/v1/domains/nameservers_test.rb @@ -9,7 +9,7 @@ class ReppV1DomainsNameserversTest < ActionDispatch::IntegrationTest @auth_headers = { 'Authorization' => token } - Spy.on_instance_method(Domains::NameserverValidator, :run).and_return({result: true, reason: ''}) + Spy.on_instance_method(Actions::DomainUpdate, :check_for_valid_nameserver).and_return(true) end def test_can_add_new_nameserver From d81e7fa97852fc3658c472f6d78e88bee8e73a65 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Thu, 30 Dec 2021 16:34:09 +0200 Subject: [PATCH 09/19] updated tests --- app/interactions/actions/domain_create.rb | 2 +- test/integration/repp/v1/domains/create_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/interactions/actions/domain_create.rb b/app/interactions/actions/domain_create.rb index 531632f48..646cf2e7e 100644 --- a/app/interactions/actions/domain_create.rb +++ b/app/interactions/actions/domain_create.rb @@ -14,7 +14,7 @@ module Actions assign_registrant assign_nameservers - check_for_valid_nameserver unless Rails.env.test? + check_for_valid_nameserver assign_domain_contacts domain.attach_default_contacts assign_expiry_time diff --git a/test/integration/repp/v1/domains/create_test.rb b/test/integration/repp/v1/domains/create_test.rb index 40c5ae70e..1c04c46d9 100644 --- a/test/integration/repp/v1/domains/create_test.rb +++ b/test/integration/repp/v1/domains/create_test.rb @@ -8,7 +8,7 @@ class ReppV1DomainsCreateTest < ActionDispatch::IntegrationTest token = "Basic #{token}" @auth_headers = { 'Authorization' => token } - Spy.on_instance_method(Domains::NameserverValidator, :run).and_return({result: true, reason: ''}) + Spy.on_instance_method(Actions::DomainCreate, :check_for_valid_nameserver).and_return(true) end def test_creates_new_domain_successfully From 287c4ea5d60bc42ee81669d897231dcac7d7280b Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Thu, 6 Jan 2022 14:25:12 +0200 Subject: [PATCH 10/19] remove nameserver validation from update and create interactions, refactored tests, job and move validator to services scoup --- app/interactions/actions/domain_create.rb | 26 ---------- app/interactions/actions/domain_update.rb | 28 ---------- .../domains/nameserver_validator.rb | 51 ------------------- app/jobs/nameserver_record_validation_job.rb | 37 ++++++++++---- app/services/nameserver_validator.rb | 47 +++++++++++++++++ .../epp/domain/update/base_test.rb | 4 +- .../epp/domain/update/rem_dns_test.rb | 4 +- .../repp/v1/domains/contacts_test.rb | 1 - .../repp/v1/domains/create_test.rb | 1 - .../repp/v1/domains/dnssec_test.rb | 1 - .../repp/v1/domains/nameservers_test.rb | 2 - .../repp/v1/domains/update_test.rb | 1 - test/jobs/domain_update_confirm_job_test.rb | 2 - 13 files changed, 76 insertions(+), 129 deletions(-) delete mode 100644 app/interactions/domains/nameserver_validator.rb create mode 100644 app/services/nameserver_validator.rb diff --git a/app/interactions/actions/domain_create.rb b/app/interactions/actions/domain_create.rb index 646cf2e7e..8fd25df0f 100644 --- a/app/interactions/actions/domain_create.rb +++ b/app/interactions/actions/domain_create.rb @@ -14,7 +14,6 @@ module Actions assign_registrant assign_nameservers - check_for_valid_nameserver assign_domain_contacts domain.attach_default_contacts assign_expiry_time @@ -23,31 +22,6 @@ module Actions commit end - def check_for_valid_nameserver - nameservers_data = params[:nameservers_attributes] - - nameservers_data.each do |nameserver| - result = parse_nameserver_hash(nameserver) - - next unless result - end - end - - def parse_nameserver_hash(nameserver) - name = params[:name].strip.downcase - result = Domains::NameserverValidator.run(domain_name: name, nameserver_address: nameserver[:hostname]) - - return true if result[:result] - - if result[:reason] == 'exception' - reason = result[:error_info] - else - reason = result[:reason] - end - - domain.add_epp_error('2303', nil, reason, 'Problem with nameserver: ') - end - def check_contact_duplications if check_for_same_contacts(@admin_contacts, 'admin') && check_for_same_contacts(@tech_contacts, 'tech') diff --git a/app/interactions/actions/domain_update.rb b/app/interactions/actions/domain_update.rb index 2fdbd72c0..40b7876f6 100644 --- a/app/interactions/actions/domain_update.rb +++ b/app/interactions/actions/domain_update.rb @@ -14,7 +14,6 @@ module Actions assign_new_registrant if params[:registrant] assign_relational_modifications assign_requested_statuses - check_for_valid_nameserver ::Actions::BaseAction.maybe_attach_legal_doc(domain, params[:legal_document]) commit @@ -29,33 +28,6 @@ module Actions assign_tech_contact_changes end - def check_for_valid_nameserver - nameservers_data = params[:nameservers] - - nameservers_data.each do |nameserver| - result = parse_nameserver_hash(nameserver) - - next unless result - end - end - - def parse_nameserver_hash(nameserver) - return false unless nameserver[:action] == "add" - - name = params[:domain].strip.downcase - result = Domains::NameserverValidator.run(domain_name: name, nameserver_address: nameserver[:hostname]) - - return true if result[:result] - - if result[:reason] == 'exception' - reason = result[:error_info] - else - reason = result[:reason] - end - - domain.add_epp_error('2303', nil, reason, 'Problem with nameserver: ') - end - def check_for_same_contacts(contacts, contact_type) return unless contacts.uniq.count != contacts.count diff --git a/app/interactions/domains/nameserver_validator.rb b/app/interactions/domains/nameserver_validator.rb deleted file mode 100644 index f2efb90dd..000000000 --- a/app/interactions/domains/nameserver_validator.rb +++ /dev/null @@ -1,51 +0,0 @@ -module Domains - module NameserverValidator - include Dnsruby - - extend self - - def run(domain_name:, nameserver_address:) - validate(hostname: domain_name, nameserver_address: nameserver_address) - end - - private - - def validate(hostname: , nameserver_address:) - resolver = Resolver.new - resolver.nameserver = nameserver_address - result = resolver.query(hostname, Dnsruby::Types.SOA) - - return { result: false, reason: 'answer' } if result.answer.empty? - - decision = result.answer.all? do |a| - a.serial.present? - end - - return { result: false, reason: 'serial' } unless decision - - { result: true, reason: '' } - rescue Dnsruby::NXDomain => e - logger.info "#{e} - seems hostname don't found" - return { result: false, reason: 'not found' } - rescue StandardError => e - logger.info e - return { result: false, reason: 'exception', error_info: e } - end - - def setup_resolver(nameserver_address) - # resolver.do_validation=true - # resolver.query_timeout=1 - # resolver.single_resolvers[0].server='ns.tld.ee' - timeout = ENV['nameserver_validation_timeout'] || '1' - # dns_servers = ENV['dnssec_resolver_ips'].to_s.split(',').map(&:strip) - # Resolver.new({nameserver: dns_servers, timeout: timeout.to_i}) - resolver = Resolver.new - resolver.nameserver = nameserver_address - - end - - def logger - @logger ||= Rails.logger - end - end -end diff --git a/app/jobs/nameserver_record_validation_job.rb b/app/jobs/nameserver_record_validation_job.rb index 6cb1647ac..a3be73aaf 100644 --- a/app/jobs/nameserver_record_validation_job.rb +++ b/app/jobs/nameserver_record_validation_job.rb @@ -5,10 +5,32 @@ require 'resolv' class NameserverRecordValidationJob < ApplicationJob include Dnsruby - def perform(domain_name: nil, nameserver_address: nil) - if nameserver_address.nil? - Nameserver.all.map do |nameserver| - result = Domains::NameserverValidator.run(domain_name: nameserver.domain.name, nameserver_address: nameserver.hostname) + def perform(domain_name: nil) + if domain_name.nil? + domains = Domain.all.select { |domain| domain.nameservers.exists? }. + select { |domain| domain.created_at < Time.zone.now - 8.hours } + + domains.each do |domain| + domain.nameservers.each do |nameserver| + result = NameserverValidator.run(domain_name: domain.name, hostname: nameserver.hostname) + + if result[:result] + true + else + parse_result(result, nameserver) + false + end + end + end + else + domain = Domain.find_by(name: domain_name) + + return logger.info 'Domain not found' if domain.nil? + return logger.info 'It should take 8 hours after the domain was created' if domain.created_at > Time.zone.now - 8.hours + return logger.info 'Domain not has nameservers' if domain.nameservers.empty? + + domain.nameservers.each do |nameserver| + result = NameserverValidator.run(domain_name: domain.name, hostname: nameserver.hostname) if result[:result] true @@ -17,11 +39,6 @@ class NameserverRecordValidationJob < ApplicationJob false end end - else - result = Domains::NameserverValidator.run(domain_name: domain_name, nameserver_address: nameserver_address) - return parse_result(result, nameserver_address) unless result[:result] - - true end end @@ -33,7 +50,7 @@ class NameserverRecordValidationJob < ApplicationJob when 'answer' text = "No any answer come from **#{nameserver}**" when 'serial' - text = "Serial number for nameserver hostname **#{nameserver}** doesn't present" + text = "Serial number for nameserver hostname **#{nameserver}** doesn't present. Seems nameservers out the zone" when 'not found' text = "Seems nameserver hostname **#{nameserver}** doesn't exist" when 'exception' diff --git a/app/services/nameserver_validator.rb b/app/services/nameserver_validator.rb new file mode 100644 index 000000000..4fd3a7e55 --- /dev/null +++ b/app/services/nameserver_validator.rb @@ -0,0 +1,47 @@ +module NameserverValidator + include Dnsruby + + extend self + + def run(domain_name:, hostname:) + validate(domain_name: domain_name, hostname: hostname) + end + + private + + def validate(domain_name: , hostname:) + resolver = setup_resolver(hostname) + result = resolver.query(domain_name, 'SOA', 'IN') + + return { result: false, reason: 'answer' } if result.answer.empty? + + decision = result.answer.all? do |a| + a.serial.present? + end + + return { result: false, reason: 'serial' } unless decision + + logger.info "Serial number - #{result.answer[0].serial.to_s} of #{hostname} - domain name: #{domain_name}" + + { result: true, reason: '' } + rescue StandardError => e + logger.error e.message + logger.error "failed #{hostname} validation of #{domain_name} domain name" + return { result: false, reason: 'exception', error_info: e } + end + + def setup_resolver(hostname) + resolver = Dnsruby::Resolver.new + resolver.retry_times = 3 + resolver.recurse = 0 # Send out non-recursive queries + # disable caching otherwise SOA is cached from first nameserver queried + resolver.do_caching = false + resolver.nameserver = hostname + + resolver + end + + def logger + @logger ||= Rails.logger + end +end diff --git a/test/integration/epp/domain/update/base_test.rb b/test/integration/epp/domain/update/base_test.rb index f11007978..d021b496d 100644 --- a/test/integration/epp/domain/update/base_test.rb +++ b/test/integration/epp/domain/update/base_test.rb @@ -8,10 +8,8 @@ class EppDomainUpdateBaseTest < EppTestCase @domain = domains(:shop) @contact = contacts(:john) @original_registrant_change_verification = - Setting.request_confirmation_on_registrant_change_enabled + Setting.request_confirmation_on_registrant_change_enabled ActionMailer::Base.deliveries.clear - - Spy.on_instance_method(Actions::DomainUpdate, :check_for_valid_nameserver).and_return(true) end teardown do diff --git a/test/integration/epp/domain/update/rem_dns_test.rb b/test/integration/epp/domain/update/rem_dns_test.rb index c2a624e29..6e079b126 100644 --- a/test/integration/epp/domain/update/rem_dns_test.rb +++ b/test/integration/epp/domain/update/rem_dns_test.rb @@ -10,10 +10,8 @@ class EppDomainUpdateRemDnsTest < EppTestCase @dnskey = dnskeys(:one) @dnskey.update(domain: @domain) @original_registrant_change_verification = - Setting.request_confirmation_on_registrant_change_enabled + Setting.request_confirmation_on_registrant_change_enabled ActionMailer::Base.deliveries.clear - - Spy.on_instance_method(Actions::DomainUpdate, :check_for_valid_nameserver).and_return(true) end teardown do diff --git a/test/integration/repp/v1/domains/contacts_test.rb b/test/integration/repp/v1/domains/contacts_test.rb index 229511a99..b9b26a745 100644 --- a/test/integration/repp/v1/domains/contacts_test.rb +++ b/test/integration/repp/v1/domains/contacts_test.rb @@ -8,7 +8,6 @@ class ReppV1DomainsContactsTest < ActionDispatch::IntegrationTest token = "Basic #{token}" @auth_headers = { 'Authorization' => token } - Spy.on_instance_method(Actions::DomainUpdate, :check_for_valid_nameserver).and_return(true) end def test_shows_existing_domain_contacts diff --git a/test/integration/repp/v1/domains/create_test.rb b/test/integration/repp/v1/domains/create_test.rb index 1c04c46d9..7907e709e 100644 --- a/test/integration/repp/v1/domains/create_test.rb +++ b/test/integration/repp/v1/domains/create_test.rb @@ -8,7 +8,6 @@ class ReppV1DomainsCreateTest < ActionDispatch::IntegrationTest token = "Basic #{token}" @auth_headers = { 'Authorization' => token } - Spy.on_instance_method(Actions::DomainCreate, :check_for_valid_nameserver).and_return(true) end def test_creates_new_domain_successfully diff --git a/test/integration/repp/v1/domains/dnssec_test.rb b/test/integration/repp/v1/domains/dnssec_test.rb index 5363729e9..6835e2600 100644 --- a/test/integration/repp/v1/domains/dnssec_test.rb +++ b/test/integration/repp/v1/domains/dnssec_test.rb @@ -8,7 +8,6 @@ class ReppV1DomainsDnssecTest < ActionDispatch::IntegrationTest token = "Basic #{token}" @auth_headers = { 'Authorization' => token } - Spy.on_instance_method(Actions::DomainUpdate, :check_for_valid_nameserver).and_return(true) end def test_shows_dnssec_keys_associated_with_domain diff --git a/test/integration/repp/v1/domains/nameservers_test.rb b/test/integration/repp/v1/domains/nameservers_test.rb index dd8434215..780e889c1 100644 --- a/test/integration/repp/v1/domains/nameservers_test.rb +++ b/test/integration/repp/v1/domains/nameservers_test.rb @@ -8,8 +8,6 @@ class ReppV1DomainsNameserversTest < ActionDispatch::IntegrationTest token = "Basic #{token}" @auth_headers = { 'Authorization' => token } - - Spy.on_instance_method(Actions::DomainUpdate, :check_for_valid_nameserver).and_return(true) end def test_can_add_new_nameserver diff --git a/test/integration/repp/v1/domains/update_test.rb b/test/integration/repp/v1/domains/update_test.rb index b60bdd804..d924fe7a3 100644 --- a/test/integration/repp/v1/domains/update_test.rb +++ b/test/integration/repp/v1/domains/update_test.rb @@ -8,7 +8,6 @@ class ReppV1DomainsUpdateTest < ActionDispatch::IntegrationTest token = "Basic #{token}" @auth_headers = { 'Authorization' => token } - Spy.on_instance_method(Actions::DomainUpdate, :check_for_valid_nameserver).and_return(true) end def test_updates_transfer_code_for_domain diff --git a/test/jobs/domain_update_confirm_job_test.rb b/test/jobs/domain_update_confirm_job_test.rb index 4d7cc3a9c..158729ae3 100644 --- a/test/jobs/domain_update_confirm_job_test.rb +++ b/test/jobs/domain_update_confirm_job_test.rb @@ -14,8 +14,6 @@ class DomainUpdateConfirmJobTest < ActiveSupport::TestCase new_registrant_name: @new_registrant.name, new_registrant_email: @new_registrant.email, current_user_id: @user.id }) - - Spy.on_instance_method(Actions::DomainUpdate, :check_for_valid_nameserver).and_return(true) end def teardown From d896e5f716b9dc148ce40f3920288190207e4f90 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Thu, 6 Jan 2022 14:33:06 +0200 Subject: [PATCH 11/19] added new fields into nameserver model --- ...0106123143_add_validation_fields_to_nameserver.rb | 7 +++++++ db/structure.sql | 12 +++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20220106123143_add_validation_fields_to_nameserver.rb diff --git a/db/migrate/20220106123143_add_validation_fields_to_nameserver.rb b/db/migrate/20220106123143_add_validation_fields_to_nameserver.rb new file mode 100644 index 000000000..82d9ec794 --- /dev/null +++ b/db/migrate/20220106123143_add_validation_fields_to_nameserver.rb @@ -0,0 +1,7 @@ +class AddValidationFieldsToNameserver < ActiveRecord::Migration[6.1] + def change + add_column :nameservers, :validation_datetime, :datetime + add_column :nameservers, :validation_counter, :integer + add_column :nameservers, :failed_validation_reason, :string + end +end diff --git a/db/structure.sql b/db/structure.sql index 4d539a7a6..9afc1742f 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -824,7 +824,8 @@ CREATE TABLE public.dnskeys ( creator_str character varying, updator_str character varying, legacy_domain_id integer, - updated_at timestamp without time zone + updated_at timestamp without time zone, + validation_datetime timestamp without time zone ); @@ -2177,7 +2178,10 @@ CREATE TABLE public.nameservers ( creator_str character varying, updator_str character varying, legacy_domain_id integer, - hostname_puny character varying + hostname_puny character varying, + validation_datetime timestamp without time zone, + validation_counter integer, + failed_validation_reason character varying ); @@ -5389,6 +5393,8 @@ INSERT INTO "schema_migrations" (version) VALUES ('20211124084308'), ('20211125181033'), ('20211125184334'), -('20211126085139'); +('20211126085139'), +('20211231113934'), +('20220106123143'); From a9a95e373c1240017d60e7719368ecce69afff02 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Thu, 6 Jan 2022 15:23:21 +0200 Subject: [PATCH 12/19] added record for successfully validated nameservers --- app/jobs/nameserver_record_validation_job.rb | 24 +++++++++++++++++--- app/models/nameserver.rb | 14 ++++++++++++ app/services/nameserver_validator.rb | 4 ++++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/app/jobs/nameserver_record_validation_job.rb b/app/jobs/nameserver_record_validation_job.rb index a3be73aaf..5d7994908 100644 --- a/app/jobs/nameserver_record_validation_job.rb +++ b/app/jobs/nameserver_record_validation_job.rb @@ -7,14 +7,18 @@ class NameserverRecordValidationJob < ApplicationJob def perform(domain_name: nil) if domain_name.nil? - domains = Domain.all.select { |domain| domain.nameservers.exists? }. - select { |domain| domain.created_at < Time.zone.now - 8.hours } + domains = Domain.all.select { |domain| domain.created_at < Time.zone.now - NameserverValidator::VALIDATION_DOMAIN_PERIOD } + .select { |domain| domain.nameservers.exists? } domains.each do |domain| domain.nameservers.each do |nameserver| + next if nameserver.nameserver_failed_validation? || nameserver.validated? + result = NameserverValidator.run(domain_name: domain.name, hostname: nameserver.hostname) if result[:result] + add_nameserver_to_succesfully(nameserver) + true else parse_result(result, nameserver) @@ -26,10 +30,16 @@ class NameserverRecordValidationJob < ApplicationJob domain = Domain.find_by(name: domain_name) return logger.info 'Domain not found' if domain.nil? - return logger.info 'It should take 8 hours after the domain was created' if domain.created_at > Time.zone.now - 8.hours + + if domain.created_at > Time.zone.now - NameserverValidator::VALIDATION_DOMAIN_PERIOD + return logger.info "It should take #{NameserverValidator::VALIDATION_DOMAIN_PERIOD} hours after the domain was created" + end + return logger.info 'Domain not has nameservers' if domain.nameservers.empty? domain.nameservers.each do |nameserver| + next if nameserver.nameserver_failed_validation? + result = NameserverValidator.run(domain_name: domain.name, hostname: nameserver.hostname) if result[:result] @@ -44,6 +54,14 @@ class NameserverRecordValidationJob < ApplicationJob private + def add_nameserver_to_succesfully(nameserver) + nameserver.validation_counter = nil + nameserver.failed_validation_reason = nil + nameserver.validation_datetime = Time.zone.now + + nameserver.save + end + def parse_result(result, nameserver) text = "" case result[:reason] diff --git a/app/models/nameserver.rb b/app/models/nameserver.rb index 75255eb89..20d4656c6 100644 --- a/app/models/nameserver.rb +++ b/app/models/nameserver.rb @@ -34,6 +34,8 @@ class Nameserver < ApplicationRecord delegate :name, to: :domain, prefix: true + scope :non_validated, -> { where(validation_datetime: nil) } + self.ignored_columns = %w[legacy_domain_id] def epp_code_map @@ -53,6 +55,18 @@ class Nameserver < ApplicationRecord } end + def nameserver_failed_validation? + return false if validation_counter.nil? + + validation_counter >= NameserverValidator::VALID_NAMESERVER_COUNT_THRESHOLD + end + + def validated? + return false if validation_datetime.nil? + + validation_datetime + NameserverValidator::VALIDATION_NAMESERVER_PERIOD > Time.zone.now + end + def to_s hostname end diff --git a/app/services/nameserver_validator.rb b/app/services/nameserver_validator.rb index 4fd3a7e55..319a8dc92 100644 --- a/app/services/nameserver_validator.rb +++ b/app/services/nameserver_validator.rb @@ -3,6 +3,10 @@ module NameserverValidator extend self + VALIDATION_NAMESERVER_PERIOD = 1.year.freeze + VALIDATION_DOMAIN_PERIOD = 8.hours.freeze + VALID_NAMESERVER_COUNT_THRESHOLD = 3 + def run(domain_name:, hostname:) validate(domain_name: domain_name, hostname: hostname) end From 71e80e8418909aafe6ca25583cd5136a92077795 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Thu, 6 Jan 2022 15:51:35 +0200 Subject: [PATCH 13/19] added records for invalid nameservers --- app/jobs/nameserver_record_validation_job.rb | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/app/jobs/nameserver_record_validation_job.rb b/app/jobs/nameserver_record_validation_job.rb index 5d7994908..5647b62ba 100644 --- a/app/jobs/nameserver_record_validation_job.rb +++ b/app/jobs/nameserver_record_validation_job.rb @@ -62,21 +62,33 @@ class NameserverRecordValidationJob < ApplicationJob nameserver.save end + def add_nameserver_to_failed(nameserver:, reason:) + if nameserver.validation_counter.nil? + nameserver.validation_counter = 1 + else + nameserver.validation_counter = nameserver.validation_counter + 1 + end + + nameserver.failed_validation_reason = reason + nameserver.save + end + def parse_result(result, nameserver) text = "" case result[:reason] when 'answer' - text = "No any answer come from **#{nameserver}**" + text = "No any answer comes from **#{nameserver}**. Nameserver not exist" when 'serial' - text = "Serial number for nameserver hostname **#{nameserver}** doesn't present. Seems nameservers out the zone" + text = "Serial number for nameserver hostname **#{nameserver}** doesn't present. SOA validation failed." when 'not found' text = "Seems nameserver hostname **#{nameserver}** doesn't exist" when 'exception' - text = "Something goes wrong, exception name: **#{result[:error_info]}**" + text = "Something goes wrong, exception reason: **#{result[:error_info]}**" end logger.info text failed_log(text: text, nameserver: nameserver) + add_nameserver_to_failed(nameserver: nameserver, reason: text) false end From 2ad5eb9dccd0a297c31613af4114feba4ed75a61 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Thu, 6 Jan 2022 16:25:28 +0200 Subject: [PATCH 14/19] update error messages reason --- app/jobs/nameserver_record_validation_job.rb | 10 +++++++--- app/services/nameserver_validator.rb | 8 ++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/jobs/nameserver_record_validation_job.rb b/app/jobs/nameserver_record_validation_job.rb index 5647b62ba..aa1761995 100644 --- a/app/jobs/nameserver_record_validation_job.rb +++ b/app/jobs/nameserver_record_validation_job.rb @@ -74,16 +74,20 @@ class NameserverRecordValidationJob < ApplicationJob end def parse_result(result, nameserver) + domain = Domain.find(nameserver.domain_id) + text = "" case result[:reason] when 'answer' - text = "No any answer comes from **#{nameserver}**. Nameserver not exist" + text = "No any answer comes from **#{nameserver.hostname}**. Nameserver not exist" when 'serial' - text = "Serial number for nameserver hostname **#{nameserver}** doesn't present. SOA validation failed." + text = "Serial number for nameserver hostname **#{nameserver.hostname}** doesn't present. SOA validation failed." when 'not found' - text = "Seems nameserver hostname **#{nameserver}** doesn't exist" + text = "Seems nameserver hostname **#{nameserver.hostname}** doesn't exist" when 'exception' text = "Something goes wrong, exception reason: **#{result[:error_info]}**" + when 'domain' + text = "#{domain} not found in zone" end logger.info text diff --git a/app/services/nameserver_validator.rb b/app/services/nameserver_validator.rb index 319a8dc92..aaa1f00a3 100644 --- a/app/services/nameserver_validator.rb +++ b/app/services/nameserver_validator.rb @@ -28,6 +28,14 @@ module NameserverValidator logger.info "Serial number - #{result.answer[0].serial.to_s} of #{hostname} - domain name: #{domain_name}" { result: true, reason: '' } + rescue Dnsruby::Refused => e + logger.error e.message + logger.error "failed #{hostname} validation of #{domain_name} domain name. Domain not found" + return { result: false, reason: 'domain', error_info: e } + rescue Dnsruby::NXDomain => e + logger.error e.message + logger.error "failed #{hostname} validation of #{domain_name} domain name. Domain not found" + return { result: false, reason: 'domain', error_info: e } rescue StandardError => e logger.error e.message logger.error "failed #{hostname} validation of #{domain_name} domain name" From 7ea08518fe741f5d10aaee785734a62cbce82130 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Thu, 6 Jan 2022 16:42:17 +0200 Subject: [PATCH 15/19] updated old tests --- test/models/validation_event_test.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/models/validation_event_test.rb b/test/models/validation_event_test.rb index e4cb6d5b7..86f13885f 100644 --- a/test/models/validation_event_test.rb +++ b/test/models/validation_event_test.rb @@ -6,7 +6,6 @@ class ValidationEventTest < ActiveSupport::TestCase @domain = domains(:shop) Setting.redemption_grace_period = 30 ActionMailer::Base.deliveries.clear - Spy.on_instance_method(Domains::NameserverValidator, :run).and_return({result: true, reason: ''}) end teardown do From e24301ad0c73fa7c0a0ee6cfe44e4707d6344f2e Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Fri, 7 Jan 2022 11:04:14 +0200 Subject: [PATCH 16/19] added test --- app/jobs/nameserver_record_validation_job.rb | 2 + .../nameserver_record_validation_job_test.rb | 90 ++++++++++++++++++- 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/app/jobs/nameserver_record_validation_job.rb b/app/jobs/nameserver_record_validation_job.rb index aa1761995..da32196c3 100644 --- a/app/jobs/nameserver_record_validation_job.rb +++ b/app/jobs/nameserver_record_validation_job.rb @@ -43,6 +43,8 @@ class NameserverRecordValidationJob < ApplicationJob result = NameserverValidator.run(domain_name: domain.name, hostname: nameserver.hostname) if result[:result] + add_nameserver_to_succesfully(nameserver) + true else parse_result(result, nameserver) diff --git a/test/jobs/nameserver_record_validation_job_test.rb b/test/jobs/nameserver_record_validation_job_test.rb index 7362cc6c1..a8d625ca1 100644 --- a/test/jobs/nameserver_record_validation_job_test.rb +++ b/test/jobs/nameserver_record_validation_job_test.rb @@ -5,6 +5,94 @@ class NameserverRecordValidationJobTest < ActiveSupport::TestCase setup do @nameserver = nameservers(:shop_ns1) - Spy.on_instance_method(Domains::NameserverValidator, :run).and_return({result: true, reason: ''}) + @domain = domains(:shop) + @domain.update(created_at: Time.zone.now - 10.hours) + @domain.reload + end + + def test_nameserver_should_validate_succesfully_and_set_validation_datetime + mock_dns_response = OpenStruct.new + answer = OpenStruct.new + answer.serial = '12343' + mock_dns_response.answer = [ answer ] + + Spy.on_instance_method(NameserverValidator, :setup_resolver).and_return(Dnsruby::Resolver.new) + Spy.on_instance_method(Dnsruby::Resolver, :query).and_return(mock_dns_response) + + assert_nil @nameserver.validation_datetime + assert_nil @nameserver.validation_counter + assert_nil @nameserver.failed_validation_reason + + NameserverRecordValidationJob.perform_now(domain_name: @domain.name) + @nameserver.reload + + assert_not_nil @nameserver.validation_datetime + assert_nil @nameserver.validation_counter + assert_nil @nameserver.failed_validation_reason + end + + def test_should_return_failed_validation_with_answer_reason + mock_dns_response = OpenStruct.new + mock_dns_response.answer = [ ] + + Spy.on_instance_method(NameserverValidator, :setup_resolver).and_return(Dnsruby::Resolver.new) + Spy.on_instance_method(Dnsruby::Resolver, :query).and_return(mock_dns_response) + + assert_nil @nameserver.validation_datetime + assert_nil @nameserver.validation_counter + assert_nil @nameserver.failed_validation_reason + + NameserverRecordValidationJob.perform_now(domain_name: @domain.name) + @nameserver.reload + + assert_nil @nameserver.validation_datetime + assert @nameserver.validation_counter, 1 + assert @nameserver.failed_validation_reason.include? "No any answer comes from **#{@nameserver.hostname}**" + end + + def test_should_return_failed_validation_with_serial_reason + mock_dns_response = OpenStruct.new + answer = OpenStruct.new + answer.some_field = '12343' + mock_dns_response.answer = [ answer ] + + Spy.on_instance_method(NameserverValidator, :setup_resolver).and_return(Dnsruby::Resolver.new) + Spy.on_instance_method(Dnsruby::Resolver, :query).and_return(mock_dns_response) + + assert_nil @nameserver.validation_datetime + assert_nil @nameserver.validation_counter + assert_nil @nameserver.failed_validation_reason + + NameserverRecordValidationJob.perform_now(domain_name: @domain.name) + @nameserver.reload + + assert_nil @nameserver.validation_datetime + assert @nameserver.validation_counter, 1 + assert @nameserver.failed_validation_reason.include? "Serial number for nameserver hostname **#{@nameserver.hostname}** doesn't present. SOA validation failed." + end + + def test_after_third_invalid_times_nameserver_should_be_invalid + mock_dns_response = OpenStruct.new + answer = OpenStruct.new + answer.some_field = '12343' + mock_dns_response.answer = [ answer ] + + Spy.on_instance_method(NameserverValidator, :setup_resolver).and_return(Dnsruby::Resolver.new) + Spy.on_instance_method(Dnsruby::Resolver, :query).and_return(mock_dns_response) + + assert_nil @nameserver.validation_datetime + assert_nil @nameserver.validation_counter + assert_nil @nameserver.failed_validation_reason + + 3.times do + NameserverRecordValidationJob.perform_now(domain_name: @domain.name) + end + + @nameserver.reload + assert_nil @nameserver.validation_datetime + assert @nameserver.validation_counter, 1 + assert @nameserver.failed_validation_reason.include? "Serial number for nameserver hostname **#{@nameserver.hostname}** doesn't present. SOA validation failed." + + assert @nameserver.nameserver_failed_validation? end end From 156633cd024d6d38b21108f72480f06d4a53da29 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Fri, 7 Jan 2022 12:27:16 +0200 Subject: [PATCH 17/19] added glue record support --- app/jobs/nameserver_record_validation_job.rb | 8 +++---- app/services/nameserver_validator.rb | 22 +++++++++++++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/app/jobs/nameserver_record_validation_job.rb b/app/jobs/nameserver_record_validation_job.rb index da32196c3..11c0038b7 100644 --- a/app/jobs/nameserver_record_validation_job.rb +++ b/app/jobs/nameserver_record_validation_job.rb @@ -14,7 +14,7 @@ class NameserverRecordValidationJob < ApplicationJob domain.nameservers.each do |nameserver| next if nameserver.nameserver_failed_validation? || nameserver.validated? - result = NameserverValidator.run(domain_name: domain.name, hostname: nameserver.hostname) + result = NameserverValidator.run(domain_name: domain.name, nameserver: nameserver) if result[:result] add_nameserver_to_succesfully(nameserver) @@ -40,7 +40,7 @@ class NameserverRecordValidationJob < ApplicationJob domain.nameservers.each do |nameserver| next if nameserver.nameserver_failed_validation? - result = NameserverValidator.run(domain_name: domain.name, hostname: nameserver.hostname) + result = NameserverValidator.run(domain_name: domain.name, nameserver: nameserver) if result[:result] add_nameserver_to_succesfully(nameserver) @@ -87,9 +87,9 @@ class NameserverRecordValidationJob < ApplicationJob when 'not found' text = "Seems nameserver hostname **#{nameserver.hostname}** doesn't exist" when 'exception' - text = "Something goes wrong, exception reason: **#{result[:error_info]}**" + text = "Something went wrong, exception reason: **#{result[:error_info]}**" when 'domain' - text = "#{domain} not found in zone" + text = "#{domain} zone is not in nameserver**#{nameserver.hostname}**" end logger.info text diff --git a/app/services/nameserver_validator.rb b/app/services/nameserver_validator.rb index aaa1f00a3..25ebf16b0 100644 --- a/app/services/nameserver_validator.rb +++ b/app/services/nameserver_validator.rb @@ -7,13 +7,28 @@ module NameserverValidator VALIDATION_DOMAIN_PERIOD = 8.hours.freeze VALID_NAMESERVER_COUNT_THRESHOLD = 3 - def run(domain_name:, hostname:) - validate(domain_name: domain_name, hostname: hostname) + def run(domain_name:, nameserver:) + result_response = validate(domain_name: domain_name, hostname: nameserver.hostname) + + unless result_response[:result] && result_response[:reason] == :exception + if result_response[:error_info].to_s.include? "Nameserver invalid!" + if nameserver.ipv4.present? + p "+++++++" + result_response = validate(domain_name: domain_name, hostname: nameserver.ipv4) + elsif nameserver.ipv6.present? + result_response = validate(domain_name: domain_name, hostname: nameserver.ipv6) + end + + result_response + end + end + + result_response end private - def validate(domain_name: , hostname:) + def validate(domain_name:, hostname:) resolver = setup_resolver(hostname) result = resolver.query(domain_name, 'SOA', 'IN') @@ -44,6 +59,7 @@ module NameserverValidator def setup_resolver(hostname) resolver = Dnsruby::Resolver.new + resolver.query_timeout = 2 resolver.retry_times = 3 resolver.recurse = 0 # Send out non-recursive queries # disable caching otherwise SOA is cached from first nameserver queried From ded671530963821bfd27b1b48505451a1c864a5a Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Fri, 7 Jan 2022 13:18:05 +0200 Subject: [PATCH 18/19] change query timeout --- app/services/nameserver_validator.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/services/nameserver_validator.rb b/app/services/nameserver_validator.rb index 25ebf16b0..d4ce8dc71 100644 --- a/app/services/nameserver_validator.rb +++ b/app/services/nameserver_validator.rb @@ -13,7 +13,6 @@ module NameserverValidator unless result_response[:result] && result_response[:reason] == :exception if result_response[:error_info].to_s.include? "Nameserver invalid!" if nameserver.ipv4.present? - p "+++++++" result_response = validate(domain_name: domain_name, hostname: nameserver.ipv4) elsif nameserver.ipv6.present? result_response = validate(domain_name: domain_name, hostname: nameserver.ipv6) @@ -59,7 +58,7 @@ module NameserverValidator def setup_resolver(hostname) resolver = Dnsruby::Resolver.new - resolver.query_timeout = 2 + resolver.query_timeout = ENV['nameserver_validation_timeout'].to_i || 5 resolver.retry_times = 3 resolver.recurse = 0 # Send out non-recursive queries # disable caching otherwise SOA is cached from first nameserver queried From 37a80faf1430bd7597a0612196099e7f292199e0 Mon Sep 17 00:00:00 2001 From: olegphenomenon Date: Fri, 7 Jan 2022 14:51:58 +0200 Subject: [PATCH 19/19] changed conditions for glue records --- app/jobs/nameserver_record_validation_job.rb | 2 ++ app/services/nameserver_validator.rb | 11 +++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/jobs/nameserver_record_validation_job.rb b/app/jobs/nameserver_record_validation_job.rb index 11c0038b7..a1787e9d7 100644 --- a/app/jobs/nameserver_record_validation_job.rb +++ b/app/jobs/nameserver_record_validation_job.rb @@ -90,6 +90,8 @@ class NameserverRecordValidationJob < ApplicationJob text = "Something went wrong, exception reason: **#{result[:error_info]}**" when 'domain' text = "#{domain} zone is not in nameserver**#{nameserver.hostname}**" + when 'glup record' + text = "Hostname #{nameserver.hostname} didn't resovle by glue record to #{domain}" end logger.info text diff --git a/app/services/nameserver_validator.rb b/app/services/nameserver_validator.rb index d4ce8dc71..410448472 100644 --- a/app/services/nameserver_validator.rb +++ b/app/services/nameserver_validator.rb @@ -14,11 +14,13 @@ module NameserverValidator if result_response[:error_info].to_s.include? "Nameserver invalid!" if nameserver.ipv4.present? result_response = validate(domain_name: domain_name, hostname: nameserver.ipv4) - elsif nameserver.ipv6.present? - result_response = validate(domain_name: domain_name, hostname: nameserver.ipv6) + # elsif nameserver.ipv6.present? + # result_response = validate(domain_name: domain_name, hostname: nameserver.ipv6) end - result_response + return { result: false, reason: 'glup record' } if result.answer.empty? if result_response[:result] + + return result_response end end @@ -58,7 +60,8 @@ module NameserverValidator def setup_resolver(hostname) resolver = Dnsruby::Resolver.new - resolver.query_timeout = ENV['nameserver_validation_timeout'].to_i || 5 + timeouts = ENV['nameserver_validation_timeout'] || 4 + resolver.query_timeout = timeouts.to_i resolver.retry_times = 3 resolver.recurse = 0 # Send out non-recursive queries # disable caching otherwise SOA is cached from first nameserver queried