From 0f5879b87f4c3e28adfbc486de5fe573618e0833 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 13 May 2012 01:14:25 +0200 Subject: Reimplement in Ruby --- mdns-resolver | 213 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 120 insertions(+), 93 deletions(-) diff --git a/mdns-resolver b/mdns-resolver index b58d55b..cbaf35b 100755 --- a/mdns-resolver +++ b/mdns-resolver @@ -1,99 +1,126 @@ -#!/usr/bin/env python3 - -import errno -import signal -import socket -import subprocess -import sys -import threading -import traceback - -import dns.flags -import dns.message -import dns.name -import dns.opcode -import dns.query -import dns.rcode -import dns.rdataclass -import dns.rdatatype -import dns.rdtypes.IN.A -import dns.rdtypes.IN.AAAA -import dns.rrset +#!/usr/bin/env ruby +require 'set' +require 'socket' +require 'net/dns' +require 'net/dns/packet' ### Config ### -domain = 'mesh.ffhl' -port = 3535 -ttl = 600 +domain = 'mesh.ffhl' +port = 53 +ttl = 600 +ns = ['mesh.ffhl. 3600 IN NS paul.ffhl.'] +soa = 'mesh.ffhl. 0 IN SOA paul.ffhl. freifunk\.luebeck.asta.uni-luebeck.de. 1 3600 180 600 60' ### Config end ### -class ResolverThread(threading.Thread): - def __init__(self, data, addr): - threading.Thread.__init__(self) - - self.daemon = True - - self._data = data - self._addr = addr - - def run(self): - request = dns.message.from_wire(self._data, question_only=True) - if request.opcode() != dns.opcode.QUERY: - return - - reply = dns.message.Message(request.id) - reply.flags = dns.flags.QR - reply.set_opcode(dns.opcode.QUERY) - reply.set_rcode(dns.rcode.NOERROR) - - for question in request.question: - if question.rdclass != dns.rdataclass.IN: - continue - - domainname = dns.name.from_text(domain) - if len(question.name) != len(domainname)+1: - continue - if not question.name.is_subdomain(domainname): - continue - - - answer = dns.rrset.RRset(question.name, question.rdclass, question.rdtype) - answer.ttl = ttl - - if question.rdtype == dns.rdatatype.A: - output = subprocess.check_output(['avahi-resolve', '-n', '-4', question.name[0]+b'.local']).split(b'\t') - if output[0] == question.name[0]+b'.local': - answer.add(dns.rdtypes.IN.A.A(question.rdclass, question.rdtype, str(output[1], encoding='ASCII').strip())) - elif question.rdtype == dns.rdatatype.AAAA: - output = subprocess.check_output(['avahi-resolve', '-n', '-6', question.name[0]+b'.local']).split(b'\t') - if output[0] == question.name[0]+b'.local': - answer.add(dns.rdtypes.IN.AAAA.AAAA(question.rdclass, question.rdtype, str(output[1], encoding='ASCII').strip())) - else: - continue - - reply.answer.append(answer) - - sock.sendto(reply.to_wire(), self._addr) - -run = True - -def exitHandler(signum, frame): - global run - run = False - -signal.signal(signal.SIGINT, exitHandler) -signal.signal(signal.SIGTERM, exitHandler) -signal.signal(signal.SIGQUIT, exitHandler) - - -sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) -sock.bind(('::1', port)) - -while run: - try: - data, addr = sock.recvfrom(1024) - ResolverThread(data, addr).start() - except socket.error: - if sys.exc_info()[1].errno != errno.EINTR: - traceback.print_exc(file=sys.stderr) +# 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 + + +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? + + packet = Net::DNS::Packet::parse(data) + + return unless packet.header.opCode == Net::DNS::Header::QUERY + return unless packet.header.query? + + packet.header.qr = 1 + packet.header.aa = 1 + packet.header.tc = 0 + packet.header.ra = 0 + packet.header.cd = 0 + packet.header.ad = 0 + packet.header.rCode = Net::DNS::Header::RCode::NOERROR + + packet.question.each do |q| + next unless q.qClass.to_i == Net::DNS::IN + + host, qdomain = q.qName.split('.', 2) + next unless qdomain == domain+'.' + + begin + if q.qType.to_i == Net::DNS::A + addresses = Set.new + Socket.getaddrinfo(host+'.local', nil, :INET).each { |addr| addresses << addr[3] } + + 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) + end + elsif q.qType.to_i == Net::DNS::AAAA + addresses = Set.new + Socket.getaddrinfo(host+'.local', nil, :INET6).each { |addr| addresses << addr[3] } + + 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) + end + end + rescue + packet.header.rCode = Net::DNS::Header::RCode::NAME + end + end + + if packet.answer.empty? + record = Net::DNS::RR::SOA.new(soa) + packet.authority = [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)] + else + ns.each { |line| packet.authority << Net::DNS::RR::NS.new(line) } + end + + sock.sendmsg(packet.data, 0, sendaddr, pktinfo) + #end +end -- cgit v1.2.3