summaryrefslogtreecommitdiffstats
path: root/mdns-resolver
blob: 78d0169802431e337216bc8f790a6a8d5caa9298 (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