dnssd 1.2 → 1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|