From sroberts at uniserve.com Wed Mar 2 21:40:01 2005 From: sroberts at uniserve.com (Sam Roberts) Date: Wed Mar 2 21:36:26 2005 Subject: [dnssd-developers] BrowseReply doesn't allow you to see service shutdown? Message-ID: <20050303024000.GA4884@ensemble.local> I'm writing a clone of apple's dns-sd cmdline tool, building APIs that look like the DNSSD:: ones on top of my net/dns/mdns.rb. dns-sd -B allows you to see startup and shutdown of services, based on the DNS ttl being zero, I assume. BrowseReply doesn't have a TTL attribute, or a added/removed attribute. Oversight? Sam From sroberts at uniserve.com Wed Mar 2 22:10:33 2005 From: sroberts at uniserve.com (Sam Roberts) Date: Wed Mar 2 22:06:59 2005 Subject: [dnssd-developers] ResolveReply only gives the fullname? Message-ID: <20050303031033.GA4927@ensemble.local> This seems odd, why not give name, type, domain like BrowseReply? Particularly since there is no API to parse it, and you can't just split at the dots... they could be escaped. Sam From cmills at freeshell.org Thu Mar 3 00:23:04 2005 From: cmills at freeshell.org (Charles Mills) Date: Thu Mar 3 00:19:10 2005 Subject: [dnssd-developers] BrowseReply doesn't allow you to see service shutdown? In-Reply-To: <20050303024000.GA4884@ensemble.local> References: <20050303024000.GA4884@ensemble.local> Message-ID: <2dfa91d22ac7df692be44b9627174b17@freeshell.org> On Mar 2, 2005, at 6:40 PM, Sam Roberts wrote: > > I'm writing a clone of apple's dns-sd cmdline tool, building APIs that > look like the DNSSD:: ones on top of my net/dns/mdns.rb. dns-sd -B > allows you to see startup and shutdown of services, based on the DNS > ttl being zero, I assume. > > BrowseReply doesn't have a TTL attribute, or a added/removed attribute. > There is an add flag. If the add flag is not set then it indicates remove. See DNSSD::Flags. Not sure about the TTL though. When you start a service using the DNSSD api you get a service handle with which you can call #stop to stop the service. -Charlie From cmills at freeshell.org Thu Mar 3 00:28:43 2005 From: cmills at freeshell.org (Charles Mills) Date: Thu Mar 3 00:24:40 2005 Subject: [dnssd-developers] ResolveReply only gives the fullname? In-Reply-To: <20050303031033.GA4927@ensemble.local> References: <20050303031033.GA4927@ensemble.local> Message-ID: <79858689de01068fbd96fb348ab4694f@freeshell.org> On Mar 2, 2005, at 7:10 PM, Sam Roberts wrote: > > This seems odd, why not give name, type, domain like BrowseReply? > > Particularly since there is no API to parse it, and you can't just > split > at the dots... they could be escaped. > Yeah I agree this is weird, but it is what the C api does... :/ It certainly wouldn't be out of the question (or that difficult) for DNSSD to split up the fullname. > "a.b.c".scan(/(?:\\.|[^\.])+/) => ["a", "b", "c"] > 'a\.b.c'.scan(/(?:\\.|[^\.])+/) => ["a\\.b", "c"] -Charlie From sroberts at uniserve.com Thu Mar 3 10:08:13 2005 From: sroberts at uniserve.com (Sam Roberts) Date: Thu Mar 3 10:04:37 2005 Subject: [dnssd-developers] BrowseReply doesn't allow you to see service shutdown? In-Reply-To: <2dfa91d22ac7df692be44b9627174b17@freeshell.org> References: <20050303024000.GA4884@ensemble.local> <2dfa91d22ac7df692be44b9627174b17@freeshell.org> Message-ID: <20050303150813.GA5061@ensemble.local> Quoting cmills@freeshell.org, on Wed, Mar 02, 2005 at 09:23:04PM -0800: > On Mar 2, 2005, at 6:40 PM, Sam Roberts wrote: > > > > >I'm writing a clone of apple's dns-sd cmdline tool, building APIs that > >look like the DNSSD:: ones on top of my net/dns/mdns.rb. dns-sd -B > >allows you to see startup and shutdown of services, based on the DNS > >ttl being zero, I assume. > > > >BrowseReply doesn't have a TTL attribute, or a added/removed attribute. > > > There is an add flag. If the add flag is not set then it indicates > remove. See DNSSD::Flags. Ok, I searched harder, and found the flags in the Reply class, I hadn't seen it. Thanks, Sam From sroberts at uniserve.com Wed Mar 9 20:14:00 2005 From: sroberts at uniserve.com (Sam Roberts) Date: Wed Mar 9 20:10:14 2005 Subject: [dnssd-developers] I can't resolve services after "browsing" them Message-ID: <20050310011359.GA3530@ensemble.local> I can resolve a _presence: $ ruby18 other/cvs-dnssd/test/test_browse.rb Press to start (and I hack test_resolve.rb: $ cvs diff .... -rservice = DNSSD.resolve("foo bar", "_http._tcp", "local") do |resolve_reply| +rservice = DNSSD.resolve("sam@ensemble", "_presence._tcp", "local") do |resolve_reply| ^^^^^^^^^^^^ But resolve returns nothing: $ ruby18 other/cvs-dnssd/test/test_resolve.rb Press to start (and References: <20050310011359.GA3530@ensemble.local> Message-ID: <20050310013235.GA3615@ensemble.local> More info, it may be related to the callback not being called. I can register services, but register never yields a reply for me, though it does for test_register.rb, apparently. I'm somewhat mystified. Sam From sroberts at uniserve.com Wed Mar 9 20:56:52 2005 From: sroberts at uniserve.com (Sam Roberts) Date: Wed Mar 9 20:53:02 2005 Subject: [dnssd-developers] cmd line mdns tool, a question about TextRecord, and DNSServiceQueryRecord Message-ID: <20050310015652.GB3615@ensemble.local> Below is a cmd-line mdns tool I use for testing net-mdns. If called with the -n option it will attempt to use "native" DNSSD, the dns_sd.h bindings. This makes it a decent example utility for DNSSD, too, except that it doesn't work. Specifically, browse works, resolve fails, and register kindof does - the service is registered but never calls the callback to indicate it worked. -> Am I doing something obviously wrong? Note that I call #register with a Hash, not a TextRecord. Its not clear from the API docs whether this is legal, and the presence of an #encode method in TextRecord made me think it wouldn't work, but it appears to. -> Can text_record be a Hash as input to #register? I would like to implement resolver lookup using DNSSD, but examining dns_sd.h, I think I would need bindings to DNSServiceQueryRecord(), so that I can query for an A record. I don't have the time for that now, though it sounds interesting and not to hard. If anybody wants to do it, though, I could bind DNSSD into the resolv.rb ruby resolver framework. Cheers, Sam From sroberts at uniserve.com Wed Mar 9 21:09:54 2005 From: sroberts at uniserve.com (Sam Roberts) Date: Wed Mar 9 21:06:04 2005 Subject: [dnssd-developers] net-mdns 0.1 coming up, can I make a new package on the downloads page? Message-ID: <20050310020954.GC3615@ensemble.local> I've done a total rewrite of the 0.0 prototype, and I'm working through my todo list for 0.1, releasing this weekend if I'm lucky. Current packages are: Apple's mDNSResponder Sources mDNSResponder.tar.gz dnssd-0_6_0 dnssd-0.6.0.gem dnssd-0_6_0.tar.gz dnssd-0_5_0 dnssd-0_5_0.tar.gz I'd like to add: net-mdns (pure-ruby mDNS support) net-mdns-0.1.tgz ... Is that OK? And maybe I can put the docs at: http://dnssd.rubyforge.org/doc-mdns/... Is that OK? Cheers, Sam From chad at chadfowler.com Wed Mar 9 22:52:52 2005 From: chad at chadfowler.com (chad@chadfowler.com) Date: Wed Mar 9 21:42:06 2005 Subject: [dnssd-developers] net-mdns 0.1 coming up, can I make a new package on the downloads page? In-Reply-To: <20050310020954.GC3615@ensemble.local> References: <20050310020954.GC3615@ensemble.local> Message-ID: <1728.12.223.95.153.1110426772.squirrel@12.223.95.153> Sure. Good idea. I assume "..." means you'll have more than just the tgz? > I've done a total rewrite of the 0.0 prototype, and I'm working through > my todo list for 0.1, releasing this weekend if I'm lucky. > > Current packages are: > > Apple's mDNSResponder Sources > mDNSResponder.tar.gz > dnssd-0_6_0 > dnssd-0.6.0.gem > dnssd-0_6_0.tar.gz > dnssd-0_5_0 > dnssd-0_5_0.tar.gz > > I'd like to add: > > net-mdns (pure-ruby mDNS support) > net-mdns-0.1.tgz > ... > > > Is that OK? > > And maybe I can put the docs at: > > http://dnssd.rubyforge.org/doc-mdns/... > > Is that OK? > > Cheers, > Sam > > _______________________________________________ > dnssd-developers mailing list > dnssd-developers@rubyforge.org > http://rubyforge.org/mailman/listinfo/dnssd-developers > From sroberts at uniserve.com Wed Mar 9 23:57:43 2005 From: sroberts at uniserve.com (Sam Roberts) Date: Thu Mar 10 08:28:44 2005 Subject: [dnssd-developers] net-mdns 0.1 coming up, can I make a new package on the downloads page? In-Reply-To: <1728.12.223.95.153.1110426772.squirrel@12.223.95.153> References: <20050310020954.GC3615@ensemble.local> <1728.12.223.95.153.1110426772.squirrel@12.223.95.153> Message-ID: <20050310045743.GA3881@ensemble.local> Quoting chad@chadfowler.com, on Wed, Mar 09, 2005 at 10:52:52PM -0500: > Sure. Good idea. I assume "..." means you'll have more than just the tgz? Oops, no, I meant more than one release. Why do you have a package per release, is that some kid of gems requirement? > > I've done a total rewrite of the 0.0 prototype, and I'm working through > > my todo list for 0.1, releasing this weekend if I'm lucky. > > > > I'd like to add: > > > > net-mdns (pure-ruby mDNS support) > > net-mdns-0.1.tgz net-mdns-0.2.tgz net-mdns-0.3.tgz ... net-mdns-1.0.tgz > > ... Made good progress tonight, a chunk of docs for the (mostly) underlying layers and (re) integration into resolv.rb is mostly whats blocking 0.1 now. Also, I would like it if mdns.rb worked with DNSSD, but that won't block release of net-mdns. Cheers, Sam From chad at chadfowler.com Thu Mar 10 10:54:23 2005 From: chad at chadfowler.com (chad@chadfowler.com) Date: Thu Mar 10 09:43:34 2005 Subject: [dnssd-developers] net-mdns 0.1 coming up, can I make a new package on the downloads page? In-Reply-To: <20050310045743.GA3881@ensemble.local> References: <20050310020954.GC3615@ensemble.local> <1728.12.223.95.153.1110426772.squirrel@12.223.95.153> <20050310045743.GA3881@ensemble.local> Message-ID: <16059.208.252.131.50.1110470063.squirrel@208.252.131.50> > Quoting chad@chadfowler.com, on Wed, Mar 09, 2005 at 10:52:52PM -0500: >> Sure. Good idea. I assume "..." means you'll have more than just the >> tgz? > > Oops, no, I meant more than one release. > > Why do you have a package per release, is that some kid of gems > requirement? > What do you mean by "package per release"? There are no special gems requirements about file releases. If a gem exists, it gets included in the gem repository. Chad From sroberts at uniserve.com Thu Mar 10 18:23:31 2005 From: sroberts at uniserve.com (Sam Roberts) Date: Thu Mar 10 18:19:39 2005 Subject: [dnssd-developers] net-mdns 0.1 coming up, can I make a new package on the downloads page? In-Reply-To: <16059.208.252.131.50.1110470063.squirrel@208.252.131.50> References: <20050310020954.GC3615@ensemble.local> <1728.12.223.95.153.1110426772.squirrel@12.223.95.153> <20050310045743.GA3881@ensemble.local> <16059.208.252.131.50.1110470063.squirrel@208.252.131.50> Message-ID: <20050310232331.GA3988@ensemble.local> Quoting chad@chadfowler.com, on Thu, Mar 10, 2005 at 10:54:23AM -0500: > What do you mean by "package per release"? Sorry, its been several months since my last rubyforge release, I actually forgot what the files page looks like, I thought dnssd looked different from vPim, but its the same. I put the last release, net-mdns-0.0, up. Should be a 0.1 "soon". Thanks for your support. Sam From cmills at freeshell.org Fri Mar 11 12:52:25 2005 From: cmills at freeshell.org (Charles Mills) Date: Fri Mar 11 12:48:03 2005 Subject: [dnssd-developers] cmd line mdns tool, a question about TextRecord, and DNSServiceQueryRecord In-Reply-To: <20050310015652.GB3615@ensemble.local> References: <20050310015652.GB3615@ensemble.local> Message-ID: On Mar 9, 2005, at 5:56 PM, Sam Roberts wrote: > Below is a cmd-line mdns tool I use for testing net-mdns. If called > with > the -n option it will attempt to use "native" DNSSD, the dns_sd.h > bindings. This makes it a decent example utility for DNSSD, too, > except that it doesn't work. > > Specifically, browse works, resolve fails, and register kindof does - > the service is registered but never calls the callback to indicate it > worked. > > -> Am I doing something obviously wrong? > I will check. There is one major outstanding bug in DNSSD detailed here: http://rubyforge.org/pipermail/dnssd-developers/2004-December/ 000000.html > > Note that I call #register with a Hash, not a TextRecord. Its not clear > from the API docs whether this is legal, and the presence of an #encode > method in TextRecord made me think it wouldn't work, but it appears to. > > -> Can text_record be a Hash as input to #register? > Yes. TextRecord is a subclass of hash. An #encode and TextRecord.decode method are added. But hashes work just fine. > > I would like to implement resolver lookup using DNSSD, but examining > dns_sd.h, I think I would need bindings to DNSServiceQueryRecord(), so > that I can query for an A record. > OK. This is not implemented in the binding yet. I happy to add it. > I don't have the time for that now, though it sounds interesting and > not > to hard. If anybody wants to do it, though, I could bind DNSSD into the > resolv.rb ruby resolver framework. OK. Very cool. -Charlie From sroberts at uniserve.com Fri Mar 11 13:54:11 2005 From: sroberts at uniserve.com (Sam Roberts) Date: Fri Mar 11 13:50:17 2005 Subject: [dnssd-developers] cmd line mdns tool, a question about TextRecord, and DNSServiceQueryRecord In-Reply-To: References: <20050310015652.GB3615@ensemble.local> Message-ID: <20050311185410.GB5278@ensemble.local> Quoting cmills@freeshell.org, on Fri, Mar 11, 2005 at 09:52:25AM -0800: > I will check. There is one major outstanding bug in DNSSD detailed > here: > http://rubyforge.org/pipermail/dnssd-developers/2004-December/ > 000000.html I don't know if this is possible, but can you cause the exception to be raised on the thread that made the handle/called #browse,#resolve? It appears this is doable in ruby using Thread#raise, so I'd think you could do it in C, too. I think this would be ideal, the exception won't get lost, and it will go to the user of the handle, where they can catch it and stop the handle (or not catch it and it will kill their thread!). Btw, something I've been meaning to ask about DNSSD and threads. Is there something about the implementation, either of the extension or the dns_sd.h api itself, that is encouraging you to yield the callbacks in a thread? I mention this now, because if you didn't have threads at all, you wouldn't have the problem. :-) And threads can be added back on top easily enough in ruby. I emulated the reply-blocks-called-in-another-thread behaviour for my high-level APIs because making them DNSSD-compatible seems like the best idea for the first release, and I can make other kinds of APIs later. It's nice if you want a thread that goes off and does things every time it sees a service: h = DNSSD.resolve(instance) do |r| update a gui screen, ... end If you want to use the answer in your main thread, though, you need to get the answers back to it (in a thread safe way) and handle not blocking forever, and that probably means using a Queue and a timeout: host = nil port = nil q = Queue.new h = DNSSD.resolve(...) do |r| q.push [r.target, r.port] end begin Timeout::timeout(5) do host,port = q.pop end rescue Timeout::Error ... hey, we didn't find it! ensure h.stop end s = TCPSocket.new(host, port) ... If you add in the code to "browse" a service first, so you can find he instace name, it gets even more complicated. Perhaps I am missing something, and there is a much easier way? My low-level API in net-mdns uses a Queue to receive results from the main resolver thread, which is usually blocked on the network. You can block waiting for answers: q = Question.new(...) answer = q.pop you can use a timeout so you don't wait forever: begin Timeout::timeout(5) do an = q.pop end rescue Timeout::Error ... hey, no answer ensure q.stop end you can loop for lots of answers: q.each do |an| ... end (which you could wrap in a timeout, as well). And if you want to do things in a background thread, its easy, just make a thread: q = Question.new(...) Thread.new do q.each do |an| ... use answer in thread ... end end $stdin.gets q.stop Which you don't have to do, actually, because there is is a wrapper class which does it for you: q = BackgroundQuery.new(...) do |an| ... use answer in thread ... end $stdin.gets q.stop This in no way meant as a criticism of the DNSSD api, I'm just curious about the background and thinking behind the API, because I'll need to do single shot lookup APIs (for addresses in the resolver), and particularly for #resolve, single shot seems the rule, not the exception. At the end, any non-threading lookup api can be built on top of a threaded one (maybe what the beginnings of the high-level API thats been talked about will do?), and threaded APIs can be built on top of non-threaded ones (as I've done in net-mdns). Cheers, Sam From cmills at freeshell.org Sat Mar 12 23:34:19 2005 From: cmills at freeshell.org (Charles Mills) Date: Sat Mar 12 23:29:54 2005 Subject: [dnssd-developers] cmd line mdns tool, a question about TextRecord, and DNSServiceQueryRecord In-Reply-To: <20050311185410.GB5278@ensemble.local> References: <20050310015652.GB3615@ensemble.local> <20050311185410.GB5278@ensemble.local> Message-ID: <9183848404e3a2f496724f7ba201dc06@freeshell.org> On Mar 11, 2005, at 10:54 AM, Sam Roberts wrote: > Quoting cmills@freeshell.org, on Fri, Mar 11, 2005 at 09:52:25AM -0800: >> I will check. There is one major outstanding bug in DNSSD detailed >> here: >> http://rubyforge.org/pipermail/dnssd-developers/2004-December/ >> 000000.html > > I don't know if this is possible, but can you cause the exception to > be raised on the thread that made the handle/called #browse,#resolve? > > It appears this is doable in ruby using Thread#raise, so I'd think you > could do it in C, too. > It is possible. I implemented it, but there is a problem - see below. > I think this would be ideal, the exception won't get lost, and it will > go to the user of the handle, where they can catch it and stop the > handle (or not catch it and it will kill their thread!). > Basically code would look like this: begin DNSSD.browse(...) do |b| ... end rescue end The problem is browse happens asynchronously so the main thread will probably already have passed the rescue clause when the error is thrown, unless you wait inside the block. (But this is probably what will happen anyway - waiting inside the block... thoughts on this?) > > Btw, something I've been meaning to ask about DNSSD and threads. > > Is there something about the implementation, either of the extension or > the dns_sd.h api itself, that is encouraging you to yield the callbacks > in a thread? > When an api call is made a thread is started which selects a domain socket used to communicate with the mDNSResponder daemon. When data becomes available DNSServiceProcessResult() is called which in turn calls the registered call back which executes the Ruby block. See DNSServiceRefSockFD() and DNSServiceProcessResult() in dns_sd.h for more information. It would be possible to allow the Ruby client code to control the polling on the domain socket and such, but it seems it would leave a lot of room for errors. I am open to alternatives... > I mention this now, because if you didn't have threads at all, you > wouldn't have the problem. :-) And threads can be added back on top > easily enough in ruby. > Yeah, but there is the problem of keeping the data flowing between the daemon and the app. > I emulated the reply-blocks-called-in-another-thread behaviour for my > high-level APIs because making them DNSSD-compatible seems like the > best > idea for the first release, and I can make other kinds of APIs later. > > > It's nice if you want a thread that goes off and does things every time > it sees a service: > > h = DNSSD.resolve(instance) do |r| update a gui screen, ... end > > If you want to use the answer in your main thread, though, you need to > get the answers back to it (in a thread safe way) and handle not > blocking forever, and that probably means using a Queue and a timeout: > > host = nil > port = nil > q = Queue.new > h = DNSSD.resolve(...) do |r| q.push [r.target, r.port] end > > begin > Timeout::timeout(5) do > host,port = q.pop > end > rescue Timeout::Error > ... hey, we didn't find it! > ensure > h.stop > end > > s = TCPSocket.new(host, port) > > ... > > If you add in the code to "browse" a service first, so you can find he > instace name, it gets even more complicated. > > Perhaps I am missing something, and there is a much easier way? > The above seems reasonable. It is a bit complicated and probably justifies a higher level api. You could just use an array, since when you call h.stop the underlying thread is killed. You would just have to call #pop after the service was killed. > > My low-level API in net-mdns uses a Queue to receive results from the > main resolver thread, which is usually blocked on the network. You can > block waiting for answers: > > q = Question.new(...) > answer = q.pop > > you can use a timeout so you don't wait forever: > > begin > Timeout::timeout(5) do > an = q.pop > end > rescue Timeout::Error > ... hey, no answer > ensure > q.stop > end > > you can loop for lots of answers: > > q.each do |an| ... end > > (which you could wrap in a timeout, as well). > > And if you want to do things in a background thread, its easy, just > make a thread: > > q = Question.new(...) > > Thread.new do > q.each do |an| > ... use answer in thread ... > end > end > > $stdin.gets > q.stop > > > Which you don't have to do, actually, because there is is a wrapper > class > which does it for you: > > q = BackgroundQuery.new(...) do |an| > ... use answer in thread ... > end > $stdin.gets > q.stop > > This in no way meant as a criticism of the DNSSD api, I'm just curious > about the background and thinking behind the API, because I'll need to > do single shot lookup APIs (for addresses in the resolver), and > particularly for #resolve, single shot seems the rule, not the > exception. > > At the end, any non-threading lookup api can be built on top of a > threaded one (maybe what the beginnings of the high-level API thats > been > talked about will do?), and threaded APIs can be built on top of > non-threaded ones (as I've done in net-mdns). Good point. -Charlie From sroberts at uniserve.com Tue Mar 15 08:36:11 2005 From: sroberts at uniserve.com (Sam Roberts) Date: Tue Mar 15 08:32:08 2005 Subject: [dnssd-developers] I'll be releasing net-mdns-0.1 tomorrow. Message-ID: <20050315133611.GA1308@ensemble.local> This is a preannouncement in case something is horribly wrong and anybody has the time to tell me. Probably I'll upload Thursday night. Documentation uploaded to: http://dnssd.rubyforge.org/net-mdns/ already. net-mdns-0.1 attached, I hope, if it doesn't make it and you want to beta test, just email me and ask. (didn't make it, too large) Ben: It's a total rewrite, but it now does advertising. This was way more work than I wanted to do just to advertise iCalendar servers from an OS that doesn't have an mDNSResponder... I hope it works ok. Thanks for your support, all of you! Cheers, Sam From sroberts at uniserve.com Sun Mar 20 13:20:08 2005 From: sroberts at uniserve.com (Sam Roberts) Date: Sun Mar 20 13:15:54 2005 Subject: [dnssd-developers] mdns.rb cmd-line tool, and a suggestion for DNSSD apis Message-ID: <20050320182008.GA898@ensemble.local> This is mdns.rb from net-mdns. It now works with DNSSD (if you use the -n switch). Maybe it can be added to DNSSD package, its pretty useful, I think. It needs the following, which I suggest be added to DNSSD: module DNSSD def self.namesplit(n) n.scan(/(?:\\.|[^\.])+/) end class ResolveReply def domain DNSSD.namesplit(fullname)[-1] end def type DNSSD.namesplit(fullname)[1,2].join('.') end def name DNSSD.namesplit(fullname)[0] end end end I had a lot of trouble remembering the mapping between api arguments and attributes in replies with DNSSD, and which replies had which attributes. The almost-consistency made my poor head swim. Here's my comments/requests from the net-mdns TODO (I prepared this info while working on correctly emulating the DNSSD API): ---------------- * #domain and #type end in a '.', is that necessary/desireable? * Names of Stuff I'm getting lost in trying to remember what goes into a method, and what is available in it's Reply object. I think the following convention would help: Every function argument maps to a Reply attribute of the same name. The Reply attributes and function arguments should be the same. This makes things easy to remember, as data goes into #browse, comes out BrowseReply, goes into #resolve, comes out ResolveReply it doesn't change its name! Currently: DNSSD::BrowseReply.instance_methods: ["flags", "service", "domain", "fullname", "interface", "name", "type"] Note that #type overrides Object#type. service_type had its name changed to type DNSSD::ResolveReply.instance_methods: [ "flags", "service", "fullname", "interface", "port", "target", "text_record"] Missing #domain! Missing #name! Missing #type! #resolve takes argument #service_name, it comes from BrowseReply#name #resolve takes argument #service_type, it comes from BrowseReply#type #resolve takes argument #service_domain, it comes from BrowseReply#domain Note that interface is still interface. #browse has argument domain, #register has argument #service_domain DNSSD::RegisterReply.instance_methods: [ "flags", "service", "domain", "name", "type" ] Missing #interface, #port, #target, #text_record. As it is, I find the naming really confusing. Changing the names in the replies causes interface breakage, so how about changing the names in the arguments so they are the same as the attributes in the reply, and so that all 3 functions use the same name for the same thing? -------------- next part -------------- #!/usr/local/bin/ruby18 -w # Author: Sam Roberts # Licence: this file is placed in the public domain $:.unshift(File.dirname($0)) require 'getoptlong' $stdout.sync = true $stderr.sync = true =begin Apple's dns-sd options: mdns -E (Enumerate recommended registration domains) mdns -F (Enumerate recommended browsing domains) mdns -B (Browse for service instances) mdns -L (Look up a service instance) mdns -R [...] (Register a service) mdns -P [...] (Proxy) mdns -Q (Generic query for any record type) =end @debug = false @log = nil @recursive = false @domain = 'local' @type = nil @name = nil @port = nil @txt = {} @cmd = nil # TODO - can I use introspection on class names to determine all supported # RR types in DNS::Resource::IN? HELP =< [domain] (Browse for service instances) mdns [options] -L [domain] (Look up a service instance) mdns [options] -R [domain] [...] (Register a service) mdns [options] -Q [rrtype] [rrclass] (Generic query for any record type) Note: -Q is not yet implemented. For -B, -L, and -R, [domain] is optional and defaults to "local". For -Q, [rrtype] defaults to A, other values are TXT, PTR, SRV, CNAME, ... For -Q, [rrclass] defaults to 1 (IN). [...] is optional for -R, it can be a series of key=value pairs. You can use long names --browse, --lookup, and --register instead of -B, -L, and -R. Options: -m,--mdnssd Attempt to use 'net/dns/mdns-sd', a pure-ruby DNS-SD resolver library (this is the default). -n,--dnssd Attempt to use 'dnssd', the interface to the native ("-n") DNS-SD resolver library APIs, "dns_sd.h" from Apple. Note: YMMV, this doesn't entirely work, currently. -d,--debug Print debug messages to stderr. Examples: mdns -B _http._tcp mdns -L "My Music" _daap._tcp mdns -R me _example._tcp local 4321 key=value key2=value2 These work with the test modes of Apple's dns-sd utility: mdns -L Test _testupdate._tcp (for dns-sd -A, -U, -N) mdns -L Test _testlargetxt._tcp (for dns-sd -T) mdns -L Test _testdualtxt._tcp (for dns-sd -M) mdns -L Test _testtxt._tcp (for dns-sd -I) EOF opts = GetoptLong.new( [ "--debug", "-d", GetoptLong::NO_ARGUMENT ], [ "--help", "-h", GetoptLong::NO_ARGUMENT ], [ "--dnssd", "-n", GetoptLong::NO_ARGUMENT ], [ "--mdnssd", "-m", GetoptLong::NO_ARGUMENT ], [ "--browse", "-B", GetoptLong::NO_ARGUMENT ], [ "--lookup", "-L", GetoptLong::NO_ARGUMENT ], [ "--register", "-R", GetoptLong::NO_ARGUMENT ] ) opts.each do |opt, arg| case opt when "--debug" require 'pp' require 'logger' @debug = true @log = Logger.new(STDERR) @log.level = Logger::DEBUG when "--help" print HELP exit 0 when '--dnssd' require 'dnssd' require 'socket' when "--browse" @cmd = :browse @type = ARGV.shift @domain = ARGV.shift || @domain when "--lookup" @cmd = :lookup @name = ARGV.shift @type = ARGV.shift @domain = ARGV.shift || @domain unless @name && @type puts 'name and type required for -L' exit 1 end when "--register" @cmd = :register @name = ARGV.shift @type = ARGV.shift @port = ARGV.shift if @port.to_i == 0 @domain = @port @port = ARGV.shift.to_i else @port = @port.to_i end ARGV.each do |kv| kv.match(/([^=]+)=([^=]+)/) @txt[$1] = $2 end ARGV.replace([]) end end begin DNSSD.class puts "Using native DNSSD..." module DNSSD def self.namesplit(n) n.scan(/(?:\\.|[^\.])+/) end class ResolveReply def domain DNSSD.namesplit(fullname)[-1] end def type DNSSD.namesplit(fullname)[1,2].join('.') end def name DNSSD.namesplit(fullname)[0] end end end rescue NameError require 'net/dns/mdns-sd' DNSSD = Net::DNS::MDNSSD Net::DNS::MDNS::Responder.instance.log = @log if @log puts "Using Net::DNS::MDNSSD..." end unless @cmd print HELP exit 1 end case @cmd when :browse STDERR.puts( "DNSSD.#{@cmd}(#{@type}, #{@domain}) =>" ) if @debug fmt = "%-3.3s %-8.8s %-15.15s %-20.20s\n" printf fmt, "Ttl", "Domain", "Service Type", "Instance Name" handle = DNSSD.browse(@type, @domain) do |reply| begin printf fmt, reply.flags.to_i, reply.domain, reply.type, reply.name rescue p $! end end $stdin.gets handle.stop when :lookup STDERR.puts( "DNSSD.#{@cmd}(#{@name}, #{@type}, #{@domain}) =>" ) if @debug fmt = "%-3.3s %-8.8s %-19.19s %-20.20s %-20.20s %s\n" printf fmt, "Ttl", "Domain", "Service Type", "Instance Name", "Location", "Text" handle = DNSSD.resolve(@name, @type, @domain) do |reply| begin location = "#{reply.target}:#{reply.port}" text = reply.text_record.to_a.map { |kv| "#{kv[0]}=#{kv[1].inspect}" }.join(', ') printf fmt, reply.flags.to_i, reply.domain, reply.type, reply.name, location, text rescue p $! end end $stdin.gets handle.stop when :register STDERR.puts( "DNSSD.#{@cmd}(#{@name}, #{@type}, #{@domain}, #{@port}, #{@txt.inspect}) =>" ) if @debug fmt = "%-3.3s %-8.8s %-19.19s %-20.20s %-20.20s %s\n" printf fmt, "Ttl", "Domain", "Service Type", "Instance Name", "Location", "Text" handle = DNSSD.register(@name, @type, @domain, @port, @txt) do |notice| begin location = "#{Socket.gethostname}:#{@port}" text = @txt.to_a.map { |kv| "#{kv[0]}=#{kv[1].inspect}" }.join(', ') printf fmt, 'N/A', notice.domain, notice.type, notice.name, location, text rescue p $! end end $stdin.gets handle.stop end From sroberts at uniserve.com Sun Mar 20 13:44:52 2005 From: sroberts at uniserve.com (Sam Roberts) Date: Sun Mar 20 13:40:39 2005 Subject: [dnssd-developers] Re: how to query for one reply, and then use it In-Reply-To: <9183848404e3a2f496724f7ba201dc06@freeshell.org> References: <20050310015652.GB3615@ensemble.local> <20050311185410.GB5278@ensemble.local> <9183848404e3a2f496724f7ba201dc06@freeshell.org> Message-ID: <20050320184452.GA911@ensemble.local> Quoting cmills@freeshell.org, on Sat, Mar 12, 2005 at 08:34:19PM -0800: > >I emulated the reply-blocks-called-in-another-thread behaviour for my > >high-level APIs because making them DNSSD-compatible seems like the > >best > >idea for the first release, and I can make other kinds of APIs later. > > > > > >It's nice if you want a thread that goes off and does things every time > >it sees a service: > > > > h = DNSSD.resolve(instance) do |r| update a gui screen, ... end > > > >If you want to use the answer in your main thread, though, you need to > >get the answers back to it (in a thread safe way) and handle not > >blocking forever, and that probably means using a Queue and a timeout: > > > > host = nil > > port = nil > > q = Queue.new > > h = DNSSD.resolve(...) do |r| q.push [r.target, r.port] end > > > > begin > > Timeout::timeout(5) do > > host,port = q.pop > > end > > rescue Timeout::Error > > ... hey, we didn't find it! > > ensure > > h.stop > > end > > > > s = TCPSocket.new(host, port) > > > > ... > > > >If you add in the code to "browse" a service first, so you can find he > >instace name, it gets even more complicated. > > > >Perhaps I am missing something, and there is a much easier way? > > > The above seems reasonable. It is a bit complicated and probably > justifies a higher level api. You could just use an array, since when > you call h.stop the underlying thread is killed. You would just > have to call #pop after the service was killed. I don't think you can use an array, not without ALWAYS waiting 5 seconds or writing a busy-loop to poll the Array (or using a condition variable, but thats what a Queue is, Array+a condvar). Polling or always waiting for the max timeout would both suck, and I don't think you can avoid this without a Queue/condvar. Do you see another way? Notice that I don't h.stop the the underlying thread until there is a response, and that I stop it "instantly" when there is a response, and continue. If thats in 0.1 seconds (typical) it will happen really fast. If it takes a little longer, it still works. And if 5 seconds pass... I give up. Cheers, Sam From sroberts at uniserve.com Sun Mar 20 14:28:14 2005 From: sroberts at uniserve.com (Sam Roberts) Date: Sun Mar 20 14:24:01 2005 Subject: [dnssd-developers] Re: DNSSD, threads, and exceptions noone can see In-Reply-To: <9183848404e3a2f496724f7ba201dc06@freeshell.org> References: <20050310015652.GB3615@ensemble.local> <20050311185410.GB5278@ensemble.local> <9183848404e3a2f496724f7ba201dc06@freeshell.org> Message-ID: <20050320192814.GC911@ensemble.local> Quoting cmills@freeshell.org, on Sat, Mar 12, 2005 at 08:34:19PM -0800: > On Mar 11, 2005, at 10:54 AM, Sam Roberts wrote: > > >Quoting cmills@freeshell.org, on Fri, Mar 11, 2005 at 09:52:25AM -0800: > The problem is browse happens asynchronously so the main thread will > probably already have passed the rescue clause when the error is > thrown, unless you wait inside the block. I see your point. I think the best you can do if you are in a service thread is call the block not with a Reply object but with an Exception object. The block can check the type of its argument. Or, if it tries to use the Exception as a Reply that will cause a NameError. Its not perfect, but better than silent failure. > >Is there something about the implementation, either of the extension or > >the dns_sd.h api itself, that is encouraging you to yield the callbacks > >in a thread? > > > When an api call is made a thread is started which selects a domain > socket used to communicate with the mDNSResponder daemon. When data > becomes available DNSServiceProcessResult() is called which in turn > calls the registered call back which executes the Ruby block. See > DNSServiceRefSockFD() and DNSServiceProcessResult() in dns_sd.h for > more information. > > It would be possible to allow the Ruby client code to control the > polling on the domain socket and such, but it seems it would leave a > lot of room for errors. > I am open to alternatives... Ok, I stared at the extension code a bit, and it doesn't look to my eyes that you need threads in the extension. Straighten me out if I don't understand the troubles you have. :-) DNSSD#browse basically maps down to rb_thread_create(dnssd_process, (void *)service); What would happen if you changed that to RUBY_BEGIN dnssd_process(service); /* no thread */ RUBY_ENSURE dnssd_service_stop(service); RUBY_END /* I don't know what the C macros that do the equivalent of a ruby * begin... ensure... end are, but they must exist. */ ??? dnssd_process() calls rb_thread_select(), and that will block, but only the current thread. Other threads would still be free to call the dns_sd APIs. This means that: DNSSD.browse(..) do |r| ... end would block. It would never return, UNTIL the block did a break or return, or raised an exception (or in case of the error return code we were talking about, dnssd_process raised an exception). Is this a problem? In a C library, yes, in Ruby, no. If something can block in ruby, but you don't want to wait for it, you just make a new Thread. If something blocks in C, maybe you use threads, but threading libraries are a PITA, and usually you prefer to run stuff from a main loop that selects on all possible inputs. So, here's what I think DNSSD would look like if you did this: Trivial to emulate how it works now: service = Thread.new do DNSSD.browse do |r| ... whatever end end ... stuff service.stop So, it can work the same, but users make their own thread, if they want one, its not the C code's job to do this for them (but you will provide a backwards-compatible API in ruby on top DNSSD helps them out). One-shot query, wait forever for answer: reply = DNSSD.browse(...) begin do |r| break(r) end One-shot with timeout: Timeout(5).do reply = DNSSD.browse(...) begin do |r| break(r) end end As many as you can get in timout: Timeout(5).do DNSSD.browse(...) begin do |r| ... stuff end end (Timeouts as well could be handled by a timeout parameter to a higher-level API, with nil meaning don't timeout). Using Queue and friends to get the current DNSSDs replies back into your current thread is a little complicated right now - though that can be fixed with hi-level wrappers easily. This isn't a reason to make the underlying extension blocking. The advantage of making the underlying extension blocking (ruby-blocking, the kind of blocking that files and sockets do and that ruby programmers know how to use threads to avoid when they need to) is it makes the implementation less complicated for you, and fixes the dissappearing exception problem. What do you think? Do you have to do it the way it is now? Does my suggestion make sense? Sam From cmills at freeshell.org Sun Mar 20 17:39:17 2005 From: cmills at freeshell.org (Charles Mills) Date: Sun Mar 20 17:34:40 2005 Subject: [dnssd-developers] Re: DNSSD, threads, and exceptions noone can see In-Reply-To: <20050320192814.GC911@ensemble.local> References: <20050310015652.GB3615@ensemble.local> <20050311185410.GB5278@ensemble.local> <9183848404e3a2f496724f7ba201dc06@freeshell.org> <20050320192814.GC911@ensemble.local> Message-ID: On Mar 20, 2005, at 11:28 AM, Sam Roberts wrote: > > What do you think? Do you have to do it the way it is now? Does my > suggestion make sense? > Yes. Your suggestion also solves the error problem. See: Timeout(5).do DNSSD.browse(...) begin do |r| ... stuff end rescue DNSSD::Error puts "#{$!} happened" end This is way better because since the thread is created in Ruby, not in C we don't have the problem of the C code never catching the error. This change will require less work than implementing some other error handling system. I will try. We can always roll it back if it doesn't work out. -Charlie From cmills at freeshell.org Sun Mar 20 17:43:07 2005 From: cmills at freeshell.org (Charles Mills) Date: Sun Mar 20 17:38:24 2005 Subject: [dnssd-developers] mdns.rb cmd-line tool, and a suggestion for DNSSD apis In-Reply-To: <20050320182008.GA898@ensemble.local> References: <20050320182008.GA898@ensemble.local> Message-ID: <24216941cb3eb47d0f1d878f9149aa73@freeshell.org> On Mar 20, 2005, at 10:20 AM, Sam Roberts wrote: > This is mdns.rb from net-mdns. It now works with DNSSD (if you use the > -n switch). Maybe it can be added to DNSSD package, its pretty useful, > I > think. > > It needs the following, which I suggest be added to DNSSD: > > module DNSSD > def self.namesplit(n) > n.scan(/(?:\\.|[^\.])+/) > end > class ResolveReply > def domain > DNSSD.namesplit(fullname)[-1] > end > def type > DNSSD.namesplit(fullname)[1,2].join('.') > end > def name > DNSSD.namesplit(fullname)[0] > end > end > end > Added. DNSSD.split and DNSSD.split_fullname instead of namesplit. > I had a lot of trouble remembering the mapping between api arguments > and > attributes in replies with DNSSD, and which replies had which > attributes. The almost-consistency made my poor head swim. > > Here's my comments/requests from the net-mdns TODO (I prepared this > info > while working on correctly emulating the DNSSD API): > > ---------------- > > * #domain and #type end in a '.', is that necessary/desireable? > I think the dot at the end is OK for now... we can discuss it further. > * Names of Stuff > > I'm getting lost in trying to remember what goes into a method, and > what is available > in it's Reply object. I think the following convention would help: > > Every function argument maps to a Reply attribute of the same name. > > The Reply attributes and function arguments should be the same. > > This makes things easy to remember, as data goes into #browse, comes > out > BrowseReply, goes into #resolve, comes out ResolveReply it doesn't > change its > name! > > > > Currently: > > DNSSD::BrowseReply.instance_methods: > ["flags", "service", "domain", "fullname", "interface", "name", > "type"] > > Note that #type overrides Object#type. > service_type had its name changed to type > > DNSSD::ResolveReply.instance_methods: > [ "flags", "service", "fullname", "interface", > "port", "target", "text_record"] > > Missing #domain! > Missing #name! > Missing #type! Fixed. > > #resolve takes argument #service_name, it comes from BrowseReply#name > #resolve takes argument #service_type, it comes from BrowseReply#type > #resolve takes argument #service_domain, it comes from > BrowseReply#domain > OK I changed the docs so service_ is not part of the argument name. > Note that interface is still interface. > > #browse has argument domain, #register has argument #service_domain > fixed. > > DNSSD::RegisterReply.instance_methods: > > [ "flags", "service", "domain", "name", > "type" ] > > Missing #interface, #port, #target, #text_record. > Added interface, port, target, and text record. Target will just be the hostname as returned by gethostname() if it is available (nil otherwise). > > As it is, I find the naming really confusing. Changing the names in > the replies > causes interface breakage, so how about changing the names in the > arguments so > they are the same as the attributes in the reply, and so that all 3 > functions > use the same name for the same thing? > I just got rid of the BrowseReply, ResolveReply, etc classes all together and there is just one class Reply. It is basically a struct with the following fields: service flags name type domain interface fullname text_record target port Some of the fields may/will be nil depending on if browse(), register(), etc are called. The docs will have to be improved. -Charlie From cmills at freeshell.org Sun Mar 20 17:57:16 2005 From: cmills at freeshell.org (Charles Mills) Date: Sun Mar 20 17:52:33 2005 Subject: [dnssd-developers] mdns.rb cmd-line tool, and a suggestion for DNSSD apis In-Reply-To: <24216941cb3eb47d0f1d878f9149aa73@freeshell.org> References: <20050320182008.GA898@ensemble.local> <24216941cb3eb47d0f1d878f9149aa73@freeshell.org> Message-ID: <0c8b7faea3962f6fdeaabbd95783f89a@freeshell.org> these changes are now in CVS. From cmills at freeshell.org Sun Mar 20 18:22:34 2005 From: cmills at freeshell.org (Charles Mills) Date: Sun Mar 20 18:17:51 2005 Subject: [dnssd-developers] Re: DNSSD, threads, and exceptions noone can see In-Reply-To: References: <20050310015652.GB3615@ensemble.local> <20050311185410.GB5278@ensemble.local> <9183848404e3a2f496724f7ba201dc06@freeshell.org> <20050320192814.GC911@ensemble.local> Message-ID: On Mar 20, 2005, at 2:39 PM, Charles Mills wrote: > On Mar 20, 2005, at 11:28 AM, Sam Roberts wrote: > >> >> What do you think? Do you have to do it the way it is now? Does my >> suggestion make sense? >> > Yes. Your suggestion also solves the error problem. See: > > Timeout(5).do > DNSSD.browse(...) begin do |r| > ... stuff > end > rescue DNSSD::Error > puts "#{$!} happened" > end > > This is way better because since the thread is created in Ruby, not in > C we don't have the problem of the C code never catching the error. > This change will require less work than implementing some other error > handling system. > I will try. We can always roll it back if it doesn't work out. > Just committed this change to CVS. Although only a few line of code changed, this breaks the API completely ;) I think this is much better though. Anyone disagree? -Charlie From sroberts at uniserve.com Sun Mar 20 19:30:55 2005 From: sroberts at uniserve.com (Sam Roberts) Date: Sun Mar 20 19:26:41 2005 Subject: [dnssd-developers] Re: DNSSD, threads, and exceptions noone can see In-Reply-To: References: <20050310015652.GB3615@ensemble.local> <20050311185410.GB5278@ensemble.local> <9183848404e3a2f496724f7ba201dc06@freeshell.org> <20050320192814.GC911@ensemble.local> Message-ID: <20050321003055.GA460@ensemble.local> Quoting cmills@freeshell.org, on Sun, Mar 20, 2005 at 03:22:34PM -0800: > On Mar 20, 2005, at 2:39 PM, Charles Mills wrote: > > >On Mar 20, 2005, at 11:28 AM, Sam Roberts wrote: > > > >> > >>What do you think? Do you have to do it the way it is now? Does my > >>suggestion make sense? > >> > >Yes. Your suggestion also solves the error problem. See: > > > > Timeout(5).do > > DNSSD.browse(...) begin do |r| > > ... stuff > > end > > rescue DNSSD::Error > > puts "#{$!} happened" > > end > > > >This is way better because since the thread is created in Ruby, not in > >C we don't have the problem of the C code never catching the error. > >This change will require less work than implementing some other error > >handling system. > >I will try. We can always roll it back if it doesn't work out. > > > Just committed this change to CVS. You're fast! I just updated. Seems to work OK. mdns.rb still runs, though I see an odd warning: mdns.rb:221: warning: instance variable @interface not initialized mdns.rb:221: warning: instance variable @port not initialized mdns.rb:221: warning: instance variable @text_record not initialized That looks like its my code, but mdns.rb:221 is this: handle = DNSSD.register(@name, @type, @domain, @port, @txt) do |notice| Is it possible this is coming from inside DNSSD? If not, I'll figure it out later. > Although only a few line of code changed, this breaks the API > completely ;) Oh no, I just finished an emulation layer! :-) > I think this is much better though. Anyone disagree? If the C APIs had a different name, the old background-thread APIs can be done as a wrapper. I think if I got to pick the names, I would do this: Block C-lang extension calls: DNSSD#svc_browse/svc_resolve/svc_register Blocking versions of the threaded APIs, with a different name, so that they don't step on backwards-compat. Maybe it would make sense for svc_register to be kind of like File.open? if given a block: yield a Reply object with all the fields set to the name the service actually got, and deregister the service when the block exits. if no block: return the reply object/service handle, it has to be manually stopped Would that be useful? Also, I assume the new version will block until the service is actually registered? Backwards compatible: DNSSD#browse/resolve/register Backwards compat, if necesary. Do things have to be backwards compatible? How many users are there of DNSSD? I think Rendezvous is the greatest thing since sliced bread and iCalendar, but maybe not everybody agrees. Perhaps its OK to change the API? Since the blocking APIs don't need handles, they make nice module methods. For APIs that yield in a background thread, you need a handle to stop them. I'd prefer a more OO style API. Conveniently, this also means you don't have to invent a new method name like "thrd_browse": DNSSD::Browse.new(.., &block) Returns an object that can have #stop called on it. Does DNSSD#svc_browse in a thread. DNSSD::Resolve.new(.., &block) Like Browse. DNSSD::Register.new() --> don't need this, it doesn't do anything different from DNSSD.svc_register when called without a block. What do you think? Implementing this stuff is close to trivial, deciding what to implement is a little harder. What are your plans for rerelease with the new slightly changed implementation? If you want to just patch the docs and rerelease, I guess that could be fast. I'd like to know though, so I can rerelease net-mdns in sync, with the same APIs. Btw, feel free to put mdns.rb into DNSSD if you think it's a useful example, also exwebrick.rb from net-dns is a webrick example that works with Safari (advertises its port). I'd like to have DNSSD and Net::DNDS::MDNSSD to have the same APIs, so I can do this in my code: begin require 'dnssd' rescue LoadError require 'net/dns/mdns-sd' DNSSD = Net::DNS::MDNSSD end Btw, the last thing missing from DNSSD is arbitrary record querying. Looks like a cut-n-paste job with dnssd_resolve(), replacing name_str and type_str with Fixnum (rrtype) and Fixnum (rrclass). Looks so easy I might even be able to do it. With this, I can make DNSSD a Resolv plug-in, and that allows it to be used by library functions like open-uri(). But doing it involves using the modified version of resolv.rb from net-mdns though. Hm. Maybe another night. By the way, I don't know what your goals for DNSSD are, but long range I think it would be cool if DNSSD and net-mdns could be pseudo-merged, and both would exist/install together, defaulting to using the C extension that talks to a responder daemon, but allowing pure-ruby if you wanted to or had to. Doing the packaging work for this is too much for me, right now, I don't even want to start until the code has seen some use. In the short term, I just want them to be easy to use together, or as alternatives to each other. I want to go back to working on vPim, I've all kinds of services I want to implement with it. Rendezvous is just glue-tech for that. Cheers, Sam From sroberts at uniserve.com Sun Mar 20 19:44:40 2005 From: sroberts at uniserve.com (Sam Roberts) Date: Sun Mar 20 19:40:26 2005 Subject: [dnssd-developers] mdns.rb cmd-line tool, and a suggestion for DNSSD apis In-Reply-To: <24216941cb3eb47d0f1d878f9149aa73@freeshell.org> References: <20050320182008.GA898@ensemble.local> <24216941cb3eb47d0f1d878f9149aa73@freeshell.org> Message-ID: <20050321004440.GB460@ensemble.local> Quoting cmills@freeshell.org, on Sun, Mar 20, 2005 at 02:43:07PM -0800: > On Mar 20, 2005, at 10:20 AM, Sam Roberts wrote: > Added. DNSSD.split and DNSSD.split_fullname instead of namesplit. Great. > >I had a lot of trouble remembering the mapping between api arguments > >and > >attributes in replies with DNSSD, and which replies had which > >attributes. The almost-consistency made my poor head swim. > > > >Here's my comments/requests from the net-mdns TODO (I prepared this > >info > >while working on correctly emulating the DNSSD API): > > > >---------------- > > > >* #domain and #type end in a '.', is that necessary/desireable? > > > I think the dot at the end is OK for now... we can discuss it further. It's not a big deal. If you want to keep it, I'll emulate it. I noticed because mdns.rb's output was a little different depending on DNSSD/net-mdns. Seems nicer without, though. > Added interface, port, target, and text record. Target will just be > the hostname as returned by gethostname() if it is available (nil > otherwise). Hm. Actually, I think you were right not to have target. The server doesn't need to know its own name, and it's not something you can choose when calling DNSSD.resolve(). Sorry! > I just got rid of the BrowseReply, ResolveReply, etc classes all > together and there is just one class Reply. It is basically a struct > with the following fields: > service > flags > name > type > domain > interface > fullname > text_record > target > port Thats attractively simple! I did like the classes a bit, because it makes documenting what you'll get back easy. i.e., #resolv => ResolvReply, everything in ResolvReply is non-nil. I think ResolvReply is a pure super-set, extension of BrowseReply, so having it inherit models this nicely. #register could return ResolvReply, your service is like someone elses, its the same information (minus the #target, except that can be synthesized with gethostname or left nil). That would give two classes, where ResolveReply < BrowseReply. But really, nobody should be looking at the type of the reply object, just accessing it's attributes, so what the class names are hardly matters. Cheers, Sam From cmills at freeshell.org Sun Mar 20 21:14:02 2005 From: cmills at freeshell.org (Charles Mills) Date: Sun Mar 20 21:09:20 2005 Subject: [dnssd-developers] mdns.rb cmd-line tool, and a suggestion for DNSSD apis In-Reply-To: <20050321004440.GB460@ensemble.local> References: <20050320182008.GA898@ensemble.local> <24216941cb3eb47d0f1d878f9149aa73@freeshell.org> <20050321004440.GB460@ensemble.local> Message-ID: <7135108455847ccc6fc9c054763169b9@freeshell.org> On Mar 20, 2005, at 4:44 PM, Sam Roberts wrote: > Quoting cmills@freeshell.org, on Sun, Mar 20, 2005 at 02:43:07PM -0800: >> On Mar 20, 2005, at 10:20 AM, Sam Roberts wrote: >> Added. DNSSD.split and DNSSD.split_fullname instead of namesplit. > > Great. > >>> I had a lot of trouble remembering the mapping between api arguments >>> and >>> attributes in replies with DNSSD, and which replies had which >>> attributes. The almost-consistency made my poor head swim. >>> >>> Here's my comments/requests from the net-mdns TODO (I prepared this >>> info >>> while working on correctly emulating the DNSSD API): >>> >>> ---------------- >>> >>> * #domain and #type end in a '.', is that necessary/desireable? >>> >> I think the dot at the end is OK for now... we can discuss it further. > > It's not a big deal. If you want to keep it, I'll emulate it. I noticed > because mdns.rb's output was a little different depending on > DNSSD/net-mdns. Seems nicer without, though. > >> Added interface, port, target, and text record. Target will just be >> the hostname as returned by gethostname() if it is available (nil >> otherwise). > > Hm. Actually, I think you were right not to have target. The server > doesn't need to know its own name, and it's not something you can > choose when calling DNSSD.resolve(). Sorry! Also you know that the text record and the port will be the same as those passed in, right? > >> I just got rid of the BrowseReply, ResolveReply, etc classes all >> together and there is just one class Reply. It is basically a struct >> with the following fields: >> service >> flags >> name >> type >> domain >> interface >> fullname >> text_record >> target >> port > > Thats attractively simple! > > I did like the classes a bit, because it makes documenting what you'll > get back easy. i.e., #resolv => ResolvReply, everything in ResolvReply > is non-nil. I think ResolvReply is a pure super-set, extension of > BrowseReply, so having it inherit models this nicely. > It could certainly go either way. Either way works for me. > #register could return ResolvReply, your service is like someone elses, > its the same information (minus the #target, except that can be > synthesized with gethostname or left nil). > > That would give two classes, where ResolveReply < BrowseReply. > > But really, nobody should be looking at the type of the reply object, > just accessing it's attributes, so what the class names are hardly > matters. > -Charlie From cmills at freeshell.org Sun Mar 20 21:16:29 2005 From: cmills at freeshell.org (Charles Mills) Date: Sun Mar 20 21:11:48 2005 Subject: [dnssd-developers] Re: DNSSD, threads, and exceptions noone can see In-Reply-To: <20050321003055.GA460@ensemble.local> References: <20050310015652.GB3615@ensemble.local> <20050311185410.GB5278@ensemble.local> <9183848404e3a2f496724f7ba201dc06@freeshell.org> <20050320192814.GC911@ensemble.local> <20050321003055.GA460@ensemble.local> Message-ID: <5cca10f0b69328dfc32dcb9abc40a856@freeshell.org> On Mar 20, 2005, at 4:30 PM, Sam Roberts wrote: > Quoting cmills@freeshell.org, on Sun, Mar 20, 2005 at 03:22:34PM -0800: >> On Mar 20, 2005, at 2:39 PM, Charles Mills wrote: >> >>> On Mar 20, 2005, at 11:28 AM, Sam Roberts wrote: >>> >>>> >>>> What do you think? Do you have to do it the way it is now? Does my >>>> suggestion make sense? >>>> >>> Yes. Your suggestion also solves the error problem. See: >>> >>> Timeout(5).do >>> DNSSD.browse(...) begin do |r| >>> ... stuff >>> end >>> rescue DNSSD::Error >>> puts "#{$!} happened" >>> end >>> >>> This is way better because since the thread is created in Ruby, not >>> in >>> C we don't have the problem of the C code never catching the error. >>> This change will require less work than implementing some other error >>> handling system. >>> I will try. We can always roll it back if it doesn't work out. >>> >> Just committed this change to CVS. > > You're fast! > > I just updated. Seems to work OK. mdns.rb still runs, though I see an > odd warning: > > mdns.rb:221: warning: instance variable @interface not initialized > mdns.rb:221: warning: instance variable @port not initialized > mdns.rb:221: warning: instance variable @text_record not initialized > This was a bug, now fixed in CVS. > That looks like its my code, but mdns.rb:221 is this: > > handle = DNSSD.register(@name, @type, @domain, @port, @txt) do > |notice| > > Is it possible this is coming from inside DNSSD? If not, I'll figure it > out later. > >> Although only a few line of code changed, this breaks the API >> completely ;) > > Oh no, I just finished an emulation layer! > :-) Oh... I think your probably the only user of the DNS SD bindings that I know, unless Rich and Chad have been using it? > >> I think this is much better though. Anyone disagree? > > If the C APIs had a different name, the old background-thread APIs can > be done as a wrapper. > Yeah. This can be done in the dnssd.rb file. > I think if I got to pick the names, I would do this: > > Block C-lang extension calls: > > DNSSD#svc_browse/svc_resolve/svc_register > > Blocking versions of the threaded APIs, with a different name, so > that > they don't step on backwards-compat. > Could also make the bang methods blocking and the regular methods non blocking. browse! <- blocking browse <- non blocking > Maybe it would make sense for svc_register to be kind of like > File.open? yeah I was just thinking the same thing... Perhaps this would be good to do with a DNSSD::Register class. (also could be put in dnssd.rb) > > if given a block: yield a Reply object with all the fields set to > the name the service actually got, and deregister the service when > the block exits. > > if no block: return the reply object/service handle, it has to > be manually stopped > > Would that be useful? > yes. > Also, I assume the new version will block until the service is > actually registered? > > Backwards compatible: > > DNSSD#browse/resolve/register > > Backwards compat, if necesary. > I don't know if backward compat is necessary, but I like the idea of having the bang methods being blocking. > Do things have to be backwards compatible? How many users are there > of > DNSSD? no idea. > I think Rendezvous is the greatest thing since sliced bread and > iCalendar, but maybe not everybody agrees. > > Perhaps its OK to change the API? we haven't hit 1.0 :) > > Since the blocking APIs don't need handles, they make nice module > methods. For APIs that yield in a background thread, you need a handle > to stop them. > > I'd prefer a more OO style API. Conveniently, this also means you don't > have to invent a new method name like "thrd_browse": > > DNSSD::Browse.new(.., &block) > > Returns an object that can have #stop called on it. Does > DNSSD#svc_browse in a thread. > > DNSSD::Resolve.new(.., &block) > > Like Browse. > > > DNSSD::Register.new() --> don't need this, it doesn't do anything > different from DNSSD.svc_register when called without a block. > > > What do you think? > good idea. > Implementing this stuff is close to trivial, deciding what to implement > is a little harder. > > What are your plans for rerelease with the new slightly changed > implementation? If you want to just patch the docs and rerelease, I > guess that could be fast. I'd like to know though, so I can rerelease > net-mdns in sync, with the same APIs. I think it would be great to release the two together. I think relying on apples mDNS Responder has limited the acceptance of DNSSD. > > Btw, feel free to put mdns.rb into DNSSD if you think it's a useful > example, also exwebrick.rb from net-dns is a webrick example that > works with Safari (advertises its port). > > I'd like to have DNSSD and Net::DNDS::MDNSSD to have the same APIs, so > I > can do this in my code: > > begin > require 'dnssd' > rescue LoadError > require 'net/dns/mdns-sd' > DNSSD = Net::DNS::MDNSSD > end > right. this could be put in dnssd.rb, but like this: begin require 'rdnssd' # C library rescue LoadError require 'net/dns/mdns-sd' DNSSD = Net::DNS::MDNSSD end > > Btw, the last thing missing from DNSSD is arbitrary record querying. > Looks like a cut-n-paste job with dnssd_resolve(), replacing name_str > and type_str with Fixnum (rrtype) and Fixnum (rrclass). Looks so easy I > might even be able to do it. > > With this, I can make DNSSD a Resolv plug-in, and that allows it to be > used by library functions like open-uri(). > > But doing it involves using the modified version of resolv.rb from > net-mdns though. Hm. Maybe another night. > > By the way, I don't know what your goals for DNSSD are, but long range > I > think it would be cool if DNSSD and net-mdns could be pseudo-merged, > and > both would exist/install together, defaulting to using the C extension > that talks to a responder daemon, but allowing pure-ruby if you wanted > to or had to. > I think it would be great if rdnssd.bundle could be replaced by net-mdns and the rest of the code wouldn't care. Seems like that is what you have been doing. Anyway I think that is a good direction and it would be great if we could minimize the over lapping code (like the code to implement the higher level classes discussed above). > Doing the packaging work for this is too much for me, right now, I > don't > even want to start until the code has seen some use. In the short term, > I just want them to be easy to use together, or as alternatives to each > other. > Right. > I want to go back to working on vPim, I've all kinds of services I want > to implement with it. Rendezvous is just glue-tech for that. > -Charlie From cmills at freeshell.org Mon Mar 21 00:20:06 2005 From: cmills at freeshell.org (Charles Mills) Date: Mon Mar 21 00:15:25 2005 Subject: [dnssd-developers] blocking and non blocking Message-ID: <8d0649dad8e554e69fbc4f02dc36223c@freeshell.org> So what is in CVS is currently backwards compatible, but includes blocking versions of DNSSD.browse, resolve, register, enumerate_domains. The blocking versions are followed by a bang - '!'. -Charlie From sroberts at uniserve.com Mon Mar 21 19:18:03 2005 From: sroberts at uniserve.com (Sam Roberts) Date: Mon Mar 21 19:13:47 2005 Subject: [dnssd-developers] blocking and non blocking In-Reply-To: <8d0649dad8e554e69fbc4f02dc36223c@freeshell.org> References: <8d0649dad8e554e69fbc4f02dc36223c@freeshell.org> Message-ID: <20050322001803.GA609@ensemble.local> Quoting cmills@freeshell.org, on Sun, Mar 20, 2005 at 09:20:06PM -0800: > So what is in CVS is currently backwards compatible, but includes > blocking versions of DNSSD.browse, resolve, register, > enumerate_domains. The blocking versions are followed by a bang - '!'. Doc patch: You won't get to the puts because timeout() raises an exception, need to rescue it. And a little spelling error, unless this is the American spelling? Cheers, Sam Index: ext/rdnssd_service.c =================================================================== RCS file: /var/cvs/dnssd/dnssd/ext/rdnssd_service.c,v retrieving revision 1.23 diff -u -r1.23 rdnssd_service.c --- ext/rdnssd_service.c 21 Mar 2005 05:12:16 -0000 1.23 +++ ext/rdnssd_service.c 22 Mar 2005 00:11:21 -0000 @@ -311,7 +311,7 @@ * call-seq: * DNSSD.enumerate_domains!(flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => obj * - * Syncronously enumerate domains available for browsing and registration. + * Synchronously enumerate domains available for browsing and registration. * For each domain found a DNSSD::Reply object is passed to block with #domain * set to the enumerated domain. * @@ -320,6 +320,7 @@ * DNSSD.enumerate_domains! do |r| * available_domains << r.domain * end + * rescue TimeoutError * end * puts available_domains.inspect * @@ -336,7 +337,7 @@ * call-seq: * DNSSD.enumerate_domains(flags=0, interface=DNSSD::InterfaceAny) {|reply| bloc } => serivce_handle * - * Asyncronously enumerate domains available for browsing and registration. + * Asynchronously enumerate domains available for browsing and registration. * For each domain found a DNSSD::DomainEnumReply object is passed to block. * The returned _service_handle_ can be used to control when to * stop enumerating domains (see DNSSD::Service#stop). @@ -411,13 +412,14 @@ * call-seq: * DNSSD.browse!(type, domain=nil, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => obj * - * Syncronously browse for services. + * Synchronously browse for services. * For each service found a DNSSD::Reply object is passed to block. * * timeout(6) do * DNSSD.browse!('_http._tcp') do |r| * puts "found: #{r.inspect}" * end + * rescue TimeoutError * end * */ @@ -434,7 +436,7 @@ * call-seq: * DNSSD.browse(type, domain=nil, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => service_handle * - * Asyncronously browse for services. + * Asynchronously browse for services. * For each service found a DNSSD::BrowseReply object is passed to block. * The returned _service_handle_ can be used to control when to * stop browsing for services (see DNSSD::Service#stop). @@ -524,7 +526,7 @@ * call-seq: * DNSSD.register!(name, type, domain, port, text_record=nil, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => obj * - * Syncronously register a service. A DNSSD::Reply object is passed + * Synchronously register a service. A DNSSD::Reply object is passed * to the block when the registration completes. * * DNSSD.register!("My Files", "_http._tcp", nil, 8080) do |r| @@ -544,7 +546,7 @@ * call-seq: * DNSSD.register(name, type, domain, port, text_record=nil, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => service_handle * - * Asyncronously register a service. A DNSSD::Reply object is + * Asynchronously register a service. A DNSSD::Reply object is * passed to the block when the registration completes. * The returned _service_handle_ can be used to control when to * stop the service (see DNSSD::Service#stop). @@ -623,7 +625,7 @@ * call-seq: * DNSSD.resolve!(name, type, domain, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => obj * - * Syncronously resolve a service discovered via DNSSD.browse(). + * Synchronously resolve a service discovered via DNSSD.browse(). * The service is resolved to a target host name, port number, and * text record - all contained in the DNSSD::Reply object * passed to the required block. @@ -632,6 +634,7 @@ * DNSSD.resolve!("foo bar", "_http._tcp", "local") do |r| * puts r.inspect * end + * rescue TimeoutError * end * */ @@ -647,7 +650,7 @@ * call-seq: * DNSSD.resolve(name, type, domain, flags=0, interface=DNSSD::InterfaceAny) {|reply| block } => service_handle * - * Asyncronously resolve a service discovered via DNSSD.browse(). + * Asynchronously resolve a service discovered via DNSSD.browse(). * The service is resolved to a target host name, port number, and * text record - all contained in the DNSSD::Reply object * passed to the required block. @@ -690,7 +693,7 @@ rb_define_singleton_method(cDNSSDService, "split_fullname", dnssd_service_s_split, 1); rb_define_singleton_method(cDNSSDService, "split", dnssd_service_s_split, 1); - /* Access the services underlying thread. Returns nil if the service is syncronous. */ + /* Access the services underlying thread. Returns nil if the service is synchronous. */ rb_define_attr(cDNSSDService, "thread", 1, 0); rb_define_method(cDNSSDService, "stop", dnssd_service_stop, 0);