em-dns 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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