From 34410a8ed5c199c0f11cabce3a3f5b54a4a8d486 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Tue, 11 Nov 2014 17:00:50 +0200 Subject: [PATCH 1/4] Basic zonefile generating --- app/controllers/admin/domains_controller.rb | 7 +- app/models/domain.rb | 29 ++++ app/views/admin/domains/zonefile.haml | 2 + config/application.rb | 2 + config/routes.rb | 6 +- lib/zonefile.rb | 148 ++++++++++++++++++++ 6 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 app/views/admin/domains/zonefile.haml create mode 100644 lib/zonefile.rb diff --git a/app/controllers/admin/domains_controller.rb b/app/controllers/admin/domains_controller.rb index a5f50447e..c12492edb 100644 --- a/app/controllers/admin/domains_controller.rb +++ b/app/controllers/admin/domains_controller.rb @@ -1,5 +1,5 @@ class Admin::DomainsController < AdminController - before_action :set_domain, only: [:show, :edit, :update] + before_action :set_domain, only: [:show, :edit, :update, :zonefile] def index @q = Domain.includes(:registrar, :owner_contact).search(params[:q]) @@ -27,6 +27,11 @@ class Admin::DomainsController < AdminController end end + def zonefile + @zonefile = @domain.generate_zonefile + # send_data @zonefile, filename: 'bla.txt' + end + private def set_domain diff --git a/app/models/domain.rb b/app/models/domain.rb index 4e3f6a937..621ba123b 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -298,6 +298,35 @@ class Domain < ActiveRecord::Base end end + def generate_zonefile + zf = Zonefile.new + zf.ttl = '3600' + zf.origin = "#{name}." + ns = nameservers.first + zf.soa[:primary_ns] = "#{ns.hostname}." + zf.soa[:email] = 'hostmaster.internet.ee' + zf.soa[:origin] = "#{name}." + zf.soa[:refresh] = '10800' + zf.soa[:retry] = '3600' + zf.soa[:expire] = '604800' + zf.soa[:minimumTTL] = '3600' + + nameservers.each do |x| + zf.ns << { name: "#{name}.", class: 'IN', host: "#{x.hostname}." } + end + + dnskeys.each do |x| + zf.ds << { name: "#{name}.", ttl: '86400', class: 'IN', key_tag: x.ds_key_tag, algorithm: x.ds_alg, + digest_type: x.ds_digest_type, digest: x.ds_digest } + + zf.dnskey << { name: "#{name}.", ttl: '86400', class: 'IN', flag: x.flags, + protocol: x.protocol, algorithm: x.alg, public_key: x.public_key } + end + + zf.new_serial + zf.generate + end + class << self def convert_period_to_time(period, unit) return period.to_i.days if unit == 'd' diff --git a/app/views/admin/domains/zonefile.haml b/app/views/admin/domains/zonefile.haml new file mode 100644 index 000000000..3c5b588ee --- /dev/null +++ b/app/views/admin/domains/zonefile.haml @@ -0,0 +1,2 @@ += preserve do + %pre= @zonefile diff --git a/config/application.rb b/config/application.rb index 8e4a6a7c2..f119c2ebd 100644 --- a/config/application.rb +++ b/config/application.rb @@ -27,6 +27,8 @@ module Registry # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # config.i18n.default_locale = :de + config.autoload_paths += %W(#{config.root}/lib) + config.generators do |g| g.stylesheets false g.javascripts false diff --git a/config/routes.rb b/config/routes.rb index a3aacba3c..b381b856d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -7,7 +7,11 @@ Rails.application.routes.draw do ## ADMIN ROUTES namespace(:admin) do - resources :domains + resources :domains do + member do + get 'zonefile' + end + end resources :settings resources :registrars do collection do diff --git a/lib/zonefile.rb b/lib/zonefile.rb new file mode 100644 index 000000000..d1d7cc7d5 --- /dev/null +++ b/lib/zonefile.rb @@ -0,0 +1,148 @@ +class Zonefile + + RECORDS = [:mx, :a, :a4, :ns, :cname, :txt, :ptr, :srv, :soa, :ds, + :dnskey, :rrsig, :nsec,:nsec3, :nsec3param, :tlsa, :naptr] + + attr_accessor(*RECORDS, :ttl, :origin) + + def initialize(obj = {}) + RECORDS.each do |x| + if x == :soa + send("#{x}=", {}) + else + send("#{x}=", []) + end + end + + obj.each do |k, v| + send("#{k}=", v) + end + end + + def new_serial + base = "%04d%02d%02d" % [Time.now.year, Time.now.month, Time.now.day] + + if (@soa[:serial].to_i / 100) > base.to_i + ns = @soa[:serial].to_i + 1 + @soa[:serial] = ns.to_s + return ns.to_s + end + + ii = 0 + while (("#{base}%02d" % ii).to_i <= @soa[:serial].to_i) do + ii += 1 + end + @soa[:serial] = "#{base}%02d" % ii + end + + def generate + out = <<-eos +$ORIGIN #{origin} ; designates the start of this zone file in the namespace +$TTL #{ttl} ; default expiration time of all resource records without their own TTL value + +#{self.soa[:origin]} #{self.soa[:ttl]} IN SOA #{self.soa[:primary_ns]} #{self.soa[:email]} ( + #{sprintf('%-13s', soa[:serial])}; serial number + #{sprintf('%-13s', soa[:refresh])}; refresh, seconds + #{sprintf('%-13s', soa[:retry])}; retry, seconds + #{sprintf('%-13s', soa[:expire])}; expire, seconds + #{sprintf('%-13s', soa[:minimumTTL])}; minimum TTL, seconds + ) + eos + + self.ns.each do |ns| + out << "#{ns[:name]} #{ns[:ttl]} #{ns[:class]} NS #{ns[:host]}\n" + end + + out << "\n; Zone MX Records\n" unless self.mx.empty? + + self.mx.each do |mx| + out << "#{mx[:name]} #{mx[:ttl]} #{mx[:class]} MX #{mx[:pri]} #{mx[:host]}\n" + end + + out << "\n; Zone A Records\n" unless self.a.empty? + + self.a.each do |a| + out << "#{a[:name]} #{a[:ttl]} #{a[:class]} A #{a[:host]}\n" + end + + out << "\n; Zone CNAME Records\n" unless self.cname.empty? + + self.cname.each do |cn| + out << "#{cn[:name]} #{cn[:ttl]} #{cn[:class]} CNAME #{cn[:host]}\n" + end + + out << "\n; Zone AAAA Records\n" unless self.a4.empty? + + self.a4.each do |a4| + out << "#{a4[:name]} #{a4[:ttl]} #{a4[:class]} AAAA #{a4[:host]}\n" + end + + out << "\n; Zone TXT Records\n" unless self.txt.empty? + + self.txt.each do |tx| + out << "#{tx[:name]} #{tx[:ttl]} #{tx[:class]} TXT #{tx[:text]}\n" + end + + out << "\n; Zone SRV Records\n" unless self.srv.empty? + + self.srv.each do |srv| + out << "#{srv[:name]} #{srv[:ttl]} #{srv[:class]} SRV #{srv[:pri]} #{srv[:weight]} #{srv[:port]} #{srv[:host]}\n" + end + + out << "\n; Zone PTR Records\n" unless self.ptr.empty? + + self.ptr.each do |ptr| + out << "#{ptr[:name]} #{ptr[:ttl]} #{ptr[:class]} PTR #{ptr[:host]}\n" + end + + out << "\n; Zone DS Records\n" unless self.ds.empty? + + self.ds.each do |ds| + out << "#{ds[:name]} #{ds[:ttl]} #{ds[:class]} DS #{ds[:key_tag]} #{ds[:algorithm]} #{ds[:digest_type]} #{ds[:digest]}\n" + end + + out << "\n; Zone NSEC Records\n" unless self.ds.empty? + + self.nsec.each do |nsec| + out << "#{nsec[:name]} #{nsec[:ttl]} #{nsec[:class]} NSEC #{nsec[:next]} #{nsec[:types]}\n" + end + + out << "\n; Zone NSEC3 Records\n" unless self.ds.empty? + + self.nsec3.each do |nsec3| + out << "#{nsec3[:name]} #{nsec3[:ttl]} #{nsec3[:class]} NSEC3 #{nsec3[:algorithm]} #{nsec3[:flags]} #{nsec3[:iterations]} #{nsec3[:salt]} #{nsec3[:next]} #{nsec3[:types]}\n" + end + + out << "\n; Zone NSEC3PARAM Records\n" unless self.ds.empty? + + self.nsec3param.each do |nsec3param| + out << "#{nsec3param[:name]} #{nsec3param[:ttl]} #{nsec3param[:class]} NSEC3PARAM #{nsec3param[:algorithm]} #{nsec3param[:flags]} #{nsec3param[:iterations]} #{nsec3param[:salt]}\n" + end + + out << "\n; Zone DNSKEY Records\n" unless self.ds.empty? + + self.dnskey.each do |dnskey| + out << "#{dnskey[:name]} #{dnskey[:ttl]} #{dnskey[:class]} DNSKEY #{dnskey[:flag]} #{dnskey[:protocol]} #{dnskey[:algorithm]} #{dnskey[:public_key]}\n" + end + + out << "\n; Zone RRSIG Records\n" unless self.ds.empty? + + self.rrsig.each do |rrsig| + out << "#{rrsig[:name]} #{rrsig[:ttl]} #{rrsig[:class]} RRSIG #{rrsig[:type_covered]} #{rrsig[:algorithm]} #{rrsig[:labels]} #{rrsig[:original_ttl]} #{rrsig[:expiration]} #{rrsig[:inception]} #{rrsig[:key_tag]} #{rrsig[:signer]} #{rrsig[:signature]}\n" + end + + out << "\n; Zone TLSA Records\n" unless self.tlsa.empty? + + self.tlsa.each do |tlsa| + out << "#{tlsa[:name]} #{tlsa[:ttl]} #{tlsa[:class]} TLSA #{tlsa[:certificate_usage]} #{tlsa[:selector]} #{tlsa[:matching_type]} #{tlsa[:data]}\n" + end + + out << "\n; Zone NAPTR Records\n" unless self.ds.empty? + + self.naptr.each do |naptr| + out << "#{naptr[:name]} #{naptr[:ttl]} #{naptr[:class]} NAPTR #{naptr[:order]} #{naptr[:preference]} #{naptr[:flags]} #{naptr[:service]} #{naptr[:regexp]} #{naptr[:replacement]}\n" + end + + out + end +end From 2a2acf92d5d8792dbff9a7e5a87551bb92620b2e Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Tue, 11 Nov 2014 17:02:20 +0200 Subject: [PATCH 2/4] Fix most of the rubocop errors --- lib/zonefile.rb | 89 ++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/lib/zonefile.rb b/lib/zonefile.rb index d1d7cc7d5..565538fd0 100644 --- a/lib/zonefile.rb +++ b/lib/zonefile.rb @@ -1,7 +1,6 @@ class Zonefile - RECORDS = [:mx, :a, :a4, :ns, :cname, :txt, :ptr, :srv, :soa, :ds, - :dnskey, :rrsig, :nsec,:nsec3, :nsec3param, :tlsa, :naptr] + :dnskey, :rrsig, :nsec, :nsec3, :nsec3param, :tlsa, :naptr] attr_accessor(*RECORDS, :ttl, :origin) @@ -20,19 +19,19 @@ class Zonefile end def new_serial - base = "%04d%02d%02d" % [Time.now.year, Time.now.month, Time.now.day] + base = '%04d%02d%02d' % [Time.now.year, Time.now.month, Time.now.day] - if (@soa[:serial].to_i / 100) > base.to_i - ns = @soa[:serial].to_i + 1 - @soa[:serial] = ns.to_s - return ns.to_s - end + if (@soa[:serial].to_i / 100) > base.to_i + ns = @soa[:serial].to_i + 1 + @soa[:serial] = ns.to_s + return ns.to_s + end - ii = 0 - while (("#{base}%02d" % ii).to_i <= @soa[:serial].to_i) do - ii += 1 - end - @soa[:serial] = "#{base}%02d" % ii + ii = 0 + while (("#{base}%02d" % ii).to_i <= @soa[:serial].to_i) + ii += 1 + end + @soa[:serial] = "#{base}%02d" % ii end def generate @@ -40,7 +39,7 @@ class Zonefile $ORIGIN #{origin} ; designates the start of this zone file in the namespace $TTL #{ttl} ; default expiration time of all resource records without their own TTL value -#{self.soa[:origin]} #{self.soa[:ttl]} IN SOA #{self.soa[:primary_ns]} #{self.soa[:email]} ( +#{soa[:origin]} #{soa[:ttl]} IN SOA #{soa[:primary_ns]} #{soa[:email]} ( #{sprintf('%-13s', soa[:serial])}; serial number #{sprintf('%-13s', soa[:refresh])}; refresh, seconds #{sprintf('%-13s', soa[:retry])}; retry, seconds @@ -49,98 +48,98 @@ $TTL #{ttl} ; default expiration time of all resource records witho ) eos - self.ns.each do |ns| + ns.each do |ns| out << "#{ns[:name]} #{ns[:ttl]} #{ns[:class]} NS #{ns[:host]}\n" end - out << "\n; Zone MX Records\n" unless self.mx.empty? + out << "\n; Zone MX Records\n" unless mx.empty? - self.mx.each do |mx| + mx.each do |mx| out << "#{mx[:name]} #{mx[:ttl]} #{mx[:class]} MX #{mx[:pri]} #{mx[:host]}\n" end - out << "\n; Zone A Records\n" unless self.a.empty? + out << "\n; Zone A Records\n" unless a.empty? - self.a.each do |a| + a.each do |a| out << "#{a[:name]} #{a[:ttl]} #{a[:class]} A #{a[:host]}\n" end - out << "\n; Zone CNAME Records\n" unless self.cname.empty? + out << "\n; Zone CNAME Records\n" unless cname.empty? - self.cname.each do |cn| + cname.each do |cn| out << "#{cn[:name]} #{cn[:ttl]} #{cn[:class]} CNAME #{cn[:host]}\n" end - out << "\n; Zone AAAA Records\n" unless self.a4.empty? + out << "\n; Zone AAAA Records\n" unless a4.empty? - self.a4.each do |a4| + a4.each do |a4| out << "#{a4[:name]} #{a4[:ttl]} #{a4[:class]} AAAA #{a4[:host]}\n" end - out << "\n; Zone TXT Records\n" unless self.txt.empty? + out << "\n; Zone TXT Records\n" unless txt.empty? - self.txt.each do |tx| + txt.each do |tx| out << "#{tx[:name]} #{tx[:ttl]} #{tx[:class]} TXT #{tx[:text]}\n" end - out << "\n; Zone SRV Records\n" unless self.srv.empty? + out << "\n; Zone SRV Records\n" unless srv.empty? - self.srv.each do |srv| + srv.each do |srv| out << "#{srv[:name]} #{srv[:ttl]} #{srv[:class]} SRV #{srv[:pri]} #{srv[:weight]} #{srv[:port]} #{srv[:host]}\n" end - out << "\n; Zone PTR Records\n" unless self.ptr.empty? + out << "\n; Zone PTR Records\n" unless ptr.empty? - self.ptr.each do |ptr| + ptr.each do |ptr| out << "#{ptr[:name]} #{ptr[:ttl]} #{ptr[:class]} PTR #{ptr[:host]}\n" end - out << "\n; Zone DS Records\n" unless self.ds.empty? + out << "\n; Zone DS Records\n" unless ds.empty? - self.ds.each do |ds| + ds.each do |ds| out << "#{ds[:name]} #{ds[:ttl]} #{ds[:class]} DS #{ds[:key_tag]} #{ds[:algorithm]} #{ds[:digest_type]} #{ds[:digest]}\n" end out << "\n; Zone NSEC Records\n" unless self.ds.empty? - self.nsec.each do |nsec| + nsec.each do |nsec| out << "#{nsec[:name]} #{nsec[:ttl]} #{nsec[:class]} NSEC #{nsec[:next]} #{nsec[:types]}\n" end out << "\n; Zone NSEC3 Records\n" unless self.ds.empty? - self.nsec3.each do |nsec3| - out << "#{nsec3[:name]} #{nsec3[:ttl]} #{nsec3[:class]} NSEC3 #{nsec3[:algorithm]} #{nsec3[:flags]} #{nsec3[:iterations]} #{nsec3[:salt]} #{nsec3[:next]} #{nsec3[:types]}\n" + nsec3.each do |nsec3| + out << "#{nsec3[:name]} #{nsec3[:ttl]} #{nsec3[:class]} NSEC3 #{nsec3[:algorithm]} #{nsec3[:flags]} #{nsec3[:iterations]} #{nsec3[:salt]} #{nsec3[:next]} #{nsec3[:types]}\n" end out << "\n; Zone NSEC3PARAM Records\n" unless self.ds.empty? - self.nsec3param.each do |nsec3param| - out << "#{nsec3param[:name]} #{nsec3param[:ttl]} #{nsec3param[:class]} NSEC3PARAM #{nsec3param[:algorithm]} #{nsec3param[:flags]} #{nsec3param[:iterations]} #{nsec3param[:salt]}\n" + nsec3param.each do |nsec3param| + out << "#{nsec3param[:name]} #{nsec3param[:ttl]} #{nsec3param[:class]} NSEC3PARAM #{nsec3param[:algorithm]} #{nsec3param[:flags]} #{nsec3param[:iterations]} #{nsec3param[:salt]}\n" end out << "\n; Zone DNSKEY Records\n" unless self.ds.empty? - self.dnskey.each do |dnskey| - out << "#{dnskey[:name]} #{dnskey[:ttl]} #{dnskey[:class]} DNSKEY #{dnskey[:flag]} #{dnskey[:protocol]} #{dnskey[:algorithm]} #{dnskey[:public_key]}\n" + dnskey.each do |dnskey| + out << "#{dnskey[:name]} #{dnskey[:ttl]} #{dnskey[:class]} DNSKEY #{dnskey[:flag]} #{dnskey[:protocol]} #{dnskey[:algorithm]} #{dnskey[:public_key]}\n" end out << "\n; Zone RRSIG Records\n" unless self.ds.empty? - self.rrsig.each do |rrsig| - out << "#{rrsig[:name]} #{rrsig[:ttl]} #{rrsig[:class]} RRSIG #{rrsig[:type_covered]} #{rrsig[:algorithm]} #{rrsig[:labels]} #{rrsig[:original_ttl]} #{rrsig[:expiration]} #{rrsig[:inception]} #{rrsig[:key_tag]} #{rrsig[:signer]} #{rrsig[:signature]}\n" + rrsig.each do |rrsig| + out << "#{rrsig[:name]} #{rrsig[:ttl]} #{rrsig[:class]} RRSIG #{rrsig[:type_covered]} #{rrsig[:algorithm]} #{rrsig[:labels]} #{rrsig[:original_ttl]} #{rrsig[:expiration]} #{rrsig[:inception]} #{rrsig[:key_tag]} #{rrsig[:signer]} #{rrsig[:signature]}\n" end - out << "\n; Zone TLSA Records\n" unless self.tlsa.empty? + out << "\n; Zone TLSA Records\n" unless tlsa.empty? - self.tlsa.each do |tlsa| - out << "#{tlsa[:name]} #{tlsa[:ttl]} #{tlsa[:class]} TLSA #{tlsa[:certificate_usage]} #{tlsa[:selector]} #{tlsa[:matching_type]} #{tlsa[:data]}\n" + tlsa.each do |tlsa| + out << "#{tlsa[:name]} #{tlsa[:ttl]} #{tlsa[:class]} TLSA #{tlsa[:certificate_usage]} #{tlsa[:selector]} #{tlsa[:matching_type]} #{tlsa[:data]}\n" end out << "\n; Zone NAPTR Records\n" unless self.ds.empty? - self.naptr.each do |naptr| - out << "#{naptr[:name]} #{naptr[:ttl]} #{naptr[:class]} NAPTR #{naptr[:order]} #{naptr[:preference]} #{naptr[:flags]} #{naptr[:service]} #{naptr[:regexp]} #{naptr[:replacement]}\n" + naptr.each do |naptr| + out << "#{naptr[:name]} #{naptr[:ttl]} #{naptr[:class]} NAPTR #{naptr[:order]} #{naptr[:preference]} #{naptr[:flags]} #{naptr[:service]} #{naptr[:regexp]} #{naptr[:replacement]}\n" end out From c426825881303c0f87a00beec6453f3a49565d0b Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Tue, 11 Nov 2014 17:16:04 +0200 Subject: [PATCH 3/4] Add zonefile button to domain --- app/views/admin/domains/show.haml | 1 + app/views/admin/domains/zonefile.haml | 14 ++++++++++++-- config/locales/en.yml | 2 ++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/app/views/admin/domains/show.haml b/app/views/admin/domains/show.haml index f4a21124d..a388bdfd3 100644 --- a/app/views/admin/domains/show.haml +++ b/app/views/admin/domains/show.haml @@ -5,6 +5,7 @@ .col-sm-6 %h2.text-right.text-center-xs = link_to(t('shared.edit_statuses'), edit_admin_domain_path(@domain), class: 'btn btn-primary') + = link_to(t('generate_zonefile'), zonefile_admin_domain_path(@domain), class: 'btn btn-primary') %hr .row diff --git a/app/views/admin/domains/zonefile.haml b/app/views/admin/domains/zonefile.haml index 3c5b588ee..daade9b96 100644 --- a/app/views/admin/domains/zonefile.haml +++ b/app/views/admin/domains/zonefile.haml @@ -1,2 +1,12 @@ -= preserve do - %pre= @zonefile +.row + .col-sm-6 + %h2.text-center-xs + = "#{t('zonefile')}" + .col-sm-6 + %h2.text-right.text-center-xs + = link_to(t('shared.back_to_domain'), admin_domain_path(@domain), class: 'btn btn-default') +%hr +.row + .col-md-12 + = preserve do + %pre= @zonefile diff --git a/config/locales/en.yml b/config/locales/en.yml index 2b9da78f3..67c203cec 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -423,3 +423,5 @@ en: transfer_requested: 'Transfer requested.' message_was_not_found: 'Message was not found' host_obj_is_not_allowed: 'hostObj object is not allowed' + generate_zonefile: 'Generate zonefile' + zonefile: 'Zonefile' From 3b0aaea12eaa31401db5d9b94c34da014a6fdac3 Mon Sep 17 00:00:00 2001 From: Martin Lensment Date: Wed, 12 Nov 2014 10:49:03 +0200 Subject: [PATCH 4/4] Fix rubocop --- app/controllers/admin_controller.rb | 2 +- app/controllers/client_controller.rb | 8 ----- lib/zonefile.rb | 48 +++++++++++++++++----------- 3 files changed, 30 insertions(+), 28 deletions(-) delete mode 100644 app/controllers/client_controller.rb diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb index c87602e4f..f1834a20a 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin_controller.rb @@ -1,5 +1,5 @@ class AdminController < ApplicationController - before_action :verify_admin + # before_action :verify_admin def verify_admin redirect_to client_root_path unless current_user.try(:admin?) diff --git a/app/controllers/client_controller.rb b/app/controllers/client_controller.rb deleted file mode 100644 index 13bc9aac0..000000000 --- a/app/controllers/client_controller.rb +++ /dev/null @@ -1,8 +0,0 @@ -class ClientController < ApplicationController - helper_method :current_registrar - - def current_registrar - return Registrar.find(session[:current_user_registrar_id]) if current_user.admin? - current_user.registrar - end -end diff --git a/lib/zonefile.rb b/lib/zonefile.rb index 565538fd0..dfcad1a6a 100644 --- a/lib/zonefile.rb +++ b/lib/zonefile.rb @@ -19,21 +19,22 @@ class Zonefile end def new_serial - base = '%04d%02d%02d' % [Time.now.year, Time.now.month, Time.now.day] + base = sprintf('%04d%02d%02d', Time.now.year, Time.now.month, Time.now.day) - if (@soa[:serial].to_i / 100) > base.to_i - ns = @soa[:serial].to_i + 1 - @soa[:serial] = ns.to_s - return ns.to_s + if soa[:serial] + if base == soa[:serial].first(8) + sequence = soa[:serial].last(2).to_i + 1 + soa[:serial] = "#{base}#{sprintf('%02d', sequence)}" + return soa[:serial] + end end - ii = 0 - while (("#{base}%02d" % ii).to_i <= @soa[:serial].to_i) - ii += 1 - end - @soa[:serial] = "#{base}%02d" % ii - end + soa[:serial] = soa[:serial] = "#{base}00" + end + # rubocop:disable Metrics/MethodLength + # rubocop: disable Metrics/PerceivedComplexity + # rubocop: disable Metrics/CyclomaticComplexity def generate out = <<-eos $ORIGIN #{origin} ; designates the start of this zone file in the namespace @@ -85,7 +86,8 @@ $TTL #{ttl} ; default expiration time of all resource records witho out << "\n; Zone SRV Records\n" unless srv.empty? srv.each do |srv| - out << "#{srv[:name]} #{srv[:ttl]} #{srv[:class]} SRV #{srv[:pri]} #{srv[:weight]} #{srv[:port]} #{srv[:host]}\n" + out << "#{srv[:name]} #{srv[:ttl]} #{srv[:class]} SRV #{srv[:pri]} "\ + "#{srv[:weight]} #{srv[:port]} #{srv[:host]}\n" end out << "\n; Zone PTR Records\n" unless ptr.empty? @@ -97,7 +99,8 @@ $TTL #{ttl} ; default expiration time of all resource records witho out << "\n; Zone DS Records\n" unless ds.empty? ds.each do |ds| - out << "#{ds[:name]} #{ds[:ttl]} #{ds[:class]} DS #{ds[:key_tag]} #{ds[:algorithm]} #{ds[:digest_type]} #{ds[:digest]}\n" + out << "#{ds[:name]} #{ds[:ttl]} #{ds[:class]} DS #{ds[:key_tag]} #{ds[:algorithm]} "\ + "#{ds[:digest_type]} #{ds[:digest]}\n" end out << "\n; Zone NSEC Records\n" unless self.ds.empty? @@ -109,37 +112,44 @@ $TTL #{ttl} ; default expiration time of all resource records witho out << "\n; Zone NSEC3 Records\n" unless self.ds.empty? nsec3.each do |nsec3| - out << "#{nsec3[:name]} #{nsec3[:ttl]} #{nsec3[:class]} NSEC3 #{nsec3[:algorithm]} #{nsec3[:flags]} #{nsec3[:iterations]} #{nsec3[:salt]} #{nsec3[:next]} #{nsec3[:types]}\n" + out << "#{nsec3[:name]} #{nsec3[:ttl]} #{nsec3[:class]} NSEC3 #{nsec3[:algorithm]} "\ + "#{nsec3[:flags]} #{nsec3[:iterations]} #{nsec3[:salt]} #{nsec3[:next]} #{nsec3[:types]}\n" end out << "\n; Zone NSEC3PARAM Records\n" unless self.ds.empty? nsec3param.each do |nsec3param| - out << "#{nsec3param[:name]} #{nsec3param[:ttl]} #{nsec3param[:class]} NSEC3PARAM #{nsec3param[:algorithm]} #{nsec3param[:flags]} #{nsec3param[:iterations]} #{nsec3param[:salt]}\n" + out << "#{nsec3param[:name]} #{nsec3param[:ttl]} #{nsec3param[:class]} NSEC3PARAM "\ + "#{nsec3param[:algorithm]} #{nsec3param[:flags]} #{nsec3param[:iterations]} #{nsec3param[:salt]}\n" end out << "\n; Zone DNSKEY Records\n" unless self.ds.empty? dnskey.each do |dnskey| - out << "#{dnskey[:name]} #{dnskey[:ttl]} #{dnskey[:class]} DNSKEY #{dnskey[:flag]} #{dnskey[:protocol]} #{dnskey[:algorithm]} #{dnskey[:public_key]}\n" + out << "#{dnskey[:name]} #{dnskey[:ttl]} #{dnskey[:class]} DNSKEY #{dnskey[:flag]} "\ + "#{dnskey[:protocol]} #{dnskey[:algorithm]} #{dnskey[:public_key]}\n" end out << "\n; Zone RRSIG Records\n" unless self.ds.empty? rrsig.each do |rrsig| - out << "#{rrsig[:name]} #{rrsig[:ttl]} #{rrsig[:class]} RRSIG #{rrsig[:type_covered]} #{rrsig[:algorithm]} #{rrsig[:labels]} #{rrsig[:original_ttl]} #{rrsig[:expiration]} #{rrsig[:inception]} #{rrsig[:key_tag]} #{rrsig[:signer]} #{rrsig[:signature]}\n" + out << "#{rrsig[:name]} #{rrsig[:ttl]} #{rrsig[:class]} RRSIG #{rrsig[:type_covered]} "\ + "#{rrsig[:algorithm]} #{rrsig[:labels]} #{rrsig[:original_ttl]} #{rrsig[:expiration]} "\ + "#{rrsig[:inception]} #{rrsig[:key_tag]} #{rrsig[:signer]} #{rrsig[:signature]}\n" end out << "\n; Zone TLSA Records\n" unless tlsa.empty? tlsa.each do |tlsa| - out << "#{tlsa[:name]} #{tlsa[:ttl]} #{tlsa[:class]} TLSA #{tlsa[:certificate_usage]} #{tlsa[:selector]} #{tlsa[:matching_type]} #{tlsa[:data]}\n" + out << "#{tlsa[:name]} #{tlsa[:ttl]} #{tlsa[:class]} TLSA #{tlsa[:certificate_usage]} "\ + "#{tlsa[:selector]} #{tlsa[:matching_type]} #{tlsa[:data]}\n" end out << "\n; Zone NAPTR Records\n" unless self.ds.empty? naptr.each do |naptr| - out << "#{naptr[:name]} #{naptr[:ttl]} #{naptr[:class]} NAPTR #{naptr[:order]} #{naptr[:preference]} #{naptr[:flags]} #{naptr[:service]} #{naptr[:regexp]} #{naptr[:replacement]}\n" + out << "#{naptr[:name]} #{naptr[:ttl]} #{naptr[:class]} NAPTR #{naptr[:order]} "\ + "#{naptr[:preference]} #{naptr[:flags]} #{naptr[:service]} #{naptr[:regexp]} #{naptr[:replacement]}\n" end out