dnssd 0.7.1 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
Binary file
@@ -0,0 +1,5 @@
1
+ === 0.7.2 / 2009-08-05
2
+
3
+ * 1 major enhancement
4
+ * 1.9 compatibility
5
+
@@ -0,0 +1,12 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ ext/dnssd/dns_sd.h
6
+ ext/dnssd/dnssd.c
7
+ ext/dnssd/dnssd.h
8
+ ext/dnssd/dnssd_service.c
9
+ ext/dnssd/dnssd_structs.c
10
+ ext/dnssd/dnssd_tr.c
11
+ ext/dnssd/extconf.rb
12
+ lib/dnssd.rb
@@ -0,0 +1,48 @@
1
+ = dnssd
2
+
3
+ * http://rubyforge.org/projects/dnssd
4
+ * http://github.com/tenderlove/dnssd
5
+
6
+ == DESCRIPTION:
7
+
8
+ DNS Service Discovery (aka Bonjour, MDNS) API for Ruby. Implements browsing,
9
+ resolving, registration and domain enumeration.
10
+
11
+ == FEATURES/PROBLEMS:
12
+
13
+ * Too much C, too little ruby
14
+
15
+ == SYNOPSIS:
16
+
17
+ Registering a service:
18
+
19
+ registration = DNSSD.register 'my web service', '_http._tcp', nil, 80
20
+
21
+ # ...
22
+
23
+ registration.stop # unregister when we're done
24
+
25
+ Browsing services:
26
+
27
+ require 'dnssd'
28
+
29
+ DNSSD.browse '_http._tcp.' do |reply|
30
+ p reply
31
+ end
32
+
33
+ == REQUIREMENTS:
34
+
35
+ * The mdns library on OS X
36
+ * The dns-sd library on other operating systems
37
+
38
+ == INSTALL:
39
+
40
+ * FIX (sudo gem install, anything else)
41
+
42
+ == LICENSE:
43
+
44
+ Copyright (c) 2004 Chad Fowler, Charles Mills, Rich Kilmer
45
+ Copyright (c) 2009 Phil Hagelberg, Aaron Patterson, Eric Hodel
46
+
47
+ Licensed under the ruby license
48
+
@@ -0,0 +1,34 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ Hoe.plugin :minitest
7
+ Hoe.plugin :email
8
+
9
+ HOE = Hoe.spec 'dnssd' do
10
+ self.rubyforge_name = 'dnssd'
11
+
12
+ developer 'Chad Fowler', 'chad@chadfowler.com'
13
+ developer 'Charles Mills', ''
14
+ developer 'Rich Kilmer', ''
15
+ developer 'Phil Hagelberg', 'phil@hagelb.org'
16
+ developer 'Aaron Patterson', 'aaronp@rubyforge.org'
17
+ developer 'Eric Hodel', 'drbrain@segment.net'
18
+
19
+ spec_extras[:extensions] = 'ext/dnssd/extconf.rb'
20
+
21
+ clean_globs << 'lib/dnssd/*.{so,bundle,dll}'
22
+
23
+ extra_dev_deps << ['hoe-seattlerb', '~> 1.2']
24
+ extra_dev_deps << ['minitest', '~> 1.4']
25
+ extra_dev_deps << ['rake-complier', '~> 0.6']
26
+ end
27
+
28
+ require 'rake/extensiontask'
29
+
30
+ Rake::ExtensionTask.new 'dnssd', HOE.spec do |ext|
31
+ ext.lib_dir = File.join 'lib', 'dnssd'
32
+ end
33
+
34
+ # vim: syntax=Ruby
File without changes
@@ -0,0 +1,141 @@
1
+ /*
2
+ * == Authors
3
+ * Chad Fowler, Charles Mills, Rich Kilmer
4
+ *
5
+ * == Copyright
6
+ * Copyright (c) 2004 Chad Fowler, Charles Mills, Rich Kilmer
7
+ * Licensed under the same terms as Ruby.
8
+ * This software has absolutely no warranty.
9
+ */
10
+
11
+ #include "dnssd.h"
12
+ #include <assert.h>
13
+
14
+ VALUE mDNSSD;
15
+ VALUE eDNSSDError;
16
+ static VALUE eDNSSDUnknownError;
17
+
18
+ #define DNSSD_ERROR_START (-65556)
19
+ #define DNSSD_ERROR_END (-65536)
20
+
21
+ static VALUE dnssd_errors[DNSSD_ERROR_END - DNSSD_ERROR_START];
22
+
23
+ static void
24
+ dnssd_errors_store(VALUE error, int num) {
25
+ assert(DNSSD_ERROR_START <= num && num < DNSSD_ERROR_END);
26
+ dnssd_errors[num - DNSSD_ERROR_START] = error;
27
+ }
28
+
29
+ void
30
+ dnssd_check_error_code(DNSServiceErrorType e) {
31
+ int num = (int)e;
32
+ if (num) {
33
+ if(DNSSD_ERROR_START <= num && num < DNSSD_ERROR_END) {
34
+ rb_raise(dnssd_errors[num - DNSSD_ERROR_START],
35
+ "DNSSD operation failed with error code: %d", num);
36
+ } else {
37
+ rb_raise(eDNSSDUnknownError,
38
+ "DNSSD operation failed with unrecognized error code: %d", num);
39
+ }
40
+ }
41
+ }
42
+
43
+ void
44
+ dnssd_instantiation_error(const char *what) {
45
+ rb_raise(rb_eRuntimeError,
46
+ "cannot instantiate %s, use DNSSD "
47
+ "enumerate_domains(), browse(), resolve() or register() instead",
48
+ what);
49
+ }
50
+
51
+ /*
52
+ * Document-module: DNSSD
53
+ *
54
+ */
55
+
56
+ static void
57
+ Init_DNSSD(void) {
58
+ VALUE error_class;
59
+ mDNSSD = rb_define_module("DNSSD");
60
+ eDNSSDError = rb_define_class_under(mDNSSD, "Error", rb_eStandardError);
61
+
62
+ /* Specifies all interfaces. */
63
+ rb_define_const(mDNSSD, "InterfaceAny", ULONG2NUM(kDNSServiceInterfaceIndexAny));
64
+ /* Specifies local interfaces only. */
65
+ rb_define_const(mDNSSD, "InterfaceLocalOnly", ULONG2NUM(kDNSServiceInterfaceIndexLocalOnly));
66
+
67
+ /* errors - these are 2 stepped (create and store) so that rdoc is happy */
68
+ eDNSSDUnknownError = rb_define_class_under(mDNSSD, "UnknownError", eDNSSDError);
69
+ dnssd_errors_store(eDNSSDUnknownError, -65537);
70
+
71
+ error_class = rb_define_class_under(mDNSSD, "NoSuchNameError", eDNSSDError);
72
+ dnssd_errors_store(error_class, -65538);
73
+
74
+ dnssd_errors_store(rb_eNoMemError, -65539);
75
+
76
+ error_class = rb_define_class_under(mDNSSD, "BadParamError", eDNSSDError);
77
+ dnssd_errors_store(error_class, -65540);
78
+
79
+ error_class = rb_define_class_under(mDNSSD, "BadReferenceError", eDNSSDError);
80
+ dnssd_errors_store(error_class, -65541);
81
+
82
+ error_class = rb_define_class_under(mDNSSD, "BadStateError", eDNSSDError);
83
+ dnssd_errors_store(error_class, -65542);
84
+
85
+ error_class = rb_define_class_under(mDNSSD, "BadFlagsError", eDNSSDError);
86
+ dnssd_errors_store(error_class, -65543);
87
+
88
+ error_class = rb_define_class_under(mDNSSD, "UnsupportedError", eDNSSDError);
89
+ dnssd_errors_store(error_class, -65544);
90
+
91
+ error_class = rb_define_class_under(mDNSSD, "NotInitializedError", eDNSSDError);
92
+ dnssd_errors_store(error_class, -65545);
93
+
94
+ error_class = rb_define_class_under(mDNSSD, "AlreadyRegisteredError", eDNSSDError);
95
+ dnssd_errors_store(error_class, -65547);
96
+
97
+ error_class = rb_define_class_under(mDNSSD, "NameConflictError", eDNSSDError);
98
+ dnssd_errors_store(error_class, -65548);
99
+
100
+ error_class = rb_define_class_under(mDNSSD, "InvalidError", eDNSSDError);
101
+ dnssd_errors_store(error_class, -65549);
102
+
103
+ error_class = rb_define_class_under(mDNSSD, "ClientIncompatibleError", eDNSSDError);
104
+ dnssd_errors_store(error_class, -65551);
105
+
106
+ error_class = rb_define_class_under(mDNSSD, "BadInterfaceIndexError", eDNSSDError);
107
+ dnssd_errors_store(error_class, -65552);
108
+
109
+ error_class = rb_define_class_under(mDNSSD, "ReferenceUsedError", eDNSSDError);
110
+ dnssd_errors_store(error_class, -65553);
111
+
112
+ error_class = rb_define_class_under(mDNSSD, "NoSuchRecordError", eDNSSDError);
113
+ dnssd_errors_store(error_class, -65554);
114
+
115
+ error_class = rb_define_class_under(mDNSSD, "NoAuthenticationError", eDNSSDError);
116
+ dnssd_errors_store(error_class, -65555);
117
+
118
+ error_class = rb_define_class_under(mDNSSD, "NoSuchKeyError", eDNSSDError);
119
+ dnssd_errors_store(error_class, -65556);
120
+ }
121
+
122
+ /* Document-class: DNSSD::Error
123
+ *
124
+ * Base class of all DNS Service Discovery related errors.
125
+ *
126
+ */
127
+
128
+ /* defined in other .c files */
129
+ void Init_DNSSD_Service(void);
130
+ void Init_DNSSD_TextRecord(void);
131
+ void Init_DNSSD_Replies(void);
132
+
133
+ void
134
+ Init_dnssd(void) {
135
+ /* called when library is required */
136
+ Init_DNSSD();
137
+ Init_DNSSD_Service();
138
+ Init_DNSSD_TextRecord();
139
+ Init_DNSSD_Replies();
140
+ }
141
+
@@ -0,0 +1,60 @@
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
+ #ifndef RDNSSD_INCLUDED
7
+ #define RDNSSD_INCLUDED
8
+
9
+ #include <ruby.h>
10
+ #include <dns_sd.h>
11
+
12
+ /* for if_indextoname() and other unix networking functions */
13
+ #ifdef HAVE_UNISTD_H
14
+ #include <unistd.h>
15
+ #endif
16
+ #ifdef HAVE_SYS_TYPES_H
17
+ #include <sys/types.h>
18
+ #endif
19
+ #ifdef HAVE_SYS_SOCKET_H
20
+ #include <sys/socket.h>
21
+ #endif
22
+ #ifdef HAVE_SYS_PARAM_H
23
+ #include <sys/param.h>
24
+ #endif
25
+ #ifdef HAVE_NET_IF_H
26
+ #include <net/if.h>
27
+ #endif
28
+ #ifdef HAVE_SYS_IF_H
29
+ #include <sys/if.h>
30
+ #endif
31
+
32
+ extern VALUE mDNSSD;
33
+ extern VALUE eDNSSDError;
34
+
35
+ void dnssd_check_error_code(DNSServiceErrorType e);
36
+ void dnssd_instantiation_error(const char *what);
37
+
38
+ VALUE dnssd_create_fullname(const char *name, const char *regtype, const char *domain, int err_flag);
39
+ VALUE dnssd_split_fullname(VALUE fullname);
40
+
41
+ /* decodes a buffer, creating a new text record */
42
+ VALUE dnssd_tr_new(long len, const char *buf);
43
+
44
+ VALUE dnssd_tr_to_encoded_str(VALUE v);
45
+
46
+ VALUE dnssd_domain_enum_new(VALUE service, DNSServiceFlags flags,
47
+ uint32_t interface, const char *domain);
48
+
49
+ VALUE dnssd_browse_new(VALUE service, DNSServiceFlags flags, uint32_t interface,
50
+ const char *name, const char *regtype, const char *domain);
51
+
52
+ VALUE dnssd_register_new(VALUE service, DNSServiceFlags flags, const char *name,
53
+ const char *regtype, const char *domain);
54
+
55
+ VALUE dnssd_resolve_new(VALUE service, DNSServiceFlags flags, uint32_t interface,
56
+ const char *fullname, const char *host_target,
57
+ uint16_t opaqueport, uint16_t txt_len, const char *txt_rec);
58
+
59
+ #endif /* RDNSSD_INCLUDED */
60
+
@@ -0,0 +1,698 @@
1
+ /*
2
+ * Ruby Rendezvous Binding
3
+ *
4
+ * Copyright (c) 2004 Chad Fowler, Charles Mills, Rich Kilmer
5
+ * Licenced under the same terms as Ruby.
6
+ * This software has absolutely no warranty.
7
+ */
8
+
9
+ #include "dnssd.h"
10
+ #include <assert.h>
11
+
12
+ #ifndef DNSSD_API
13
+ /* define as nothing if not defined in Apple's "dns_sd.h" header */
14
+ #define DNSSD_API
15
+ #endif
16
+
17
+ static VALUE cDNSSDService;
18
+ static ID dnssd_id_call;
19
+ static ID dnssd_id_to_str;
20
+ static ID dnssd_id_to_i;
21
+ static ID dnssd_iv_block;
22
+ static ID dnssd_iv_thread;
23
+ static ID dnssd_iv_result;
24
+ static ID dnssd_iv_service;
25
+
26
+ #define IsDNSSDService(obj) (rb_obj_is_kind_of(obj,cDNSSDService)==Qtrue)
27
+ #define GetDNSSDService(obj, var) \
28
+ do { assert(IsDNSSDService(obj)); Data_Get_Struct(obj, DNSServiceRef, var); } while (0)
29
+
30
+ static DNSServiceFlags
31
+ dnssd_to_flags(VALUE obj) {
32
+ return (DNSServiceFlags)NUM2ULONG(rb_funcall(obj, dnssd_id_to_i, 0));
33
+ }
34
+
35
+ void
36
+ dnssd_callback(VALUE service, VALUE reply) {
37
+ VALUE result = rb_funcall2( rb_ivar_get(service, dnssd_iv_block),
38
+ dnssd_id_call, 1, &reply );
39
+ rb_ivar_set(service, dnssd_iv_result, result);
40
+ }
41
+
42
+ static const char *
43
+ dnssd_get_domain(VALUE service_domain) {
44
+ const char *domain = StringValueCStr(service_domain);
45
+ /* max len including the null terminator and trailing '.' */
46
+ if (RSTRING_LEN(service_domain) >= kDNSServiceMaxDomainName - 1)
47
+ rb_raise(rb_eArgError, "domain name string too large");
48
+ return domain;
49
+ }
50
+
51
+ static uint32_t
52
+ dnssd_get_interface_index(VALUE interface) {
53
+ /* if the interface is a string then convert it to the interface index */
54
+ if (rb_respond_to(interface, dnssd_id_to_str)) {
55
+ return if_nametoindex(StringValueCStr(interface));
56
+ } else {
57
+ return (uint32_t)NUM2ULONG(interface);
58
+ }
59
+ }
60
+
61
+ /*
62
+ * call-seq:
63
+ * DNSSD::Service.fullname(name, type, domain) => string
64
+ *
65
+ * Concatenate a three-part domain name (as seen in DNSSD::Reply#fullname())
66
+ * into a properly-escaped full domain name.
67
+ *
68
+ * Any dots or slashes in the _name_ must NOT be escaped.
69
+ * May be <code>nil</code> (to construct a PTR record name, e.g. "_ftp._tcp.apple.com").
70
+ *
71
+ * The _type_ is the service type followed by the protocol, separated by a dot (e.g. "_ftp._tcp").
72
+ *
73
+ * The _domain_ is the domain name, e.g. "apple.com". Any literal dots or backslashes
74
+ * must be escaped.
75
+ *
76
+ * Raises a <code>ArgumentError</code> if the full service name cannot be constructed from
77
+ * the arguments.
78
+ */
79
+
80
+ static VALUE
81
+ dnssd_service_s_fullname(VALUE klass, VALUE name, VALUE type, VALUE domain) {
82
+ return dnssd_create_fullname( StringValueCStr(name), StringValueCStr(type),
83
+ StringValueCStr(domain), 1 );
84
+ }
85
+
86
+ /*
87
+ * call-seq:
88
+ * DNSSD::Service.split(fullname) => array
89
+ * DNSSD::Service.split_fullname(fullname) => array
90
+ *
91
+ * Split a properly escaped multi-part domain name (as seen in DNSSD::Reply#fullname())
92
+ * into an array of names.
93
+ *
94
+ * DNSSD::Service.split('_http._tcp.local.') #=> ["_http.", "_tcp.", "local."]
95
+ */
96
+
97
+ static VALUE
98
+ dnssd_service_s_split(VALUE klass, VALUE fullname) {
99
+ return dnssd_split_fullname(fullname);
100
+ }
101
+
102
+ /*
103
+ * call-seq:
104
+ * DNSSD::Service.new() => raises a RuntimeError
105
+ *
106
+ * Services can only be instantiated using DNSSD.enumerate_domains(),
107
+ * DNSSD.browse(), DNSSD.register(), and DNSSD.resolve().
108
+ */
109
+
110
+ static VALUE
111
+ dnssd_service_new(int argc, VALUE *argv, VALUE klass) {
112
+ dnssd_instantiation_error(rb_class2name(klass));
113
+ return Qnil;
114
+ }
115
+
116
+ static void
117
+ dnssd_service_free_client(DNSServiceRef *client) {
118
+ DNSServiceRefDeallocate(*client);
119
+ free(client); /* free the pointer */
120
+ }
121
+
122
+ static void
123
+ dnssd_service_free(void *ptr) {
124
+ DNSServiceRef *client = (DNSServiceRef*)ptr;
125
+ if (client) {
126
+ /* client will be non-null only if client has not been deallocated */
127
+ dnssd_service_free_client(client);
128
+ }
129
+ }
130
+
131
+ static VALUE
132
+ dnssd_service_alloc(VALUE block) {
133
+ DNSServiceRef *client = ALLOC(DNSServiceRef);
134
+ VALUE service = Data_Wrap_Struct(cDNSSDService, 0, dnssd_service_free, client);
135
+ rb_ivar_set(service, dnssd_iv_block, block);
136
+ rb_ivar_set(service, dnssd_iv_thread, Qnil);
137
+ rb_ivar_set(service, dnssd_iv_result, Qnil);
138
+ return service;
139
+ }
140
+
141
+ /*
142
+ * call-seq:
143
+ * service.stopped? => true or false
144
+ *
145
+ * Returns <code>true</code> if _service_ has been stopped, <code>false</code> otherwise.
146
+ */
147
+
148
+ static VALUE
149
+ dnssd_service_is_stopped(VALUE service) {
150
+ DNSServiceRef *client = (DNSServiceRef*)RDATA(service)->data;
151
+ return client == NULL ? Qtrue : Qfalse;
152
+ }
153
+
154
+ /*
155
+ * call-seq:
156
+ * service.stop => service
157
+ *
158
+ * Stops _service_ closing the underlying socket and killing
159
+ * the underlying thread.
160
+ *
161
+ * It is good practice to all stop running services before exit.
162
+ *
163
+ * service = DNSSD.browse('_http._tcp') do |r|
164
+ * # found a service ...
165
+ * end
166
+ * sleep(2)
167
+ * service.stop
168
+ *
169
+ */
170
+
171
+ static VALUE
172
+ dnssd_service_stop(VALUE service) {
173
+ VALUE thread;
174
+ DNSServiceRef *client = (DNSServiceRef*)RDATA(service)->data;
175
+ /* set to null right away for a bit more thread safety */
176
+ RDATA(service)->data = NULL;
177
+ if (client == NULL) rb_raise(eDNSSDError, "service is already stopped");
178
+ dnssd_service_free_client(client);
179
+ thread = rb_ivar_get(service, dnssd_iv_thread);
180
+ rb_ivar_set(service, dnssd_iv_block, Qnil);
181
+ rb_ivar_set(service, dnssd_iv_thread, Qnil);
182
+
183
+ if (!NIL_P(thread)) {
184
+ /* will raise error if thread is not a Ruby Thread */
185
+ rb_thread_kill(thread);
186
+ }
187
+ return service;
188
+ }
189
+
190
+ static VALUE
191
+ dnssd_service_process(VALUE service) {
192
+ int dns_sd_fd, nfds, result;
193
+ fd_set readfds;
194
+
195
+ DNSServiceRef *client;
196
+ GetDNSSDService(service, client);
197
+
198
+ dns_sd_fd = DNSServiceRefSockFD(*client);
199
+ nfds = dns_sd_fd + 1;
200
+ for ( ;; ) {
201
+ FD_ZERO(&readfds);
202
+ FD_SET(dns_sd_fd, &readfds);
203
+ result = rb_thread_select(nfds, &readfds,
204
+ (fd_set *) NULL,
205
+ (fd_set *) NULL,
206
+ (struct timeval *) NULL);
207
+ if (result > 0) {
208
+ if ( FD_ISSET(dns_sd_fd, &readfds) ) {
209
+ DNSServiceErrorType e = DNSServiceProcessResult(*client);
210
+ dnssd_check_error_code(e);
211
+ }
212
+ } else {
213
+ break;
214
+ }
215
+ }
216
+ /* return the result from the processing */
217
+ return rb_ivar_get(service, dnssd_iv_result);
218
+ }
219
+
220
+ /* stop the service only if it is still running */
221
+
222
+ static VALUE
223
+ dnssd_service_stop2(VALUE service) {
224
+ if (dnssd_service_is_stopped(service)) {
225
+ return service;
226
+ }
227
+ return dnssd_service_stop(service);
228
+ }
229
+
230
+ static VALUE
231
+ dnssd_service_start(VALUE service) {
232
+ return rb_ensure(dnssd_service_process, service, dnssd_service_stop2, service);
233
+ }
234
+
235
+ static VALUE
236
+ dnssd_service_start_in_thread(VALUE service) {
237
+ /* race condition - service.@block could be called before the service.@thread
238
+ * is set and if the block calls service.stop() will raise an error, even though
239
+ * the service has been started and is running. */
240
+ VALUE thread = rb_thread_create(dnssd_service_process, (void *)service);
241
+ rb_ivar_set(service, dnssd_iv_thread, thread);
242
+ /* !! IMPORTANT: prevents premature garbage collection of the service,
243
+ * this way the thread holds a reference to the service and
244
+ * the service gets marked as long as the thread is running.
245
+ * Running threads are always marked by Ruby. !! */
246
+ rb_ivar_set(thread, dnssd_iv_service, service);
247
+ return service;
248
+ }
249
+
250
+ /*
251
+ * call-seq:
252
+ * service.inspect => string
253
+ *
254
+ */
255
+
256
+ static VALUE
257
+ dnssd_service_inspect(VALUE self) {
258
+ VALUE buf = rb_str_buf_new(32);
259
+ rb_str_buf_cat2(buf, "<#");
260
+ rb_str_buf_cat2(buf, rb_obj_classname(self));
261
+ if (dnssd_service_is_stopped(self)) {
262
+ rb_str_buf_cat2(buf, " (stopped)");
263
+ }
264
+ rb_str_buf_cat2(buf, ">");
265
+ return buf;
266
+ }
267
+
268
+ static void DNSSD_API
269
+ dnssd_domain_enum_reply(DNSServiceRef sdRef, DNSServiceFlags flags,
270
+ uint32_t interface_index, DNSServiceErrorType e,
271
+ const char *domain, void *context) {
272
+ VALUE service;
273
+ /* other parameters are undefined if errorCode != 0 */
274
+ dnssd_check_error_code(e);
275
+ service = (VALUE)context;
276
+ dnssd_callback(service, dnssd_domain_enum_new(service, flags, interface_index, domain));
277
+ }
278
+
279
+ static VALUE
280
+ sd_enumerate_domains(int argc, VALUE *argv, VALUE service) {
281
+ VALUE tmp_flags, interface;
282
+
283
+ DNSServiceFlags flags = 0;
284
+ uint32_t interface_index = 0;
285
+
286
+ DNSServiceErrorType e;
287
+ DNSServiceRef *client;
288
+
289
+ rb_scan_args (argc, argv, "02", &tmp_flags, &interface);
290
+
291
+ /* optional parameters */
292
+ if (!NIL_P(tmp_flags))
293
+ flags = dnssd_to_flags(tmp_flags);
294
+ if (!NIL_P(interface))
295
+ interface_index = dnssd_get_interface_index(interface);
296
+
297
+ GetDNSSDService(service, client);
298
+ e = DNSServiceEnumerateDomains (client, flags, interface_index,
299
+ dnssd_domain_enum_reply, (void *)service);
300
+ dnssd_check_error_code(e);
301
+ return service;
302
+ }
303
+
304
+ /*
305
+ * call-seq:
306
+ * DNSSD.enumerate_domains!(flags, interface) { |reply| } => obj
307
+ *
308
+ * Synchronously enumerate domains available for browsing and registration.
309
+ * For each domain found a DNSSD::Reply object is passed to block with #domain
310
+ * set to the enumerated domain.
311
+ *
312
+ * available_domains = []
313
+ * timeout(2) do
314
+ * DNSSD.enumerate_domains! do |r|
315
+ * available_domains << r.domain
316
+ * end
317
+ * rescue TimeoutError
318
+ * end
319
+ * puts available_domains.inspect
320
+ *
321
+ */
322
+
323
+ static VALUE
324
+ dnssd_enumerate_domains_bang (int argc, VALUE * argv, VALUE self) {
325
+ return dnssd_service_start(
326
+ sd_enumerate_domains(argc, argv, dnssd_service_alloc(rb_block_proc()))
327
+ );
328
+ }
329
+
330
+ /*
331
+ * call-seq:
332
+ * DNSSD.enumerate_domains(flags=0, interface=DNSSD::InterfaceAny) {|reply| bloc } => serivce_handle
333
+ *
334
+ * Asynchronously enumerate domains available for browsing and registration.
335
+ * For each domain found a DNSSD::DomainEnumReply object is passed to block.
336
+ * The returned _service_handle_ can be used to control when to
337
+ * stop enumerating domains (see DNSSD::Service#stop).
338
+ *
339
+ * available_domains = []
340
+ * s = DNSSD.enumerate_domains do |d|
341
+ * available_domains << d.domain
342
+ * end
343
+ * sleep(0.2)
344
+ * s.stop
345
+ * puts available_domains.inspect
346
+ *
347
+ */
348
+
349
+ static VALUE
350
+ dnssd_enumerate_domains(int argc, VALUE * argv, VALUE self) {
351
+ return dnssd_service_start_in_thread(
352
+ sd_enumerate_domains(argc, argv, dnssd_service_alloc(rb_block_proc()))
353
+ );
354
+ }
355
+
356
+ static void DNSSD_API
357
+ dnssd_browse_reply (DNSServiceRef client, DNSServiceFlags flags,
358
+ uint32_t interface_index, DNSServiceErrorType e,
359
+ const char *name, const char *type,
360
+ const char *domain, void *context) {
361
+ VALUE service;
362
+ /* other parameters are undefined if errorCode != 0 */
363
+ dnssd_check_error_code(e);
364
+ service = (VALUE)context;
365
+ dnssd_callback(service,
366
+ dnssd_browse_new (service, flags, interface_index, name, type, domain)
367
+ );
368
+ }
369
+
370
+ static VALUE
371
+ sd_browse(int argc, VALUE *argv, VALUE service) {
372
+ VALUE type, domain, tmp_flags, interface;
373
+
374
+ const char *type_str;
375
+ const char *domain_str = NULL;
376
+ DNSServiceFlags flags = 0;
377
+ uint32_t interface_index = 0;
378
+
379
+ DNSServiceErrorType e;
380
+ DNSServiceRef *client;
381
+
382
+ rb_scan_args (argc, argv, "13", &type,
383
+ &domain, &tmp_flags, &interface);
384
+ type_str = StringValueCStr(type);
385
+
386
+ /* optional parameters */
387
+ if (!NIL_P(domain))
388
+ domain_str = dnssd_get_domain(domain);
389
+ if (!NIL_P(tmp_flags))
390
+ flags = dnssd_to_flags(tmp_flags);
391
+ if (!NIL_P(interface))
392
+ interface_index = dnssd_get_interface_index(interface);
393
+
394
+ GetDNSSDService(service, client);
395
+ e = DNSServiceBrowse (client, flags, interface_index,
396
+ type_str, domain_str,
397
+ dnssd_browse_reply, (void *)service);
398
+ dnssd_check_error_code(e);
399
+ return service;
400
+ }
401
+
402
+ /*
403
+ * call-seq:
404
+ * DNSSD.browse!(type, domain=nil, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => obj
405
+ *
406
+ * Synchronously browse for services.
407
+ * For each service found a DNSSD::Reply object is passed to block.
408
+ *
409
+ * timeout(6) do
410
+ * DNSSD.browse!('_http._tcp') do |r|
411
+ * puts "found: #{r.inspect}"
412
+ * end
413
+ * rescue TimeoutError
414
+ * end
415
+ *
416
+ */
417
+
418
+ static VALUE
419
+ dnssd_browse_bang(int argc, VALUE * argv, VALUE self) {
420
+ return dnssd_service_start(
421
+ sd_browse(argc, argv, dnssd_service_alloc(rb_block_proc()))
422
+ );
423
+ }
424
+
425
+ /*
426
+ * call-seq:
427
+ * DNSSD.browse(type, domain=nil, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => service_handle
428
+ *
429
+ * Asynchronously browse for services.
430
+ * For each service found a DNSSD::BrowseReply object is passed to block.
431
+ * The returned _service_handle_ can be used to control when to
432
+ * stop browsing for services (see DNSSD::Service#stop).
433
+ *
434
+ * s = DNSSD.browse('_http._tcp') do |b|
435
+ * puts "found: #{b.inspect}"
436
+ * end
437
+ *
438
+ */
439
+
440
+ static VALUE
441
+ dnssd_browse(int argc, VALUE * argv, VALUE self) {
442
+ return dnssd_service_start_in_thread(
443
+ sd_browse(argc, argv, dnssd_service_alloc(rb_block_proc()))
444
+ );
445
+ }
446
+
447
+ static void DNSSD_API
448
+ dnssd_register_reply (DNSServiceRef client, DNSServiceFlags flags,
449
+ DNSServiceErrorType e,
450
+ const char *name, const char *regtype,
451
+ const char *domain, void *context) {
452
+ VALUE service;
453
+ /* other parameters are undefined if errorCode != 0 */
454
+ dnssd_check_error_code(e);
455
+ service = (VALUE)context;
456
+ dnssd_callback(service, dnssd_register_new(service, flags, name, regtype, domain));
457
+ }
458
+
459
+ static VALUE
460
+ sd_register(int argc, VALUE *argv, VALUE service) {
461
+ VALUE name, type, domain, port,
462
+ text_record, tmp_flags, interface;
463
+
464
+ const char *name_str, *type_str, *domain_str = NULL;
465
+ uint16_t opaqueport;
466
+ uint16_t txt_len = 0;
467
+ char *txt_rec = NULL;
468
+ DNSServiceFlags flags = 0;
469
+ uint32_t interface_index = 0;
470
+
471
+ DNSServiceErrorType e;
472
+ DNSServiceRef *client;
473
+
474
+ rb_scan_args (argc, argv, "43",
475
+ &name, &type, &domain, &port,
476
+ &text_record, &tmp_flags, &interface);
477
+
478
+ /* required parameters */
479
+ name_str = StringValueCStr(name);
480
+ type_str = StringValueCStr(type);
481
+
482
+ if (!NIL_P(domain))
483
+ domain_str = dnssd_get_domain(domain);
484
+ /* convert from host to net byte order */
485
+ opaqueport = htons((uint16_t)NUM2UINT(port));
486
+
487
+ /* optional parameters */
488
+ if (!NIL_P(text_record)) {
489
+ text_record = dnssd_tr_to_encoded_str(text_record);
490
+ txt_rec = RSTRING_PTR(text_record);
491
+ txt_len = RSTRING_LEN(text_record);
492
+ }
493
+ if (!NIL_P(tmp_flags))
494
+ flags = dnssd_to_flags(tmp_flags);
495
+ if(!NIL_P(interface))
496
+ interface_index = dnssd_get_interface_index(interface);
497
+
498
+ GetDNSSDService(service, client);
499
+
500
+ /* HACK */
501
+ rb_iv_set(service, "@interface", interface);
502
+ rb_iv_set(service, "@port", port);
503
+ rb_iv_set(service, "@text_record", text_record);
504
+ /********/
505
+ e = DNSServiceRegister (client, flags, interface_index,
506
+ name_str, type_str, domain_str,
507
+ NULL, opaqueport, txt_len, txt_rec,
508
+ dnssd_register_reply, (void*)service );
509
+ dnssd_check_error_code(e);
510
+ return service;
511
+ }
512
+
513
+ /*
514
+ * call-seq:
515
+ * DNSSD.register!(name, type, domain, port, text_record=nil, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => obj
516
+ *
517
+ * Synchronously register a service. A DNSSD::Reply object is passed
518
+ * to the block when the registration completes.
519
+ *
520
+ * DNSSD.register!("My Files", "_http._tcp", nil, 8080) do |r|
521
+ * warn("successfully registered: #{r.inspect}")
522
+ * end
523
+ *
524
+ */
525
+
526
+ static VALUE
527
+ dnssd_register_bang(int argc, VALUE * argv, VALUE self) {
528
+ return dnssd_service_start(
529
+ sd_register(argc, argv, dnssd_service_alloc(rb_block_proc()))
530
+ );
531
+ }
532
+
533
+ /*
534
+ * call-seq:
535
+ * DNSSD.register(name, type, domain, port, text_record=nil, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => service_handle
536
+ *
537
+ * Asynchronously register a service. A DNSSD::Reply object is
538
+ * passed to the block when the registration completes.
539
+ * The returned _service_handle_ can be used to control when to
540
+ * stop the service (see DNSSD::Service#stop).
541
+ *
542
+ * # Start a webserver and register it using DNS Service Discovery
543
+ * require 'dnssd'
544
+ * require 'webrick'
545
+ *
546
+ * web_s = WEBrick::HTTPServer.new(:Port=>8080, :DocumentRoot=>Dir::pwd)
547
+ * dns_s = DNSSD.register("My Files", "_http._tcp", nil, 8080) do |r|
548
+ * warn("successfully registered: #{r.inspect}")
549
+ * end
550
+ *
551
+ * trap("INT"){ dns_s.stop; web_s.shutdown }
552
+ * web_s.start
553
+ *
554
+ */
555
+
556
+ static VALUE
557
+ dnssd_register(int argc, VALUE * argv, VALUE self) {
558
+ return dnssd_service_start_in_thread(
559
+ sd_register(argc, argv, dnssd_service_alloc(rb_block_proc()))
560
+ );
561
+ }
562
+
563
+ static void DNSSD_API
564
+ dnssd_resolve_reply (DNSServiceRef client, DNSServiceFlags flags,
565
+ uint32_t interface_index, DNSServiceErrorType e,
566
+ const char *fullname, const char *host_target,
567
+ uint16_t opaqueport, uint16_t txt_len,
568
+ const char *txt_rec, void *context) {
569
+ VALUE service;
570
+ /* other parameters are undefined if errorCode != 0 */
571
+ dnssd_check_error_code(e);
572
+ service = (VALUE)context;
573
+ dnssd_callback(service,
574
+ dnssd_resolve_new(service, flags, interface_index, fullname,
575
+ host_target, opaqueport, txt_len, txt_rec)
576
+ );
577
+ }
578
+
579
+ static VALUE
580
+ sd_resolve(int argc, VALUE *argv, VALUE service) {
581
+ VALUE name, type, domain, tmp_flags, interface;
582
+
583
+ const char *name_str, *type_str, *domain_str;
584
+ DNSServiceFlags flags = 0;
585
+ uint32_t interface_index = 0;
586
+
587
+ DNSServiceErrorType err;
588
+ DNSServiceRef *client;
589
+
590
+ rb_scan_args(argc, argv, "32", &name, &type, &domain, &tmp_flags, &interface);
591
+
592
+ /* required parameters */
593
+ name_str = StringValueCStr(name),
594
+ type_str = StringValueCStr(type),
595
+ domain_str = dnssd_get_domain(domain);
596
+
597
+ /* optional parameters */
598
+ if (!NIL_P(tmp_flags))
599
+ flags = dnssd_to_flags(tmp_flags);
600
+ if (!NIL_P(interface))
601
+ interface_index = dnssd_get_interface_index(interface);
602
+
603
+ GetDNSSDService(service, client);
604
+ err = DNSServiceResolve (client, flags, interface_index, name_str, type_str,
605
+ domain_str, dnssd_resolve_reply, (void *)service);
606
+ dnssd_check_error_code(err);
607
+ return service;
608
+ }
609
+
610
+ /*
611
+ * call-seq:
612
+ * DNSSD.resolve!(name, type, domain, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => obj
613
+ *
614
+ * Synchronously resolve a service discovered via DNSSD.browse().
615
+ * The service is resolved to a target host name, port number, and
616
+ * text record - all contained in the DNSSD::Reply object
617
+ * passed to the required block.
618
+ *
619
+ * timeout(2) do
620
+ * DNSSD.resolve!("foo bar", "_http._tcp", "local") do |r|
621
+ * puts r.inspect
622
+ * end
623
+ * rescue TimeoutError
624
+ * end
625
+ *
626
+ */
627
+
628
+ static VALUE
629
+ dnssd_resolve_bang(int argc, VALUE * argv, VALUE self) {
630
+ return dnssd_service_start(
631
+ sd_resolve(argc, argv, dnssd_service_alloc(rb_block_proc()))
632
+ );
633
+ }
634
+
635
+ /*
636
+ * call-seq:
637
+ * DNSSD.resolve(name, type, domain, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => service_handle
638
+ *
639
+ * Asynchronously resolve a service discovered via DNSSD.browse().
640
+ * The service is resolved to a target host name, port number, and
641
+ * text record - all contained in the DNSSD::Reply object
642
+ * passed to the required block.
643
+ * The returned _service_handle_ can be used to control when to
644
+ * stop resolving the service (see DNSSD::Service#stop).
645
+ *
646
+ * s = DNSSD.resolve("foo bar", "_http._tcp", "local") do |r|
647
+ * puts r.inspect
648
+ * end
649
+ * sleep(2)
650
+ * s.stop
651
+ *
652
+ */
653
+
654
+ static VALUE
655
+ dnssd_resolve(int argc, VALUE * argv, VALUE self) {
656
+ return dnssd_service_start_in_thread(
657
+ sd_resolve(argc, argv, dnssd_service_alloc(rb_block_proc()))
658
+ );
659
+ }
660
+
661
+ void
662
+ Init_DNSSD_Service(void) {
663
+ /* hack so rdoc documents the project correctly */
664
+ #ifdef mDNSSD_RDOC_HACK
665
+ mDNSSD = rb_define_module("DNSSD");
666
+ #endif
667
+ dnssd_id_call = rb_intern("call");
668
+ dnssd_id_to_str = rb_intern("to_str");
669
+ dnssd_id_to_i = rb_intern("to_i");
670
+ dnssd_iv_block = rb_intern("@block");
671
+ dnssd_iv_thread = rb_intern("@thread");
672
+ dnssd_iv_result = rb_intern("@result");
673
+ dnssd_iv_service = rb_intern("@service");
674
+
675
+ cDNSSDService = rb_define_class_under(mDNSSD, "Service", rb_cObject);
676
+
677
+ rb_define_singleton_method(cDNSSDService, "new", dnssd_service_new, -1);
678
+ rb_define_singleton_method(cDNSSDService, "fullname", dnssd_service_s_fullname, 3);
679
+ rb_define_singleton_method(cDNSSDService, "split_fullname", dnssd_service_s_split, 1);
680
+ rb_define_singleton_method(cDNSSDService, "split", dnssd_service_s_split, 1);
681
+
682
+ /* Access the services underlying thread. Returns nil if the service is synchronous. */
683
+ rb_define_attr(cDNSSDService, "thread", 1, 0);
684
+
685
+ rb_define_method(cDNSSDService, "stop", dnssd_service_stop, 0);
686
+ rb_define_method(cDNSSDService, "stopped?", dnssd_service_is_stopped, 0);
687
+ rb_define_method(cDNSSDService, "inspect", dnssd_service_inspect, 0);
688
+
689
+ rb_define_module_function(mDNSSD, "enumerate_domains", dnssd_enumerate_domains, -1);
690
+ rb_define_module_function(mDNSSD, "enumerate_domains!", dnssd_enumerate_domains_bang, -1);
691
+ rb_define_module_function(mDNSSD, "browse", dnssd_browse, -1);
692
+ rb_define_module_function(mDNSSD, "browse!", dnssd_browse_bang, -1);
693
+ rb_define_module_function(mDNSSD, "resolve", dnssd_resolve, -1);
694
+ rb_define_module_function(mDNSSD, "resolve!", dnssd_resolve_bang, -1);
695
+ rb_define_module_function(mDNSSD, "register", dnssd_register, -1);
696
+ rb_define_module_function(mDNSSD, "register!", dnssd_register_bang, -1);
697
+ }
698
+