zeroconf 1.2.0 → 1.3.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +2 -2
- data/.github/workflows/release.yml +52 -0
- data/Gemfile +2 -0
- data/Rakefile +3 -1
- data/lib/zeroconf/service.rb +87 -3
- data/lib/zeroconf/utils.rb +6 -2
- data/lib/zeroconf/version.rb +1 -1
- data/lib/zeroconf.rb +1 -1
- data/test/client_test.rb +67 -0
- data/test/helper.rb +9 -0
- data/test/service_test.rb +126 -0
- data/zeroconf.gemspec +1 -1
- metadata +7 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7e5fe766c828fdaf41bdacf9ecce12d863c4a636f7c2292ec3ca8aef89413950
|
|
4
|
+
data.tar.gz: 51786741d2dbc2dffb92a1b4ec25b31a994db81aa1d099dc6f530dded84964e7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 89974e69dce75b540f73e70313944bea011fcfe00bd8519af0cba02cb27e823600325d117efabe2febaac05d8d782edf2ecd5014143e35bfa8a31f27019314bc
|
|
7
|
+
data.tar.gz: e4f99e2f61956d457d2ae10fdb4969e682f86cb959a9bd7f4937c389a160c07c4a8e9f5f4ca4d5dab925390ed3ac80909b3603698c80629b70e83072ef51ea62
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: Publish gem to rubygems.org
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
push:
|
|
13
|
+
if: github.repository == 'tenderlove/zeroconf'
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
|
|
16
|
+
environment:
|
|
17
|
+
name: rubygems.org
|
|
18
|
+
url: https://rubygems.org/gems/zeroconf
|
|
19
|
+
|
|
20
|
+
permissions:
|
|
21
|
+
contents: write
|
|
22
|
+
id-token: write
|
|
23
|
+
|
|
24
|
+
strategy:
|
|
25
|
+
matrix:
|
|
26
|
+
ruby: ["ruby"]
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
- name: Harden Runner
|
|
30
|
+
uses: step-security/harden-runner@v2
|
|
31
|
+
with:
|
|
32
|
+
egress-policy: audit
|
|
33
|
+
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
|
|
36
|
+
- name: Set up Ruby
|
|
37
|
+
uses: ruby/setup-ruby@v1
|
|
38
|
+
with:
|
|
39
|
+
ruby-version: ${{ matrix.ruby }}
|
|
40
|
+
|
|
41
|
+
- name: Install dependencies
|
|
42
|
+
run: bundle install --jobs 4 --retry 3
|
|
43
|
+
|
|
44
|
+
- name: Publish to RubyGems
|
|
45
|
+
uses: rubygems/release-gem@v1
|
|
46
|
+
|
|
47
|
+
- name: Create GitHub release
|
|
48
|
+
run: |
|
|
49
|
+
tag_name="$(git describe --tags --abbrev=0)"
|
|
50
|
+
gh release create "${tag_name}" --verify-tag --generate-notes
|
|
51
|
+
env:
|
|
52
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
data/lib/zeroconf/service.rb
CHANGED
|
@@ -7,9 +7,10 @@ module ZeroConf
|
|
|
7
7
|
include Utils
|
|
8
8
|
|
|
9
9
|
attr_reader :service, :service_port, :hostname, :service_interfaces,
|
|
10
|
-
:service_name, :qualified_host, :text, :abort_on_malformed_requests
|
|
10
|
+
:service_name, :qualified_host, :text, :abort_on_malformed_requests,
|
|
11
|
+
:subtypes
|
|
11
12
|
|
|
12
|
-
def initialize service, service_port, hostname = Socket.gethostname, service_interfaces: ZeroConf.service_interfaces, instance_name: nil, text: [""], abort_on_malformed_requests: false, started_callback: nil
|
|
13
|
+
def initialize service, service_port, hostname = Socket.gethostname, service_interfaces: ZeroConf.service_interfaces, instance_name: nil, text: [""], subtypes: [], abort_on_malformed_requests: false, started_callback: nil
|
|
13
14
|
@service = service
|
|
14
15
|
@service_port = service_port
|
|
15
16
|
|
|
@@ -19,13 +20,14 @@ module ZeroConf
|
|
|
19
20
|
|
|
20
21
|
instance_name ||= @hostname
|
|
21
22
|
if instance_name.include?(".")
|
|
22
|
-
raise ArgumentError, "instance_name must not contain dots (is #{instance_name.inspect})"
|
|
23
|
+
raise ArgumentError, "instance_name must not contain dots (is #{instance_name.inspect})"
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
@service_name = "#{instance_name}.#{@service}"
|
|
26
27
|
@qualified_host = "#{@hostname}.local."
|
|
27
28
|
@started_callback = started_callback
|
|
28
29
|
@text = text
|
|
30
|
+
@subtypes = subtypes.map { |st| "#{st}._sub.#{@service}" }
|
|
29
31
|
@started = false
|
|
30
32
|
@rd, @wr = IO.pipe
|
|
31
33
|
end
|
|
@@ -61,6 +63,11 @@ module ZeroConf
|
|
|
61
63
|
60,
|
|
62
64
|
Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(service_name))
|
|
63
65
|
|
|
66
|
+
subtypes.each do |st|
|
|
67
|
+
msg.add_answer st, 60,
|
|
68
|
+
Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(service_name))
|
|
69
|
+
end
|
|
70
|
+
|
|
64
71
|
msg
|
|
65
72
|
end
|
|
66
73
|
|
|
@@ -93,6 +100,11 @@ module ZeroConf
|
|
|
93
100
|
0,
|
|
94
101
|
Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(service_name))
|
|
95
102
|
|
|
103
|
+
subtypes.each do |st|
|
|
104
|
+
msg.add_answer st, 0,
|
|
105
|
+
Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(service_name))
|
|
106
|
+
end
|
|
107
|
+
|
|
96
108
|
msg
|
|
97
109
|
end
|
|
98
110
|
|
|
@@ -174,6 +186,12 @@ module ZeroConf
|
|
|
174
186
|
else
|
|
175
187
|
name_answer_multicast
|
|
176
188
|
end
|
|
189
|
+
when *subtypes
|
|
190
|
+
if unicast
|
|
191
|
+
subtype_unicast_answer qn
|
|
192
|
+
else
|
|
193
|
+
subtype_multicast_answer qn
|
|
194
|
+
end
|
|
177
195
|
else
|
|
178
196
|
#p [:QUERY2, type, type::ClassValue, name]
|
|
179
197
|
end
|
|
@@ -313,6 +331,72 @@ module ZeroConf
|
|
|
313
331
|
msg
|
|
314
332
|
end
|
|
315
333
|
|
|
334
|
+
def subtype_multicast_answer subtype
|
|
335
|
+
msg = Resolv::DNS::Message.new(0)
|
|
336
|
+
msg.qr = 1
|
|
337
|
+
msg.aa = 1
|
|
338
|
+
|
|
339
|
+
msg.add_additional service_name, 60, Resolv::DNS::Resource::IN::SRV.new(0, 0, service_port, qualified_host)
|
|
340
|
+
|
|
341
|
+
service_interfaces.each do |iface|
|
|
342
|
+
if iface.addr.ipv4?
|
|
343
|
+
msg.add_additional qualified_host,
|
|
344
|
+
60,
|
|
345
|
+
Resolv::DNS::Resource::IN::A.new(iface.addr.ip_address)
|
|
346
|
+
else
|
|
347
|
+
msg.add_additional qualified_host,
|
|
348
|
+
60,
|
|
349
|
+
Resolv::DNS::Resource::IN::AAAA.new(iface.addr.ip_address)
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
if @text
|
|
354
|
+
msg.add_additional service_name,
|
|
355
|
+
60,
|
|
356
|
+
Resolv::DNS::Resource::IN::TXT.new(*@text)
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
msg.add_answer subtype,
|
|
360
|
+
60,
|
|
361
|
+
Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(service_name))
|
|
362
|
+
|
|
363
|
+
msg
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def subtype_unicast_answer subtype
|
|
367
|
+
msg = Resolv::DNS::Message.new(0)
|
|
368
|
+
msg.qr = 1
|
|
369
|
+
msg.aa = 1
|
|
370
|
+
|
|
371
|
+
msg.add_additional service_name, 10, Resolv::DNS::Resource::IN::SRV.new(0, 0, service_port, qualified_host)
|
|
372
|
+
|
|
373
|
+
service_interfaces.each do |iface|
|
|
374
|
+
if iface.addr.ipv4?
|
|
375
|
+
msg.add_additional qualified_host,
|
|
376
|
+
10,
|
|
377
|
+
Resolv::DNS::Resource::IN::A.new(iface.addr.ip_address)
|
|
378
|
+
else
|
|
379
|
+
msg.add_additional qualified_host,
|
|
380
|
+
10,
|
|
381
|
+
Resolv::DNS::Resource::IN::AAAA.new(iface.addr.ip_address)
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
if @text
|
|
386
|
+
msg.add_additional service_name,
|
|
387
|
+
10,
|
|
388
|
+
Resolv::DNS::Resource::IN::TXT.new(*@text)
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
msg.add_answer subtype,
|
|
392
|
+
10,
|
|
393
|
+
Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(service_name))
|
|
394
|
+
|
|
395
|
+
msg.add_question subtype, PTR
|
|
396
|
+
|
|
397
|
+
msg
|
|
398
|
+
end
|
|
399
|
+
|
|
316
400
|
def service_instance_multicast_answer
|
|
317
401
|
msg = Resolv::DNS::Message.new(0)
|
|
318
402
|
msg.qr = 1
|
data/lib/zeroconf/utils.rb
CHANGED
|
@@ -23,18 +23,22 @@ module ZeroConf
|
|
|
23
23
|
::Resolv::DNS::Resource::ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
# ZeroConf::A and ZeroConf::SRV are referenced by name when constructing
|
|
27
|
+
# outgoing queries (see ZeroConf::Resolver). They share the numeric
|
|
28
|
+
# ClassValue (0x8001) with the MDNS::Announce::IN equivalents below, so
|
|
29
|
+
# only one of the two should populate Resolv's ClassHash to avoid
|
|
30
|
+
# "already initialized constant" warnings. The MDNS::Announce::IN side
|
|
31
|
+
# owns the registration.
|
|
26
32
|
class A < Resolv::DNS::Resource::IN::A
|
|
27
33
|
MDNS_UNICAST_RESPONSE = 0x8000
|
|
28
34
|
|
|
29
35
|
ClassValue = Resolv::DNS::Resource::IN::ClassValue | MDNS_UNICAST_RESPONSE
|
|
30
|
-
ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
|
|
31
36
|
end
|
|
32
37
|
|
|
33
38
|
class SRV < Resolv::DNS::Resource::IN::SRV
|
|
34
39
|
MDNS_UNICAST_RESPONSE = 0x8000
|
|
35
40
|
|
|
36
41
|
ClassValue = Resolv::DNS::Resource::IN::ClassValue | MDNS_UNICAST_RESPONSE
|
|
37
|
-
ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
|
|
38
42
|
end
|
|
39
43
|
|
|
40
44
|
module MDNS
|
data/lib/zeroconf/version.rb
CHANGED
data/lib/zeroconf.rb
CHANGED
data/test/client_test.rb
CHANGED
|
@@ -1,6 +1,45 @@
|
|
|
1
1
|
require "helper"
|
|
2
2
|
|
|
3
3
|
module ZeroConf
|
|
4
|
+
# Mimics responders like Nanoleaf which place SRV in the answer section of
|
|
5
|
+
# the PTR response instead of in the additional section. See
|
|
6
|
+
# https://github.com/tenderlove/zeroconf/issues/10
|
|
7
|
+
class NanoleafService < Service
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def service_unicast_answer
|
|
11
|
+
msg = Resolv::DNS::Message.new(0)
|
|
12
|
+
msg.qr = 1
|
|
13
|
+
msg.aa = 1
|
|
14
|
+
|
|
15
|
+
# SRV goes in ANSWER (Nanoleaf-style), not additional
|
|
16
|
+
msg.add_answer service_name, 10,
|
|
17
|
+
Resolv::DNS::Resource::IN::SRV.new(0, 0, service_port, qualified_host)
|
|
18
|
+
|
|
19
|
+
service_interfaces.each do |iface|
|
|
20
|
+
if iface.addr.ipv4?
|
|
21
|
+
msg.add_additional qualified_host, 10,
|
|
22
|
+
Resolv::DNS::Resource::IN::A.new(iface.addr.ip_address)
|
|
23
|
+
else
|
|
24
|
+
msg.add_additional qualified_host, 10,
|
|
25
|
+
Resolv::DNS::Resource::IN::AAAA.new(iface.addr.ip_address)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if @text
|
|
30
|
+
msg.add_additional service_name, 10,
|
|
31
|
+
Resolv::DNS::Resource::IN::TXT.new(*@text)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
msg.add_answer service, 10,
|
|
35
|
+
Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(service_name))
|
|
36
|
+
|
|
37
|
+
msg.add_question service, PTR
|
|
38
|
+
|
|
39
|
+
msg
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
4
43
|
class ClientTest < Test
|
|
5
44
|
attr_reader :iface
|
|
6
45
|
|
|
@@ -9,6 +48,34 @@ module ZeroConf
|
|
|
9
48
|
@iface = ZeroConf.interfaces.find_all { |x| x.addr.ipv4? }.first
|
|
10
49
|
end
|
|
11
50
|
|
|
51
|
+
# Regression test for https://github.com/tenderlove/zeroconf/issues/10
|
|
52
|
+
# If a responder places SRV in the answer section (rather than additional),
|
|
53
|
+
# find_addrinfos must still return a usable [host, Addrinfo] tuple instead
|
|
54
|
+
# of crashing with "no implicit conversion from nil to integer".
|
|
55
|
+
def test_find_addrinfos_with_srv_in_answer
|
|
56
|
+
latch = Queue.new
|
|
57
|
+
s = NanoleafService.new SERVICE + ".",
|
|
58
|
+
42424,
|
|
59
|
+
HOST_NAME,
|
|
60
|
+
service_interfaces: [iface],
|
|
61
|
+
text: ["test=1", "other=value"],
|
|
62
|
+
started_callback: -> { latch << :start }
|
|
63
|
+
runner = Thread.new { s.start }
|
|
64
|
+
latch.pop
|
|
65
|
+
|
|
66
|
+
addrinfos = ZeroConf.find_addrinfos(SERVICE, timeout: 1)
|
|
67
|
+
|
|
68
|
+
s.stop
|
|
69
|
+
runner.join
|
|
70
|
+
|
|
71
|
+
ours = addrinfos.find { |host, _| host == "#{HOST_NAME}.local" }
|
|
72
|
+
assert ours, "expected to find #{HOST_NAME}.local in #{addrinfos.inspect}"
|
|
73
|
+
host, addr = ours
|
|
74
|
+
assert_equal "#{HOST_NAME}.local", host
|
|
75
|
+
assert_equal 42424, addr.ip_port
|
|
76
|
+
assert_equal iface.addr.ip_address, addr.ip_address
|
|
77
|
+
end
|
|
78
|
+
|
|
12
79
|
def test_resolve
|
|
13
80
|
latch = Queue.new
|
|
14
81
|
s = make_server iface, "coolhostname", started_callback: -> { latch << :start }
|
data/test/helper.rb
CHANGED
|
@@ -47,6 +47,15 @@ module ZeroConf
|
|
|
47
47
|
**opts
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
def make_subtype_server iface, host = HOST_NAME, **opts
|
|
51
|
+
Service.new SERVICE + ".",
|
|
52
|
+
42424,
|
|
53
|
+
host,
|
|
54
|
+
service_interfaces: [iface], text: ["test=1", "other=value"],
|
|
55
|
+
subtypes: ["_universal"],
|
|
56
|
+
**opts
|
|
57
|
+
end
|
|
58
|
+
|
|
50
59
|
def make_listener rd, q, started_callback: nil
|
|
51
60
|
Thread.new do
|
|
52
61
|
sock = open_ipv4 Addrinfo.new(Socket.sockaddr_in(Resolv::MDNS::Port, Socket::INADDR_ANY)), Resolv::MDNS::Port
|
data/test/service_test.rb
CHANGED
|
@@ -440,6 +440,132 @@ module ZeroConf
|
|
|
440
440
|
assert_equal expected, res
|
|
441
441
|
end
|
|
442
442
|
|
|
443
|
+
def test_subtypes_expanded
|
|
444
|
+
s = Service.new "_ipp._tcp.local.", 631, "printer",
|
|
445
|
+
service_interfaces: [iface],
|
|
446
|
+
subtypes: ["_universal", "_print"]
|
|
447
|
+
|
|
448
|
+
assert_equal ["_universal._sub._ipp._tcp.local.", "_print._sub._ipp._tcp.local."], s.subtypes
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
def test_subtypes_default_empty
|
|
452
|
+
s = make_server iface
|
|
453
|
+
assert_equal [], s.subtypes
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def test_announcement_includes_subtype_ptrs
|
|
457
|
+
s = Service.new "_test-mdns._tcp.local.", 42424,
|
|
458
|
+
"tc-lan-adapter",
|
|
459
|
+
service_interfaces: [iface],
|
|
460
|
+
subtypes: ["_universal"]
|
|
461
|
+
|
|
462
|
+
ann = s.announcement
|
|
463
|
+
subtype_ptrs = ann.answer.select { |name, ttl, data|
|
|
464
|
+
name.to_s == "_universal._sub._test-mdns._tcp.local"
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
assert_equal 1, subtype_ptrs.length
|
|
468
|
+
assert_equal s.service_name, subtype_ptrs.first.last.name.to_s + "."
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
def test_disconnect_includes_subtype_ptrs
|
|
472
|
+
s = Service.new "_test-mdns._tcp.local.", 42424,
|
|
473
|
+
"tc-lan-adapter",
|
|
474
|
+
service_interfaces: [iface],
|
|
475
|
+
subtypes: ["_universal"]
|
|
476
|
+
|
|
477
|
+
msg = s.disconnect_msg
|
|
478
|
+
subtype_ptrs = msg.answer.select { |name, ttl, data|
|
|
479
|
+
name.to_s == "_universal._sub._test-mdns._tcp.local"
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
assert_equal 1, subtype_ptrs.length
|
|
483
|
+
assert_equal 0, subtype_ptrs.first[1] # TTL 0 for disconnect
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def test_subtype_unicast_answer
|
|
487
|
+
latch = Queue.new
|
|
488
|
+
s = make_subtype_server iface, started_callback: -> { latch << :start }
|
|
489
|
+
runner = Thread.new { s.start }
|
|
490
|
+
latch.pop
|
|
491
|
+
|
|
492
|
+
subtype = "_universal._sub._test-mdns._tcp.local."
|
|
493
|
+
query = Resolv::DNS::Message.new 0
|
|
494
|
+
query.add_question subtype, PTR
|
|
495
|
+
|
|
496
|
+
sock = open_ipv4 iface.addr, 0
|
|
497
|
+
multicast_send sock, query.encode
|
|
498
|
+
res = Resolv::DNS::Message.decode read_with_timeout(sock).first
|
|
499
|
+
s.stop
|
|
500
|
+
runner.join
|
|
501
|
+
|
|
502
|
+
expected = Resolv::DNS::Message.new(0)
|
|
503
|
+
expected.qr = 1
|
|
504
|
+
expected.aa = 1
|
|
505
|
+
|
|
506
|
+
expected.add_additional s.service_name, 10, Resolv::DNS::Resource::IN::SRV.new(0, 0, s.service_port, s.qualified_host)
|
|
507
|
+
|
|
508
|
+
expected.add_additional s.qualified_host,
|
|
509
|
+
10,
|
|
510
|
+
Resolv::DNS::Resource::IN::A.new(iface.addr.ip_address)
|
|
511
|
+
|
|
512
|
+
expected.add_additional s.service_name,
|
|
513
|
+
10,
|
|
514
|
+
Resolv::DNS::Resource::IN::TXT.new(*s.text)
|
|
515
|
+
|
|
516
|
+
expected.add_answer subtype,
|
|
517
|
+
10,
|
|
518
|
+
Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(s.service_name))
|
|
519
|
+
|
|
520
|
+
expected.add_question subtype, PTR
|
|
521
|
+
|
|
522
|
+
assert_equal expected, res
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
def test_subtype_multicast_answer
|
|
526
|
+
q = Thread::Queue.new
|
|
527
|
+
rd, wr = IO.pipe
|
|
528
|
+
|
|
529
|
+
latch = Queue.new
|
|
530
|
+
listen = make_listener rd, q, started_callback: -> { latch << :start }
|
|
531
|
+
s = make_subtype_server iface, started_callback: -> { latch << :start }
|
|
532
|
+
server = Thread.new { s.start }
|
|
533
|
+
latch.pop
|
|
534
|
+
latch.pop
|
|
535
|
+
|
|
536
|
+
subtype = "_universal._sub._test-mdns._tcp.local."
|
|
537
|
+
query = Resolv::DNS::Message.new 0
|
|
538
|
+
query.add_question subtype, Resolv::DNS::Resource::IN::PTR
|
|
539
|
+
sock = open_ipv4 iface.addr, 0
|
|
540
|
+
multicast_send sock, query.encode
|
|
541
|
+
|
|
542
|
+
subtype_name = Resolv::DNS::Name.create subtype
|
|
543
|
+
service_name = Resolv::DNS::Name.create s.service_name
|
|
544
|
+
|
|
545
|
+
while res = q.pop
|
|
546
|
+
if res.answer.find { |name, ttl, data| name == subtype_name && data.name == service_name }
|
|
547
|
+
wr.write "x"
|
|
548
|
+
break
|
|
549
|
+
end
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
listen.join
|
|
553
|
+
s.stop
|
|
554
|
+
server.join
|
|
555
|
+
|
|
556
|
+
# Verify the response contains the subtype PTR
|
|
557
|
+
subtype_ptr = res.answer.find { |name, ttl, data|
|
|
558
|
+
name == subtype_name && data.name == service_name
|
|
559
|
+
}
|
|
560
|
+
assert subtype_ptr, "expected subtype PTR in answer"
|
|
561
|
+
|
|
562
|
+
# Verify it has SRV in additional
|
|
563
|
+
srv = res.additional.find { |_, _, data|
|
|
564
|
+
ZeroConf::MDNS::Announce::IN::SRV == data.class
|
|
565
|
+
}
|
|
566
|
+
assert srv, "expected SRV in additional"
|
|
567
|
+
end
|
|
568
|
+
|
|
443
569
|
def test_raise_on_malformed_requests
|
|
444
570
|
latch = Queue.new
|
|
445
571
|
s = make_server iface, abort_on_malformed_requests: true, started_callback: -> { latch << :start }
|
data/zeroconf.gemspec
CHANGED
|
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
|
|
|
12
12
|
s.homepage = "https://github.com/tenderlove/zeroconf"
|
|
13
13
|
s.license = "Apache-2.0"
|
|
14
14
|
|
|
15
|
-
s.add_dependency("resolv", "
|
|
15
|
+
s.add_dependency("resolv", ">= 0.7.1")
|
|
16
16
|
s.add_development_dependency("rake", "~> 13.0")
|
|
17
17
|
s.add_development_dependency("minitest", "~> 5.20")
|
|
18
18
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: zeroconf
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aaron Patterson
|
|
@@ -13,16 +13,16 @@ dependencies:
|
|
|
13
13
|
name: resolv
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
|
-
- - "
|
|
16
|
+
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 0.7.1
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
|
-
- - "
|
|
23
|
+
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.
|
|
25
|
+
version: 0.7.1
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: rake
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -58,6 +58,7 @@ extensions: []
|
|
|
58
58
|
extra_rdoc_files: []
|
|
59
59
|
files:
|
|
60
60
|
- ".github/workflows/ci.yml"
|
|
61
|
+
- ".github/workflows/release.yml"
|
|
61
62
|
- CODE_OF_CONDUCT.md
|
|
62
63
|
- Gemfile
|
|
63
64
|
- LICENSE
|
|
@@ -93,7 +94,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
93
94
|
- !ruby/object:Gem::Version
|
|
94
95
|
version: '0'
|
|
95
96
|
requirements: []
|
|
96
|
-
rubygems_version:
|
|
97
|
+
rubygems_version: 4.0.10
|
|
97
98
|
specification_version: 4
|
|
98
99
|
summary: Multicast DNS client and server
|
|
99
100
|
test_files:
|