zeroconf 1.0.0 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c389ff9fea665db344da8713c4cb1e3a8af014eee5c7fef1201dba139b6e8ea6
4
- data.tar.gz: 340781dbba64cef0228016f79685d4f48de201c6aacd9df16da502dadcec492c
3
+ metadata.gz: 39d1c369a77bb16908b77e600e166c264662ec1fc624de8aee2f62fc752e3aae
4
+ data.tar.gz: 6f58a84ef9926d9e900182390249b6b029312332a7376cc1f1229b182812bb59
5
5
  SHA512:
6
- metadata.gz: '0928363c430656e6d72454462d2d53e265073abb83c54b405161fe48342825c10400fe6038c6265558c1471bb48e070917b1a44fc8854b5c4446b5f024c73caa'
7
- data.tar.gz: a59a327c0bd4391c1511b5367d642e498f56e528c3881e17b4861a06a79398f59771ea1fce76ad17e7762e74b141e854b144cc5fd1b2e91ec33281f41dbedde6
6
+ metadata.gz: 5a515ce4490d93f37439bec362b84a6bb99c4d42098892f8250d9059537984c9ac6f0c90e9cbf005374f0fdb9abd0a78cb4fd2f7b54e87e05f8acce6eee892cb
7
+ data.tar.gz: be3a38c8bfab29934aca1d2d1100fa4fdda4aa7bfc8c31fe095a461337cffc67b44ac1d982c2faf8f6131891c9eee1a9d444932fa5899b353b0f12e9b8968210
@@ -0,0 +1,25 @@
1
+ name: CI
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ runs-on: ${{ matrix.os }}-latest
8
+
9
+ strategy:
10
+ fail-fast: false
11
+ matrix:
12
+ os: [ubuntu, macos]
13
+ ruby: [ head, 3.2 ]
14
+
15
+ steps:
16
+ - uses: actions/checkout@v3
17
+ - name: Set up Ruby
18
+ uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: ${{ matrix.ruby }}
21
+
22
+ - name: Install dependencies
23
+ run: bundle install
24
+ - name: Run tests
25
+ run: bundle exec rake test
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeroconf/client"
4
+
5
+ module ZeroConf
6
+ class Browser < Client
7
+ private
8
+
9
+ def get_query
10
+ q = PTR.new(name)
11
+ query = Resolv::DNS::Message.new 0
12
+ query.add_question q.name, q.class
13
+ query
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeroconf/utils"
4
+
5
+ module ZeroConf
6
+ class Client
7
+ include Utils
8
+
9
+ attr_reader :name, :interfaces
10
+
11
+ def initialize name, interfaces: ZeroConf.interfaces
12
+ @name = name
13
+ @interfaces = interfaces
14
+ end
15
+
16
+ def run timeout: 3
17
+ sockets = open_interfaces interfaces.map(&:addr), Resolv::MDNS::Port
18
+
19
+ query = get_query
20
+ sockets.each { |socket| multicast_send(socket, query.encode) }
21
+
22
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
23
+ now = start
24
+ msgs = block_given? ? nil : []
25
+
26
+ loop do
27
+ wait = timeout && timeout - (now - start)
28
+ return if wait && wait < 0
29
+
30
+ readers, = IO.select(sockets, [], [], wait)
31
+
32
+ return msgs unless readers
33
+
34
+ readers.each do |reader|
35
+ buf, _ = reader.recvfrom 2048
36
+ msg = Resolv::DNS::Message.decode(buf)
37
+ # only yield replies to this question
38
+ if interested? msg
39
+ if block_given?
40
+ if :done == yield(msg)
41
+ return msg
42
+ end
43
+ else
44
+ msgs << msg
45
+ end
46
+ end
47
+ end
48
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
49
+ end
50
+ ensure
51
+ sockets.each(&:close) if sockets
52
+ end
53
+
54
+ private
55
+
56
+ def interested? _; true; end
57
+
58
+ def open_interfaces addrs, port
59
+ addrs.map { |addr|
60
+ if addr.ipv4?
61
+ open_ipv4 addr, port
62
+ else
63
+ open_ipv6 addr, port
64
+ end
65
+ }.compact
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeroconf/client"
4
+
5
+ module ZeroConf
6
+ class Discoverer < Client
7
+ DISCOVER_QUERY = Resolv::DNS::Message.new 0
8
+ DISCOVER_QUERY.add_question DISCOVERY_NAME, PTR
9
+
10
+ def initialize interfaces: ZeroConf.interfaces
11
+ super(nil, interfaces:)
12
+ end
13
+
14
+ private
15
+
16
+ def interested? msg
17
+ msg.question.length > 0 && msg.question.first.last == PTR
18
+ end
19
+
20
+ def get_query
21
+ DISCOVER_QUERY
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeroconf/client"
4
+
5
+ module ZeroConf
6
+ class Resolver < Client
7
+ private
8
+
9
+ def get_query
10
+ query = Resolv::DNS::Message.new 0
11
+ query.add_question Resolv::DNS::Name.create(name), A
12
+ query
13
+ end
14
+ end
15
+ end
@@ -1,7 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "zeroconf/utils"
4
+
3
5
  module ZeroConf
4
6
  class Service
7
+ include Utils
8
+
5
9
  attr_reader :service, :service_port, :hostname, :service_interfaces,
6
10
  :service_name, :qualified_host, :text
7
11
 
@@ -13,9 +17,12 @@ module ZeroConf
13
17
  @service_name = "#{hostname}.#{service}"
14
18
  @qualified_host = "#{hostname}.local."
15
19
  @text = text
20
+ @started = false
16
21
  @rd, @wr = IO.pipe
17
22
  end
18
23
 
24
+ def started?; @started; end
25
+
19
26
  def announcement
20
27
  msg = Resolv::DNS::Message.new(0)
21
28
  msg.qr = 1
