#!/usr/bin/env ruby require 'set' require 'socket' require 'net/dns' require 'net/dns/packet' ### Config ### $domain = 'mesh.ffhl' $port = 53 $ttl = 10 $ns = ['mesh.ffhl. 3600 IN NS paul.ffhl.'] $soa = 'mesh.ffhl. 3600 IN SOA paul.ffhl. freifunk\.luebeck.asta.uni-luebeck.de. 1 3600 180 600 60' ### Config end ### # Fix bugs and hack FQDN validation :> module Net module DNS class RR class SOA def build_pack @soa_pack = pack_name(@mname) @soa_pack += pack_name(@rname) @soa_pack += [@serial,@refresh,@retry,@expire,@minimum].pack("N5") @rdlength = @soa_pack.size end end end module Names def valid?(name) return true end end end end def soaRecord record = Net::DNS::RR::SOA.new $soa return Net::DNS::RR::SOA.new(:name => record.name, :ttl => record.ttl, :mname => record.mname, :rname => record.rname, :refresh => record.refresh, :retry => record.retry, :expire => record.expire, :minimum => record.minimum, :serial => Time.now.to_i) end run = true sock = UDPSocket.new Socket::AF_INET6 sock.setsockopt Socket::Option.bool(:INET6, :IPV6, :V6ONLY, false) sock.setsockopt Socket::Option.bool(:INET, :IP, :PKTINFO, true) sock.setsockopt Socket::Option.bool(:INET6, :IPV6, :RECVPKTINFO, true) sock.bind('::', $port) while run do data, sendaddr, rflags, *controls = sock.recvmsg Thread.new(data, sendaddr, controls) do |data, sendaddr, controls| pktinfo = nil controls.each do |c| pktinfo = c if c.cmsg_is?(:IP, :PKTINFO) or c.cmsg_is?(:IPV6, :PKTINFO) end return if pktinfo.nil? query = Net::DNS::Packet::parse(data) return unless query.header.opCode == Net::DNS::Header::QUERY return unless query.header.query? packet = Net::DNS::Packet.new packet.header.id = query.header.id packet.header.opCode = Net::DNS::Header::QUERY packet.header.qr = 1 packet.header.aa = 1 packet.header.rd = query.header.recursive? query.question.each do |q| packet.question << q next unless q.qClass.to_i == Net::DNS::IN if q.qName == $domain+'.' packet.answer << soaRecord if q.qType.to_i == Net::DNS::SOA or q.qType.to_i == Net::DNS::ANY $ns.each { |line| packet.answer << Net::DNS::RR::NS.new(line) } if q.qType.to_i == Net::DNS::NS or q.qType.to_i == Net::DNS::ANY else host, qdomain = q.qName.split('.', 2) next unless qdomain == $domain+'.' begin addresses = Set.new begin Socket.getaddrinfo(host+'.local.', nil, :INET).each { |addr| addresses << [addr[0], addr[3]] } rescue end begin Socket.getaddrinfo(host+'.local.', nil, :INET6).each { |addr| addresses << [addr[0], addr[3]] } rescue end raise if addresses.empty? if q.qType.to_i == Net::DNS::A or q.qType.to_i == Net::DNS::ANY addresses.each do |addr| packet.answer << Net::DNS::RR::A.new(:name => q.qName, :ttl => $ttl, :cls => Net::DNS::IN, :type => Net::DNS::A, :address => addr[1]) if addr[0] == "AF_INET" end end if q.qType.to_i == Net::DNS::AAAA or q.qType.to_i == Net::DNS::ANY addresses.each do |addr| packet.answer << Net::DNS::RR::AAAA.new(:name => q.qName, :ttl => $ttl, :cls => Net::DNS::IN, :type => Net::DNS::AAAA, :address => addr[1]) if addr[0] == "AF_INET6" end end rescue packet.header.rCode = Net::DNS::Header::RCode::NAME end end end if packet.answer.empty? packet.authority = [soaRecord] else $ns.each { |line| packet.authority << Net::DNS::RR::NS.new(line) } end sock.sendmsg(packet.data, 0, sendaddr, pktinfo) end end