dnsmasq: update to 2.73 (23.06.2015)
[tomato.git] / release / src / router / dnsmasq / contrib / guns / cache / Rakefile
blob63cd183f9dac871cd0fe46fa62c529601a75b256
1 # -*- encoding: utf-8 -*-
3 task :default => :dump
5 desc 'Dump and merge dnsmasq host cache'
6 task :dump do
7   require 'set'
8   require 'etc'
9   require 'fileutils'
11   pid = %x(ps axo pid,ucomm).lines.find { |l| l.strip =~ /\bdnsmasq\z/ }.to_i
12   return if pid.zero?
14   # Create a table of hostnames to addresses, eliding any aliases of localhost
15   parse_hosts = lambda do |buf|
16     hosts, locals = {}, Set.new(%w[127.0.0.1 ::1 0.0.0.0 ::])
17     buf.lines.each do |l|
18       addr, host = l.chomp.split "\t"
19       next if locals.include? addr
20       hosts[host] ||= Set.new
21       hosts[host] << addr
22     end
23     hosts
24   end
26   # Record current cache
27   cache = File.expand_path 'hosts.cache'
28   hosts = File.exists?(cache) ? parse_hosts.call(File.read cache) : {}
30   # Send USR1 and wait for dnsmasq to close the cache file
31   user = Etc.getpwnam 'dnsmasq'
32   FileUtils.touch cache
33   File.chown user.uid, user.gid, cache
34   Process.kill 'SIGUSR1', pid
35   sleep 0.1 until %x(lsof -p #{pid} 2>/dev/null).lines.grep(Regexp.new cache).empty?
37   # Merge old cache records with new ones so we don't lose any records
38   hosts.merge! parse_hosts.call(File.read cache)
39   File.open cache, 'w' do |f|
40     f.puts hosts.sort.reduce('') { |buf, (host, addrs)|
41       buf << addrs.sort.map { |a| "#{a}\t#{host}\n" }.join
42     }
43   end
44 end
46 def blacklisted? blacklist, host
47   h0, h1, h2, _ = host
48   blacklist.each do |bhost|
49     b0, b1, b2, _ = bhost
50     case host.size
51     when 1 then return true if b0 === h0
52     when 2 then return true if b0 === h0 and b1 === h1
53     else        return true if b0 === h0 and b1 === h1 and b2 === h2
54     end
55   end
56   false
57 end
59 def name host
60   host.reverse.join '.'
61 end
63 desc 'Create a dnsmasq whitelist conf file from hosts.cache'
64 task :whitelist do
65   require 'set'
67   # Host to direct whitelisted DNS queries
68   nameserver = '192.168.1.1'
70   # Domains that should never enter the whitelist.
71   #
72   # Each entry must be an array of three case matchers (i.e. match is done
73   # with ===) that correspond to the top three levels of a domain name in
74   # hierarchical order.
75   blacklist = [
76     # ['com', 'google', /\A(www|images|video)\z/],
77     # ['xxx',      /./,                      /./]
78   ]
80   # Cached hosts as arrays of hierarchical domains
81   hosts = File.readlines('hosts.cache').map do |l|
82     l.strip.split.last.split('.').reverse
83   end
85   oldlist = if File.exists? 'whitelist.conf'
86     File.readlines('whitelist.conf').map { |l| l[%r{/(.*)/}, 1] }
87   else
88     []
89   end
91   # Reduce cached hosts to a set of secondary domains. If the secondary domain
92   # is blacklisted and the tertiary domain is not, then the tertiary domain is
93   # added instead.
94   whitelist = hosts.reduce Set.new(oldlist) do |s, host|
95     # Don't add TLDs
96     if host.size < 2
97       warn "Rejecting #{name host}"
98       s
99     # Try the secondary first
100     elsif blacklisted? blacklist, host.take(2)
101       # Then try the tertiary (when in doubt, use brute force)
102       if blacklisted? blacklist, host.take(3)
103         warn "Rejecting #{name host}"
104         s
105       else
106         s << name(host.take 3)
107       end
108     else
109       s << name(host.take 2)
110     end
111   end
113   File.open 'whitelist.conf', 'w' do |f|
114     f.puts whitelist.map { |host| "server=/#{host}/#{nameserver}" }.sort
115   end