zeroconf 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,334 @@
1
+ require "helper"
2
+
3
+ module ZeroConf
4
+ class ServiceTest < Test
5
+ include ZeroConf::Utils
6
+
7
+ attr_reader :iface
8
+
9
+ def setup
10
+ super
11
+ @iface = ZeroConf.interfaces.find_all { |x| x.addr.ipv4? }.first
12
+ end
13
+
14
+ def test_service_info
15
+ s = Service.new "_test-mdns._tcp.local.",
16
+ 42424,
17
+ "tc-lan-adapter",
18
+ service_interfaces: [iface]
19
+
20
+ assert_equal "_test-mdns._tcp.local.", s.service
21
+ assert_equal 42424, s.service_port
22
+ assert_equal "tc-lan-adapter", s.hostname
23
+ assert_equal [iface], s.service_interfaces
24
+ assert_equal "#{s.hostname}.#{s.service}", s.service_name
25
+ assert_equal "#{s.hostname}.local.", s.qualified_host
26
+ assert_equal [""], s.text
27
+ end
28
+
29
+ def test_unicast_service_instance_answer
30
+ s = make_server iface
31
+ runner = Thread.new { s.start }
32
+
33
+ query = Resolv::DNS::Message.new 0
34
+ query.add_question "tc-lan-adapter._test-mdns._tcp.local.", ZeroConf::SRV
35
+
36
+ sock = open_ipv4 iface.addr, 0
37
+ multicast_send sock, query.encode
38
+ res = Resolv::DNS::Message.decode sock.recvfrom(2048).first
39
+ s.stop
40
+ runner.join
41
+
42
+ expected = Resolv::DNS::Message.new(0)
43
+ expected.qr = 1
44
+ expected.aa = 1
45
+
46
+ expected.add_additional s.qualified_host,
47
+ 10,
48
+ Resolv::DNS::Resource::IN::A.new(iface.addr.ip_address)
49
+
50
+ expected.add_additional s.service_name,
51
+ 10,
52
+ Resolv::DNS::Resource::IN::TXT.new(*s.text)
53
+ expected.add_answer s.service_name, 10, Resolv::DNS::Resource::IN::SRV.new(0, 0, s.service_port, s.qualified_host)
54
+ expected.add_question s.service_name, ZeroConf::MDNS::Announce::IN::SRV
55
+
56
+ assert_equal expected, res
57
+ end
58
+
59
+ def test_multicast_discover
60
+ q = Queue.new
61
+ rd, wr = IO.pipe
62
+
63
+ listen = make_listener rd, q
64
+ s = make_server iface
65
+ server = Thread.new { s.start }
66
+
67
+ query = Resolv::DNS::Message.new 0
68
+ query.add_question "_services._dns-sd._udp.local.", Resolv::DNS::Resource::IN::PTR
69
+ sock = open_ipv4 iface.addr, 0
70
+ multicast_send sock, query.encode
71
+
72
+ while res = q.pop
73
+ if res.answer.find { |name, ttl, data| name.to_s == "_services._dns-sd._udp.local" && data.name.to_s == SERVICE }
74
+ wr.write "x"
75
+ break
76
+ end
77
+ end
78
+
79
+ listen.join
80
+ s.stop
81
+ server.join
82
+
83
+ msg = Resolv::DNS::Message.new(0)
84
+ msg.qr = 1
85
+ msg.aa = 1
86
+
87
+ msg.add_answer DISCOVERY_NAME, 60,
88
+ Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(s.service))
89
+
90
+ assert_equal msg, res
91
+ end
92
+
93
+ def test_announcement
94
+ ann = "\x00\x00\x84\x00\x00\x00\x00\x01\x00\x00\x00\x04\n_test-mdns\x04_tcp\x05local\x00\x00\f\x00\x01\x00\x00\x00<\x00\x15\x0Etc-lan-adapter\x03lan\xC0\f\xC0-\x00!\x80\x01\x00\x00\x00<\x00 \x00\x00\x00\x00\xA5\xB8\x0Etc-lan-adapter\x03lan\x05local\x00\xC0T\x00\x01\x80\x01\x00\x00\x00<\x00\x04\n\x00\x01\x95\xC0T\x00\x1C\x80\x01\x00\x00\x00<\x00\x10\xFD\xDA\x85k\tL\x00\x00\x10\xF6\x892\xEA\xBB\\H\xC0-\x00\x10\x80\x01\x00\x00\x00<\x00\x01\x00".b
95
+ msg = Resolv::DNS::Message.decode ann
96
+ service = Service.new "_test-mdns._tcp.local.", 42424
97
+ assert_equal msg, service.announcement
98
+ end
99
+
100
+ def test_disconnect
101
+ data = "\x00\x00\x84\x00\x00\x00\x00\x01\x00\x00\x00\x03\n_test-mdns\x04_tcp\x05local\x00\x00\f\x00\x01\x00\x00\x00\x00\x00\x11\x0Etc-lan-adapter\xC0\f\xC0-\x00!\x00\x01\x00\x00\x00\x00\x00\x1C\x00\x00\x00\x00\xA5\xB8\x0Etc-lan-adapter\x05local\x00\xC0P\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\n\x00\x01\x95\xC0-\x00\x10\x00\x01\x00\x00\x00\x00\x00\x13\x06test=1\vother=value".b
102
+ m = Resolv::DNS::Message.decode data
103
+ s = make_server iface
104
+ assert_equal m, s.disconnect_msg
105
+ end
106
+
107
+ def test_dnssd_unicast_answer
108
+ s = make_server iface
109
+ runner = Thread.new { s.start }
110
+
111
+ query = Resolv::DNS::Message.new 0
112
+ query.add_question "_services._dns-sd._udp.local.", ZeroConf::PTR
113
+
114
+ sock = open_ipv4 iface.addr, 0
115
+ multicast_send sock, query.encode
116
+
117
+ res = nil
118
+ loop do
119
+ buf, from = sock.recvfrom(2048)
120
+ res = Resolv::DNS::Message.decode buf
121
+ if from.last == iface.addr.ip_address
122
+ break if res.answer.find { |name, ttl, data| data.name.to_s == SERVICE }
123
+ end
124
+ end
125
+
126
+ s.stop
127
+ runner.join
128
+
129
+ expected = Resolv::DNS::Message.new(0)
130
+ expected.qr = 1
131
+ expected.aa = 1
132
+
133
+ expected.add_answer DISCOVERY_NAME, 10,
134
+ Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(s.service))
135
+
136
+ expected.add_question DISCOVERY_NAME, ZeroConf::PTR
137
+
138
+ assert_equal expected, res
139
+ end
140
+
141
+ def test_service_multicast_answer
142
+ q = Queue.new
143
+ rd, wr = IO.pipe
144
+
145
+ listen = make_listener rd, q
146
+ s = make_server iface
147
+ server = Thread.new { s.start }
148
+
149
+ query = Resolv::DNS::Message.new 0
150
+ query.add_question "_test-mdns._tcp.local.", Resolv::DNS::Resource::IN::PTR
151
+ sock = open_ipv4 iface.addr, 0
152
+ multicast_send sock, query.encode
153
+
154
+ service = Resolv::DNS::Name.create s.service
155
+ service_name = Resolv::DNS::Name.create s.service_name
156
+
157
+ while res = q.pop
158
+ if res.answer.find { |name, ttl, data| name == service && data.name == service_name }
159
+ wr.write "x"
160
+ break
161
+ end
162
+ end
163
+
164
+ listen.join
165
+ s.stop
166
+ server.join
167
+
168
+ msg = Resolv::DNS::Message.new(0)
169
+ msg.qr = 1
170
+ msg.aa = 1
171
+
172
+ msg.add_additional s.service_name, 60, MDNS::Announce::IN::SRV.new(0, 0, s.service_port, s.qualified_host)
173
+
174
+ msg.add_additional s.qualified_host,
175
+ 60,
176
+ MDNS::Announce::IN::A.new(iface.addr.ip_address)
177
+
178
+ msg.add_additional s.service_name,
179
+ 60,
180
+ MDNS::Announce::IN::TXT.new(*s.text)
181
+
182
+ msg.add_answer s.service,
183
+ 60,
184
+ Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(s.service_name))
185
+
186
+ assert_equal msg.encode, res.encode
187
+ end
188
+
189
+ def test_service_unicast_answer
190
+ s = make_server iface
191
+ runner = Thread.new { s.start }
192
+
193
+ query = Resolv::DNS::Message.new 0
194
+ query.add_question "_test-mdns._tcp.local.", ZeroConf::PTR
195
+
196
+ sock = open_ipv4 iface.addr, 0
197
+ multicast_send sock, query.encode
198
+ res = Resolv::DNS::Message.decode sock.recvfrom(2048).first
199
+ s.stop
200
+ runner.join
201
+
202
+ expected = Resolv::DNS::Message.new(0)
203
+ expected.qr = 1
204
+ expected.aa = 1
205
+
206
+ expected.add_additional s.service_name, 10, Resolv::DNS::Resource::IN::SRV.new(0, 0, s.service_port, s.qualified_host)
207
+ expected.add_additional s.qualified_host,
208
+ 10,
209
+ Resolv::DNS::Resource::IN::A.new(iface.addr.ip_address)
210
+
211
+ expected.add_additional s.service_name,
212
+ 10,
213
+ Resolv::DNS::Resource::IN::TXT.new(*s.text)
214
+ expected.add_answer s.service,
215
+ 10,
216
+ Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(s.service_name))
217
+ expected.add_question s.service, ZeroConf::PTR
218
+
219
+ assert_equal expected, res
220
+ end
221
+
222
+ def test_multicast_service_instance_answer
223
+ q = Queue.new
224
+ rd, wr = IO.pipe
225
+
226
+ listen = make_listener rd, q
227
+ s = make_server iface
228
+ server = Thread.new { s.start }
229
+
230
+ query = Resolv::DNS::Message.new 0
231
+ query.add_question "tc-lan-adapter._test-mdns._tcp.local.", Resolv::DNS::Resource::IN::PTR
232
+ sock = open_ipv4 iface.addr, 0
233
+ multicast_send sock, query.encode
234
+
235
+ service_name = Resolv::DNS::Name.create s.service_name
236
+
237
+ while res = q.pop
238
+ if res.answer.find { |name, ttl, data| name == service_name }
239
+ wr.write "x"
240
+ break
241
+ end
242
+ end
243
+
244
+ listen.join
245
+ s.stop
246
+ server.join
247
+
248
+ msg = Resolv::DNS::Message.new(0)
249
+ msg.qr = 1
250
+ msg.aa = 1
251
+
252
+ msg.add_answer s.service_name, 60, Resolv::DNS::Resource::IN::SRV.new(0, 0, s.service_port, s.qualified_host)
253
+
254
+ msg.add_additional s.qualified_host,
255
+ 60,
256
+ Resolv::DNS::Resource::IN::A.new(iface.addr.ip_address)
257
+
258
+ msg.add_additional service_name,
259
+ 60,
260
+ Resolv::DNS::Resource::IN::TXT.new(*s.text)
261
+
262
+
263
+ assert_equal msg, res
264
+ end
265
+
266
+ def test_unicast_name_lookup
267
+ s = make_server iface
268
+ runner = Thread.new { s.start }
269
+
270
+ query = Resolv::DNS::Message.new 0
271
+ query.add_question "tc-lan-adapter.local.", ZeroConf::A
272
+
273
+ sock = open_ipv4 iface.addr, 0
274
+ multicast_send sock, query.encode
275
+ res = Resolv::DNS::Message.decode sock.recvfrom(2048).first
276
+ s.stop
277
+ runner.join
278
+
279
+ expected = Resolv::DNS::Message.new(0)
280
+ expected.qr = 1
281
+ expected.aa = 1
282
+
283
+ expected.add_answer s.qualified_host, 10, Resolv::DNS::Resource::IN::A.new(iface.addr.ip_address)
284
+
285
+ expected.add_additional s.service_name,
286
+ 10,
287
+ Resolv::DNS::Resource::IN::TXT.new(*s.text)
288
+
289
+ expected.add_question s.qualified_host,
290
+ ZeroConf::MDNS::Announce::IN::A
291
+
292
+ assert_equal expected, res
293
+ end
294
+
295
+ def test_multicast_name
296
+ q = Queue.new
297
+ rd, wr = IO.pipe
298
+
299
+ listen = make_listener rd, q
300
+ s = make_server iface
301
+ server = Thread.new { s.start }
302
+
303
+ query = Resolv::DNS::Message.new 0
304
+ query.add_question "tc-lan-adapter.local.", Resolv::DNS::Resource::IN::A
305
+ sock = open_ipv4 iface.addr, 0
306
+ multicast_send sock, query.encode
307
+
308
+ host = Resolv::DNS::Name.create s.qualified_host
309
+
310
+ while res = q.pop
311
+ if res.answer.find { |name, ttl, data| name == host }
312
+ wr.write "x"
313
+ break
314
+ end
315
+ end
316
+
317
+ listen.join
318
+ s.stop
319
+ server.join
320
+
321
+ expected = Resolv::DNS::Message.new(0)
322
+ expected.qr = 1
323
+ expected.aa = 1
324
+
325
+ expected.add_answer s.qualified_host, 60, Resolv::DNS::Resource::IN::A.new(iface.addr.ip_address)
326
+
327
+ expected.add_additional s.service_name,
328
+ 60,
329
+ Resolv::DNS::Resource::IN::TXT.new(*s.text)
330
+
331
+ assert_equal expected, res
332
+ end
333
+ end
334
+ end
data/zeroconf.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ require_relative "lib/zeroconf/version"
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "zeroconf"
5
+ s.version = ZeroConf::VERSION
6
+ s.summary = "Multicast DNS client and server"
7
+ s.description = "This is a multicast DNS client and server written in Ruby"
8
+ s.authors = [ "Aaron Patterson" ]
9
+ s.email = "tenderlove@ruby-lang.org"
10
+ s.files = `git ls-files -z`.split("\x0")
11
+ s.test_files = s.files.grep(%r{^test/})
12
+ s.homepage = "https://github.com/tenderlove/zeroconf"
13
+ s.license = "Apache-2.0"
14
+
15
+ s.add_dependency("resolv", "~> 0.3.0")
16
+ s.add_development_dependency("rake", "~> 13.0")
17
+ s.add_development_dependency("minitest", "~> 5.20")
18
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zeroconf
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Aaron Patterson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-12-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: resolv
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.3.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.3.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.20'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.20'
55
+ description: This is a multicast DNS client and server written in Ruby
56
+ email: tenderlove@ruby-lang.org
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - CODE_OF_CONDUCT.md
62
+ - Gemfile
63
+ - LICENSE
64
+ - README.md
65
+ - Rakefile
66
+ - lib/zeroconf.rb
67
+ - lib/zeroconf/service.rb
68
+ - lib/zeroconf/utils.rb
69
+ - lib/zeroconf/version.rb
70
+ - test/client_test.rb
71
+ - test/helper.rb
72
+ - test/service_test.rb
73
+ - zeroconf.gemspec
74
+ homepage: https://github.com/tenderlove/zeroconf
75
+ licenses:
76
+ - Apache-2.0
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubygems_version: 3.5.0.dev
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: Multicast DNS client and server
97
+ test_files:
98
+ - test/client_test.rb
99
+ - test/helper.rb
100
+ - test/service_test.rb