@@ -80,11 +87,10 @@ module ZeroConf
80
87
  msg
81
88
  end
82
89
 
83
- include Utils
84
-
85
90
  def stop
86
91
  @wr.write "x"
87
92
  @wr.close
93
+ @started = false
88
94
  end
89
95
 
90
96
  def start
@@ -97,6 +103,8 @@ module ZeroConf
97
103
  # announce
98
104
  multicast_send(sock, msg.encode)
99
105
 
106
+ @started = true
107
+
100
108
  loop do
101
109
  readers, = IO.select(sockets, [], [])
102
110
  next unless readers
@@ -114,7 +122,9 @@ module ZeroConf
114
122
 
115
123
  break unless class_type == 1 || class_type == 255
116
124
 
117
- unicast = type::ClassValue & PTR::MDNS_UNICAST_RESPONSE > 0
125
+ legacy = from[1] != Resolv::MDNS::Port
126
+ unicast = legacy || type::ClassValue & PTR::MDNS_UNICAST_RESPONSE > 0
127
+ reply_id = legacy ? msg.id : 0
118
128
 
119
129
  qn = name.to_s + "."
120
130
 
@@ -123,25 +133,25 @@ module ZeroConf
123
133
  break if has_flags
124
134
 
125
135
  if unicast
126
- dnssd_unicast_answer
136
+ dnssd_unicast_answer(id: reply_id, legacy: legacy)
127
137
  else
128
138
  dnssd_multicast_answer
129
139
  end
130
140
  when service
131
141
  if unicast
132
- service_unicast_answer
142
+ service_unicast_answer(id: reply_id, legacy: legacy)
133
143
  else
134
144
  service_multicast_answer
135
145
  end
136
146
  when service_name
137
147
  if unicast
138
- service_instance_unicast_answer
148
+ service_instance_unicast_answer(id: reply_id, legacy: legacy)
139
149
  else
140
150
  service_instance_multicast_answer
141
151
  end
142
152
  when qualified_host
143
153
  if unicast
144
- name_answer_unicast
154
+ name_answer_unicast(id: reply_id, legacy: legacy)
145
155
  else
146
156
  name_answer_multicast
147
157
  end
@@ -162,16 +172,17 @@ module ZeroConf
162
172
  end
163
173
  end
164
174
  ensure
165
- multicast_send sock, disconnect_msg.encode
166
- sockets.map(&:close)
175
+ multicast_send(sock, disconnect_msg.encode) if sock
176
+ sockets.map(&:close) if sockets
167
177
  end
168
178
 
169
179
  private
170
180
 
171
- def service_instance_unicast_answer
181
+ def service_instance_unicast_answer(id:, legacy:)
172
182
  msg = Resolv::DNS::Message.new(0)
173
183
  msg.qr = 1
174
184
  msg.aa = 1
185
+ msg.id = id
175
186
 
176
187
  service_interfaces.each do |iface|
177
188
  if iface.addr.ipv4?
@@ -191,15 +202,16 @@ module ZeroConf
191
202
  Resolv::DNS::Resource::IN::TXT.new(*@text)
192
203
  end
193
204
  msg.add_answer service_name, 10, Resolv::DNS::Resource::IN::SRV.new(0, 0, service_port, qualified_host)
194
- msg.add_question service_name, ZeroConf::MDNS::Announce::IN::SRV
205
+ msg.add_question service_name, legacy ? Resolv::DNS::Resource::IN::SRV : MDNS::Announce::IN::SRV
195
206
 
196
207
  msg
197
208
  end
198
209
 
199
- def service_unicast_answer
210
+ def service_unicast_answer(id:, legacy:)
200
211
  msg = Resolv::DNS::Message.new(0)
201
212
  msg.qr = 1
202
213
  msg.aa = 1
214
+ msg.id = id
203
215
 
204
216
  msg.add_additional service_name, 10, Resolv::DNS::Resource::IN::SRV.new(0, 0, service_port, qualified_host)
205
217
 
@@ -225,20 +237,21 @@ module ZeroConf
225
237
  10,
226
238
  Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(service_name))
227
239
 
228
- msg.add_question service, ZeroConf::PTR
240
+ msg.add_question service, legacy ? Resolv::DNS::Resource::IN::PTR : PTR
229
241
 
230
242
  msg
231
243
  end
232
244
 
233
- def dnssd_unicast_answer
245
+ def dnssd_unicast_answer(id:, legacy:)
234
246
  msg = Resolv::DNS::Message.new(0)
235
247
  msg.qr = 1
236
248
  msg.aa = 1
249
+ msg.id = id
237
250
 
238
251
  msg.add_answer DISCOVERY_NAME, 10,
239
252
  Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(service))
240
253
 
241
- msg.add_question DISCOVERY_NAME, ZeroConf::PTR
254
+ msg.add_question DISCOVERY_NAME, legacy ? Resolv::DNS::Resource::IN::PTR : PTR
242
255
  msg
243
256
  end
244
257
 
@@ -312,10 +325,11 @@ module ZeroConf
312
325
  msg
313
326
  end
314
327
 
315
- def name_answer_unicast
328
+ def name_answer_unicast(id:, legacy:)
316
329
  msg = Resolv::DNS::Message.new(0)
317
330
  msg.qr = 1
318
331
  msg.aa = 1
332
+ msg.id = id
319
333
 
320
334
  first = true
321
335
 
@@ -344,7 +358,7 @@ module ZeroConf
344
358
  end
345
359
  end
346
360
 
347
- msg.add_question qualified_host, ZeroConf::MDNS::Announce::IN::A
361
+ msg.add_question qualified_host, legacy ? Resolv::DNS::Resource::IN::A : MDNS::Announce::IN::A
348
362
 
349
363
  if @text
350
364
  msg.add_additional service_name,
@@ -4,15 +4,61 @@ require "socket"
4
4
  require "ipaddr"
5
5
  require "fcntl"
