dnssd 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []