toolmantim-zeroconf 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +44 -0
- data/Rakefile +71 -0
- data/lib/dnssd.rb +137 -0
- data/lib/net/dns.rb +49 -0
- data/lib/net/dns/mdns-sd.rb +240 -0
- data/lib/net/dns/mdns.rb +1189 -0
- data/lib/net/dns/resolv-mdns.rb +230 -0
- data/lib/net/dns/resolv-replace.rb +66 -0
- data/lib/net/dns/resolv.rb +2012 -0
- data/lib/net/dns/resolvx.rb +219 -0
- data/lib/zeroconf.rb +15 -0
- data/lib/zeroconf/common.rb +0 -0
- data/lib/zeroconf/ext.rb +7 -0
- data/lib/zeroconf/pure.rb +13 -0
- data/lib/zeroconf/version.rb +3 -0
- data/originals/dnssd-0.6.0/COPYING +56 -0
- data/originals/dnssd-0.6.0/README +50 -0
- data/originals/net-mdns-0.4/COPYING +58 -0
- data/originals/net-mdns-0.4/README +21 -0
- data/originals/net-mdns-0.4/TODO +278 -0
- data/samples/exhttp.rb +50 -0
- data/samples/exhttpv1.rb +29 -0
- data/samples/exwebrick.rb +56 -0
- data/samples/mdns-watch.rb +132 -0
- data/samples/mdns.rb +238 -0
- data/samples/test_dns.rb +128 -0
- data/samples/v1demo.rb +167 -0
- data/samples/v1mdns.rb +111 -0
- data/test/stress/stress_register.rb +48 -0
- data/test/test_browse.rb +24 -0
- data/test/test_highlevel_api.rb +35 -0
- data/test/test_register.rb +32 -0
- data/test/test_resolve.rb +23 -0
- data/test/test_resolve_ichat.rb +56 -0
- data/test/test_textrecord.rb +58 -0
- metadata +93 -0
data/samples/mdns.rb
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
#!/usr/local/bin/ruby18 -w
|
2
|
+
# Author: Sam Roberts <sroberts@uniserve.com>
|
3
|
+
# Licence: this file is placed in the public domain
|
4
|
+
|
5
|
+
$:.unshift(File.dirname($0))
|
6
|
+
|
7
|
+
require 'getoptlong'
|
8
|
+
|
9
|
+
$stdout.sync = true
|
10
|
+
$stderr.sync = true
|
11
|
+
|
12
|
+
=begin
|
13
|
+
Apple's dns-sd options:
|
14
|
+
|
15
|
+
mdns -E (Enumerate recommended registration domains)
|
16
|
+
mdns -F (Enumerate recommended browsing domains)
|
17
|
+
mdns -B <Type> <Domain> (Browse for service instances)
|
18
|
+
mdns -L <Name> <Type> <Domain> (Look up a service instance)
|
19
|
+
mdns -R <Name> <Type> <Domain> <Port> [<TXT>...] (Register a service)
|
20
|
+
mdns -P <Name> <Type> <Domain> <Port> <Host> <IP> [<TXT>...] (Proxy)
|
21
|
+
mdns -Q <FQDN> <rrtype> <rrclass> (Generic query for any record type)
|
22
|
+
=end
|
23
|
+
|
24
|
+
@debug = false
|
25
|
+
@log = nil
|
26
|
+
|
27
|
+
@recursive = false
|
28
|
+
@domain = 'local'
|
29
|
+
@type = nil
|
30
|
+
@name = nil
|
31
|
+
@port = nil
|
32
|
+
@txt = {}
|
33
|
+
|
34
|
+
@cmd = nil
|
35
|
+
|
36
|
+
|
37
|
+
# TODO - can I use introspection on class names to determine all supported
|
38
|
+
# RR types in DNS::Resource::IN?
|
39
|
+
|
40
|
+
HELP =<<EOF
|
41
|
+
Usage:
|
42
|
+
mdns [options] -B <type> [domain] (Browse for service instances)
|
43
|
+
mdns [options] -L <name> <type> [domain] (Look up a service instance)
|
44
|
+
mdns [options] -R <name> <type> [domain] <port> [<TXT>...] (Register a service)
|
45
|
+
mdns [options] -Q <fqdn> [rrtype] [rrclass] (Generic query for any record type)
|
46
|
+
|
47
|
+
Note: -Q is not yet implemented.
|
48
|
+
|
49
|
+
For -B, -L, and -R, [domain] is optional and defaults to "local".
|
50
|
+
|
51
|
+
For -Q, [rrtype] defaults to A, other values are TXT, PTR, SRV, CNAME, ...
|
52
|
+
|
53
|
+
For -Q, [rrclass] defaults to 1 (IN).
|
54
|
+
|
55
|
+
|
56
|
+
[<TXT>...] is optional for -R, it can be a series of key=value pairs.
|
57
|
+
|
58
|
+
You can use long names --browse, --lookup, and --register instead of -B, -L,
|
59
|
+
and -R.
|
60
|
+
|
61
|
+
Options:
|
62
|
+
-m,--mdnssd Attempt to use 'net/dns/mdns-sd', a pure-ruby DNS-SD resolver
|
63
|
+
library (this is the default).
|
64
|
+
-n,--dnssd Attempt to use 'dnssd', the interface to the native ("-n")
|
65
|
+
DNS-SD resolver library APIs, "dns_sd.h" from Apple.
|
66
|
+
-d,--debug Print debug messages to stderr.
|
67
|
+
|
68
|
+
Examples:
|
69
|
+
mdns -B _http._tcp
|
70
|
+
mdns -L "My Music" _daap._tcp
|
71
|
+
mdns -R me _example._tcp local 4321 key=value key2=value2
|
72
|
+
|
73
|
+
These work with the test modes of Apple's dns-sd utility:
|
74
|
+
mdns -L Test _testupdate._tcp (for dns-sd -A, -U, -N)
|
75
|
+
mdns -L Test _testlargetxt._tcp (for dns-sd -T)
|
76
|
+
mdns -L Test _testdualtxt._tcp (for dns-sd -M)
|
77
|
+
mdns -L Test _testtxt._tcp (for dns-sd -I)
|
78
|
+
|
79
|
+
EOF
|
80
|
+
|
81
|
+
opts = GetoptLong.new(
|
82
|
+
[ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
|
83
|
+
[ "--help", "-h", GetoptLong::NO_ARGUMENT ],
|
84
|
+
[ "--dnssd", "-n", GetoptLong::NO_ARGUMENT ],
|
85
|
+
[ "--mdnssd", "-m", GetoptLong::NO_ARGUMENT ],
|
86
|
+
|
87
|
+
[ "--browse", "-B", GetoptLong::NO_ARGUMENT ],
|
88
|
+
[ "--lookup", "-L", GetoptLong::NO_ARGUMENT ],
|
89
|
+
[ "--register", "-R", GetoptLong::NO_ARGUMENT ]
|
90
|
+
)
|
91
|
+
|
92
|
+
opts.each do |opt, arg|
|
93
|
+
case opt
|
94
|
+
when "--debug"
|
95
|
+
require 'pp'
|
96
|
+
require 'logger'
|
97
|
+
|
98
|
+
@debug = true
|
99
|
+
@log = Logger.new(STDERR)
|
100
|
+
@log.level = Logger::DEBUG
|
101
|
+
|
102
|
+
when "--help"
|
103
|
+
print HELP
|
104
|
+
exit 0
|
105
|
+
|
106
|
+
when '--dnssd'
|
107
|
+
require 'dnssd'
|
108
|
+
require 'socket'
|
109
|
+
|
110
|
+
when "--browse"
|
111
|
+
@cmd = :browse
|
112
|
+
@type = ARGV.shift
|
113
|
+
@domain = ARGV.shift || @domain
|
114
|
+
|
115
|
+
when "--lookup"
|
116
|
+
@cmd = :lookup
|
117
|
+
@name = ARGV.shift
|
118
|
+
@type = ARGV.shift
|
119
|
+
@domain = ARGV.shift || @domain
|
120
|
+
|
121
|
+
unless @name && @type
|
122
|
+
puts 'name and type required for -L'
|
123
|
+
exit 1
|
124
|
+
end
|
125
|
+
|
126
|
+
when "--register"
|
127
|
+
@cmd = :register
|
128
|
+
@name = ARGV.shift
|
129
|
+
@type = ARGV.shift
|
130
|
+
@port = ARGV.shift
|
131
|
+
if @port.to_i == 0
|
132
|
+
@domain = @port
|
133
|
+
@port = ARGV.shift.to_i
|
134
|
+
else
|
135
|
+
@port = @port.to_i
|
136
|
+
end
|
137
|
+
ARGV.each do |kv|
|
138
|
+
kv.match(/([^=]+)=([^=]+)/)
|
139
|
+
@txt[$1] = $2
|
140
|
+
end
|
141
|
+
ARGV.replace([])
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
begin
|
146
|
+
DNSSD.class
|
147
|
+
puts "Using native DNSSD..."
|
148
|
+
|
149
|
+
Thread.abort_on_exception = true # So we notice exceptions in DNSSD threads.
|
150
|
+
|
151
|
+
module DNSSD
|
152
|
+
def self.namesplit(n)
|
153
|
+
n.scan(/(?:\\.|[^\.])+/)
|
154
|
+
end
|
155
|
+
# DNSSD > 0.6.0 uses class Reply which has these methods already
|
156
|
+
class ResolveReply
|
157
|
+
def domain
|
158
|
+
DNSSD.namesplit(fullname)[-1]
|
159
|
+
end
|
160
|
+
def type
|
161
|
+
DNSSD.namesplit(fullname)[1,2].join('.')
|
162
|
+
end
|
163
|
+
def name
|
164
|
+
DNSSD.namesplit(fullname)[0]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
rescue NameError
|
169
|
+
require 'net/dns/mdns-sd'
|
170
|
+
DNSSD = Net::DNS::MDNSSD
|
171
|
+
Net::DNS::MDNS::Responder.instance.log = @log if @log
|
172
|
+
puts "Using Net::DNS::MDNSSD..."
|
173
|
+
end
|
174
|
+
|
175
|
+
unless @cmd
|
176
|
+
print HELP
|
177
|
+
exit 1
|
178
|
+
end
|
179
|
+
|
180
|
+
case @cmd
|
181
|
+
when :browse
|
182
|
+
STDERR.puts( "DNSSD.#{@cmd}(#{@type}, #{@domain}) =>" ) if @debug
|
183
|
+
|
184
|
+
fmt = "%-3.3s %-8.8s %-15.15s %-20.20s\n"
|
185
|
+
printf fmt, "Ttl", "Domain", "Service Type", "Instance Name"
|
186
|
+
|
187
|
+
handle = DNSSD.browse(@type, @domain) do |reply|
|
188
|
+
begin
|
189
|
+
printf fmt, reply.flags.to_i, reply.domain, reply.type, reply.name
|
190
|
+
rescue
|
191
|
+
p $!
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
$stdin.gets
|
196
|
+
handle.stop
|
197
|
+
|
198
|
+
|
199
|
+
when :lookup
|
200
|
+
STDERR.puts( "DNSSD.#{@cmd}(#{@name}, #{@type}, #{@domain}) =>" ) if @debug
|
201
|
+
|
202
|
+
fmt = "%-3.3s %-8.8s %-19.19s %-20.20s %-20.20s %s\n"
|
203
|
+
printf fmt, "Ttl", "Domain", "Service Type", "Instance Name", "Location", "Text"
|
204
|
+
|
205
|
+
handle = DNSSD.resolve(@name, @type, @domain) do |reply|
|
206
|
+
begin
|
207
|
+
location = "#{reply.target}:#{reply.port}"
|
208
|
+
text = reply.text_record.to_a.map { |kv| "#{kv[0]}=#{kv[1].inspect}" }.join(', ')
|
209
|
+
printf fmt, reply.flags.to_i, reply.domain, reply.type, reply.name, location, text
|
210
|
+
rescue
|
211
|
+
p $!
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
$stdin.gets
|
216
|
+
handle.stop
|
217
|
+
|
218
|
+
when :register
|
219
|
+
STDERR.puts( "DNSSD.#{@cmd}(#{@name}, #{@type}, #{@domain}, #{@port}, #{@txt.inspect}) =>" ) if @debug
|
220
|
+
|
221
|
+
fmt = "%-3.3s %-8.8s %-19.19s %-20.20s %-20.20s %s\n"
|
222
|
+
printf fmt, "Ttl", "Domain", "Service Type", "Instance Name", "Location", "Text"
|
223
|
+
|
224
|
+
handle = DNSSD.register(@name, @type, @domain, @port, @txt) do |notice|
|
225
|
+
begin
|
226
|
+
location = "#{Socket.gethostname}:#{@port}"
|
227
|
+
text = @txt.to_a.map { |kv| "#{kv[0]}=#{kv[1].inspect}" }.join(', ')
|
228
|
+
printf fmt, 'N/A', notice.domain, notice.type, notice.name, location, text
|
229
|
+
rescue
|
230
|
+
p $!
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
$stdin.gets
|
235
|
+
handle.stop
|
236
|
+
|
237
|
+
end
|
238
|
+
|
data/samples/test_dns.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
$:.unshift File.dirname($0)
|
4
|
+
|
5
|
+
require 'net/dns/resolvx.rb'
|
6
|
+
require 'test/unit'
|
7
|
+
|
8
|
+
require 'pp'
|
9
|
+
|
10
|
+
Name = Resolv::DNS::Name
|
11
|
+
|
12
|
+
class TestDns < Test::Unit::TestCase
|
13
|
+
|
14
|
+
def test_name_what_I_think_are_odd_behaviours
|
15
|
+
# Why can't test against strings?
|
16
|
+
assert_equal(false, Name.create("example.CoM") == "example.com")
|
17
|
+
assert_equal(false, Name.create("example.CoM").eql?("example.com"))
|
18
|
+
|
19
|
+
# Why does making it absolute mean they aren't equal?
|
20
|
+
assert_equal(false, Name.create("example.CoM").eql?(Name.create("example.com.")))
|
21
|
+
assert_equal(false, Name.create("example.CoM") == Name.create("example.com."))
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_name_CoMparisons
|
25
|
+
|
26
|
+
assert_equal(true, Name.create("example.CoM").eql?(Name.create("example.com")))
|
27
|
+
assert_equal(true, Name.create("example.CoM") == Name.create("example.com"))
|
28
|
+
|
29
|
+
assert_equal(true, Name.create("example.CoM").equal?("example.com."))
|
30
|
+
assert_equal(true, Name.create("example.CoM").equal?("example.com"))
|
31
|
+
|
32
|
+
assert_equal(true, Name.create("www.example.CoM") < "example.com")
|
33
|
+
assert_equal(true, Name.create("www.example.CoM") <= "example.com")
|
34
|
+
assert_equal(-1, Name.create("www.example.CoM") <=> "example.com")
|
35
|
+
assert_equal(false, Name.create("www.example.CoM") >= "example.com")
|
36
|
+
assert_equal(false, Name.create("www.example.CoM") > "example.com")
|
37
|
+
|
38
|
+
assert_equal(false, Name.create("example.CoM") < "example.com")
|
39
|
+
assert_equal(true, Name.create("example.CoM") <= "example.com")
|
40
|
+
assert_equal(0, Name.create("example.CoM") <=> "example.com")
|
41
|
+
assert_equal(true, Name.create("example.CoM") >= "example.com")
|
42
|
+
assert_equal(false, Name.create("example.CoM") > "example.com")
|
43
|
+
|
44
|
+
assert_equal(false, Name.create("CoM") < "example.com")
|
45
|
+
assert_equal(false, Name.create("CoM") <= "example.com")
|
46
|
+
assert_equal(+1, Name.create("CoM") <=> "example.com")
|
47
|
+
assert_equal(true, Name.create("CoM") >= "example.com")
|
48
|
+
assert_equal(true, Name.create("CoM") > "example.com")
|
49
|
+
|
50
|
+
assert_equal(nil, Name.create("bar.CoM") < "example.com")
|
51
|
+
assert_equal(nil, Name.create("bar.CoM") <= "example.com")
|
52
|
+
assert_equal(nil, Name.create("bar.CoM") <=> "example.com")
|
53
|
+
assert_equal(nil, Name.create("bar.CoM") >= "example.com")
|
54
|
+
assert_equal(nil, Name.create("bar.CoM") > "example.com")
|
55
|
+
|
56
|
+
assert_equal(nil, Name.create("net.") < "com")
|
57
|
+
assert_equal(nil, Name.create("net.") <= "com")
|
58
|
+
assert_equal(nil, Name.create("net.") <=> "com")
|
59
|
+
assert_equal(nil, Name.create("net.") >= "com")
|
60
|
+
assert_equal(nil, Name.create("net.") > "com")
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_txt_with_0_strs
|
65
|
+
# Packet collected from the wild, it is non-conformant with DNS
|
66
|
+
# specification, TXT record has zero strings, but should have 1 or more.
|
67
|
+
d = "\000\000\204\000\000\000\000\005\000\000\000\000\002me\005local\000\000\001\200\001\000\000\000\360\000\004\300\250\003\003\005proxy\010_example\004_tcp\300\017\000!\200\001\000\000\000\360\000\010\000\000\000\000'\017\300\f\300$\000\020\200\001\000\000\000\360\000\000\t_services\a_dns-sd\004_udp\300\017\000\f\000\001\000\000\034 \000\002\300*\300*\000\f\000\001\000\000\034 \000\002\300$"
|
68
|
+
|
69
|
+
m = Resolv::DNS::Message.decode( d )
|
70
|
+
|
71
|
+
assert_equal('', m.answer[2][2].data)
|
72
|
+
assert_equal([''], m.answer[2][2].strings)
|
73
|
+
end
|
74
|
+
|
75
|
+
def txt_codec(*args)
|
76
|
+
m = Resolv::DNS::Message.new
|
77
|
+
m.add_answer('example.local', 0, Resolv::DNS::Resource::IN::TXT.new(*args))
|
78
|
+
# pp m
|
79
|
+
m = Resolv::DNS::Message.decode(m.encode)
|
80
|
+
txt = m.answer[0][2]
|
81
|
+
# pp txt
|
82
|
+
txt
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_txt_with_large_str
|
86
|
+
# short or no strings
|
87
|
+
txt = txt_codec()
|
88
|
+
assert_equal('', txt.data)
|
89
|
+
assert_equal([''], txt.strings)
|
90
|
+
|
91
|
+
txt = txt_codec('')
|
92
|
+
assert_equal('', txt.data)
|
93
|
+
assert_equal([''], txt.strings)
|
94
|
+
|
95
|
+
txt = txt_codec('s')
|
96
|
+
assert_equal('s', txt.data)
|
97
|
+
assert_equal(['s'], txt.strings)
|
98
|
+
|
99
|
+
txt = txt_codec('s', 'a', 'm')
|
100
|
+
assert_equal('sam', txt.data)
|
101
|
+
assert_equal(['s', 'a', 'm'], txt.strings)
|
102
|
+
|
103
|
+
s = '0' * 255
|
104
|
+
|
105
|
+
# long strings
|
106
|
+
txt = txt_codec(s)
|
107
|
+
assert_equal(s, txt.data)
|
108
|
+
assert_equal([s], txt.strings)
|
109
|
+
|
110
|
+
# long strings
|
111
|
+
f = s + 'a'
|
112
|
+
txt = txt_codec(f)
|
113
|
+
assert_equal(f, txt.data)
|
114
|
+
assert_equal([s, 'a'], txt.strings)
|
115
|
+
|
116
|
+
f = s + s
|
117
|
+
txt = txt_codec(f)
|
118
|
+
assert_equal(f, txt.data)
|
119
|
+
assert_equal([s, s], txt.strings)
|
120
|
+
|
121
|
+
assert_raise(ArgumentError) { txt_codec(f, 'a') }
|
122
|
+
assert_raise(ArgumentError) { txt_codec('a', f) }
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
end
|
128
|
+
|
data/samples/v1demo.rb
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
#!/usr/local/bin/ruby18 -w
|
2
|
+
#
|
3
|
+
# Example code to use the multicast library
|
4
|
+
# (c) 2005 Ben Giddings
|
5
|
+
# License: Ruby's License
|
6
|
+
|
7
|
+
# Include local dir
|
8
|
+
$: << File.dirname($0)
|
9
|
+
|
10
|
+
require 'net/dns/resolv-mdns'
|
11
|
+
require 'pp'
|
12
|
+
|
13
|
+
# default to browsing
|
14
|
+
operation = ARGV[0] || 'browse'
|
15
|
+
|
16
|
+
resolver = Resolv::MDNS.new
|
17
|
+
resolver.lazy_initialize
|
18
|
+
|
19
|
+
def usage
|
20
|
+
$stderr.puts <<-EOF
|
21
|
+
Usage: #{$0} [browse|discover|resolve|lookup] [service_str]
|
22
|
+
|
23
|
+
Use "browse" to find names offering the service requested.
|
24
|
+
|
25
|
+
Examples:
|
26
|
+
zsh% #{$0} browse
|
27
|
+
Searching for _http._tcp.local services
|
28
|
+
Matching entries in _http._tcp.local
|
29
|
+
Cool Webserver
|
30
|
+
|
31
|
+
zsh% #{$0} browse http
|
32
|
+
Searching for _http._tcp.local services
|
33
|
+
Matching entries in _http._tcp.local
|
34
|
+
Cool Webserver
|
35
|
+
|
36
|
+
zsh% #{$0} browse _http._tcp.local
|
37
|
+
Searching for _http._tcp.local services
|
38
|
+
Matching entries in _http._tcp.local
|
39
|
+
Cool Webserver
|
40
|
+
|
41
|
+
zsh% #{$0} browse _telnet._tcp.local
|
42
|
+
Searching for _telnet._tcp.local services
|
43
|
+
Matching entries in _telnet._tcp.local
|
44
|
+
Embedded Device (MAC addr: 00:01:02:03:04:05)
|
45
|
+
|
46
|
+
By default 'browse' looks for _http._tcp.local services/
|
47
|
+
If the only parameter supplied is a protocol, it looks for
|
48
|
+
local tcp services of that protocol
|
49
|
+
|
50
|
+
Use "resolve" to look up the dns name, port and ip address of a device
|
51
|
+
|
52
|
+
zsh% #{$0} resolve 'Cool Webserver'
|
53
|
+
Searching for Cool Webserver._http._tcp.local instance
|
54
|
+
Cool Webserver._http._tcp.local is
|
55
|
+
coolweb.local:80 at IP 192.168.0.12
|
56
|
+
|
57
|
+
zsh% #{$0} resolve 'Cool Webserver._http._tcp.local'
|
58
|
+
Searching for Cool Webserver._http._tcp.local instance
|
59
|
+
Cool Webserver._http._tcp.local is
|
60
|
+
coolweb.local:80 at IP 192.168.0.12
|
61
|
+
|
62
|
+
zsh% #{$0} resolve 'Embedded Device (MAC addr: 00:01:02:03:04:05)._telnet._tcp.local'
|
63
|
+
Searching for Embedded Device (MAC addr: 00:01:02:03:04:05)._telnet._tcp.local instance
|
64
|
+
Embedded Device (MAC addr: 00:01:02:03:04:05)._telnet._tcp.local is
|
65
|
+
emb000102030405.local:23 at IP 192.168.0.93
|
66
|
+
|
67
|
+
By default 'resolve' will append _http._tcp.local if it is missing
|
68
|
+
|
69
|
+
EOF
|
70
|
+
end
|
71
|
+
|
72
|
+
case (operation.downcase)
|
73
|
+
when 'browse', 'discover'
|
74
|
+
#
|
75
|
+
# To browse / discover services, use a ptr lookup on the service protocol
|
76
|
+
# A typical call might be
|
77
|
+
# resolver.getresources('_http._tcp.local', Resolv::DNS::Resource::IN::PTR)
|
78
|
+
#
|
79
|
+
|
80
|
+
# default to browsing for http hosts
|
81
|
+
service_str = ARGV[1] || 'http'
|
82
|
+
|
83
|
+
# prepend an underscore if necessary
|
84
|
+
# p service_str
|
85
|
+
|
86
|
+
if ?_ != service_str[0]
|
87
|
+
puts "prepending underscore to #{service_str}"
|
88
|
+
service_str = '_' + service_str
|
89
|
+
end
|
90
|
+
|
91
|
+
# append a ._tcp.local if necessary (assume tcp and local)
|
92
|
+
if service_str.index('.').nil?
|
93
|
+
puts "No dot found in service name, appending ._tcp.local"
|
94
|
+
service_str += '._tcp.local'
|
95
|
+
end
|
96
|
+
|
97
|
+
puts "Searching for #{service_str} services"
|
98
|
+
|
99
|
+
entries = resolver.getresources(service_str, Resolv::DNS::Resource::IN::PTR)
|
100
|
+
|
101
|
+
# p entries
|
102
|
+
|
103
|
+
puts "Matching entries in #{service_str}"
|
104
|
+
entries.each {
|
105
|
+
|entry|
|
106
|
+
# p entry
|
107
|
+
# I think this will always match but just in case...
|
108
|
+
friendly_name_regexp = /(.*?)\.#{service_str}/
|
109
|
+
|
110
|
+
match = friendly_name_regexp.match(entry.name.to_s)
|
111
|
+
if match
|
112
|
+
puts match[1]
|
113
|
+
else
|
114
|
+
puts entry.name
|
115
|
+
end
|
116
|
+
}
|
117
|
+
when 'resolve', 'lookup'
|
118
|
+
#
|
119
|
+
# To resolve / lookup services, first use a SRV lookup to lookup the service
|
120
|
+
# details, then extract the hostname from the result. Using this hostname,
|
121
|
+
# lookup the A record to find the IP address.
|
122
|
+
#
|
123
|
+
|
124
|
+
service_str = ARGV[1]
|
125
|
+
if service_str.nil?
|
126
|
+
$stderr.puts("Service string required for resolve / lookup")
|
127
|
+
usage()
|
128
|
+
exit(1)
|
129
|
+
end
|
130
|
+
|
131
|
+
# append a ._http._tcp.local if necessary (assume http, tcp and local)
|
132
|
+
if service_str.index('.').nil?
|
133
|
+
puts "No dot found in service name, appending ._http._tcp.local"
|
134
|
+
service_str += '._http._tcp.local'
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
puts "Searching for #{service_str} instance"
|
139
|
+
entries = resolver.getresources(service_str, Resolv::DNS::Resource::IN::SRV)
|
140
|
+
|
141
|
+
if 0 == entries.size
|
142
|
+
puts "Unable to find #{service_str}"
|
143
|
+
exit(0)
|
144
|
+
end
|
145
|
+
|
146
|
+
entry = entries[0]
|
147
|
+
# puts "Found match at #{entry.target}, port #{entry.port}"
|
148
|
+
|
149
|
+
hostname = entry.target
|
150
|
+
port = entry.port
|
151
|
+
entries = resolver.getresources(hostname, Resolv::DNS::Resource::IN::A)
|
152
|
+
|
153
|
+
if 0 == entries.size
|
154
|
+
puts "Unable to resolve #{hostname}"
|
155
|
+
exit(1)
|
156
|
+
end
|
157
|
+
|
158
|
+
entry = entries[0]
|
159
|
+
# p entry
|
160
|
+
# p entry.address
|
161
|
+
puts "#{service_str} is\n#{hostname}:#{port} at IP #{entry.address.to_s}"
|
162
|
+
|
163
|
+
else
|
164
|
+
$stderr.puts("unknown operation #{operation.inspect}")
|
165
|
+
usage()
|
166
|
+
exit(1)
|
167
|
+
end
|