6
6
  require "resolv"
7
+ require "rbconfig"
7
8
 
8
9
  module ZeroConf
10
+ MDNS_CACHE_FLUSH = 0x8000
11
+
12
+ # :stopdoc:
13
+ class PTR < Resolv::DNS::Resource::IN::PTR
14
+ MDNS_UNICAST_RESPONSE = 0x8000
15
+
16
+ ClassValue = Resolv::DNS::Resource::IN::ClassValue | MDNS_UNICAST_RESPONSE
17
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
18
+ end
19
+
20
+ class ANY < Resolv::DNS::Resource::IN::ANY
21
+ MDNS_UNICAST_RESPONSE = 0x8000
22
+
23
+ ClassValue = Resolv::DNS::Resource::IN::ClassValue | MDNS_UNICAST_RESPONSE
24
+ ::Resolv::DNS::Resource::ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
25
+ end
26
+
27
+ class A < Resolv::DNS::Resource::IN::A
28
+ MDNS_UNICAST_RESPONSE = 0x8000
29
+
30
+ ClassValue = Resolv::DNS::Resource::IN::ClassValue | MDNS_UNICAST_RESPONSE
31
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
32
+ end
33
+
34
+ class SRV < Resolv::DNS::Resource::IN::SRV
35
+ MDNS_UNICAST_RESPONSE = 0x8000
36
+
37
+ ClassValue = Resolv::DNS::Resource::IN::ClassValue | MDNS_UNICAST_RESPONSE
38
+ ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
39
+ end
40
+
41
+ module MDNS
42
+ module Announce
43
+ module IN
44
+ [:SRV, :A, :AAAA, :TXT].each do |name|
45
+ const_set(name, Class.new(Resolv::DNS::Resource::IN.const_get(name)) {
46
+ const_set(:ClassValue, superclass::ClassValue | MDNS_CACHE_FLUSH)
47
+ self::ClassHash[[self::TypeValue, self::ClassValue]] = self
48
+ })
49
+ end
50
+ end
51
+ end
52
+ end
53
+ # :startdoc:
54
+
9
55
  module Utils
10
56
  DISCOVERY_NAME = "_services._dns-sd._udp.local."
11
57
 
12
58
  def open_ipv4 saddr, port
13
59
  sock = UDPSocket.new Socket::AF_INET
14
60
  sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
15
- sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEPORT, true)
61
+ sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEPORT, true) unless RbConfig::CONFIG["host_os"].include?("linux")
16
62
  sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_MULTICAST_TTL, true)
17
63
  sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_MULTICAST_LOOP, true)
18
64
  sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP,
@@ -29,14 +75,22 @@ module ZeroConf
29
75
 
30
76
  def multicast_send sock, query
31
77
  dest = if sock.local_address.ipv4?
32
- BROADCAST_V4
78
+ broadcast_v4
33
79
  else
34
- BROADCAST_V6
80
+ broadcast_v6
35
81
  end
36
82
 
37
83
  sock.send(query, 0, dest)
38
84
  end
39
85
 
86
+ def broadcast_v4
87
+ BROADCAST_V4
88
+ end
89
+
90
+ def broadcast_v6
91
+ BROADCAST_V6
92
+ end
93
+
40
94
  def unicast_send sock, data, to
41
95
  sock.send(data, 0, Addrinfo.new(to))
42
96
  end
@@ -44,7 +98,7 @@ module ZeroConf
44
98
  def open_ipv6 saddr, port
45
99
  sock = UDPSocket.new Socket::AF_INET6
46
100
  sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
47
- sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEPORT, true)
101
+ sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEPORT, true) unless RbConfig::CONFIG["host_os"].include?("linux")
48
102
  sock.setsockopt(Socket::IPPROTO_IPV6, Socket::IPV6_MULTICAST_HOPS, true)
49
103
  sock.setsockopt(Socket::IPPROTO_IPV6, Socket::IPV6_MULTICAST_LOOP, true)
50
104
 
@@ -1,3 +1,3 @@
1
1
  module ZeroConf
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.1"
3
3
  end
data/lib/zeroconf.rb CHANGED
@@ -1,60 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "zeroconf/utils"
4
3
  require "zeroconf/service"
4
+ require "zeroconf/browser"
5
+ require "zeroconf/resolver"
6
+ require "zeroconf/discoverer"
5
7
 
6
8
  module ZeroConf
7
- MDNS_CACHE_FLUSH = 0x8000
8
-
9
- extend Utils
10
- include Utils
11
-
12
- # :stopdoc:
13
- class PTR < Resolv::DNS::Resource::IN::PTR
14
- MDNS_UNICAST_RESPONSE = 0x8000
15
-
16
- ClassValue = Resolv::DNS::Resource::IN::ClassValue | MDNS_UNICAST_RESPONSE
17
- ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
18
- end
19
-
20
- class ANY < Resolv::DNS::Resource::IN::ANY
21
- MDNS_UNICAST_RESPONSE = 0x8000
22
-
23
- ClassValue = Resolv::DNS::Resource::IN::ClassValue | MDNS_UNICAST_RESPONSE
24
- ::Resolv::DNS::Resource::ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
25
- end
26
-
27
- class A < Resolv::DNS::Resource::IN::A
28
- MDNS_UNICAST_RESPONSE = 0x8000
29
-
30
- ClassValue = Resolv::DNS::Resource::IN::ClassValue | MDNS_UNICAST_RESPONSE
31
- ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
32
- end
33
-
34
- class SRV < Resolv::DNS::Resource::IN::SRV
35
- MDNS_UNICAST_RESPONSE = 0x8000
36
-
37
- ClassValue = Resolv::DNS::Resource::IN::ClassValue | MDNS_UNICAST_RESPONSE
38
- ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
39
- end
40
-
41
- module MDNS
42
- module Announce
43
- module IN
44
- [:SRV, :A, :AAAA, :TXT].each do |name|
45
- const_set(name, Class.new(Resolv::DNS::Resource::IN.const_get(name)) {
46
- const_set(:ClassValue, superclass::ClassValue | MDNS_CACHE_FLUSH)
47
- self::ClassHash[[self::TypeValue, self::ClassValue]] = self
48
- })
49
- end
50
- end
51
- end
52
- end
53
-
54
- DISCOVER_QUERY = Resolv::DNS::Message.new 0
55
- DISCOVER_QUERY.add_question DISCOVERY_NAME, PTR
56
-
57
- # :startdoc:
9
+ #include Utils
58
10
 
