astro-em-dns 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ Jeweler::Tasks.new do |s|
11
11
  s.homepage = "http://github.com/astro/em-dns"
12
12
  s.description = "DNS::Resolv made ready for EventMachine"
13
13
  s.authors = ["Aman Gupta", "Stephan Maka"]
14
- s.files = FileList["[A-Z]*", "{lib,test}/**/*"]
14
+ s.files = FileList["[A-Z]*", "{lib,test,examples}/**/*"]
15
15
  s.add_dependency 'eventmachine'
16
16
  end
17
17
 
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
- :minor: 0
4
- :patch: 2
3
+ :minor: 1
4
+ :patch: 0
@@ -3,7 +3,7 @@
3
3
  require 'uri'
4
4
  require 'eventmachine'
5
5
  $: << File.dirname(__FILE__) + '/../lib'
6
- require 'em/dns_cache'
6
+ require 'em/dns_resolver'
7
7
 
8
8
  if ARGV.size == 0
9
9
  puts "Usage: #{$0} <url-files>"
@@ -21,35 +21,35 @@ ARGV.each do |fn|
21
21
  end
22
22
  end
23
23
  end
24
+ hosts.uniq!
24
25
  puts "Will resolve #{hosts.size} hosts"
25
26
 
26
- EM::DnsCache.add_nameservers_from_file
27
- EM::DnsCache.verbose
28
-
27
+ BATCH_SIZE = 10
28
+ successes = 0
29
+ failures = 0
30
+ t1 = Time.now
29
31
  EM.run {
30
32
  pending = 0
31
- hosts.each do |host|
32
- df = EM::DnsCache.resolve(host)
33
- df.callback { |*a|
34
- if a.size == 1
35
- if a.kind_of?(Array)
36
- # Good!
37
- else
38
- p host => a[0]
39
- end
40
- else
41
- p host => {:args => a}
42
- end
43
- pending -= 1
44
- EM.stop if pending < 1
45
- }
46
- df.errback { |*a|
47
- puts "Cannot resolve #{host}: #{a.inspect}"
48
- pending -= 1
49
- EM.stop if pending < 1
50
- }
51
- pending += 1
33
+ EM.add_periodic_timer(0.1) do
34
+ batch, hosts = hosts[0..(BATCH_SIZE-1)], (hosts[BATCH_SIZE..-1] || [])
35
+ batch.each do |host|
36
+ df = EM::DnsResolver.resolve(host)
37
+ df.callback { |a|
38
+ p host => a
39
+ successes += 1
40
+ pending -= 1
41
+ EM.stop if pending < 1 && hosts.empty?
42
+ }
43
+ df.errback { |*a|
44
+ puts "Cannot resolve #{host}: #{a.inspect}"
45
+ failures += 1
46
+ pending -= 1
47
+ EM.stop if pending < 1 && hosts.empty?
48
+ }
49
+ pending += 1
50
+ end
52
51
  puts "#{pending} pending"
53
52
  end
54
- puts "Started all: #{pending} pending"
55
53
  }
