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.
Files changed (45) hide show
  1. data.tar.gz.sig +0 -0
  2. data/.autotest +14 -0
  3. data/History.txt +22 -3
  4. data/Manifest.txt +15 -0
  5. data/README.txt +27 -3
  6. data/Rakefile +5 -4
  7. data/ext/dnssd/dnssd.c +4 -0
  8. data/ext/dnssd/dnssd.h +13 -18
  9. data/ext/dnssd/errors.c +9 -3
  10. data/ext/dnssd/extconf.rb +51 -44
  11. data/ext/dnssd/flags.c +68 -14
  12. data/ext/dnssd/record.c +218 -0
  13. data/ext/dnssd/service.c +341 -121
  14. data/lib/dnssd.rb +46 -11
  15. data/lib/dnssd/record.rb +97 -0
  16. data/lib/dnssd/reply.rb +39 -92
  17. data/lib/dnssd/reply/addr_info.rb +47 -0
  18. data/lib/dnssd/reply/browse.rb +52 -0
  19. data/lib/dnssd/reply/domain.rb +22 -0
  20. data/lib/dnssd/reply/query_record.rb +183 -0
  21. data/lib/dnssd/reply/register.rb +37 -0
  22. data/lib/dnssd/reply/resolve.rb +105 -0
  23. data/lib/dnssd/service.rb +123 -16
  24. data/lib/dnssd/text_record.rb +28 -19
  25. data/sample/browse.rb +24 -6
  26. data/sample/enumerate_domains.rb +7 -1
  27. data/sample/getaddrinfo.rb +28 -0
  28. data/sample/growl.rb +2 -0
  29. data/sample/query_record.rb +15 -0
  30. data/sample/register.rb +19 -20
  31. data/sample/resolve.rb +31 -7
  32. data/sample/resolve_ichat.rb +5 -6
  33. data/sample/server.rb +2 -0
  34. data/sample/socket.rb +4 -0
  35. data/test/test_dnssd.rb +6 -4
  36. data/test/test_dnssd_flags.rb +1 -15
  37. data/test/test_dnssd_record.rb +92 -0
  38. data/test/test_dnssd_reply.rb +13 -79
  39. data/test/test_dnssd_reply_browse.rb +28 -0
  40. data/test/test_dnssd_reply_query_record.rb +92 -0
  41. data/test/test_dnssd_reply_resolve.rb +47 -0
  42. data/test/test_dnssd_service.rb +12 -0
  43. data/test/test_dnssd_text_record.rb +3 -3
  44. metadata +32 -11
  45. metadata.gz.sig +0 -0
@@ -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.2'
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
- Thread.start do
124
- run(service, :register, name, type, domain, port, nil, text_record,
125
- flags, interface, &block)
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
- run(service, :register, name, type, domain, port, nil, text_record, flags,
139
- interface, &block)
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 'dnssd/flags'
187
+ require 'socket'
188
+
179
189
  require 'dnssd/reply'
180
- require 'dnssd/service'
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
+
@@ -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
+
@@ -4,131 +4,78 @@
4
4
  class DNSSD::Reply
5
5
 
6
6
  ##
7
- # The service domain
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 on which the service is available
12
+ # The interface name for this reply
18
13
 
19
14
  attr_reader :interface
20
15
 
21
16
  ##
22
- # The service name
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
- # The hostname of the host provide the service
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 self.from_service(service, flags)
55
- reply = new
56
- reply.instance_variable_set :@service, service
57
- reply.instance_variable_set :@flags, DNSSD::Flags.new(flags)
58
- reply
59
- end
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 %p type: %s domain: %s interface: %s flags: %s>" % [
116
- self.class, object_id, @name, @type, @domain, @interface, @flags
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
- type.split('.').last.sub '_', ''
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
- type.split('.').first.sub '_', ''
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, 2].join '.'
145
- @domain = fullname.last + '.'
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
+