dnssd 0.7.1 → 1.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.
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
+