59
11
  ##
60
12
  # ZeroConf.browse
@@ -72,45 +24,8 @@ module ZeroConf
72
24
  # @param [Array<Socket::Ifaddr>] interfaces list of interfaces to query
73
25
  # @param [Numeric] timeout number of seconds before returning
74
26
  def self.browse name, interfaces: self.interfaces, timeout: 3, &blk
75
- port = 0
76
- sockets = interfaces.map { |iface|
77
- if iface.addr.ipv4?
78
- open_ipv4 iface.addr, port
79
- else
80
- open_ipv6 iface.addr, port
81
- end
82
- }.compact
83
-
84
- q = PTR.new(name)
85
-
86
- sockets.each { |socket|
87
- query = Resolv::DNS::Message.new 0
88
-
89
- query.add_question q.name, q.class
90
-
91
- multicast_send socket, query.encode
92
- }
93
-
94
- start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
95
- now = start
96
- msgs = block_given? ? nil : []
97
-
98
- loop do
99
- readers, = IO.select(sockets, [], [], timeout - (now - start))
100
- return msgs unless readers
101
- readers.each do |reader|
102
- buf, = reader.recvfrom 2048
103
- msg = Resolv::DNS::Message.decode(buf)
104
- if block_given?
105
- return msg if :done == yield(msg)
106
- else
107
- msgs << msg
108
- end
109
- end
110
- now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
111
- end
112
- ensure
113
- sockets.map(&:close) if sockets
27
+ browser = ZeroConf::Browser.new(name, interfaces:)
28
+ browser.run(timeout:, &blk)
114
29
  end
115
30
 
116
31
  ###
@@ -131,7 +46,6 @@ module ZeroConf
131
46
  port = nil
132
47
  ipv4 = []
133
48
  ipv6 = []
134
- pp r
135
49
  r.additional.each { |name, ttl, data|
136
50
  case data
137
51
  when Resolv::DNS::Resource::IN::SRV
@@ -149,37 +63,8 @@ module ZeroConf
149
63
  end
150
64
 
151
65
  def self.resolve name, interfaces: self.interfaces, timeout: 3, &blk
152
- port = 0
153
- sockets = interfaces.map { |iface|
154
- if iface.addr.ipv4?
155
- open_ipv4 iface.addr, port
156
- else
157
- open_ipv6 iface.addr, port
158
- end
159
- }.compact
160
-
161
- query = Resolv::DNS::Message.new 0
162
- query.add_question Resolv::DNS::Name.create(name), A
163
-
164
- sockets.each do |sock|
165
- multicast_send sock, query.encode
166
- end
167
-
168
- start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
169
- now = start
170
-
171
- loop do
172
- readers, = IO.select(sockets, [], [], timeout - (now - start))
173
- return unless readers
174
- readers.each do |reader|
175
- buf, = reader.recvfrom 2048
176
- msg = Resolv::DNS::Message.decode(buf)
177
- return msg if :done == yield(msg)
178
- end
179
- now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
180
- end
181
- ensure
182
- sockets.map(&:close) if sockets
66
+ resolver = ZeroConf::Resolver.new(name, interfaces:)
67
+ resolver.run(timeout:, &blk)
183
68
  end
184
69
 
185
70
  def self.service service, service_port, hostname = Socket.gethostname, service_interfaces: self.service_interfaces, text: [""]
@@ -222,46 +107,9 @@ module ZeroConf
222
107
  #
223
108
  # @param [Array<Socket::Ifaddr>] interfaces list of interfaces to query
224
109
  # @param [Numeric] timeout number of seconds before returning
225
- def self.discover interfaces: self.interfaces, timeout: 3
226
- port = 0
227
-
228
- sockets = interfaces.map { |iface|
229
- if iface.addr.ipv4?
230
- open_ipv4 iface.addr, port
231
- else
232
- open_ipv6 iface.addr, port
233
- end
234
- }.compact
235
-
236
- discover_query = DISCOVER_QUERY
237
- sockets.each { |socket| multicast_send(socket, discover_query.encode) }
238
-
239
- start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
240
- now = start
241
- msgs = nil
242
-
243
- loop do
244
- readers, = IO.select(sockets, [], [], timeout && (timeout - (now - start)))
245
- return msgs unless readers
246
- readers.each do |reader|
247
- buf, _ = reader.recvfrom 2048
248
- msg = Resolv::DNS::Message.decode(buf)
249
- # only yield replies to this question
250
- if msg.question.length > 0 && msg.question.first.last == PTR
251
- if block_given?
252
- if :done == yield(msg)
253
- return msg
254
- end
255
- else
256
- msgs ||= []
257
- msgs << msg
258
- end
259
- end
260
- end
261
- now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
262
- end
263
- ensure
264
- sockets.each(&:close) if sockets
110
+ def self.discover interfaces: self.interfaces, timeout: 3, &blk
111
+ discoverer = ZeroConf::Discoverer.new(interfaces:)
112
+ discoverer.run(timeout:, &blk)
265
113
  end
266
114
 
267
115
  def self.interfaces
@@ -282,7 +130,4 @@ module ZeroConf
282
130
  ipv4, ipv6 = interfaces.partition { |ifa| ifa.addr.ipv4? }
283
131
  [ipv4.first, ipv6&.first].compact
284
132
  end
285
-
286
- private_class_method def self.multiquery_send sock, queries, query_id
287
- end
288
133
  end
data/test/client_test.rb CHANGED
@@ -12,6 +12,7 @@ module ZeroConf
12
12
  def test_resolve
13
13
  s = make_server iface, "coolhostname"
14
14
  runner = Thread.new { s.start }
15
+ Thread.pass until s.started?
15
16
  found = nil
16
17
 
17
18
  name = "coolhostname.local"
@@ -28,12 +29,13 @@ module ZeroConf
28
29
  runner.join
29
30
 
30
31
  assert found
31
- assert_in_delta 3, took, 0.1
32
+ assert_in_delta 3, took, 0.2
32
33
  end
33
34
 
34
35
  def test_resolve_returns_early
35
36
  s = make_server iface, "coolhostname"
36
37
  runner = Thread.new { s.start }
38
+ Thread.pass until s.started?
37
39
  found = nil
38
40
 
39
41
  name = "coolhostname.local"
@@ -57,6 +59,7 @@ module ZeroConf
57
59
  def test_discover_works
58
60
  s = make_server iface
59
61
  runner = Thread.new { s.start }
62
+ Thread.pass until s.started?
60
63
  found = nil
61
64
 
62
65
  took = time_it do
@@ -71,12 +74,13 @@ module ZeroConf
71
74
  runner.join
72
75
 
73
76
  assert found
74
- assert_in_delta 3, took, 0.1
77
+ assert_in_delta 3, took, 0.2
75
78
  end
76
79
 
77
80
  def test_discover_return_early
78
81
  s = make_server iface
79
82
  runner = Thread.new { s.start }
83
+ Thread.pass until s.started?
80
84
  found = nil
81
85
 
82
86
  took = time_it do
@@ -97,11 +101,12 @@ module ZeroConf
97
101
  def test_browse
98
102
  s = make_server iface
99
103
  runner = Thread.new { s.start }
104
+ Thread.pass until s.started?
100
105
  found = nil
101
106
 
102
107
  took = time_it do
103
108
  ZeroConf.browse SERVICE do |msg|
104
- if msg.question.find { |name, type| name.to_s == SERVICE && type == ZeroConf::PTR }
109
+ if msg.question.find { |name, type| name.to_s == SERVICE && type == PTR }
105
110
  found = msg
106
111
  end
107
112
  end
@@ -112,17 +117,18 @@ module ZeroConf
112
117
 
113
118
  assert found
114
119
  assert_equal Resolv::DNS::Name.create(SERVICE_NAME + "."), found.answer.first.last.name
115
- assert_in_delta 3, took, 0.1
120
+ assert_in_delta 3, took, 0.2
116
121
  end
117
122
 
118
123
  def test_browse_returns_early
119
124
  s = make_server iface
120
125
  runner = Thread.new { s.start }
126
+ Thread.pass until s.started?
121
127
  found = nil
122
128
 
123
129
  took = time_it do
124
130
  found = ZeroConf.browse SERVICE do |msg|
125
- if msg.question.find { |name, type| name.to_s == SERVICE && type == ZeroConf::PTR }
131
+ if msg.question.find { |name, type| name.to_s == SERVICE && type == PTR }
126
132
  :done
127
133
  end
128
134
  end
data/test/helper.rb CHANGED
@@ -3,6 +3,30 @@ ENV["MT_NO_PLUGINS"] = "1"
3
3
  require "minitest/autorun"
4
4
  require "zeroconf"
5
5
 
6
+ Thread.abort_on_exception = true
7
+
8
+ class NotParallel
9
+ def self.start; end
10
+ def self.shutdown; end
11
+ end
12
+
13
+ Minitest.parallel_executor = NotParallel
14
+
15
+ Thread.new do
16
+ # this test suite shouldn't take any more than 60 seconds
17
+ sleep 60
18
+
19
+ Thread.list.each do |t|
20
+ next if t == Thread.current
21
+ puts "#" * 90
22
+ p t
23
+ puts t.backtrace
24
+ puts "#" * 90
25
+ end
26
+
27
+ exit!
28
+ end
29
+
6
30
  module ZeroConf
7
31
  class Test < Minitest::Test
8
32
  SERVICE = "_test-mdns._tcp.local"
@@ -25,6 +49,7 @@ module ZeroConf
25
49
  def make_listener rd, q
26
50
  Thread.new do
27
51
  sock = open_ipv4 Addrinfo.new(Socket.sockaddr_in(Resolv::MDNS::Port, Socket::INADDR_ANY)), Resolv::MDNS::Port
52
+ Thread.current[:started] = true
28
53
  loop do
29
54
  readers, = IO.select([sock, rd])
30
55
  read = readers.first
data/test/service_test.rb CHANGED
@@ -29,13 +29,14 @@ module ZeroConf
29
29
  def test_unicast_service_instance_answer
30
30
  s = make_server iface
31
31
  runner = Thread.new { s.start }
32
+ Thread.pass until s.started?
32
33
 
33
- query = Resolv::DNS::Message.new 0
34
- query.add_question "tc-lan-adapter._test-mdns._tcp.local.", ZeroConf::SRV
34
+ query = Resolv::DNS::Message.new 10
35
+ query.add_question "tc-lan-adapter._test-mdns._tcp.local.", SRV
35
36
 
36
- sock = open_ipv4 iface.addr, 0
37
+ sock = open_ipv4 iface.addr, Resolv::MDNS::Port
37
38
  multicast_send sock, query.encode
38
- res = Resolv::DNS::Message.decode sock.recvfrom(2048).first
39
+ res = Resolv::DNS::Message.decode read_with_timeout(sock).first
39
40
  s.stop
40
41
  runner.join
41
42
 
@@ -51,7 +52,38 @@ module ZeroConf
51
52
  10,
52
53
  Resolv::DNS::Resource::IN::TXT.new(*s.text)
53
54
  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
+ expected.add_question s.service_name, MDNS::Announce::IN::SRV
56
+
57
+ assert_equal expected, res
58
+ end
59
+
60
+ def test_legacy_unicast_service_instance_answer
61
+ s = make_server iface
62
+ runner = Thread.new { s.start }
63
+ Thread.pass until s.started?
64
+
65
+ query = Resolv::DNS::Message.new 10
66
+ query.add_question "tc-lan-adapter._test-mdns._tcp.local.", Resolv::DNS::Resource::IN::SRV
67
+
68
+ sock = open_ipv4 iface.addr, 0
69
+ multicast_send sock, query.encode
70
+ res = Resolv::DNS::Message.decode read_with_timeout(sock).first
71
+ s.stop
72
+ runner.join
73
+
74
+ expected = Resolv::DNS::Message.new(10)
75
+ expected.qr = 1
76
+ expected.aa = 1
77
+
78
+ expected.add_additional s.qualified_host,
79
+ 10,
80
+ Resolv::DNS::Resource::IN::A.new(iface.addr.ip_address)
81
+
82
+ expected.add_additional s.service_name,
83
+ 10,
84
+ Resolv::DNS::Resource::IN::TXT.new(*s.text)
85
+ expected.add_answer s.service_name, 10, Resolv::DNS::Resource::IN::SRV.new(0, 0, s.service_port, s.qualified_host)
86
+ expected.add_question s.service_name, Resolv::DNS::Resource::IN::SRV
55
87
 
56
88
  assert_equal expected, res
57
89
  end
@@ -63,10 +95,11 @@ module ZeroConf
63
95
  listen = make_listener rd, q
64
96
  s = make_server iface
65
97
  server = Thread.new { s.start }
98
+ Thread.pass until s.started?
66
99
 
67
- query = Resolv::DNS::Message.new 0
100
+ query = Resolv::DNS::Message.new 10
68
101
  query.add_question "_services._dns-sd._udp.local.", Resolv::DNS::Resource::IN::PTR
69
- sock = open_ipv4 iface.addr, 0
102
+ sock = open_ipv4 iface.addr, Resolv::MDNS::Port
70
103
  multicast_send sock, query.encode
71
104
 
72
105
  while res = q.pop
@@ -91,32 +124,91 @@ module ZeroConf
91
124
  end
92
125
 
93
126
  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
127
+ # FIXME: this test should be converted to an integration test.
128
+ # We need to make a client listen for the announcement and then decode that
96
129
  service = Service.new "_test-mdns._tcp.local.", 42424
130
+
131
+ msg = Resolv::DNS::Message.new(0)
132
+ msg.qr = 1
133
+ msg.aa = 1
134
+
135
+ msg.add_additional service.service_name, 60, MDNS::Announce::IN::SRV.new(0, 0, service.service_port, service.qualified_host)
136
+
137
+ service.service_interfaces.each do |iface|
138
+ if iface.addr.ipv4?
139
+ msg.add_additional service.qualified_host,
140
+ 60,
141
+ MDNS::Announce::IN::A.new(iface.addr.ip_address)
142
+ else
143
+ msg.add_additional service.qualified_host,
144
+ 60,
145
+ MDNS::Announce::IN::AAAA.new(iface.addr.ip_address)
146
+ end
147
+ end
148
+
149
+ if service.text
150
+ msg.add_additional service.service_name,
151
+ 60,
152
+ MDNS::Announce::IN::TXT.new(*service.text)
153
+ end
154
+
155
+ msg.add_answer service.service,
156
+ 60,
157
+ Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(service.service_name))
158
+
97
159
  assert_equal msg, service.announcement
