astro-em-dns 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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