em-dns 0.1.1

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.
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'jeweler'
3
+ rescue LoadError
4
+ raise "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
5
+ end
6
+
7
+ Jeweler::Tasks.new do |s|
8
+ s.name = "em-dns"
9
+ s.summary = "Resolve domain names from EventMachine natively"
10
+ s.email = "astro@spaceboyz.net"
11
+ s.homepage = "http://github.com/astro/em-dns"
12
+ s.description = "DNS::Resolv made ready for EventMachine"
13
+ s.authors = ["Aman Gupta", "Stephan Maka"]
14
+ s.files = FileList["[A-Z]*", "{lib,test,examples}/**/*"]
15
+ s.add_dependency 'eventmachine'
16
+ end
17
+
18
+ require 'rake/testtask'
19
+ Rake::TestTask.new do |t|
20
+ t.test_files = FileList["test/test*.rb"]
21
+ end
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 1
3
+ :major: 0
4
+ :minor: 1
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'uri'
4
+ require 'eventmachine'
5
+ $: << File.dirname(__FILE__) + '/../lib'
6
+ require 'em/dns_resolver'
7
+
8
+ if ARGV.size == 0
9
+ puts "Usage: #{$0} <url-files>"
10
+ exit
11
+ end
12
+
13
+
14
+ hosts = []
15
+ ARGV.each do |fn|
16
+ IO::readlines(fn).each do |line|
17
+ line.chomp!
18
+ line.split(/\s+/).each do |url|
19
+ uri = URI::parse(url)
20
+ hosts << uri.host
21
+ end
22
+ end
23
+ end
24
+ hosts.uniq!
25
+ puts "Will resolve #{hosts.size} hosts"
26
+
27
+ BATCH_SIZE = 10
28
+ successes = 0
29
+ failures = 0
30
+ t1 = Time.now
31
+ EM.run {
32
+ pending = 0
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
51
+ puts "#{pending} pending"
52
+ end
53
+ }
54
+ t2 = Time.now
55
+ puts "#{successes} successful, #{failures} failures in #{t2 - t1} s"
@@ -0,0 +1,347 @@
1
+ # $Id: dns_cache.rb 5040 2007-10-05 17:31:04Z francis $
2
+ #
3
+ #
4
+
5
+ require 'rubygems'
6
+ require 'eventmachine'
7
+ require 'resolv'
8
+
9
+
10
+ module EventMachine
11
+ module DnsCache
12
+
13
+ class Cache
14
+ def initialize
15
+ @hash = {}
16
+ end
17
+ def add domain, value, expiration
18
+ ex = ((expiration < 0) ? :none : (Time.now + expiration))
19
+ @hash[domain] = [ex, value]
20
+ end
21
+ def retrieve domain
22
+ if @hash.has_key?(domain)
23
+ d = @hash[domain]
24
+ if d.first != :none and d.first < Time.now
25
+ @hash.delete(domain)
26
+ nil
27
+ else
28
+ d.last
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ @a_cache = Cache.new
35
+ @mx_cache = Cache.new
36
+ @nameservers = []
37
+ @message_ix = 0
38
+ MAX_WAITING = 100
39
+ @waiting = 0
40
+ @pending = []
41
+
42
+ def self.add_nameserver ns
43
+ @nameservers << ns unless @nameservers.include?(ns)
44
+ end
45
+
46
+ def self.add_nameservers_from_file file='/etc/resolv.conf'
47
+ IO::readlines(file).each do |line|
48
+ if line =~ /^nameserver (.+)$/
49
+ $1.split(/\s+/).each { |ns|
50
+ @nameservers << ns unless ns.empty?
51
+ }
52
+ end
53
+ end
54
+ end
55
+
56
+ def self.verbose v=true
57
+ @verbose = v
58
+ end
59
+
60
+
61
+ def self.add_cache_entry cache_type, domain, value, expiration
62
+ cache = if cache_type == :mx
63
+ @mx_cache
64
+ elsif cache_type == :a
65
+ @a_cache
66
+ else
67
+ raise "bad cache type"
68
+ end
69
+
70
+ v = EM::DefaultDeferrable.new
71
+ v.succeed( value.dup.freeze )
72
+ cache.add domain, v, expiration
73
+ end
74
+
75
+ # Needs to be DRYed up with resolve_mx.
76
+ #
77
+ def self.resolve domain
78
+ if d = @a_cache.retrieve(domain)
79
+ puts "Cache hit for #{domain}" if @verbose
80
+ look_pending
81
+ d
82
+ else
83
+ =begin
84
+ d = @a_cache[domain]
85
+ if d.first < Time.now
86
+ STDOUT.puts "Expiring stale cache entry for #{domain}" if @verbose
87
+ @a_cache.delete domain
88
+ resolve domain
89
+ else
90
+ STDOUT.puts "Fulfilled #{domain} from cache" if @verbose
91
+ d.last
92
+ end
93
+ else
94
+ =end
95
+ if @waiting >= MAX_WAITING
96
+ puts "Postponing #{domain} because already waiting for #{@waiting} queries" if @verbose
97
+ d = EM::DefaultDeferrable.new
98
+ @pending << lambda {
99
+ d_inner = resolve domain
100
+ d_inner.callback &d.method(:succeed)
101
+ d_inner.errback &d.method(:fail)
102
+ }
103
+ puts "#{@pending.size} pending requests now" if @verbose
104
+ d
105
+ else
106
+ d = resolve_do domain
107
+ @waiting += 1
108
+ STDOUT.puts "Now waiting for #{@waiting}" if @verbose
109
+ on_one_done = lambda {
110
+ @waiting -= 1
111
+ look_pending
112
+ }
113
+ d.callback &on_one_done
114
+ d.errback &on_one_done
115
+ d
116
+ end
117
+ end
118
+ end
119
+
120
+ def self.look_pending
121
+ EM.next_tick {
122
+ while @waiting < MAX_WAITING && !@pending.empty?
123
+ pending1 = @pending.shift
124
+ pending1.call
125
+ puts "#{@pending.size} pending requests now" if @verbose
126
+ end
127
+ }
128
+ end
129
+
130
+ def self.resolve_do domain
131
+ STDOUT.puts "Fulfilling #{domain} from network" if @verbose
132
+ d = EM::DefaultDeferrable.new
133
+ #d.timeout(5)
134
+ d.callback { d.cancel_timeout }
135
+ d.errback { d.cancel_timeout }
136
+ @a_cache.add domain, d, 300 # Hard-code a 5 minute expiration
137
+ #@a_cache[domain] = [Time.now+120, d] # Hard code a 120-second expiration.
138
+
139
+ lazy_initialize
140
+ m = Resolv::DNS::Message.new
141
+ m.rd = 1
142
+ m.add_question domain, Resolv::DNS::Resource::IN::A
143
+ m = m.encode
144
+ d_inner = EM::DefaultDeferrable.new
145
+ @nameservers.each {|ns|
146
+ @message_ix = (@message_ix + 1) % 60000
147
+ Request.new d_inner, @message_ix
148
+ msg = m.dup
149
+ msg[0,2] = [@message_ix].pack("n")
150
+ @u.send_datagram msg, ns, 53
151
+ }
152
+
153
+ d_inner.callback {|resp|
154
+ r = []
155
+ resp.each_answer {|name,ttl,data|
156
+ r << data.address.to_s if data.kind_of?(Resolv::DNS::Resource::IN::A)
157
+ }
158
+
159
+ # Freeze the array since we'll be keeping it in cache and passing it
160
+ # around to multiple users. And alternative would have been to dup it.
161
+ r.freeze
162
+ puts "Succeeding with #{r.inspect}"
163
+ d.succeed r
164
+ }
165
+ d_inner.errback &d.method(:fail)
166
+
167
+
168
+ d
169
+ end
170
+
171
+
172
+ # Needs to be DRYed up with resolve.
173
+ #
174
+ def self.resolve_mx domain
175
+ if d = @mx_cache.retrieve(domain)
176
+ d
177
+ else
178
+ =begin
179
+ if @mx_cache.has_key?(domain)
180
+ d = @mx_cache[domain]
181
+ if d.first < Time.now
182
+ STDOUT.puts "Expiring stale cache entry for #{domain}" if @verbose
183
+ @mx_cache.delete domain
184
+ resolve_mx domain
185
+ else
186
+ STDOUT.puts "Fulfilled #{domain} from cache" if @verbose
187
+ d.last
188
+ end
189
+ else
190
+ =end
191
+ STDOUT.puts "Fulfilling #{domain} from network" if @verbose
192
+ d = EM::DefaultDeferrable.new
193
+ d.timeout(5)
194
+ #@mx_cache[domain] = [Time.now+120, d] # Hard code a 120-second expiration.
195
+ @mx_cache.add domain, d, 300 # Hard-code a 5 minute expiration
196
+
197
+ mx_query = MxQuery.new d
198
+
199
+ lazy_initialize
200
+ m = Resolv::DNS::Message.new
201
+ m.rd = 1
202
+ m.add_question domain, Resolv::DNS::Resource::IN::MX
203
+ m = m.encode
204
+ @nameservers.each {|ns|
205
+ @message_ix = (@message_ix + 1) % 60000
206
+ Request.new mx_query, @message_ix
207
+ msg = m.dup
208
+ msg[0,2] = [@message_ix].pack("n")
209
+ @u.send_datagram msg, ns, 53
210
+ }
211
+
212
+
213
+ d
214
+ end
215
+ end
216
+
217
+
218
+ def self.lazy_initialize
219
+ # Will throw an exception if EM is not running.
220
+ # We wire a signaller into the socket handler to tell us when that socket
221
+ # goes away. (Which can happen, among other things, if the reactor
222
+ # stops and restarts.)
223
+ #
224
+ raise "EventMachine reactor not running" unless EM.reactor_running?
225
+
226
+ unless @u
227
+ us = proc {@u = nil}
228
+ @u = EM::open_datagram_socket( "0.0.0.0", 0, Socket ) {|c|
229
+ c.unbind_signaller = us
230
+ }
231
+ end
232
+
233
+ end
234
+
235
+
236
+ def self.parse_local_mx_records txt
237
+ domain = nil
238
+ addrs = []
239
+
240
+ add_it = proc {
241
+ a = addrs.sort {|m,n| m.last <=> n.last}.map {|y| y.first}
242
+ add_cache_entry :mx, domain, a, -1
243
+ }
244
+
245
+ txt = StringIO.new( txt ) if txt.is_a?(String)
246
+ txt.each_line {|ln|
247
+ if ln =~ /\A\s*([\d\w\.\-\_]+)\s+(\d+)\s*\Z/
248
+ if domain
249
+ addrs << [$1.dup, $2.dup.to_i]
250
+ end
251
+ elsif ln =~ /\A\s*([^\s\:]+)\s*\:\s*\Z/
252
+ add_it.call if domain
253
+ domain = $1.dup
254
+ addrs.clear
255
+ end
256
+ }
257
+
258
+ add_it.call if domain
259
+ end
260
+
261
+
262
+ class MxQuery
263
+ include EM::Deferrable
264
+
265
+ def initialize rslt
266
+ @result = rslt # Deferrable
267
+ @n_a_lookups = 0
268
+
269
+ self.callback {|resp|
270
+ addrs = {}
271
+ resp.each_additional {|name,ttl,data|
272
+ addrs.has_key?(name) ? (addrs[name] << data.address.to_s) : (addrs[name] = [data.address.to_s])
273
+ }
274
+
275
+ @addresses = resp.answer.
276
+ sort {|a,b| a[2].preference <=> b[2].preference}.
277
+ map {|name,ttl,data|
278
+ ex = data.exchange
279
+ addrs[ex] or EM::DnsCache.resolve(ex.to_s)
280
+ }
281
+
282
+ @addresses.each_with_index {|a,ix|
283
+ if a.respond_to?(:set_deferred_status)
284
+ @n_a_lookups += 1
285
+ a.callback {|r|
286
+ @addresses[ix] = r
287
+ @n_a_lookups -= 1
288
+ succeed_result if @n_a_lookups == 0
289
+ }
290
+ end
291
+ }
292
+
293
+ succeed_result if @n_a_lookups == 0
294
+ }
295
+ end
296
+
297
+ def succeed_result
298
+ # Questionable whether we should uniq if it perturbs the sort order.
299
+ # Also freeze it so some user can't wipe it out on us.
300
+ @result.succeed @addresses.flatten.uniq.freeze
301
+ end
302
+
303
+ end
304
+
305
+ class Request
306
+ include EM::Deferrable
307
+
308
+ @@outstanding = {}
309
+
310
+ def self.post response
311
+ if r = @@outstanding.delete(response.id)
312
+ r.succeed response
313
+ end
314
+ end
315
+
316
+ def initialize rslt, m_id
317
+ @result = rslt
318
+ @msgid = m_id
319
+ raise "request-queue overflow" if @@outstanding.has_key?(@msgid)
320
+ @@outstanding[@msgid] = self
321
+
322
+ self.timeout(10)
323
+ self.errback { self.cancel_timeout; @@outstanding.delete(@msgid); @result.fail }
324
+ self.callback {|resp| self.cancel_timeout; @result.succeed resp }
325
+ end
326
+ end
327
+
328
+ class Socket < EM::Connection
329
+ attr_accessor :unbind_signaller
330
+
331
+ def receive_data dg
332
+ m = nil
333
+ begin
334
+ m = Resolv::DNS::Message.decode dg
335
+ rescue
336
+ end
337
+ Request.post(m) if m
338
+ end
339
+
340
+ def unbind
341
+ @unbind_signaller.call if @unbind_signaller
342
+ end
343
+ end
344
+
345
+ end
346
+ end
347
+
@@ -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
+ EM.next_tick { tick }
105
+ end
106
+ def tick
107
+ # Break early if nothing to do
108
+ return if @last_send + @retry_interval > Time.now
109
+
110
+ if @tries < @max_tries
111
+ send
112
+ else
113
+ fail 'retries exceeded'
114
+ end
115
+ end
116
+ # Called by DnsSocket#receive_data
117
+ def receive_answer(msg)
118
+ addrs = []
119
+ msg.each_answer do |name,ttl,data|
120
+ if data.kind_of?(Resolv::DNS::Resource::IN::A) ||
121
+ data.kind_of?(Resolv::DNS::Resource::IN::AAAA)
122
+ addrs << data.address.to_s
123
+ end
124
+ end
125
+ if addrs.empty?
126
+ fail "rcode=#{msg.rcode}"
127
+ else
128
+ succeed addrs
129
+ end
130
+ end
131
+ private
132
+ def send
133
+ @socket.send_packet(packet.encode)
134
+ @tries += 1
135
+ @last_send = Time.now
136
+ end
137
+ def id
138
+ begin
139
+ @id = rand(65535)
140
+ @socket.register_request(@id, self)
141
+ rescue RequestIdAlreadyUsed
142
+ retry
143
+ end unless defined?(@id)
144
+
145
+ @id
146
+ end
147
+ def 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
+ msg
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,222 @@
1
+ # $Id: test_basic.rb 5040 2007-10-05 17:31:04Z francis $
2
+ #
3
+ #
4
+
5
+ require 'test/unit'
6
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
7
+ require 'em/dns_cache'
8
+
9
+
10
+
11
+
12
+ #--------------------------------------
13
+
14
+
15
+ class TestBasic < Test::Unit::TestCase
16
+
17
+ TestNameserver = "151.202.0.85"
18
+ TestNameserver2 = "151.202.0.86"
19
+
20
+ LocalMxRecords = %Q(
21
+ boondoggle.zzz:
22
+ 65.66.67.68 10
23
+ 55.56.57.58 5
24
+ esmtp.someone.zzz 4
25
+
26
+ boondoggle.yyy:
27
+ )
28
+
29
+ def test_a
30
+ EM::DnsCache.add_nameservers_from_file
31
+ EM::DnsCache.verbose
32
+
33
+ out = nil
34
+
35
+ EM.run {
36
+ d = EM::DnsCache.resolve "bayshorenetworks.com"
37
+ d.errback {EM.stop}
38
+ d.callback {|r|
39
+ d = EM::DnsCache.resolve "bayshorenetworks.com"
40
+ d.errback { EM.stop }
41
+ d.callback {|r| puts r; out = r; EM.stop }
42
+ }
43
+ }
44
+
45
+ assert out
46
+ end
47
+
48
+ def test_a_pair
49
+ EM::DnsCache.add_nameservers_from_file
50
+ EM::DnsCache.verbose
51
+
52
+ out = nil
53
+
54
+ EM.run {
55
+ d = EM::DnsCache.resolve "maila.microsoft.com"
56
+ d.errback {EM.stop}
57
+ d.callback {|r|
58
+ out = r
59
+ EM.stop
60
+ }
61
+ }
62
+
63
+ assert_equal( Array, out.class )
64
+ assert_equal( 2, out.length )
65
+ end
66
+
67
+
68
+ # This test causes each request to hit the network because they're all scheduled
69
+ # before the first one can come back and load the cache. Although a nice mis-feature for
70
+ # stress testing, it would be nice to fix it someday, perhaps by not kicking off a
71
+ # request for a particular domain if one is already pending.
72
+ # Without epoll, this test gets really slow and usually doesn't complete.
73
+ def test_lots_of_a
74
+ EM.epoll
75
+ EM::DnsCache.add_nameserver TestNameserver
76
+ EM::DnsCache.add_nameserver TestNameserver2
77
+ EM::DnsCache.verbose
78
+
79
+ n = 250
80
+ e = 0
81
+ s = 0
82
+ EM.run {
83
+ n.times {
84
+ d = EM::DnsCache.resolve "ibm.com"
85
+ d.errback {e+=1; n -= 1; EM.stop if n == 0}
86
+ d.callback {s+=1; n -= 1; EM.stop if n == 0}
87
+ }
88
+ }
89
+ assert_equal( 0, n)
90
+ assert_equal( 250, s)
91
+ end
92
+
93
+
94
+
95
+
96
+ def test_mx
97
+ EM::DnsCache.add_nameserver TestNameserver
98
+ EM::DnsCache.verbose
99
+
100
+ out = nil
101
+
102
+ EM.run {
103
+ d = EM::DnsCache.resolve_mx "steamheat.net"
104
+ d.errback {EM.stop}
105
+ d.callback {|r|
106
+ p r
107
+ d = EM::DnsCache.resolve_mx "steamheat.net"
108
+ d.errback {EM.stop}
109
+ d.callback {|r|
110
+ out = r
111
+ p r
112
+ EM.stop
113
+ }
114
+ }
115
+ }
116
+
117
+ assert out
118
+ end
119
+
120
+
121
+ # The arrays of addresses we get back from the DnsCache are FROZEN.
122
+ # That's because the same objects get passed around to any caller that
123
+ # asks for them. If you need to modify the array, dup it.
124
+ #
125
+ def test_freeze
126
+ EM::DnsCache.add_nameserver TestNameserver
127
+ EM::DnsCache.verbose
128
+
129
+ out = nil
130
+
131
+ EM.run {
132
+ d = EM::DnsCache.resolve_mx "steamheat.net"
133
+ d.errback {EM.stop}
134
+ d.callback {|r|
135
+ out = r
136
+ EM.stop
137
+ }
138
+ }
139
+
140
+ assert out
141
+ assert( out.length > 0)
142
+ assert_raise( TypeError ) {
143
+ out.clear
144
+ }
145
+ end
146
+
147
+
148
+ def test_local_defs
149
+ EM::DnsCache.add_nameserver TestNameserver
150
+ EM::DnsCache.verbose
151
+
152
+ EM::DnsCache.add_cache_entry( :mx, "example.zzz", ["1.2.3.4"], -1 )
153
+ out = nil
154
+ EM.run {
155
+ d = EM::DnsCache.resolve_mx "example.zzz"
156
+ d.errback {EM.stop}
157
+ d.callback {|r|
158
+ out = r
159
+ EM.stop
160
+ }
161
+ }
162
+ assert_equal( ["1.2.3.4"], out )
163
+ end
164
+
165
+ def test_multiple_local_defs
166
+ EM::DnsCache.verbose
167
+
168
+ EM::DnsCache.add_cache_entry( :mx, "example.zzz", ["1.2.3.4", "5.6.7.8"], -1 )
169
+ out = nil
170
+ EM.run {
171
+ d = EM::DnsCache.resolve_mx "example.zzz"
172
+ d.errback {EM.stop}
173
+ d.callback {|r|
174
+ out = r
175
+ EM.stop
176
+ }
177
+ }
178
+ assert_equal( ["1.2.3.4","5.6.7.8"], out )
179
+ end
180
+
181
+ # Adding cache entries where they already exist causes them to be REPLACED.
182
+ #
183
+ def test_replace
184
+ EM::DnsCache.verbose
185
+
186
+ EM::DnsCache.add_cache_entry( :mx, "example.zzz", ["1.2.3.4", "5.6.7.8"], -1 )
187
+ EM::DnsCache.add_cache_entry( :mx, "example.zzz", ["10.11.12.13"], -1 )
188
+ out = nil
189
+ EM.run {
190
+ d = EM::DnsCache.resolve_mx "example.zzz"
191
+ d.errback {EM.stop}
192
+ d.callback {|r|
193
+ out = r
194
+ EM.stop
195
+ }
196
+ }
197
+ assert_equal( ["10.11.12.13"], out )
198
+ end
199
+
200
+ # We have a facility for storing locally-defined MX records.
201
+ # The DNS cache has a way to parse and process these values.
202
+ #
203
+ def test_external_mx_defs
204
+ EM::DnsCache.verbose
205
+
206
+ EM::DnsCache.parse_local_mx_records LocalMxRecords
207
+
208
+ out = nil
209
+ EM.run {
210
+ d = EM::DnsCache.resolve_mx "boondoggle.zzz"
211
+ d.errback {EM.stop}
212
+ d.callback {|r|
213
+ out = r
214
+ EM.stop
215
+ }
216
+ }
217
+ assert_equal( ["esmtp.someone.zzz", "55.56.57.58", "65.66.67.68"], out )
218
+ end
219
+
220
+ end
221
+
222
+
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-dns
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Aman Gupta
8
+ - Stephan Maka
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-11-07 00:00:00 +01:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: eventmachine
18
+ type: :runtime
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "0"
25
+ version:
26
+ description: DNS::Resolv made ready for EventMachine
27
+ email: astro@spaceboyz.net
28
+ executables: []
29
+
30
+ extensions: []
31
+
32
+ extra_rdoc_files: []
33
+
34
+ files:
35
+ - Rakefile
36
+ - VERSION.yml
37
+ - examples/lookup_many.rb
38
+ - lib/em/dns_cache.rb
39
+ - lib/em/dns_resolver.rb
40
+ - test/test_basic.rb
41
+ has_rdoc: true
42
+ homepage: http://github.com/astro/em-dns
43
+ licenses: []
44
+
45
+ post_install_message:
46
+ rdoc_options:
47
+ - --charset=UTF-8
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ requirements: []
63
+
64
+ rubyforge_project:
65
+ rubygems_version: 1.3.5
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: Resolve domain names from EventMachine natively
69
+ test_files:
70
+ - test/test_basic.rb
71
+ - examples/lookup_many.rb