dnssd 0.6.0

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.
@@ -0,0 +1,252 @@
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_error(void)
14
+ {
15
+ rb_raise(rb_eArgError, "buffer contains invalid text record");
16
+ }
17
+
18
+ static void
19
+ dnssd_tr_decode_buffer(VALUE self, long buf_len, const char *buf_ptr)
20
+ {
21
+ /* iterate through text record, inserting key, value pairs into hash */
22
+ long i = 0;
23
+ while(i < buf_len) {
24
+ VALUE key, value;
25
+ const char *p;
26
+ long key_len, len = (long)(uint8_t)buf_ptr[i++];
27
+ if (i >= buf_len || i + len > buf_len)
28
+ dnssd_tr_decode_error();
29
+
30
+ p = strchr(buf_ptr + i, '=');
31
+ if (p == NULL) {
32
+ key_len = len;
33
+ key = rb_str_new(buf_ptr + i, key_len);
34
+ value = Qnil;
35
+ } else {
36
+ key_len = p - (buf_ptr + i);
37
+ if (key_len >= len)
38
+ dnssd_tr_decode_error();
39
+ key = rb_str_new(buf_ptr + i, key_len);
40
+ key_len++; /* step over '=' */
41
+ value = rb_str_new(buf_ptr + i + key_len, len - key_len);
42
+ }
43
+ rb_hash_aset(self, key, value);
44
+ i += len;
45
+ }
46
+ }
47
+
48
+ static void
49
+ dnssd_tr_decode_str(VALUE self, VALUE str)
50
+ {
51
+ str = StringValue(str);
52
+ /* text records cannot be longer than 65535 (0xFFFF) */
53
+ if (RSTRING(str)->len > UINT16_MAX)
54
+ rb_raise(rb_eArgError, "string is to large to encode");
55
+ dnssd_tr_decode_buffer (self, RSTRING(str)->len, RSTRING(str)->ptr);
56
+ }
57
+
58
+ /*
59
+ * call-seq:
60
+ * DNSSD::TextRecord.decode(binary_string) => text_record
61
+ *
62
+ * Create a new DNSSD::TextRecord be decoding the key
63
+ * value pairs contained in _binary_string_. See DNSSD::TextRecord.encode()
64
+ * for more information.
65
+ */
66
+
67
+ static VALUE
68
+ dnssd_tr_decode(VALUE klass, VALUE str)
69
+ {
70
+ /* self needs to be on the stack - we add (allocate)
71
+ * lots of key, value pairs when decoding and this could
72
+ * cause the gc to run. */
73
+ volatile VALUE self = rb_obj_alloc(klass);
74
+ dnssd_tr_decode_str(self, str);
75
+ return self;
76
+ }
77
+
78
+ VALUE
79
+ dnssd_tr_new(long len, const char *buf)
80
+ {
81
+ VALUE self = rb_obj_alloc(cDNSSDTextRecord);
82
+ dnssd_tr_decode_buffer(self, len, buf);
83
+ return self;
84
+ }
85
+
86
+ /*
87
+ * call-seq:
88
+ * DNSSD::TextRecord.new() => text_record
89
+ * DNSSD::TextRecord.new(binary_string) => text_record
90
+ *
91
+ * The first form creates an empty text record. The second
92
+ * creates a new text record populated with the key, value pairs
93
+ * found in the encoded string _binary_string_. See
94
+ * DNSSD::TextRecord.encode() for more information.
95
+ *
96
+ * tr = DNSSD::TextRecord.new #=> {}
97
+ * tr["name"] = "Chad"
98
+ * tr["port"] = 3871.to_s
99
+ *
100
+ */
101
+
102
+ static VALUE
103
+ dnssd_tr_initialize(int argc, VALUE *argv, VALUE self)
104
+ {
105
+ VALUE encoded_str;
106
+ rb_scan_args(argc, argv, "01", &encoded_str);
107
+ if (argc == 1) {
108
+ /* try to decode the string */
109
+ dnssd_tr_decode_str(self, encoded_str);
110
+ }
111
+ return self;
112
+ }
113
+
114
+ static void
115
+ dnssd_tr_valid_key(const char *key_cstr, long len)
116
+ {
117
+ /* keys cannot contain '=' */
118
+ if (strchr(key_cstr, '='))
119
+ rb_raise(rb_eRuntimeError, "key '%s' contains '='", key_cstr);
120
+
121
+ if (len <= 0)
122
+ rb_raise(rb_eRuntimeError, "empty key given");
123
+ }
124
+
125
+ static long
126
+ dnssd_tr_convert_pairs(volatile VALUE ary)
127
+ {
128
+ long i, tot_len = 0;
129
+ VALUE *ptr = RARRAY(ary)->ptr;
130
+ /* iterate over key, value pairs checking if each one is valid */
131
+ for(i=0; i<RARRAY(ary)->len; i++) {
132
+ VALUE key = RARRAY(ptr[i])->ptr[0];
133
+ VALUE value = RARRAY(ptr[i])->ptr[1];
134
+ /* checks if key is a valid C String (null terminated)
135
+ * note: StringValueCStr takes a pointer to key by &key
136
+ * this may cause key to be reassigned. */
137
+ const char *key_cstr = StringValueCStr(key);
138
+ long len = RSTRING(key)->len;
139
+ dnssd_tr_valid_key(key_cstr, len);
140
+
141
+ if (!NIL_P(value)) {
142
+ value = StringValue(value);
143
+ len += 1 + RSTRING(value)->len;
144
+ }
145
+ /* len == sum(key length, 1 for '=' if value != nil, value length) */
146
+ if (len > UINT8_MAX)
147
+ rb_raise(rb_eRuntimeError, "key, value pair at '%s' is too large to encode", key_cstr);
148
+
149
+ /* now that we know no errors are going to occur */
150
+ if (RSTRING(key)->len > 14)
151
+ rb_warn("key '%s' is greator than 14 bytes, may not be compatible with all clients", key_cstr);
152
+ /* key and value may have been reassigned by StringValue macros */
153
+ RARRAY(ptr[i])->ptr[0] = key;
154
+ RARRAY(ptr[i])->ptr[1] = value;
155
+ tot_len += len + 1; /* plus 1 to hold key, value pair length */
156
+ }
157
+ return tot_len;
158
+ }
159
+
160
+ /*
161
+ * call-seq:
162
+ * text_record.encode => an_encoded_string
163
+ *
164
+ * Encodes the contents of _text_record_ into a sequence of <em>binary strings</em>
165
+ * (one for each key, value pair).
166
+ * The each <em>binary string</em> comprises of a <em>length</em> and a <em>payload</em>.
167
+ * The <em>length</em> gives the number of bytes in the <em>payload</em>
168
+ * (must be between <code>0</code> and <code>255</code>).
169
+ * This is an unsigned integer packed into the first byte of the binary string.
170
+ * The <em>payload</em> contains a key, value pair separated by a <code>=</code> character.
171
+ * Because <code>=</code> is used as a separator, keys must not contain any
172
+ * <code>=</code> characters. Here is an example of how the key, value pair
173
+ * <code>"1rst"</code>, <code>"Sam"</code> is encoded.
174
+ *
175
+ * [00][01][02][03][04][05][06][07][08]
176
+ * \010 1 r s t = S a m
177
+ *
178
+ * It is recommended to use keys with a length less than or equal to <code>14</code> bytes
179
+ * to ensure compatibility with all clients.
180
+ *
181
+ * text_record = DNSSD::TextRecord.new
182
+ * text_record["1rst"]="Sam"
183
+ * text_record["Last"]="Green"
184
+ * text_record["email"]="sam@green.org"
185
+ * s = text_record.encode #=> "\nLast=Green\0101rst=Sam\023email=sam@green.org"
186
+ * DNSSD::TextRecord.decode(s) #=> {"Last"=>"Green", "1rst"=>"Sam", "email"=>"sam@green.org"}
187
+ *
188
+ */
189
+
190
+ static VALUE
191
+ dnssd_tr_encode(VALUE self)
192
+ {
193
+ long i;
194
+ VALUE buf;
195
+ /* Declare ary volatile to prevent it from being reclaimed when:
196
+ * buf is allocated later, key/values are converted to strings */
197
+ volatile VALUE ary = rb_funcall2(self, rb_intern("to_a"), 0, 0);
198
+ /* array of key, value pairs */
199
+ VALUE *ptr = RARRAY(ary)->ptr;
200
+
201
+ buf = rb_str_buf_new(dnssd_tr_convert_pairs(ary));
202
+ for(i=0; i<RARRAY(ary)->len; i++) {
203
+ uint8_t len;
204
+ VALUE key = RARRAY(ptr[i])->ptr[0];
205
+ VALUE value = RARRAY(ptr[i])->ptr[1];
206
+ if (!NIL_P(value)) {
207
+ len = (uint8_t)(RSTRING(key)->len + RSTRING(value)->len + 1);
208
+ rb_str_buf_cat(buf, &len, 1);
209
+ rb_str_buf_append(buf, key);
210
+ rb_str_buf_cat(buf, "=", 1);
211
+ rb_str_buf_append(buf, value);
212
+ } else {
213
+ len = (uint8_t)RSTRING(key)->len;
214
+ rb_str_buf_cat(buf, &len, 1);
215
+ rb_str_buf_append(buf, key);
216
+ }
217
+ }
218
+ return buf;
219
+ }
220
+
221
+ VALUE
222
+ dnssd_tr_to_encoded_str(VALUE v)
223
+ {
224
+ if (rb_obj_is_kind_of(v, rb_cHash) == Qtrue)
225
+ return dnssd_tr_encode(v);
226
+ /* allow the user to use arbitrary strings as text records */
227
+ return StringValue(v);
228
+ }
229
+
230
+ /*
231
+ * Document-class: DNSSD::TextRecord
232
+ *
233
+ * DNSSD::TextRecord is a Hash with the ability to encode its contents into
234
+ * a binary string that can be send over the wire as using the DNSSD protocol.
235
+ *
236
+ */
237
+
238
+ void
239
+ Init_DNSSD_TextRecord(void)
240
+ {
241
+ /* hack so rdoc documents the project correctly */
242
+ #ifdef mDNSSD_RDOC_HACK
243
+ mDNSSD = rb_define_module("DNSSD");
244
+ #endif
245
+ cDNSSDTextRecord = rb_define_class_under(mDNSSD, "TextRecord", rb_cHash);
246
+
247
+ rb_define_singleton_method(cDNSSDTextRecord, "decode", dnssd_tr_decode, 1);
248
+
249
+ rb_define_method(cDNSSDTextRecord, "initialize", dnssd_tr_initialize, -1);
250
+ rb_define_method(cDNSSDTextRecord, "encode", dnssd_tr_encode, 0);
251
+ }
252
+
@@ -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,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.1
3
+ specification_version: 1
4
+ name: dnssd
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.6.0
7
+ date: 2004-10-07
8
+ summary: DNS Service Discovery (aka Rendezvous) API for Ruby
9
+ require_paths:
10
+ - lib
11
+ - ext
12
+ author: Charlie Mills
13
+ email: cmills@freeshell.org
14
+ homepage: http://dnssd.rubyforge.org
15
+ rubyforge_project:
16
+ description:
17
+ autorequire: dnssd
18
+ default_executable:
19
+ bindir: bin
20
+ has_rdoc: false
21
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
22
+ requirements:
23
+ -
24
+ - ">"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.0.0
27
+ version:
28
+ platform: ruby
29
+ files:
30
+ - lib/dnssd.rb
31
+ - ext/dns_sd.h
32
+ - ext/extconf.rb
33
+ - ext/MANIFEST
34
+ - ext/rdnssd.c
35
+ - ext/rdnssd.h
36
+ - ext/rdnssd_service.c
37
+ - ext/rdnssd_structs.c
38
+ - ext/rdnssd_tr.c
39
+ - ext/run_rdoc
40
+ test_files: []
41
+ rdoc_options: []
42
+ extra_rdoc_files: []
43
+ executables: []
44
+ extensions:
45
+ - ext/extconf.rb
46
+ requirements: []
47
+ dependencies: []