98
160
  end
99
161
 
100
162
  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
163
+ # FIXME: this test should be converted to an integration test.
164
+ # We need to make a client listen for the disconnect and then decode that
103
165
  s = make_server iface
104
- assert_equal m, s.disconnect_msg
166
+
167
+ msg = Resolv::DNS::Message.new(0)
168
+ msg.qr = 1
169
+ msg.aa = 1
170
+
171
+ msg.add_additional s.service_name, 0, Resolv::DNS::Resource::IN::SRV.new(0, 0, s.service_port, s.qualified_host)
172
+
173
+ s.service_interfaces.each do |iface|
174
+ if iface.addr.ipv4?
175
+ msg.add_additional s.qualified_host,
176
+ 0,
177
+ Resolv::DNS::Resource::IN::A.new(iface.addr.ip_address)
178
+ else
179
+ msg.add_additional s.qualified_host,
180
+ 0,
181
+ Resolv::DNS::Resource::IN::AAAA.new(iface.addr.ip_address)
182
+ end
183
+ end
184
+
185
+ if s.text
186
+ msg.add_additional s.service_name,
187
+ 0,
188
+ Resolv::DNS::Resource::IN::TXT.new(*s.text)
189
+ end
190
+
191
+ msg.add_answer s.service,
192
+ 0,
193
+ Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(s.service_name))
194
+
195
+ assert_equal msg, s.disconnect_msg
105
196
  end
106
197
 
107
198
  def test_dnssd_unicast_answer
108
199
  s = make_server iface
109
200
  runner = Thread.new { s.start }
201
+ Thread.pass until s.started?
110
202
 
111
- query = Resolv::DNS::Message.new 0
112
- query.add_question "_services._dns-sd._udp.local.", ZeroConf::PTR
203
+ query = Resolv::DNS::Message.new 10
204
+ query.add_question "_services._dns-sd._udp.local.", PTR
113
205
 
114
- sock = open_ipv4 iface.addr, 0
206
+ sock = open_ipv4 iface.addr, Resolv::MDNS::Port
115
207
  multicast_send sock, query.encode
116
208
 
117
209
  res = nil
118
210
  loop do
119
- buf, from = sock.recvfrom(2048)
211
+ buf, from = read_with_timeout sock
120
212
  res = Resolv::DNS::Message.decode buf
