summaryrefslogtreecommitdiffstats
path: root/mdns-resolver
blob: e00d68cf93d450a9e1b3bf3029f8c7cc0bf418c8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/env ruby

require 'set'
require 'socket'
require 'net/dns'
require 'net/dns/packet'

### Config ###
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 ###


# 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