54
+ t2 = Time.now
55
+ puts "#{successes} successful, #{failures} failures in #{t2 - t1} s"
@@ -0,0 +1,156 @@
1
+ require 'eventmachine'
2
+ require 'resolv'
3
+
4
+ module EventMachine
5
+ module DnsResolver
6
+ ##
7
+ # Global interface
8
+ ##
9
+
10
+ def self.resolve(hostname)
11
+ Request.new(socket, hostname)
12
+ end
13
+
14
+ def self.socket
15
+ unless defined?(@socket)
16
+ @socket = DnsSocket.open
17
+ end
18
+ @socket
19
+ end
20
+
21
+ def self.nameserver=(ns)
22
+ @nameserver = ns
23
+ end
24
+ def self.nameserver
25
+ unless defined?(@nameserver)
26
+ IO::readlines('/etc/resolv.conf').each do |line|
27
+ if line =~ /^nameserver (.+)$/
28
+ @nameserver = $1.split(/\s+/).first
29
+ end
30
+ end
31
+ end
32
+ @nameserver
33
+ end
34
+
35
+ ##
36
+ # Socket stuff
37
+ ##
38
+
39
+ class RequestIdAlreadyUsed < RuntimeError
40
+ end
41
+
42
+ class DnsSocket < EM::Connection
43
+ def self.open
44
+ EM::open_datagram_socket('0.0.0.0', 0, self)
45
+ end
46
+ def post_init
47
+ @requests = {}
48
+ EM.add_periodic_timer(0.1, &method(:tick))
49
+ end
50
+ # Periodically called each second to fire request retries
51
+ def tick
52
+ @requests.each do |id,req|
53
+ req.tick
54
+ end
55
+ end
56
+ def register_request(id, req)
57
+ if @requests.has_key?(id)
58
+ raise RequestIdAlreadyUsed
59
+ else
60
+ @requests[id] = req
61
+ end
62
+ end
63
+ def send_packet(pkt)
64
+ send_datagram(pkt, nameserver, 53)
65
+ end
66
+ def nameserver=(ns)
67
+ @nameserver = ns
68
+ end
69
+ def nameserver
70
+ @nameserver ||= DnsResolver.nameserver
71
+ end
72
+ # Decodes the packet, looks for the request and passes the
73
+ # response over to the requester
74
+ def receive_data(data)
75
+ msg = nil
76
+ begin
77
+ msg = Resolv::DNS::Message.decode data
78
+ rescue
79
+ else
80
+ req = @requests[msg.id]
81
+ if req
82
+ @requests.delete(msg.id)
83
+ req.receive_answer(msg)
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ ##
90
+ # Request
91
+ ##
92
+
93
+ class Request
94
+ include Deferrable
95
+ attr_accessor :retry_interval
96
+ attr_accessor :max_tries
97
+ def initialize(socket, hostname)
98
+ @socket = socket
99
+ @hostname = hostname
100
+ @tries = 0
101
+ @last_send = Time.at(0)
102
+ @retry_interval = 3
103
+ @max_tries = 5
104
+ make_id
105
+ make_packet
106
+ EM.next_tick { tick }
107
+ end
108
+ def tick
109
+ # Break early if nothing to do
110
+ return if @last_send + @retry_interval > Time.now
111
+
112
+ if @tries < @max_tries
113
+ send
114
+ else
115
+ fail 'retries exceeded'
116
+ end
117
+ end
118
+ # Called by DnsSocket#receive_data
119
+ def receive_answer(msg)
120
+ addrs = []
121
+ msg.each_answer do |name,ttl,data|
122
+ if data.kind_of?(Resolv::DNS::Resource::IN::A) ||
123
+ data.kind_of?(Resolv::DNS::Resource::IN::AAAA)
124
+ addrs << data.address.to_s
125
+ end
126
+ end
127
+ if addrs.empty?
128
+ fail "rcode=#{msg.rcode}"
129
+ else
130
+ succeed addrs
131
+ end
132
+ end
133
+ private
134
+ def send
135
+ @socket.send_packet(@pkt.encode)
136
+ @tries += 1
137
+ @last_send = Time.now
138
+ end
139
+ def make_id
140
+ begin
141
+ @id = rand(65535)
142
+ @socket.register_request(@id, self)
143
+ rescue RequestIdAlreadyUsed
144
+ retry
145
+ end
146
+ end
147
+ def make_packet
148
+ msg = Resolv::DNS::Message.new
149
+ msg.id = @id
150
+ msg.rd = 1
151
+ msg.add_question @hostname, Resolv::DNS::Resource::IN::A
152
+ @pkt = msg
153
+ end
154
+ end
155
+ end
156
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: astro-em-dns
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aman Gupta
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-05-19 00:00:00 -07:00
13
+ date: 2009-05-20 00:00:00 -07:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -34,7 +34,9 @@ extra_rdoc_files: []
34
34
  files:
35
35
  - Rakefile
36
36
  - VERSION.yml
37
+ - examples/lookup_many.rb
37
38
  - lib/em/dns_cache.rb
39
+ - lib/em/dns_resolver.rb
38
40
  - test/test_basic.rb
39
41
  has_rdoc: false
40
42
  homepage: http://github.com/astro/em-dns