121
213
  if from.last == iface.addr.ip_address
122
214
  break if res.answer.find { |name, ttl, data| data.name.to_s == SERVICE }
@@ -133,29 +225,66 @@ module ZeroConf
133
225
  expected.add_answer DISCOVERY_NAME, 10,
134
226
  Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(s.service))
135
227
 
136
- expected.add_question DISCOVERY_NAME, ZeroConf::PTR
228
+ expected.add_question DISCOVERY_NAME, PTR
229
+
230
+ assert_equal expected, res
231
+ end
232
+
233
+ def test_dnssd_legacy_unicast_answer
234
+ s = make_server iface
235
+ runner = Thread.new { s.start }
236
+ Thread.pass until s.started?
237
+
238
+ query = Resolv::DNS::Message.new 10
239
+ query.add_question "_services._dns-sd._udp.local.", Resolv::DNS::Resource::IN::PTR
240
+
241
+ sock = open_ipv4 iface.addr, 0
242
+ multicast_send sock, query.encode
243
+
244
+ res = nil
245
+ loop do
246
+ buf, from = read_with_timeout sock
247
+ res = Resolv::DNS::Message.decode buf
248
+ if from.last == iface.addr.ip_address
249
+ break if res.answer.find { |name, ttl, data| data.name.to_s == SERVICE }
250
+ end
251
+ end
252
+
253
+ s.stop
254
+ runner.join
255
+
256
+ expected = Resolv::DNS::Message.new(10)
257
+ expected.qr = 1
258
+ expected.aa = 1
259
+
260
+ expected.add_answer DISCOVERY_NAME, 10,
261
+ Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(s.service))
262
+
263
+ expected.add_question DISCOVERY_NAME, Resolv::DNS::Resource::IN::PTR
137
264
 
138
265
  assert_equal expected, res
139
266
  end
140
267
 
141
268
  def test_service_multicast_answer
142
- q = Queue.new
269
+ q = Thread::Queue.new
143
270
  rd, wr = IO.pipe
144
271
 
145
272
  listen = make_listener rd, q
146
273
  s = make_server iface
147
274
  server = Thread.new { s.start }
275
+ Thread.pass until s.started? && listen[:started]
148
276
 
149
- query = Resolv::DNS::Message.new 0
277
+ query = Resolv::DNS::Message.new 10
150
278
  query.add_question "_test-mdns._tcp.local.", Resolv::DNS::Resource::IN::PTR
151
- sock = open_ipv4 iface.addr, 0
279
+ sock = open_ipv4 iface.addr, Resolv::MDNS::Port
152
280
  multicast_send sock, query.encode
153
281
 
154
282
  service = Resolv::DNS::Name.create s.service
155
283
  service_name = Resolv::DNS::Name.create s.service_name
156
284
 
157
285
  while res = q.pop
158
- if res.answer.find { |name, ttl, data| name == service && data.name == service_name }
286
+ if res.answer.find { |name, ttl, data| name == service && data.name == service_name } &&
287
+ res.additional.find { |_, _, data| ZeroConf::MDNS::Announce::IN::SRV == data.class }
159
288
  wr.write "x"
160
289
  break
161
290
  end
@@ -183,19 +312,20 @@ module ZeroConf
183
312
  60,
184
313
  Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(s.service_name))
185
314
 
186
- assert_equal msg.encode, res.encode
315
+ assert_equal msg, res
187
316
  end
188
317
 
189
318
  def test_service_unicast_answer
190
319
  s = make_server iface
191
320
  runner = Thread.new { s.start }
321
+ Thread.pass until s.started?
192
322
 
193
- query = Resolv::DNS::Message.new 0
194
- query.add_question "_test-mdns._tcp.local.", ZeroConf::PTR
323
+ query = Resolv::DNS::Message.new 10
324
+ query.add_question "_test-mdns._tcp.local.", PTR
195
325
 
196
- sock = open_ipv4 iface.addr, 0
326
+ sock = open_ipv4 iface.addr, Resolv::MDNS::Port
197
327
  multicast_send sock, query.encode
198
- res = Resolv::DNS::Message.decode sock.recvfrom(2048).first
328
+ res = Resolv::DNS::Message.decode read_with_timeout(sock).first
199
329
  s.stop
200
330
  runner.join
201
331
 
@@ -214,11 +344,46 @@ module ZeroConf
214
344
  expected.add_answer s.service,
215
345
  10,
216
346
  Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(s.service_name))
217
- expected.add_question s.service, ZeroConf::PTR
347
+ expected.add_question s.service, PTR
348
+
349
+ assert_equal expected, res
350
+ end
351
+
352
+ def test_service_legacy_unicast_answer
353
+ s = make_server iface
354
+ runner = Thread.new { s.start }
355
+ Thread.pass until s.started?
356
+
357
+ query = Resolv::DNS::Message.new 10
358
+ query.add_question "_test-mdns._tcp.local.", PTR
359
+
360
+ sock = open_ipv4 iface.addr, 0
361
+ multicast_send sock, query.encode
362
+ res = Resolv::DNS::Message.decode read_with_timeout(sock).first
363
+ s.stop
364
+ runner.join
365
+
366
+ expected = Resolv::DNS::Message.new(10)
367
+ expected.qr = 1
368
+ expected.aa = 1
369
+
370
+ expected.add_additional s.service_name, 10, Resolv::DNS::Resource::IN::SRV.new(0, 0, s.service_port, s.qualified_host)
371
+ expected.add_additional s.qualified_host,
372
+ 10,
373
+ Resolv::DNS::Resource::IN::A.new(iface.addr.ip_address)
374
+
375
+ expected.add_additional s.service_name,
376
+ 10,
377
+ Resolv::DNS::Resource::IN::TXT.new(*s.text)
378
+ expected.add_answer s.service,
379
+ 10,
380
+ Resolv::DNS::Resource::IN::PTR.new(Resolv::DNS::Name.create(s.service_name))
381
+ expected.add_question s.service, Resolv::DNS::Resource::IN::PTR
218
382
 
