zeroconf 1.0.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.
@@ -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