Skip to content
This repository has been archived by the owner on Jun 25, 2023. It is now read-only.

Add support for arbitrary record types #62

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ If you are using a multi-machine `Vagrantfile`, configure this inside each of yo
You can add static host entries to the DNS server in your `Vagrantfile` like so:

config.landrush.host 'myhost.example.com', '1.2.3.4'
config.landrush.host '_sip._udp.example.com', [1, 0, 5060, 'myhost.example.com'], 'srv'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess some explanation would be needed here what this actually means. They make no sense to me atm. I guess they are some sort of BIND syntax.


This is great for overriding production services for nodes you might be testing locally. For example, perhaps you might want to override the hostname of your puppetmaster to point to a local vagrant box instead.

Expand Down
1 change: 1 addition & 0 deletions examples/Vagrantfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ Vagrant.configure("2") do |config|

config.landrush.host 'static1.example.com', '1.2.3.4'
config.landrush.host 'static2.example.com', '2.3.4.5'
config.landrush.host '_sip._udp.example.com', [1, 0, 5060, 'myhost.vagrant.dev'], 'srv'
end
6 changes: 6 additions & 0 deletions lib/landrush.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
raise 'The Vagrant landrush plugin must be run within Vagrant.'
end

require 'pstore'

module Landrush
def self.working_dir
@working_dir ||= Pathname(File.expand_path('~/.vagrant.d/data/landrush')).tap(&:mkpath)
Expand All @@ -12,6 +14,10 @@ def self.working_dir
def self.working_dir=(working_dir)
@working_dir = Pathname(working_dir).tap(&:mkpath)
end

def self.config
@config ||= PStore.new(working_dir.join('config.pstore'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That means the config is not a pstore which is not human readable anymore, right? I think this is a bit unfortunate. Having the ability to easily view/edit the configuration is imo a nice thing.

end
end

require 'rubydns'
Expand Down
20 changes: 8 additions & 12 deletions lib/landrush/action/setup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ def add_prerequisite_network_interface
end

def configure_server
Store.config.set('upstream', config.upstream_servers)
Landrush.config.transaction do
Landrush.config['upstream'] = config.upstream_servers
end
end

def start_server
Expand All @@ -58,13 +60,10 @@ def start_server
end

def setup_static_dns
config.hosts.each do |hostname, dns_value|
config.hosts.each do |hostname, (dns_value, type)|
dns_value ||= machine.guest.capability(:read_host_visible_ip_address)
if !Store.hosts.has?(hostname, dns_value)
info "adding static entry: #{hostname} => #{dns_value}"
Store.hosts.set hostname, dns_value
Store.hosts.set(IPAddr.new(dns_value).reverse, hostname)
end
info "adding static entry: #{hostname} => #{dns_value} as #{type}"
Store.hosts.set hostname, dns_value, type
end
end

Expand All @@ -77,11 +76,8 @@ def record_machine_dns_entry
log :error, "You will not be able to access #{machine_hostname} from the host"
end

if !Store.hosts.has?(machine_hostname, ip_address)
info "adding machine entry: #{machine_hostname} => #{ip_address}"
Store.hosts.set(machine_hostname, ip_address)
Store.hosts.set(IPAddr.new(ip_address).reverse, machine_hostname)
end
info "adding machine entry: #{machine_hostname} => #{ip_address}"
Store.hosts.set(machine_hostname, ip_address)
end

def private_network_exists?
Expand Down
14 changes: 5 additions & 9 deletions lib/landrush/action/teardown.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,14 @@ def teardown
end

def teardown_machine_dns
if Store.hosts.has? machine_hostname
info "removing machine entry: #{machine_hostname}"
Store.hosts.delete(machine_hostname)
end
info "removing machine entry: #{machine_hostname}"
Store.hosts.delete(machine_hostname)
end

def teardown_static_dns
config.hosts.each do |static_hostname, dns_value|
if Store.hosts.has? static_hostname
info "removing static entry: #{static_hostname}"
Store.hosts.delete static_hostname
end
config.hosts.each do |static_hostname, (_, type)|
info "removing static entry: #{static_hostname} as #{type}"
Store.hosts.delete static_hostname, type
end
end

Expand Down
19 changes: 13 additions & 6 deletions lib/landrush/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,7 @@ def execute
@env.ui.info("No dependent VMs")
end
elsif command == 'ls' || command == 'list'
IO.popen("/usr/bin/pr -2 -t -a", "w") do |io|
Landrush::Store.hosts.each do |key, value|
io.puts "#{key}"
io.puts "#{value}"
end
end
list_zone
elsif command == 'set'
host, ip = ARGV[1,2]
Landrush::Store.hosts.set(host, ip)
Expand Down Expand Up @@ -71,5 +66,17 @@ def help; <<-EOS.gsub(/^ /, '')
EOS
end

def list_zone
IO.popen("/usr/bin/pr -3 -t -a", "w") do |io|
Landrush::Store.hosts.each do |type, records|
records.each do |key, value|
io.puts "#{key}"
io.puts "0 IN #{type.upcase}"
io.puts "#{Array(value).join(' ')}"
end
end
end
end

end
end
4 changes: 2 additions & 2 deletions lib/landrush/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ def guest_redirect_dns?
@guest_redirect_dns
end

def host(hostname, ip_address=nil)
@hosts[hostname] = ip_address
def host(hostname, record=nil, type='a')
@hosts[hostname] = [record, type]
end

def upstream(ip, port=53, protocol=nil)
Expand Down
35 changes: 7 additions & 28 deletions lib/landrush/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@

module Landrush
class Server < RExec::Daemon::Base
Name = Resolv::DNS::Name
IN = Resolv::DNS::Resource::IN

def self.port
@port ||= 10053
end
Expand All @@ -15,8 +12,9 @@ def self.port=(port)
end

def self.upstream_servers
# Doing collect to cast protocol to symbol because JSON store doesn't know about symbols
@upstream_servers ||= Store.config.get('upstream').collect {|i| [i[0].to_sym, i[1], i[2]]}
Landrush.config.transaction do
@upstream_servers ||= Landrush.config['upstream']
end
end

def self.interfaces
Expand Down Expand Up @@ -47,35 +45,16 @@ def self.prefork
super
end

def self.check_a_record (host, transaction)
value = Store.hosts.get(host)
if (IPAddr.new(value) rescue nil)
name = transaction.name =~ /#{host}/ ? transaction.name : host
transaction.respond!(value, {:ttl => 0, :name => name})
else
transaction.respond!(Name.create(value), resource_class: IN::CNAME, ttl: 0)
check_a_record(value, transaction)
end
end

def self.run
server = self
RubyDNS::run_server(:listen => interfaces) do
self.logger.level = Logger::INFO

match(/.*/, IN::A) do |transaction|
host = Store.hosts.find(transaction.name)
if host
server.check_a_record(host, transaction)
else
transaction.passthrough!(server.upstream)
end
end

match(/.*/, IN::PTR) do |transaction|
host = Store.hosts.find(transaction.name)
match(/.*/) do |transaction|
type = transaction.resource_class.name.split('::').last.downcase
host = Store.hosts.find(transaction.name, type)
if host
transaction.respond!(Name.create(Store.hosts.get(host)))
transaction.respond!(*Store.hosts.get(host, type), ttl: 0)
else
transaction.passthrough!(server.upstream)
end
Expand Down
43 changes: 18 additions & 25 deletions lib/landrush/store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,44 @@ def self.hosts
@hosts ||= new(Landrush.working_dir.join('hosts.json'))
end

def self.config
@config ||= new(Landrush.working_dir.join('config.json'))
end

attr_accessor :backing_file

def initialize(backing_file)
@backing_file = Pathname(backing_file)
end

def set(key, value)
write(current_config.merge(key => value))
def set(key, value, type = 'a')
config = current_config
config[type] = (config[type] || {}).merge(key => value)
write(config)
end

def each(*args, &block)
current_config.each(*args, &block)
end

def delete(key)
write(current_config.reject { |k, v| k == key || v == key })
end

def has?(key, value = nil)
if value.nil?
current_config.has_key? key
else
current_config[key] == value
end
def delete(key, type = 'a')
config = current_config
config[type] = (config[type] || {}).reject { |k, v| k == key || v == key }
write(config)
end

def find(search)
search = (IPAddr.new(search).reverse) if (IPAddr.new(search) rescue nil)
current_config.keys.detect do |key|
def find(search, type = 'a')
(current_config[type] || {}).keys.detect do |key|
key.casecmp(search) == 0 ||
search =~ /#{key}$/i ||
key =~ /^#{search}\./i
end
end

def get(key)
current_config[key]
end

def clear!
write({})
def get(key, type = 'a')
value = (current_config[type] || {})[key]
case type
when 'cname'
[Resolv::DNS::Name.create(value)]
else
value
end
end

protected
Expand Down
32 changes: 16 additions & 16 deletions test/landrush/server_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,10 @@

module Landrush
describe Server do
def query(host)
output = `dig -p #{Server.port} @127.0.0.1 #{host}`
def query(host, type = 'a')
output = `dig -p #{Server.port} @127.0.0.1 #{host} #{type}`
answer_line = output.split("\n").grep(/^#{Regexp.escape(host)}/).first
answer_line.split.last
end

def query_ptr(host)
output = `dig ptr -p #{Server.port} @127.0.0.1 #{host}`
answer_line = output.split("\n").grep(/^#{Regexp.escape(host)}/).first
answer_line.split.last
answer_line || fail("No record for host #{host}")
end

describe 'start/stop' do
Expand All @@ -36,13 +30,20 @@ def query_ptr(host)
Server.start

fake_host = 'boogers.vagrant.test'
fake_ip = '99.98.97.96'

Store.hosts.set(fake_host, fake_ip)
Store.hosts.set(fake_host, '99.98.97.96')

query(fake_host).must_equal fake_ip
query_ptr(fake_host).must_equal fake_ip+'.'
query(fake_host).must_equal "boogers.vagrant.test.\t0\tIN\tA\t99.98.97.96"
end

it 'responds properly to configured SRV entries' do
Server.start

fake_host = '_sip._udp.boogers.vagrant.test'

Store.hosts.set(fake_host, [1, 0, 5060, 'boogers.vagrant.test'], 'srv')

query(fake_host, 'srv').must_equal "_sip._udp.boogers.vagrant.test.\t0 IN\tSRV\t1 0 5060 boogers.vagrant.test."
end

it 'responds properly to configured cname entries' do
Expand All @@ -53,10 +54,9 @@ def query_ptr(host)
fake_ip = '99.98.97.96'

Store.hosts.set(fake_host, fake_ip)
Store.hosts.set(fake_cname, fake_host)

query(fake_cname).must_equal fake_host+'.'
Store.hosts.set(fake_cname, fake_host, 'cname')

query(fake_cname, 'cname').must_equal "snot.vagrant.test.\t0\tIN\tCNAME\tboogers.vagrant.test."
end

it 'also resolves wildcard subdomains to a given machine' do
Expand Down
43 changes: 43 additions & 0 deletions test/landrush/store_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ module Landrush

@store.get('foo').must_equal 'qux'
end

it "allows setting records for another class" do
@store.set('foo', [1, 0, 5060, 'somehost.vagrant.dev'], 'srv')

@store.get('foo', 'srv').must_equal [1, 0, 5060, 'somehost.vagrant.dev']
@store.get('foo', Resolv::DNS::Resource::IN::A).must_equal nil
@store.get('foo').must_equal nil
end
end

describe "get" do
Expand Down Expand Up @@ -50,6 +58,16 @@ module Landrush

@store.get('now').must_equal nil # you don't!
end

it "removes keys of another class" do
@store.set('now', 'you see me', 'srv')

@store.get('now', 'srv').must_equal 'you see me'

@store.delete('now', 'srv')

@store.get('now', 'srv').must_equal nil
end
end

describe "find" do
Expand All @@ -75,6 +93,31 @@ module Landrush
@store.find('somehost.vagr').must_equal nil
@store.find('someh').must_equal nil
end

describe "for another class" do
it "returns the key that matches the end of the search term" do
@store.set('somehost.vagrant.dev', 'here', 'srv')

@store.find('foo.somehost.vagrant.dev', 'srv').must_equal 'somehost.vagrant.dev'
@store.find('bar.somehost.vagrant.dev', 'srv').must_equal 'somehost.vagrant.dev'
@store.find('foo.otherhost.vagrant.dev', 'srv').must_equal nil
@store.find('host.vagrant.dev', 'srv').must_equal nil
end

it "returns exact matches too" do
@store.set('somehost.vagrant.dev', 'here', 'srv')
@store.find('somehost.vagrant.dev', 'srv').must_equal 'somehost.vagrant.dev'
end

it "returns for prefix searches as well" do
@store.set('somehost.vagrant.dev', 'here', 'srv')

@store.find('somehost', 'srv').must_equal 'somehost.vagrant.dev'
@store.find('somehost.vagrant', 'srv').must_equal 'somehost.vagrant.dev'
@store.find('somehost.vagr', 'srv').must_equal nil
@store.find('someh', 'srv').must_equal nil
end
end
end
end
end