219
383
  assert_equal expected, res
220
384
  end
221
385
 
386
+
222
387
  def test_multicast_service_instance_answer
223
388
  q = Queue.new
224
389
  rd, wr = IO.pipe
@@ -226,10 +391,11 @@ module ZeroConf
226
391
  listen = make_listener rd, q
227
392
  s = make_server iface
228
393
  server = Thread.new { s.start }
394
+ Thread.pass until s.started?
229
395
 
230
- query = Resolv::DNS::Message.new 0
396
+ query = Resolv::DNS::Message.new 10
231
397
  query.add_question "tc-lan-adapter._test-mdns._tcp.local.", Resolv::DNS::Resource::IN::PTR
232
- sock = open_ipv4 iface.addr, 0
398
+ sock = open_ipv4 iface.addr, Resolv::MDNS::Port
233
399
  multicast_send sock, query.encode
234
400
 
235
401
  service_name = Resolv::DNS::Name.create s.service_name
@@ -266,13 +432,14 @@ module ZeroConf
266
432
  def test_unicast_name_lookup
267
433
  s = make_server iface
268
434
  runner = Thread.new { s.start }
435
+ Thread.pass until s.started?
269
436
 
270
- query = Resolv::DNS::Message.new 0
271
- query.add_question "tc-lan-adapter.local.", ZeroConf::A
437
+ query = Resolv::DNS::Message.new 10
438
+ query.add_question "tc-lan-adapter.local.", A
272
439
 
273
- sock = open_ipv4 iface.addr, 0
440
+ sock = open_ipv4 iface.addr, Resolv::MDNS::Port
274
441
  multicast_send sock, query.encode
275
- res = Resolv::DNS::Message.decode sock.recvfrom(2048).first
442
+ res = Resolv::DNS::Message.decode read_with_timeout(sock).first
276
443
  s.stop
277
444
  runner.join
278
445
 
@@ -287,7 +454,37 @@ module ZeroConf
287
454
  Resolv::DNS::Resource::IN::TXT.new(*s.text)
288
455
 
289
456
  expected.add_question s.qualified_host,
290
- ZeroConf::MDNS::Announce::IN::A
457
+ MDNS::Announce::IN::A
458
+
459
+ assert_equal expected, res
460
+ end
461
+
462
+ def test_legacy_unicast_name_lookup
463
+ s = make_server iface
464
+ runner = Thread.new { s.start }
465
+ Thread.pass until s.started?
466
+
467
+ query = Resolv::DNS::Message.new 10
468
+ query.add_question "tc-lan-adapter.local.", Resolv::DNS::Resource::IN::A
469
+
470
+ sock = open_ipv4 iface.addr, 0
471
+ multicast_send sock, query.encode
472
+ res = Resolv::DNS::Message.decode read_with_timeout(sock).first
473
+ s.stop
474
+ runner.join
475
+
476
+ expected = Resolv::DNS::Message.new(10)
477
+ expected.qr = 1
478
+ expected.aa = 1
479
+
480
+ expected.add_answer s.qualified_host, 10, Resolv::DNS::Resource::IN::A.new(iface.addr.ip_address)
481
+
482
+ expected.add_additional s.service_name,
483
+ 10,
484
+ Resolv::DNS::Resource::IN::TXT.new(*s.text)
485
+
486
+ expected.add_question s.qualified_host,
487
+ Resolv::DNS::Resource::IN::A
291
488
 
292
489
  assert_equal expected, res
293
490
  end
@@ -299,10 +496,11 @@ module ZeroConf
299
496
  listen = make_listener rd, q
300
497
  s = make_server iface
301
498
  server = Thread.new { s.start }
499
+ Thread.pass until s.started?
302
500
 
303
- query = Resolv::DNS::Message.new 0
501
+ query = Resolv::DNS::Message.new 10
304
502
  query.add_question "tc-lan-adapter.local.", Resolv::DNS::Resource::IN::A
305
- sock = open_ipv4 iface.addr, 0
503
+ sock = open_ipv4 iface.addr, Resolv::MDNS::Port
306
504
  multicast_send sock, query.encode
307
505
 
308
506
  host = Resolv::DNS::Name.create s.qualified_host
@@ -330,5 +528,10 @@ module ZeroConf
330
528
 
331
529
  assert_equal expected, res
332
530
  end
531
+
532
+ def read_with_timeout sock
533
+ return unless sock.wait_readable(3)
534
+ sock.recvfrom(2048)
535
+ end
333
536
  end
334
537
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zeroconf
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Patterson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-16 00:00:00.000000000 Z
11
+ date: 2024-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: resolv
@@ -58,12 +58,17 @@ executables: []
58
58
  extensions: []
59
59
  extra_rdoc_files: []
60
60
  files:
61
+ - ".github/workflows/ci.yml"
61
62
  - CODE_OF_CONDUCT.md
62
63
  - Gemfile
63
64
  - LICENSE
64
65
  - README.md
65
66
  - Rakefile
66
67
  - lib/zeroconf.rb
68
+ - lib/zeroconf/browser.rb
69
+ - lib/zeroconf/client.rb
70
+ - lib/zeroconf/discoverer.rb
71
+ - lib/zeroconf/resolver.rb
67
72
  - lib/zeroconf/service.rb
68
73
  - lib/zeroconf/utils.rb
69
74
  - lib/zeroconf/version.rb
@@ -90,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
95
  - !ruby/object:Gem::Version
91
96
  version: '0'
92
97
  requirements: []
93
- rubygems_version: 3.5.0.dev
98
+ rubygems_version: 3.5.11
94
99
  signing_key:
95
100
  specification_version: 4
96
101
  summary: Multicast DNS client and server