radar-dnssd 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,257 @@
1
+ /*
2
+ * Copyright (c) 2004 Chad Fowler, Charles Mills, Rich Kilmer
3
+ * Licenced under the same terms as Ruby.
4
+ * This software has absolutely no warrenty.
5
+ */
6
+ #include "rdnssd.h"
7
+ #include <intern.h>
8
+ #include <string.h> /* for strchr() */
9
+
10
+ static VALUE cDNSSDTextRecord;
11
+
12
+ static void
13
+ dnssd_tr_decode_buffer(VALUE self, long buf_len, const char *buf_ptr)
14
+ {
15
+ /* iterate through text record, inserting key, value pairs into hash */
16
+ long i = 0;
17
+ while(i < buf_len) {
18
+ VALUE key, value;
19
+ const char *p;
20
+ long key_len, len = (long)(uint8_t)buf_ptr[i++];
21
+ if (i + len > buf_len)
22
+ rb_raise(rb_eArgError, "invalid text record (string longer than buffer)");
23
+
24
+ if (len == 0) {
25
+ /* 0-length key/value, handle this by skipping over it.
26
+ * Probably the RR should have zero-length, but some services have been
27
+ * found to advertise a non-zero length TXT record, with a zero-length
28
+ * key/value string.
29
+ */
30
+ continue;
31
+ }
32
+
33
+ p = memchr(buf_ptr + i, '=', buf_len - i);
34
+ if (p == NULL) {
35
+ /* key, with no value, this is OK */
36
+ key_len = len;
37
+ key = rb_str_new(buf_ptr + i, key_len);
38
+ value = Qnil;
39
+ } else {
40
+ key_len = p - (buf_ptr + i);
41
+ if(key_len == 0)
42
+ rb_raise(rb_eArgError, "invalid text record (zero-length key)");
43
+
44
+ key = rb_str_new(buf_ptr + i, key_len);
45
+ key_len++; /* step over '=' */
46
+ value = rb_str_new(buf_ptr + i + key_len, len - key_len);
47
+ }
48
+ rb_hash_aset(self, key, value);
49
+ i += len;
50
+ }
51
+ }
52
+
53
+ static void
54
+ dnssd_tr_decode_str(VALUE self, VALUE str)
55
+ {
56
+ str = StringValue(str);
57
+ /* text records cannot be longer than 65535 (0xFFFF) */
58
+ if (RSTRING(str)->len > UINT16_MAX)
59
+ rb_raise(rb_eArgError, "string is to large to encode");
60
+ dnssd_tr_decode_buffer (self, RSTRING(str)->len, RSTRING(str)->ptr);
61
+ }
62
+
63
+ /*
64
+ * call-seq:
65
+ * DNSSD::TextRecord.decode(binary_string) => text_record
66
+ *
67
+ * Create a new DNSSD::TextRecord be decoding the key
68
+ * value pairs contained in _binary_string_. See DNSSD::TextRecord.encode()
69
+ * for more information.
70
+ */
71
+
72
+ static VALUE
73
+ dnssd_tr_decode(VALUE klass, VALUE str)
74
+ {
75
+ /* self needs to be on the stack - we add (allocate)
76
+ * lots of key, value pairs when decoding and this could
77
+ * cause the gc to run. */
78
+ volatile VALUE self = rb_obj_alloc(klass);
79
+ dnssd_tr_decode_str(self, str);
80
+ return self;
81
+ }
82
+
83
+ VALUE
84
+ dnssd_tr_new(long len, const char *buf)
85
+ {
86
+ VALUE self = rb_obj_alloc(cDNSSDTextRecord);
87
+ dnssd_tr_decode_buffer(self, len, buf);
88
+ return self;
89
+ }
90
+
91
+ /*
92
+ * call-seq:
93
+ * DNSSD::TextRecord.new() => text_record
94
+ * DNSSD::TextRecord.new(binary_string) => text_record
95
+ *
96
+ * The first form creates an empty text record. The second
97
+ * creates a new text record populated with the key, value pairs
98
+ * found in the encoded string _binary_string_. See
99
+ * DNSSD::TextRecord.encode() for more information.
100
+ *
101
+ * tr = DNSSD::TextRecord.new #=> {}
102
+ * tr["name"] = "Chad"
103
+ * tr["port"] = 3871.to_s
104
+ *
105
+ */
106
+
107
+ static VALUE
108
+ dnssd_tr_initialize(int argc, VALUE *argv, VALUE self)
109
+ {
110
+ VALUE encoded_str;
111
+ rb_scan_args(argc, argv, "01", &encoded_str);
112
+ if (argc == 1) {
113
+ /* try to decode the string */
114
+ dnssd_tr_decode_str(self, encoded_str);
115
+ }
116
+ return self;
117
+ }
118
+
119
+ static void
120
+ dnssd_tr_valid_key(const char *key_cstr, long len)
121
+ {
122
+ /* keys cannot contain '=' */
123
+ if (strchr(key_cstr, '='))
124
+ rb_raise(rb_eRuntimeError, "key '%s' contains '='", key_cstr);
125
+
126
+ if (len <= 0)
127
+ rb_raise(rb_eRuntimeError, "empty key given");
128
+ }
129
+
130
+ static long
131
+ dnssd_tr_convert_pairs(volatile VALUE ary)
132
+ {
133
+ long i, tot_len = 0;
134
+ VALUE *ptr = RARRAY(ary)->ptr;
135
+ /* iterate over key, value pairs checking if each one is valid */
136
+ for(i=0; i<RARRAY(ary)->len; i++) {
137
+ VALUE key = RARRAY(ptr[i])->ptr[0];
138
+ VALUE value = RARRAY(ptr[i])->ptr[1];
139
+ /* checks if key is a valid C String (null terminated)
140
+ * note: StringValueCStr takes a pointer to key by &key
141
+ * this may cause key to be reassigned. */
142
+ const char *key_cstr = StringValueCStr(key);
143
+ long len = RSTRING(key)->len;
144
+ dnssd_tr_valid_key(key_cstr, len);
145
+
146
+ if (!NIL_P(value)) {
147
+ value = StringValue(value);
148
+ len += 1 + RSTRING(value)->len;
149
+ }
150
+ /* len == sum(key length, 1 for '=' if value != nil, value length) */
151
+ if (len > UINT8_MAX)
152
+ rb_raise(rb_eRuntimeError, "key, value pair at '%s' is too large to encode", key_cstr);
153
+
154
+ /* now that we know no errors are going to occur */
155
+ if (RSTRING(key)->len > 14)
156
+ rb_warn("key '%s' is greator than 14 bytes, may not be compatible with all clients", key_cstr);
157
+ /* key and value may have been reassigned by StringValue macros */
158
+ RARRAY(ptr[i])->ptr[0] = key;
159
+ RARRAY(ptr[i])->ptr[1] = value;
160
+ tot_len += len + 1; /* plus 1 to hold key, value pair length */
161
+ }
162
+ return tot_len;
163
+ }
164
+
165
+ /*
166
+ * call-seq:
167
+ * text_record.encode => an_encoded_string
168
+ *
169
+ * Encodes the contents of _text_record_ into a sequence of <em>binary strings</em>
170
+ * (one for each key, value pair).
171
+ * The each <em>binary string</em> comprises of a <em>length</em> and a <em>payload</em>.
172
+ * The <em>length</em> gives the number of bytes in the <em>payload</em>
173
+ * (must be between <code>0</code> and <code>255</code>).
174
+ * This is an unsigned integer packed into the first byte of the binary string.
175
+ * The <em>payload</em> contains a key, value pair separated by a <code>=</code> character.
176
+ * Because <code>=</code> is used as a separator, keys must not contain any
177
+ * <code>=</code> characters. Here is an example of how the key, value pair
178
+ * <code>"1rst"</code>, <code>"Sam"</code> is encoded.
179
+ *
180
+ * [00][01][02][03][04][05][06][07][08]
181
+ * \010 1 r s t = S a m
182
+ *
183
+ * It is recommended to use keys with a length less than or equal to <code>14</code> bytes
184
+ * to ensure compatibility with all clients.
185
+ *
186
+ * text_record = DNSSD::TextRecord.new
187
+ * text_record["1rst"]="Sam"
188
+ * text_record["Last"]="Green"
189
+ * text_record["email"]="sam@green.org"
190
+ * s = text_record.encode #=> "\nLast=Green\0101rst=Sam\023email=sam@green.org"
191
+ * DNSSD::TextRecord.decode(s) #=> {"Last"=>"Green", "1rst"=>"Sam", "email"=>"sam@green.org"}
192
+ *
193
+ */
194
+
195
+ static VALUE
196
+ dnssd_tr_encode(VALUE self)
197
+ {
198
+ long i;
199
+ VALUE buf;
200
+ /* Declare ary volatile to prevent it from being reclaimed when:
201
+ * buf is allocated later, key/values are converted to strings */
202
+ volatile VALUE ary = rb_funcall2(self, rb_intern("to_a"), 0, 0);
203
+ /* array of key, value pairs */
204
+ VALUE *ptr = RARRAY(ary)->ptr;
205
+
206
+ buf = rb_str_buf_new(dnssd_tr_convert_pairs(ary));
207
+ for(i=0; i<RARRAY(ary)->len; i++) {
208
+ uint8_t len;
209
+ VALUE key = RARRAY(ptr[i])->ptr[0];
210
+ VALUE value = RARRAY(ptr[i])->ptr[1];
211
+ if (!NIL_P(value)) {
212
+ len = (uint8_t)(RSTRING(key)->len + RSTRING(value)->len + 1);
213
+ rb_str_buf_cat(buf, &len, 1);
214
+ rb_str_buf_append(buf, key);
215
+ rb_str_buf_cat(buf, "=", 1);
216
+ rb_str_buf_append(buf, value);
217
+ } else {
218
+ len = (uint8_t)RSTRING(key)->len;
219
+ rb_str_buf_cat(buf, &len, 1);
220
+ rb_str_buf_append(buf, key);
221
+ }
222
+ }
223
+ return buf;
224
+ }
225
+
226
+ VALUE
227
+ dnssd_tr_to_encoded_str(VALUE v)
228
+ {
229
+ if (rb_obj_is_kind_of(v, rb_cHash) == Qtrue)
230
+ return dnssd_tr_encode(v);
231
+ /* allow the user to use arbitrary strings as text records */
232
+ return StringValue(v);
233
+ }
234
+
235
+ /*
236
+ * Document-class: DNSSD::TextRecord
237
+ *
238
+ * DNSSD::TextRecord is a Hash with the ability to encode its contents into
239
+ * a binary string that can be send over the wire as using the DNSSD protocol.
240
+ *
241
+ */
242
+
243
+ void
244
+ Init_DNSSD_TextRecord(void)
245
+ {
246
+ /* hack so rdoc documents the project correctly */
247
+ #ifdef mDNSSD_RDOC_HACK
248
+ mDNSSD = rb_define_module("DNSSD");
249
+ #endif
250
+ cDNSSDTextRecord = rb_define_class_under(mDNSSD, "TextRecord", rb_cHash);
251
+
252
+ rb_define_singleton_method(cDNSSDTextRecord, "decode", dnssd_tr_decode, 1);
253
+
254
+ rb_define_method(cDNSSDTextRecord, "initialize", dnssd_tr_initialize, -1);
255
+ rb_define_method(cDNSSDTextRecord, "encode", dnssd_tr_encode, 0);
256
+ }
257
+
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+
3
+ rdoc -w 2 -S *.c
4
+
@@ -0,0 +1,137 @@
1
+ require 'rdnssd'
2
+
3
+ =begin
4
+
5
+ module DNSSD
6
+ class MalformedDomainException < Exception; end
7
+ class MalformedPortException < Exception; end
8
+
9
+ def self.new_text_record(hash={})
10
+ TextRecord.new(hash)
11
+ end
12
+
13
+ class ServiceDescription
14
+
15
+ class Location
16
+ attr_accessor :name, :port, :iface
17
+ def initialize(port, name=nil, iface=0)
18
+ @name = name
19
+ @port = port
20
+ @iface = iface
21
+ end
22
+ end
23
+
24
+ def initialize(type, name, domain, location)
25
+ @type = type
26
+ @name = name
27
+ @domain = validate_domain(domain)
28
+ @location = validate_location(location)
29
+ end
30
+
31
+ def self.for_browse_notification(name, domain,
32
+
33
+ def stop
34
+ @registrar.stop
35
+ end
36
+
37
+ def validate_location(location)
38
+ unless(location.port.respond_to?(:to_int))
39
+ raise MalformedPortException.new("#{location.port} is not a valid port number")
40
+ end
41
+ location
42
+ end
43
+
44
+ def validate_domain(domain)
45
+ unless(domain.empty? || domain =~ /^[a-z_]+$/)
46
+ raise MalformedDomainException.new("#{domain} is not a valid domain name")
47
+ end
48
+ domain
49
+ end
50
+
51
+ def advertise_and_confirm
52
+ thread = Thread.current
53
+ @registrar = register(@name, @type, @domain, @location.port, TextRecord.new) do |service, name, type, domain|
54
+ @name = name
55
+ @type = type
56
+ @domain = domain
57
+ thread.wakeup
58
+ end
59
+ Thread.stop
60
+ end
61
+
62
+ def self.advertise_http(name, port=80, domain="", iface=0, &block)
63
+ self.advertise("_http._tcp", name, port, domain, iface, &block)
64
+ end
65
+
66
+ ##
67
+ # iface: Numerical interface (0 = all interfaces, This should be used for most applications)
68
+ #
69
+ def self.advertise(type, name, port, domain="", iface=0, &block)
70
+ service_description = ServiceDescription.new(type, name, domain, Location.new(port,nil,iface))
71
+ service_description.advertise_and_confirm
72
+ yield service_description if block_given?
73
+ service_description
74
+ end
75
+ end
76
+
77
+ class Browser
78
+
79
+ Context = Struct.new(:service, :name, :type, :domain, :operation, :interface)
80
+
81
+ class Context
82
+ def ==(other)
83
+ self.to_s == other.to_s
84
+ end
85
+
86
+ def to_s
87
+ "#{name}.#{type}.#{domain}"
88
+ end
89
+
90
+ def eql?(other)
91
+ self == other
92
+ end
93
+
94
+ def hash
95
+ to_s.hash
96
+ end
97
+ end
98
+
99
+ def on_change(&block)
100
+ @change_listener ||= []
101
+ @change_listeners << block
102
+ end
103
+
104
+ def on_add(&block)
105
+ @add_listeners || = []
106
+ @add_listeners << block
107
+ end
108
+
109
+ def on_remove(&block)
110
+ @remove_listeners || = []
111
+ @remove_listeners << block
112
+ end
113
+
114
+ def initialize(type, domain="")
115
+ @list = []
116
+ @browse_service = DNSSD::Protocol.browse(type, domain) do
117
+ |service, name, type, domain, operation, interface|
118
+ context = Context.new(service, name, type, domain, operation, interface)
119
+ puts "Name: #{name} Type: #{type} Domain: #{domain} Operation: #{operation} Interface: #{interface}"
120
+ end
121
+ end
122
+
123
+ def service_descriptions
124
+ @list.clone
125
+ end
126
+
127
+ def stop
128
+ @browse_service.stop
129
+ end
130
+
131
+ def self.for_http(domain="")
132
+ self.new("_http._tcp", domain)
133
+ end
134
+ end
135
+ end
136
+
137
+ =end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: radar-dnssd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.2
5
+ platform: ruby
6
+ authors:
7
+ - Charlie Mills
8
+ - Ryan Bigg
9
+ - Andy Moreland
10
+ autorequire: dnssd
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2009-08-06 00:00:00 +10:00
15
+ default_executable:
16
+ dependencies: []
17
+
18
+ description:
19
+ email: radarlistener@gmail.com
20
+ executables: []
21
+
22
+ extensions: []
23
+
24
+ extra_rdoc_files: []
25
+
26
+ files:
27
+ - lib/dnssd.rb
28
+ - ext/extconf.rb
29
+ - ext/dns_sd.h
30
+ - ext/rdnssd_tr.c
31
+ - ext/rdnssd_structs.c
32
+ - ext/run_rdoc
33
+ - ext/rdnssd_service.c
34
+ - ext/MANIFEST
35
+ - ext/rdnssd.h
36
+ has_rdoc: true
37
+ homepage: http://github.com/radar/dnssd
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ - ext
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.3.4
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: DNS Service Discovery (aka Bonjour) API for Ruby 1.9
65
+ test_files: []
66
+