dnssd 1.2 → 1.3
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.
- data.tar.gz.sig +0 -0
- data/.autotest +14 -0
- data/History.txt +22 -3
- data/Manifest.txt +15 -0
- data/README.txt +27 -3
- data/Rakefile +5 -4
- data/ext/dnssd/dnssd.c +4 -0
- data/ext/dnssd/dnssd.h +13 -18
- data/ext/dnssd/errors.c +9 -3
- data/ext/dnssd/extconf.rb +51 -44
- data/ext/dnssd/flags.c +68 -14
- data/ext/dnssd/record.c +218 -0
- data/ext/dnssd/service.c +341 -121
- data/lib/dnssd.rb +46 -11
- data/lib/dnssd/record.rb +97 -0
- data/lib/dnssd/reply.rb +39 -92
- data/lib/dnssd/reply/addr_info.rb +47 -0
- data/lib/dnssd/reply/browse.rb +52 -0
- data/lib/dnssd/reply/domain.rb +22 -0
- data/lib/dnssd/reply/query_record.rb +183 -0
- data/lib/dnssd/reply/register.rb +37 -0
- data/lib/dnssd/reply/resolve.rb +105 -0
- data/lib/dnssd/service.rb +123 -16
- data/lib/dnssd/text_record.rb +28 -19
- data/sample/browse.rb +24 -6
- data/sample/enumerate_domains.rb +7 -1
- data/sample/getaddrinfo.rb +28 -0
- data/sample/growl.rb +2 -0
- data/sample/query_record.rb +15 -0
- data/sample/register.rb +19 -20
- data/sample/resolve.rb +31 -7
- data/sample/resolve_ichat.rb +5 -6
- data/sample/server.rb +2 -0
- data/sample/socket.rb +4 -0
- data/test/test_dnssd.rb +6 -4
- data/test/test_dnssd_flags.rb +1 -15
- data/test/test_dnssd_record.rb +92 -0
- data/test/test_dnssd_reply.rb +13 -79
- data/test/test_dnssd_reply_browse.rb +28 -0
- data/test/test_dnssd_reply_query_record.rb +92 -0
- data/test/test_dnssd_reply_resolve.rb +47 -0
- data/test/test_dnssd_service.rb +12 -0
- data/test/test_dnssd_text_record.rb +3 -3
- metadata +32 -11
- metadata.gz.sig +0 -0
data/lib/dnssd.rb
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
require 'dnssd/dnssd'
|
|
2
|
-
require 'socket'
|
|
3
|
-
|
|
4
1
|
##
|
|
5
2
|
# DNSSD is a wrapper for the DNS Service Discovery library.
|
|
6
3
|
#
|
|
@@ -16,7 +13,7 @@ module DNSSD
|
|
|
16
13
|
##
|
|
17
14
|
# The version of DNSSD you're using.
|
|
18
15
|
|
|
19
|
-
VERSION = '1.
|
|
16
|
+
VERSION = '1.3'
|
|
20
17
|
|
|
21
18
|
##
|
|
22
19
|
# Registers +socket+ with DNSSD as +name+. If +service+ is omitted it is
|
|
@@ -120,9 +117,14 @@ module DNSSD
|
|
|
120
117
|
interface = DNSSD::InterfaceAny, &block)
|
|
121
118
|
service = DNSSD::Service.new
|
|
122
119
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
120
|
+
if block_given? then
|
|
121
|
+
Thread.start do
|
|
122
|
+
run(service, :register, name, type, domain, port, nil, text_record,
|
|
123
|
+
flags, interface, &block)
|
|
124
|
+
end
|
|
125
|
+
else
|
|
126
|
+
service.register name, type, domain, port, nil, text_record, flags,
|
|
127
|
+
interface
|
|
126
128
|
end
|
|
127
129
|
|
|
128
130
|
service
|
|
@@ -135,8 +137,15 @@ module DNSSD
|
|
|
135
137
|
interface = DNSSD::InterfaceAny, &block)
|
|
136
138
|
service = DNSSD::Service.new
|
|
137
139
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
+
if block_given? then
|
|
141
|
+
run(service, :register, name, type, domain, port, nil, text_record, flags,
|
|
142
|
+
interface, &block)
|
|
143
|
+
else
|
|
144
|
+
service.register name, type, domain, port, nil, text_record, flags,
|
|
145
|
+
interface
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
service
|
|
140
149
|
end
|
|
141
150
|
|
|
142
151
|
##
|
|
@@ -175,8 +184,34 @@ module DNSSD
|
|
|
175
184
|
|
|
176
185
|
end
|
|
177
186
|
|
|
178
|
-
require '
|
|
187
|
+
require 'socket'
|
|
188
|
+
|
|
179
189
|
require 'dnssd/reply'
|
|
180
|
-
require 'dnssd/
|
|
190
|
+
require 'dnssd/reply/addr_info'
|
|
191
|
+
require 'dnssd/reply/browse'
|
|
192
|
+
require 'dnssd/reply/domain'
|
|
193
|
+
require 'dnssd/reply/query_record'
|
|
194
|
+
require 'dnssd/reply/register'
|
|
195
|
+
require 'dnssd/reply/resolve'
|
|
181
196
|
require 'dnssd/text_record'
|
|
182
197
|
|
|
198
|
+
# Suppress avahi compatibilty warning
|
|
199
|
+
# http://0pointer.de/avahi-compat?s=libdns_sd&e=ruby
|
|
200
|
+
ENV['AVAHI_COMPAT_NOWARN'] = '1'
|
|
201
|
+
|
|
202
|
+
# The C extension uses above-defined classes
|
|
203
|
+
require 'dnssd/dnssd'
|
|
204
|
+
|
|
205
|
+
module DNSSD
|
|
206
|
+
# :stopdoc:
|
|
207
|
+
class ServiceNotRunningError < UnknownError; end unless
|
|
208
|
+
const_defined? :ServiceNotRunningError
|
|
209
|
+
|
|
210
|
+
InterfaceUnicast = 4294967294 unless const_defined? :InterfaceUnicast # -2
|
|
211
|
+
# :startdoc:
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
require 'dnssd/flags'
|
|
215
|
+
require 'dnssd/service'
|
|
216
|
+
require 'dnssd/record'
|
|
217
|
+
|
data/lib/dnssd/record.rb
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require 'ipaddr'
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# Created when adding a DNS record using DNSSD::Service#add_record. Provides
|
|
5
|
+
# convenience methods for creating the DNS record.
|
|
6
|
+
#
|
|
7
|
+
# See also {RFC 1035}[http://www.rfc-editor.org/rfc/rfc1035.txt]
|
|
8
|
+
|
|
9
|
+
class DNSSD::Record
|
|
10
|
+
|
|
11
|
+
value_to_name = constants.map do |name|
|
|
12
|
+
next if name.intern == :IN
|
|
13
|
+
[const_get(name), name.to_s]
|
|
14
|
+
end.compact.flatten
|
|
15
|
+
|
|
16
|
+
##
|
|
17
|
+
# Maps record constant values to the constant name
|
|
18
|
+
|
|
19
|
+
VALUE_TO_NAME = Hash[*value_to_name]
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
# Turns +string+ into an RFC-1035 character-string
|
|
23
|
+
|
|
24
|
+
def self.string_to_character_string(string)
|
|
25
|
+
length = string.length
|
|
26
|
+
raise ArgumentError, "#{string.inspect} is too long (255 bytes max)" if
|
|
27
|
+
length > 255
|
|
28
|
+
"#{length.chr}#{string}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# Turns +string+ into an RFC-1035 domain-name
|
|
33
|
+
|
|
34
|
+
def self.string_to_domain_name(string)
|
|
35
|
+
string.split('.').map do |part|
|
|
36
|
+
string_to_character_string part
|
|
37
|
+
end.join('') << "\0"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
##
|
|
41
|
+
# Encodes resource +args+ into +type+. Handles:
|
|
42
|
+
#
|
|
43
|
+
# A AAAA CNAME MX NS PTR SOA SRV TXT
|
|
44
|
+
|
|
45
|
+
def self.to_data(type, *args)
|
|
46
|
+
raise ArgumentError, "unknown type #{type}" unless VALUE_TO_NAME.key? type
|
|
47
|
+
|
|
48
|
+
data = case type
|
|
49
|
+
when A then
|
|
50
|
+
addr = args.shift
|
|
51
|
+
addr = IPAddr.new addr unless IPAddr === addr
|
|
52
|
+
raise ArgumentError, "#{addr} is not IPv4" unless addr.ipv4?
|
|
53
|
+
addr.hton
|
|
54
|
+
when AAAA then
|
|
55
|
+
addr = args.shift
|
|
56
|
+
addr = IPAddr.new addr unless IPAddr === addr
|
|
57
|
+
raise ArgumentError, "#{addr} is not IPv6" unless addr.ipv6?
|
|
58
|
+
addr.hton
|
|
59
|
+
when CNAME, NS, PTR then
|
|
60
|
+
string_to_domain_name args.shift
|
|
61
|
+
when MX then
|
|
62
|
+
[args.shift, string_to_domain_name(args.shift)].pack 'na*'
|
|
63
|
+
when SOA then
|
|
64
|
+
[
|
|
65
|
+
string_to_domain_name(args.shift),
|
|
66
|
+
string_to_domain_name(args.shift),
|
|
67
|
+
args.shift, args.shift, args.shift, args.shift, args.shift
|
|
68
|
+
].pack 'a*a*NNNNN'
|
|
69
|
+
when SRV then
|
|
70
|
+
[
|
|
71
|
+
args.shift, args.shift, args.shift,
|
|
72
|
+
string_to_domain_name(args.shift)
|
|
73
|
+
].pack 'nnna*'
|
|
74
|
+
when TXT then
|
|
75
|
+
data = args.map do |string|
|
|
76
|
+
string_to_character_string string
|
|
77
|
+
end.join ''
|
|
78
|
+
|
|
79
|
+
raise ArgumentError,
|
|
80
|
+
"TXT record too long (#{data.length} bytes)" if
|
|
81
|
+
data.length > 65535
|
|
82
|
+
|
|
83
|
+
args.clear
|
|
84
|
+
|
|
85
|
+
data
|
|
86
|
+
else
|
|
87
|
+
raise ArgumentError, "unhandled record type #{VALUE_TO_NAME[type]}"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
raise ArgumentError, "Too many arguments for #{VALUE_TO_NAME[type]}" unless
|
|
91
|
+
args.empty?
|
|
92
|
+
|
|
93
|
+
data
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
|
data/lib/dnssd/reply.rb
CHANGED
|
@@ -4,131 +4,78 @@
|
|
|
4
4
|
class DNSSD::Reply
|
|
5
5
|
|
|
6
6
|
##
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
attr_reader :domain
|
|
10
|
-
|
|
11
|
-
##
|
|
12
|
-
# Flags describing the reply, see DNSSD::Flags
|
|
7
|
+
# Flags for this reply, see DNSSD::Flags
|
|
13
8
|
|
|
14
9
|
attr_reader :flags
|
|
15
10
|
|
|
16
11
|
##
|
|
17
|
-
# The interface
|
|
12
|
+
# The interface name for this reply
|
|
18
13
|
|
|
19
14
|
attr_reader :interface
|
|
20
15
|
|
|
21
16
|
##
|
|
22
|
-
# The
|
|
23
|
-
|
|
24
|
-
attr_reader :name
|
|
25
|
-
|
|
26
|
-
##
|
|
27
|
-
# The port for this service
|
|
28
|
-
|
|
29
|
-
attr_reader :port
|
|
30
|
-
|
|
31
|
-
##
|
|
32
|
-
# The DNSSD::Service associated with the reply
|
|
17
|
+
# The DNSSD::Service that created this reply
|
|
33
18
|
|
|
34
19
|
attr_reader :service
|
|
35
20
|
|
|
36
21
|
##
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
attr_reader :target
|
|
40
|
-
|
|
41
|
-
##
|
|
42
|
-
# The service's primary text record
|
|
43
|
-
|
|
44
|
-
attr_reader :text_record
|
|
45
|
-
|
|
46
|
-
##
|
|
47
|
-
# The service type
|
|
48
|
-
|
|
49
|
-
attr_reader :type
|
|
50
|
-
|
|
51
|
-
##
|
|
52
|
-
# Creates a DNSSD::Reply from +service+ and +flags+
|
|
22
|
+
# Creates a new reply attached to +service+ with +flags+ on interface index
|
|
23
|
+
# +interface+
|
|
53
24
|
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
##
|
|
62
|
-
# Connects to this Reply. If +target+ and +port+ are missing, DNSSD.resolve
|
|
63
|
-
# is automatically called. +family+ can be used to select a particular
|
|
64
|
-
# address family.
|
|
65
|
-
|
|
66
|
-
def connect(family = Socket::AF_UNSPEC)
|
|
67
|
-
unless target and port then
|
|
68
|
-
value = nil
|
|
69
|
-
|
|
70
|
-
DNSSD.resolve! self do |reply|
|
|
71
|
-
value = reply
|
|
72
|
-
break
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
return value.connect
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
socktype = case protocol
|
|
79
|
-
when 'tcp' then Socket::SOCK_STREAM
|
|
80
|
-
when 'udp' then Socket::SOCK_DGRAM
|
|
81
|
-
else raise ArgumentError, "invalid protocol #{protocol}"
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
addresses = Socket.getaddrinfo target, port, family, socktype
|
|
85
|
-
|
|
86
|
-
socket = nil
|
|
87
|
-
|
|
88
|
-
addresses.each do |address|
|
|
89
|
-
begin
|
|
90
|
-
case protocol
|
|
91
|
-
when 'tcp' then
|
|
92
|
-
socket = TCPSocket.new address[3], port
|
|
93
|
-
when 'udp' then
|
|
94
|
-
socket = UDPSocket.new
|
|
95
|
-
socket.connect address[3], port rescue next
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
return socket
|
|
99
|
-
rescue
|
|
100
|
-
next
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
raise DNSSD::Error, "unable to connect to #{target}:#{port}" unless socket
|
|
25
|
+
def initialize(service, flags, interface)
|
|
26
|
+
@service = service
|
|
27
|
+
@flags = DNSSD::Flags.new flags
|
|
28
|
+
@interface = if interface then
|
|
29
|
+
interface > 0 ? DNSSD.interface_name(interface) : interface
|
|
30
|
+
end
|
|
105
31
|
end
|
|
106
32
|
|
|
107
33
|
##
|
|
108
34
|
# The full service domain name, see DNSS::Service#fullname
|
|
109
35
|
|
|
110
36
|
def fullname
|
|
111
|
-
DNSSD::Service.fullname @name.gsub("\032", ' '), @type, @domain
|
|
37
|
+
fullname = DNSSD::Service.fullname @name.gsub("\032", ' '), @type, @domain
|
|
38
|
+
fullname << '.' unless fullname =~ /\.$/
|
|
39
|
+
fullname
|
|
112
40
|
end
|
|
113
41
|
|
|
114
42
|
def inspect # :nodoc:
|
|
115
|
-
"#<%s:0x%x
|
|
116
|
-
self.class, object_id,
|
|
43
|
+
"#<%s:0x%x interface: %s flags: %p>" % [
|
|
44
|
+
self.class, object_id, interface_name, @flags
|
|
117
45
|
]
|
|
118
46
|
end
|
|
119
47
|
|
|
48
|
+
##
|
|
49
|
+
# Expands the name of the interface including constants
|
|
50
|
+
|
|
51
|
+
def interface_name
|
|
52
|
+
case @interface
|
|
53
|
+
when nil then 'nil'
|
|
54
|
+
when DNSSD::InterfaceAny then 'any'
|
|
55
|
+
when DNSSD::InterfaceLocalOnly then 'local'
|
|
56
|
+
when DNSSD::InterfaceUnicast then 'unicast'
|
|
57
|
+
else @interface
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
120
61
|
##
|
|
121
62
|
# Protocol of this service
|
|
122
63
|
|
|
123
64
|
def protocol
|
|
124
|
-
|
|
65
|
+
raise TypeError, 'no type on this reply' unless
|
|
66
|
+
instance_variable_defined? :@type
|
|
67
|
+
|
|
68
|
+
@type.split('.').last.sub '_', ''
|
|
125
69
|
end
|
|
126
70
|
|
|
127
71
|
##
|
|
128
72
|
# Service name as in Socket.getservbyname
|
|
129
73
|
|
|
130
74
|
def service_name
|
|
131
|
-
|
|
75
|
+
raise TypeError, 'no type on this reply' unless
|
|
76
|
+
instance_variable_defined? :@type
|
|
77
|
+
|
|
78
|
+
@type.split('.').first.sub '_', ''
|
|
132
79
|
end
|
|
133
80
|
|
|
134
81
|
##
|
|
@@ -141,8 +88,8 @@ class DNSSD::Reply
|
|
|
141
88
|
end
|
|
142
89
|
|
|
143
90
|
@name = fullname[0]
|
|
144
|
-
@type = fullname[1,
|
|
145
|
-
@domain = fullname.
|
|
91
|
+
@type = fullname[1, 2].join '.'
|
|
92
|
+
@domain = fullname[3..-1].map { |part| part.sub '.', '\\.' }.join('.') + '.'
|
|
146
93
|
end
|
|
147
94
|
|
|
148
95
|
##
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
##
|
|
2
|
+
# Created by DNSSD::Service#getaddrinfo
|
|
3
|
+
|
|
4
|
+
class DNSSD::Reply::AddrInfo < DNSSD::Reply
|
|
5
|
+
|
|
6
|
+
##
|
|
7
|
+
# IP address of host
|
|
8
|
+
|
|
9
|
+
attr_reader :address
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
# Host name
|
|
13
|
+
|
|
14
|
+
attr_reader :hostname
|
|
15
|
+
|
|
16
|
+
##
|
|
17
|
+
# Port name
|
|
18
|
+
|
|
19
|
+
attr_reader :port
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
# Time to live see #expired?
|
|
23
|
+
|
|
24
|
+
attr_reader :ttl
|
|
25
|
+
|
|
26
|
+
##
|
|
27
|
+
# Creates a new AddrInfo, called internally by DNSSD::Service#getaddrinfo
|
|
28
|
+
|
|
29
|
+
def initialize(service, flags, interface, hostname, sockaddr, ttl)
|
|
30
|
+
super service, flags, interface
|
|
31
|
+
|
|
32
|
+
@hostname = hostname
|
|
33
|
+
@port, @address = Socket.unpack_sockaddr_in sockaddr
|
|
34
|
+
|
|
35
|
+
@created = Time.now
|
|
36
|
+
@ttl = ttl
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# Has this AddrInfo passed its TTL?
|
|
41
|
+
|
|
42
|
+
def expired?
|
|
43
|
+
Time.now > @created + ttl
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
##
|
|
2
|
+
# Returned by DNSSD::Service#browse
|
|
3
|
+
|
|
4
|
+
class DNSSD::Reply::Browse < DNSSD::Reply
|
|
5
|
+
|
|
6
|
+
##
|
|
7
|
+
# A domain for registration or browsing
|
|
8
|
+
|
|
9
|
+
attr_reader :domain
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
# The service name
|
|
13
|
+
|
|
14
|
+
attr_reader :name
|
|
15
|
+
|
|
16
|
+
##
|
|
17
|
+
# The service type
|
|
18
|
+
|
|
19
|
+
attr_reader :type
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
# Creates a new Browse, called internally by DNSSD::Service#browse
|
|
23
|
+
|
|
24
|
+
def initialize(service, flags, interface, name, type, domain)
|
|
25
|
+
super service, flags, interface
|
|
26
|
+
|
|
27
|
+
set_names name, type, domain
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
# Resolves this service's target using DNSSD::Reply::Resolve#connect which
|
|
32
|
+
# connects, returning a TCP or UDP socket.
|
|
33
|
+
|
|
34
|
+
def connect(family = Socket::AF_UNSPEC, addrinfo_flags = 0)
|
|
35
|
+
value = nil
|
|
36
|
+
|
|
37
|
+
DNSSD.resolve! self do |reply|
|
|
38
|
+
value = reply
|
|
39
|
+
break
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
value.connect family, addrinfo_flags
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def inspect # :nodoc:
|
|
46
|
+
"#<%s:0x%x %p interface: %s flags: %p>" % [
|
|
47
|
+
self.class, object_id, fullname, interface_name, @flags
|
|
48
|
+
]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
|
52
|
+
|