radar-dnssd 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,67 @@
1
+ #!/usr/local/bin/ruby
2
+ #!/usr/bin/ruby
3
+ # :nodoc: all
4
+ #
5
+ # Extension configuration script for DNS_SD C Extension.
6
+ # $Id: extconf.rb,v 1.8 2005/03/20 22:50:43 cmills Exp $
7
+ #
8
+ #
9
+
10
+ def check_for_funcs(*funcs)
11
+ funcs.flatten!
12
+ funcs.each do |f|
13
+ abort("need function #{f}") unless have_func(f)
14
+ end
15
+ end
16
+
17
+
18
+ def check_for_macros(*macros)
19
+ macros.flatten!
20
+ macros.each do |m|
21
+ abort("need macro #{m}") unless have_macro(m)
22
+ end
23
+ end
24
+
25
+ require "mkmf"
26
+
27
+ $CFLAGS << " -Wall"
28
+ $CFLAGS << " -DDEBUG" if $DEBUG
29
+
30
+ ### Print an error message and exit with an error condition
31
+ def abort( msg )
32
+ $stderr.puts( msg )
33
+ exit 1
34
+ end
35
+
36
+ unless RUBY_PLATFORM.include? "darwin"
37
+ have_library( "mdns", "DNSServiceRefSockFD" ) or
38
+ have_library( "dns_sd", "DNSServiceRefSockFD" ) or
39
+ abort( "can't find rendezvous library" )
40
+ end
41
+
42
+ #have_library( "dns-sd", "DNSServiceRefSockFD" ) or
43
+ # abort( "Can't find rendezvous client library" )
44
+
45
+ have_header("dns_sd.h") or
46
+ abort("can't find the rendezvous client headers")
47
+
48
+ have_header("unistd.h")
49
+ have_header("sys/types.h")
50
+ have_header("sys/socket.h")
51
+ have_header("sys/param.h")
52
+ have_header("sys/if.h")
53
+ have_header("net/if.h")
54
+
55
+ check_for_macros("htons", "ntohs")
56
+ check_for_funcs("if_indextoname", "if_nametoindex")
57
+ have_func("gethostname")
58
+
59
+ s1 = check_sizeof("void*")
60
+ s2 = check_sizeof("DNSServiceFlags", "dns_sd.h") or
61
+ abort("can't determine sizeof(DNSServiceFlags)")
62
+
63
+ # need to make sure storing unsigned integer in void * is OK.
64
+ s1 >= s2 or abort("sizeof(void*) < sizeof(DNSServiceFlags) please contact the authors!")
65
+
66
+ create_makefile("rdnssd")
67
+
@@ -0,0 +1,63 @@
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 <intern.h>
11
+ #include <dns_sd.h>
12
+
13
+ /* for if_indextoname() and other unix networking functions */
14
+ #ifdef HAVE_UNISTD_H
15
+ #include <unistd.h>
16
+ #endif
17
+ #ifdef HAVE_SYS_TYPES_H
18
+ #include <sys/types.h>
19
+ #endif
20
+ #ifdef HAVE_SYS_SOCKET_H
21
+ #include <sys/socket.h>
22
+ #endif
23
+ #ifdef HAVE_SYS_PARAM_H
24
+ #include <sys/param.h>
25
+ #endif
26
+ #ifdef HAVE_NET_IF_H
27
+ #include <net/if.h>
28
+ #endif
29
+ #ifdef HAVE_SYS_IF_H
30
+ #include <sys/if.h>
31
+ #endif
32
+
33
+ extern VALUE mDNSSD;
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
+ /* Get DNSServiceFlags from self */
47
+ DNSServiceFlags dnssd_to_flags(VALUE obj);
48
+
49
+ VALUE dnssd_domain_enum_new(VALUE service, DNSServiceFlags flags,
50
+ uint32_t interface, const char *domain);
51
+
52
+ VALUE dnssd_browse_new(VALUE service, DNSServiceFlags flags, uint32_t interface,
53
+ const char *name, const char *regtype, const char *domain);
54
+
55
+ VALUE dnssd_register_new(VALUE service, DNSServiceFlags flags, const char *name,
56
+ const char *regtype, const char *domain);
57
+
58
+ VALUE dnssd_resolve_new(VALUE service, DNSServiceFlags flags, uint32_t interface,
59
+ const char *fullname, const char *host_target,
60
+ uint16_t opaqueport, uint16_t txt_len, const char *txt_rec);
61
+
62
+ #endif /* RDNSSD_INCLUDED */
63
+
@@ -0,0 +1,712 @@
1
+ /*
2
+ * Ruby Rendezvous Binding
3
+ * $Id: rdnssd_service.c,v 1.24 2005/03/22 00:19:37 cmills Exp $
4
+ *
5
+ * Copyright (c) 2004 Chad Fowler, Charles Mills, Rich Kilmer
6
+ * Licenced under the same terms as Ruby.
7
+ * This software has absolutely no warranty.
8
+ */
9
+
10
+ #include "rdnssd.h"
11
+ #include <assert.h>
12
+
13
+ #ifndef DNSSD_API
14
+ /* define as nothing if not defined in Apple's "dns_sd.h" header */
15
+ #define DNSSD_API
16
+ #endif
17
+
18
+ static VALUE cDNSSDService;
19
+ static ID dnssd_id_call;
20
+ static ID dnssd_id_to_str;
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
+ void
31
+ dnssd_callback(VALUE service, VALUE reply)
32
+ {
33
+ VALUE result = rb_funcall2( rb_ivar_get(service, dnssd_iv_block),
34
+ dnssd_id_call, 1, &reply );
35
+ rb_ivar_set(service, dnssd_iv_result, result);
36
+ }
37
+
38
+ static const char *
39
+ dnssd_get_domain(VALUE service_domain)
40
+ {
41
+ const char *domain = StringValueCStr(service_domain);
42
+ /* max len including the null terminator and trailing '.' */
43
+ if (RSTRING(service_domain)->len >= kDNSServiceMaxDomainName - 1)
44
+ rb_raise(rb_eArgError, "domain name string too large");
45
+ return domain;
46
+ }
47
+
48
+ static uint32_t
49
+ dnssd_get_interface_index(VALUE interface)
50
+ {
51
+ /* if the interface is a string then convert it to the interface index */
52
+ if (rb_respond_to(interface, dnssd_id_to_str)) {
53
+ return if_nametoindex(StringValueCStr(interface));
54
+ } else {
55
+ return (uint32_t)NUM2ULONG(interface);
56
+ }
57
+ }
58
+
59
+ /*
60
+ * call-seq:
61
+ * DNSSD::Service.fullname(name, type, domain) => string
62
+ *
63
+ * Concatenate a three-part domain name (as seen in DNSSD::Reply#fullname())
64
+ * into a properly-escaped full domain name.
65
+ *
66
+ * Any dots or slashes in the _name_ must NOT be escaped.
67
+ * May be <code>nil</code> (to construct a PTR record name, e.g. "_ftp._tcp.apple.com").
68
+ *
69
+ * The _type_ is the service type followed by the protocol, separated by a dot (e.g. "_ftp._tcp").
70
+ *
71
+ * The _domain_ is the domain name, e.g. "apple.com". Any literal dots or backslashes
72
+ * must be escaped.
73
+ *
74
+ * Raises a <code>ArgumentError</code> if the full service name cannot be constructed from
75
+ * the arguments.
76
+ */
77
+ static VALUE
78
+ dnssd_service_s_fullname(VALUE klass, VALUE name, VALUE type, VALUE domain)
79
+ {
80
+ return dnssd_create_fullname( StringValueCStr(name), StringValueCStr(type),
81
+ StringValueCStr(domain), 1 );
82
+ }
83
+
84
+ /*
85
+ * call-seq:
86
+ * DNSSD::Service.split(fullname) => array
87
+ * DNSSD::Service.split_fullname(fullname) => array
88
+ *
89
+ * Split a properly escaped multi-part domain name (as seen in DNSSD::Reply#fullname())
90
+ * into an array of names.
91
+ *
92
+ * DNSSD::Service.split('_http._tcp.local.') #=> ["_http.", "_tcp.", "local."]
93
+ */
94
+ static VALUE
95
+ dnssd_service_s_split(VALUE klass, VALUE fullname)
96
+ {
97
+ return dnssd_split_fullname(fullname);
98
+ }
99
+
100
+ /*
101
+ * call-seq:
102
+ * DNSSD::Service.new() => raises a RuntimeError
103
+ *
104
+ * Services can only be instantiated using DNSSD.enumerate_domains(),
105
+ * DNSSD.browse(), DNSSD.register(), and DNSSD.resolve().
106
+ */
107
+ static VALUE
108
+ dnssd_service_new(int argc, VALUE *argv, VALUE klass)
109
+ {
110
+ dnssd_instantiation_error(rb_class2name(klass));
111
+ return Qnil;
112
+ }
113
+
114
+ static void
115
+ dnssd_service_free_client(DNSServiceRef *client)
116
+ {
117
+ DNSServiceRefDeallocate(*client);
118
+ free(client); /* free the pointer */
119
+ }
120
+
121
+ static void
122
+ dnssd_service_free(void *ptr)
123
+ {
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
+ {
134
+ DNSServiceRef *client = ALLOC(DNSServiceRef);
135
+ VALUE service = Data_Wrap_Struct(cDNSSDService, 0, dnssd_service_free, client);
136
+ rb_ivar_set(service, dnssd_iv_block, block);
137
+ rb_ivar_set(service, dnssd_iv_thread, Qnil);
138
+ rb_ivar_set(service, dnssd_iv_result, Qnil);
139
+ return service;
140
+ }
141
+
142
+ /*
143
+ * call-seq:
144
+ * service.stopped? => true or false
145
+ *
146
+ * Returns <code>true</code> if _service_ has been stopped, <code>false</code> otherwise.
147
+ */
148
+ static VALUE
149
+ dnssd_service_is_stopped(VALUE service)
150
+ {
151
+ DNSServiceRef *client = (DNSServiceRef*)RDATA(service)->data;
152
+ return client == NULL ? Qtrue : Qfalse;
153
+ }
154
+
155
+ /*
156
+ * call-seq:
157
+ * service.stop => service
158
+ *
159
+ * Stops _service_ closing the underlying socket and killing
160
+ * the underlying thread.
161
+ *
162
+ * It is good practice to all stop running services before exit.
163
+ *
164
+ * service = DNSSD.browse('_http._tcp') do |r|
165
+ * # found a service ...
166
+ * end
167
+ * sleep(2)
168
+ * service.stop
169
+ *
170
+ */
171
+ static VALUE
172
+ dnssd_service_stop(VALUE service)
173
+ {
174
+ VALUE thread;
175
+ DNSServiceRef *client = (DNSServiceRef*)RDATA(service)->data;
176
+ /* set to null right away for a bit more thread safety */
177
+ RDATA(service)->data = NULL;
178
+ if (client == NULL) rb_raise(rb_eRuntimeError, "service is already stopped");
179
+ dnssd_service_free_client(client);
180
+ thread = rb_ivar_get(service, dnssd_iv_thread);
181
+ rb_ivar_set(service, dnssd_iv_block, Qnil);
182
+ rb_ivar_set(service, dnssd_iv_thread, Qnil);
183
+
184
+ if (!NIL_P(thread)) {
185
+ /* will raise error if thread is not a Ruby Thread */
186
+ rb_thread_kill(thread);
187
+ }
188
+ return service;
189
+ }
190
+
191
+ static VALUE
192
+ dnssd_service_process(VALUE service)
193
+ {
194
+ int dns_sd_fd, nfds, result;
195
+ fd_set readfds;
196
+
197
+ DNSServiceRef *client;
198
+ GetDNSSDService(service, client);
199
+
200
+ dns_sd_fd = DNSServiceRefSockFD(*client);
201
+ nfds = dns_sd_fd + 1;
202
+ for ( ;; ) {
203
+ FD_ZERO(&readfds);
204
+ FD_SET(dns_sd_fd, &readfds);
205
+ result = rb_thread_select(nfds, &readfds,
206
+ (fd_set *) NULL,
207
+ (fd_set *) NULL,
208
+ (struct timeval *) NULL);
209
+ if (result > 0) {
210
+ if ( FD_ISSET(dns_sd_fd, &readfds) ) {
211
+ DNSServiceErrorType e = DNSServiceProcessResult(*client);
212
+ dnssd_check_error_code(e);
213
+ }
214
+ } else {
215
+ break;
216
+ }
217
+ }
218
+ /* return the result from the processing */
219
+ return rb_ivar_get(service, dnssd_iv_result);
220
+ }
221
+
222
+ /* stop the service only if it is still running */
223
+ static VALUE
224
+ dnssd_service_stop2(VALUE service)
225
+ {
226
+ if (dnssd_service_is_stopped(service)) {
227
+ return service;
228
+ }
229
+ return dnssd_service_stop(service);
230
+ }
231
+
232
+ static VALUE
233
+ dnssd_service_start(VALUE service)
234
+ {
235
+ return rb_ensure(dnssd_service_process, service, dnssd_service_stop2, service);
236
+ }
237
+
238
+ static VALUE
239
+ dnssd_service_start_in_thread(VALUE service)
240
+ {
241
+ /* race condition - service.@block could be called before the service.@thread
242
+ * is set and if the block calls service.stop() will raise an error, even though
243
+ * the service has been started and is running. */
244
+ VALUE thread = rb_thread_create(dnssd_service_process, (void *)service);
245
+ rb_ivar_set(service, dnssd_iv_thread, thread);
246
+ /* !! IMPORTANT: prevents premature garbage collection of the service,
247
+ * this way the thread holds a reference to the service and
248
+ * the service gets marked as long as the thread is running.
249
+ * Running threads are always marked by Ruby. !! */
250
+ rb_ivar_set(thread, dnssd_iv_service, service);
251
+ return service;
252
+ }
253
+
254
+ /*
255
+ * call-seq:
256
+ * service.inspect => string
257
+ *
258
+ */
259
+ static VALUE
260
+ dnssd_service_inspect(VALUE self)
261
+ {
262
+ VALUE buf = rb_str_buf_new(32);
263
+ rb_str_buf_cat2(buf, "<#");
264
+ rb_str_buf_cat2(buf, rb_obj_classname(self));
265
+ if (dnssd_service_is_stopped(self)) {
266
+ rb_str_buf_cat2(buf, " (stopped)");
267
+ }
268
+ rb_str_buf_cat2(buf, ">");
269
+ return buf;
270
+ }
271
+
272
+ static void DNSSD_API
273
+ dnssd_domain_enum_reply(DNSServiceRef sdRef, DNSServiceFlags flags,
274
+ uint32_t interface_index, DNSServiceErrorType e,
275
+ const char *domain, void *context)
276
+ {
277
+ VALUE service;
278
+ /* other parameters are undefined if errorCode != 0 */
279
+ dnssd_check_error_code(e);
280
+ service = (VALUE)context;
281
+ dnssd_callback(service, dnssd_domain_enum_new(service, flags, interface_index, domain));
282
+ }
283
+
284
+ static VALUE
285
+ sd_enumerate_domains(int argc, VALUE *argv, VALUE service)
286
+ {
287
+ VALUE tmp_flags, interface;
288
+
289
+ DNSServiceFlags flags = 0;
290
+ uint32_t interface_index = 0;
291
+
292
+ DNSServiceErrorType e;
293
+ DNSServiceRef *client;
294
+
295
+ rb_scan_args (argc, argv, "02", &tmp_flags, &interface);
296
+
297
+ /* optional parameters */
298
+ if (!NIL_P(tmp_flags))
299
+ flags = dnssd_to_flags(tmp_flags);
300
+ if (!NIL_P(interface))
301
+ interface_index = dnssd_get_interface_index(interface);
302
+
303
+ GetDNSSDService(service, client);
304
+ e = DNSServiceEnumerateDomains (client, flags, interface_index,
305
+ dnssd_domain_enum_reply, (void *)service);
306
+ dnssd_check_error_code(e);
307
+ return service;
308
+ }
309
+
310
+ /*
311
+ * call-seq:
312
+ * DNSSD.enumerate_domains!(flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => obj
313
+ *
314
+ * Synchronously enumerate domains available for browsing and registration.
315
+ * For each domain found a DNSSD::Reply object is passed to block with #domain
316
+ * set to the enumerated domain.
317
+ *
318
+ * available_domains = []
319
+ * timeout(2) do
320
+ * DNSSD.enumerate_domains! do |r|
321
+ * available_domains << r.domain
322
+ * end
323
+ * rescue TimeoutError
324
+ * end
325
+ * puts available_domains.inspect
326
+ *
327
+ */
328
+
329
+ static VALUE
330
+ dnssd_enumerate_domains_bang (int argc, VALUE * argv, VALUE self)
331
+ {
332
+ return dnssd_service_start(
333
+ sd_enumerate_domains(argc, argv, dnssd_service_alloc(rb_block_proc()))
334
+ );
335
+ }
336
+ /*
337
+ * call-seq:
338
+ * DNSSD.enumerate_domains(flags=0, interface=DNSSD::InterfaceAny) {|reply| bloc } => serivce_handle
339
+ *
340
+ * Asynchronously enumerate domains available for browsing and registration.
341
+ * For each domain found a DNSSD::DomainEnumReply object is passed to block.
342
+ * The returned _service_handle_ can be used to control when to
343
+ * stop enumerating domains (see DNSSD::Service#stop).
344
+ *
345
+ * available_domains = []
346
+ * s = DNSSD.enumerate_domains do |d|
347
+ * available_domains << d.domain
348
+ * end
349
+ * sleep(0.2)
350
+ * s.stop
351
+ * puts available_domains.inspect
352
+ *
353
+ */
354
+
355
+ static VALUE
356
+ dnssd_enumerate_domains(int argc, VALUE * argv, VALUE self)
357
+ {
358
+ return dnssd_service_start_in_thread(
359
+ sd_enumerate_domains(argc, argv, dnssd_service_alloc(rb_block_proc()))
360
+ );
361
+ }
362
+
363
+ static void DNSSD_API
364
+ dnssd_browse_reply (DNSServiceRef client, DNSServiceFlags flags,
365
+ uint32_t interface_index, DNSServiceErrorType e,
366
+ const char *name, const char *type,
367
+ const char *domain, void *context)
368
+ {
369
+ VALUE service;
370
+ /* other parameters are undefined if errorCode != 0 */
371
+ dnssd_check_error_code(e);
372
+ service = (VALUE)context;
373
+ dnssd_callback(service,
374
+ dnssd_browse_new (service, flags, interface_index, name, type, domain)
375
+ );
376
+ }
377
+
378
+ static VALUE
379
+ sd_browse(int argc, VALUE *argv, VALUE service)
380
+ {
381
+ VALUE type, domain, tmp_flags, interface;
382
+
383
+ const char *type_str;
384
+ const char *domain_str = NULL;
385
+ DNSServiceFlags flags = 0;
386
+ uint32_t interface_index = 0;
387
+
388
+ DNSServiceErrorType e;
389
+ DNSServiceRef *client;
390
+
391
+ rb_scan_args (argc, argv, "13", &type,
392
+ &domain, &tmp_flags, &interface);
393
+ type_str = StringValueCStr(type);
394
+
395
+ /* optional parameters */
396
+ if (!NIL_P(domain))
397
+ domain_str = dnssd_get_domain(domain);
398
+ if (!NIL_P(tmp_flags))
399
+ flags = dnssd_to_flags(tmp_flags);
400
+ if (!NIL_P(interface))
401
+ interface_index = dnssd_get_interface_index(interface);
402
+
403
+ GetDNSSDService(service, client);
404
+ e = DNSServiceBrowse (client, flags, interface_index,
405
+ type_str, domain_str,
406
+ dnssd_browse_reply, (void *)service);
407
+ dnssd_check_error_code(e);
408
+ return service;
409
+ }
410
+
411
+ /*
412
+ * call-seq:
413
+ * DNSSD.browse!(type, domain=nil, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => obj
414
+ *
415
+ * Synchronously browse for services.
416
+ * For each service found a DNSSD::Reply object is passed to block.
417
+ *
418
+ * timeout(6) do
419
+ * DNSSD.browse!('_http._tcp') do |r|
420
+ * puts "found: #{r.inspect}"
421
+ * end
422
+ * rescue TimeoutError
423
+ * end
424
+ *
425
+ */
426
+ static VALUE
427
+ dnssd_browse_bang(int argc, VALUE * argv, VALUE self)
428
+ {
429
+ return dnssd_service_start(
430
+ sd_browse(argc, argv, dnssd_service_alloc(rb_block_proc()))
431
+ );
432
+ }
433
+
434
+
435
+ /*
436
+ * call-seq:
437
+ * DNSSD.browse(type, domain=nil, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => service_handle
438
+ *
439
+ * Asynchronously browse for services.
440
+ * For each service found a DNSSD::BrowseReply object is passed to block.
441
+ * The returned _service_handle_ can be used to control when to
442
+ * stop browsing for services (see DNSSD::Service#stop).
443
+ *
444
+ * s = DNSSD.browse('_http._tcp') do |b|
445
+ * puts "found: #{b.inspect}"
446
+ * end
447
+ *
448
+ */
449
+ static VALUE
450
+ dnssd_browse(int argc, VALUE * argv, VALUE self)
451
+ {
452
+ return dnssd_service_start_in_thread(
453
+ sd_browse(argc, argv, dnssd_service_alloc(rb_block_proc()))
454
+ );
455
+ }
456
+
457
+ static void DNSSD_API
458
+ dnssd_register_reply (DNSServiceRef client, DNSServiceFlags flags,
459
+ DNSServiceErrorType e,
460
+ const char *name, const char *regtype,
461
+ const char *domain, void *context)
462
+ {
463
+ VALUE service;
464
+ /* other parameters are undefined if errorCode != 0 */
465
+ dnssd_check_error_code(e);
466
+ service = (VALUE)context;
467
+ dnssd_callback(service, dnssd_register_new(service, flags, name, regtype, domain));
468
+ }
469
+
470
+ static VALUE
471
+ sd_register(int argc, VALUE *argv, VALUE service)
472
+ {
473
+ VALUE name, type, domain, port,
474
+ text_record, tmp_flags, interface;
475
+
476
+ const char *name_str, *type_str, *domain_str = NULL;
477
+ uint16_t opaqueport;
478
+ uint16_t txt_len = 0;
479
+ char *txt_rec = NULL;
480
+ DNSServiceFlags flags = 0;
481
+ uint32_t interface_index = 0;
482
+
483
+ DNSServiceErrorType e;
484
+ DNSServiceRef *client;
485
+
486
+ rb_scan_args (argc, argv, "43",
487
+ &name, &type, &domain, &port,
488
+ &text_record, &tmp_flags, &interface);
489
+
490
+ /* required parameters */
491
+ name_str = StringValueCStr(name);
492
+ type_str = StringValueCStr(type);
493
+
494
+ if (!NIL_P(domain))
495
+ domain_str = dnssd_get_domain(domain);
496
+ /* convert from host to net byte order */
497
+ opaqueport = htons((uint16_t)NUM2UINT(port));
498
+
499
+ /* optional parameters */
500
+ if (!NIL_P(text_record)) {
501
+ text_record = dnssd_tr_to_encoded_str(text_record);
502
+ txt_rec = RSTRING(text_record)->ptr;
503
+ txt_len = RSTRING(text_record)->len;
504
+ }
505
+ if (!NIL_P(tmp_flags))
506
+ flags = dnssd_to_flags(tmp_flags);
507
+ if(!NIL_P(interface))
508
+ interface_index = dnssd_get_interface_index(interface);
509
+
510
+ GetDNSSDService(service, client);
511
+
512
+ /* HACK */
513
+ rb_iv_set(service, "@interface", interface);
514
+ rb_iv_set(service, "@port", port);
515
+ rb_iv_set(service, "@text_record", text_record);
516
+ /********/
517
+ e = DNSServiceRegister (client, flags, interface_index,
518
+ name_str, type_str, domain_str,
519
+ NULL, opaqueport, txt_len, txt_rec,
520
+ dnssd_register_reply, (void*)service );
521
+ dnssd_check_error_code(e);
522
+ return service;
523
+ }
524
+
525
+ /*
526
+ * call-seq:
527
+ * DNSSD.register!(name, type, domain, port, text_record=nil, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => obj
528
+ *
529
+ * Synchronously register a service. A DNSSD::Reply object is passed
530
+ * to the block when the registration completes.
531
+ *
532
+ * DNSSD.register!("My Files", "_http._tcp", nil, 8080) do |r|
533
+ * warn("successfully registered: #{r.inspect}")
534
+ * end
535
+ *
536
+ */
537
+ static VALUE
538
+ dnssd_register_bang(int argc, VALUE * argv, VALUE self)
539
+ {
540
+ return dnssd_service_start(
541
+ sd_register(argc, argv, dnssd_service_alloc(rb_block_proc()))
542
+ );
543
+ }
544
+
545
+ /*
546
+ * call-seq:
547
+ * DNSSD.register(name, type, domain, port, text_record=nil, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => service_handle
548
+ *
549
+ * Asynchronously register a service. A DNSSD::Reply object is
550
+ * passed to the block when the registration completes.
551
+ * The returned _service_handle_ can be used to control when to
552
+ * stop the service (see DNSSD::Service#stop).
553
+ *
554
+ * # Start a webserver and register it using DNS Service Discovery
555
+ * require 'dnssd'
556
+ * require 'webrick'
557
+ *
558
+ * web_s = WEBrick::HTTPServer.new(:Port=>8080, :DocumentRoot=>Dir::pwd)
559
+ * dns_s = DNSSD.register("My Files", "_http._tcp", nil, 8080) do |r|
560
+ * warn("successfully registered: #{r.inspect}")
561
+ * end
562
+ *
563
+ * trap("INT"){ dns_s.stop; web_s.shutdown }
564
+ * web_s.start
565
+ *
566
+ */
567
+ static VALUE
568
+ dnssd_register(int argc, VALUE * argv, VALUE self)
569
+ {
570
+ return dnssd_service_start_in_thread(
571
+ sd_register(argc, argv, dnssd_service_alloc(rb_block_proc()))
572
+ );
573
+ }
574
+
575
+ static void DNSSD_API
576
+ dnssd_resolve_reply (DNSServiceRef client, DNSServiceFlags flags,
577
+ uint32_t interface_index, DNSServiceErrorType e,
578
+ const char *fullname, const char *host_target,
579
+ uint16_t opaqueport, uint16_t txt_len,
580
+ const char *txt_rec, void *context)
581
+ {
582
+ VALUE service;
583
+ /* other parameters are undefined if errorCode != 0 */
584
+ dnssd_check_error_code(e);
585
+ service = (VALUE)context;
586
+ dnssd_callback(service,
587
+ dnssd_resolve_new(service, flags, interface_index, fullname,
588
+ host_target, opaqueport, txt_len, txt_rec)
589
+ );
590
+ }
591
+
592
+ static VALUE
593
+ sd_resolve(int argc, VALUE *argv, VALUE service)
594
+ {
595
+ VALUE name, type, domain, tmp_flags, interface;
596
+
597
+ const char *name_str, *type_str, *domain_str;
598
+ DNSServiceFlags flags = 0;
599
+ uint32_t interface_index = 0;
600
+
601
+ DNSServiceErrorType err;
602
+ DNSServiceRef *client;
603
+
604
+ rb_scan_args(argc, argv, "32", &name, &type, &domain, &tmp_flags, &interface);
605
+
606
+ /* required parameters */
607
+ name_str = StringValueCStr(name),
608
+ type_str = StringValueCStr(type),
609
+ domain_str = dnssd_get_domain(domain);
610
+
611
+ /* optional parameters */
612
+ if (!NIL_P(tmp_flags))
613
+ flags = dnssd_to_flags(tmp_flags);
614
+ if (!NIL_P(interface))
615
+ interface_index = dnssd_get_interface_index(interface);
616
+
617
+ GetDNSSDService(service, client);
618
+ err = DNSServiceResolve (client, flags, interface_index, name_str, type_str,
619
+ domain_str, dnssd_resolve_reply, (void *)service);
620
+ dnssd_check_error_code(err);
621
+ return service;
622
+ }
623
+
624
+ /*
625
+ * call-seq:
626
+ * DNSSD.resolve!(name, type, domain, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => obj
627
+ *
628
+ * Synchronously resolve a service discovered via DNSSD.browse().
629
+ * The service is resolved to a target host name, port number, and
630
+ * text record - all contained in the DNSSD::Reply object
631
+ * passed to the required block.
632
+ *
633
+ * timeout(2) do
634
+ * DNSSD.resolve!("foo bar", "_http._tcp", "local") do |r|
635
+ * puts r.inspect
636
+ * end
637
+ * rescue TimeoutError
638
+ * end
639
+ *
640
+ */
641
+ static VALUE
642
+ dnssd_resolve_bang(int argc, VALUE * argv, VALUE self)
643
+ {
644
+ return dnssd_service_start(
645
+ sd_resolve(argc, argv, dnssd_service_alloc(rb_block_proc()))
646
+ );
647
+ }
648
+
649
+ /*
650
+ * call-seq:
651
+ * DNSSD.resolve(name, type, domain, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => service_handle
652
+ *
653
+ * Asynchronously resolve a service discovered via DNSSD.browse().
654
+ * The service is resolved to a target host name, port number, and
655
+ * text record - all contained in the DNSSD::Reply object
656
+ * passed to the required block.
657
+ * The returned _service_handle_ can be used to control when to
658
+ * stop resolving the service (see DNSSD::Service#stop).
659
+ *
660
+ * s = DNSSD.resolve("foo bar", "_http._tcp", "local") do |r|
661
+ * puts r.inspect
662
+ * end
663
+ * sleep(2)
664
+ * s.stop
665
+ *
666
+ */
667
+ static VALUE
668
+ dnssd_resolve(int argc, VALUE * argv, VALUE self)
669
+ {
670
+ return dnssd_service_start_in_thread(
671
+ sd_resolve(argc, argv, dnssd_service_alloc(rb_block_proc()))
672
+ );
673
+ }
674
+
675
+ void
676
+ Init_DNSSD_Service(void)
677
+ {
678
+ /* hack so rdoc documents the project correctly */
679
+ #ifdef mDNSSD_RDOC_HACK
680
+ mDNSSD = rb_define_module("DNSSD");
681
+ #endif
682
+ dnssd_id_call = rb_intern("call");
683
+ dnssd_id_to_str = rb_intern("to_str");
684
+ dnssd_iv_block = rb_intern("@block");
685
+ dnssd_iv_thread = rb_intern("@thread");
686
+ dnssd_iv_result = rb_intern("@result");
687
+ dnssd_iv_service = rb_intern("@service");
688
+
689
+ cDNSSDService = rb_define_class_under(mDNSSD, "Service", rb_cObject);
690
+
691
+ rb_define_singleton_method(cDNSSDService, "new", dnssd_service_new, -1);
692
+ rb_define_singleton_method(cDNSSDService, "fullname", dnssd_service_s_fullname, 3);
693
+ rb_define_singleton_method(cDNSSDService, "split_fullname", dnssd_service_s_split, 1);
694
+ rb_define_singleton_method(cDNSSDService, "split", dnssd_service_s_split, 1);
695
+
696
+ /* Access the services underlying thread. Returns nil if the service is synchronous. */
697
+ rb_define_attr(cDNSSDService, "thread", 1, 0);
698
+
699
+ rb_define_method(cDNSSDService, "stop", dnssd_service_stop, 0);
700
+ rb_define_method(cDNSSDService, "stopped?", dnssd_service_is_stopped, 0);
701
+ rb_define_method(cDNSSDService, "inspect", dnssd_service_inspect, 0);
702
+
703
+ rb_define_module_function(mDNSSD, "enumerate_domains", dnssd_enumerate_domains, -1);
704
+ rb_define_module_function(mDNSSD, "enumerate_domains!", dnssd_enumerate_domains_bang, -1);
705
+ rb_define_module_function(mDNSSD, "browse", dnssd_browse, -1);
706
+ rb_define_module_function(mDNSSD, "browse!", dnssd_browse_bang, -1);
707
+ rb_define_module_function(mDNSSD, "resolve", dnssd_resolve, -1);
708
+ rb_define_module_function(mDNSSD, "resolve!", dnssd_resolve_bang, -1);
709
+ rb_define_module_function(mDNSSD, "register", dnssd_register, -1);
710
+ rb_define_module_function(mDNSSD, "register!", dnssd_register_bang, -1);
711
+ }
712
+