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 +1 -1
- data/VERSION.yml +2 -2
- data/examples/lookup_many.rb +26 -26
- data/lib/em/dns_resolver.rb +156 -0
- metadata +4 -2
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
|
|
data/VERSION.yml
CHANGED
data/examples/lookup_many.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'uri'
|
4
4
|
require 'eventmachine'
|
5
5
|
$: << File.dirname(__FILE__) + '/../lib'
|
6
|
-
require 'em/
|
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
|
-
|
27
|
-
|
28
|
-
|
27
|
+
BATCH_SIZE = 10
|
28
|
+
successes = 0
|
29
|
+
failures = 0
|
30
|
+
t1 = Time.now
|
29
31
|
EM.run {
|
30
32
|
pending = 0
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
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-
|
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
|