/trunk/src/Mono.Zeroconf.Providers.Bonjour/Mono.Zeroconf.Providers.Bonjour/BrowseService.cs |
@@ -1,209 +1,231 @@ |
// |
// BrowseService.cs |
// |
// Authors: |
// Aaron Bockover <abockover@novell.com> |
// |
// Copyright (C) 2006-2008 Novell, Inc (http://www.novell.com) |
// |
// Permission is hereby granted, free of charge, to any person obtaining |
// a copy of this software and associated documentation files (the |
// "Software"), to deal in the Software without restriction, including |
// without limitation the rights to use, copy, modify, merge, publish, |
// distribute, sublicense, and/or sell copies of the Software, and to |
// permit persons to whom the Software is furnished to do so, subject to |
// the following conditions: |
// |
// The above copyright notice and this permission notice shall be |
// included in all copies or substantial portions of the Software. |
// |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
// |
|
using System; |
using System.Net; |
using System.Text; |
using System.Collections; |
using System.Runtime.InteropServices; |
|
namespace Mono.Zeroconf.Providers.Bonjour |
{ |
public sealed class BrowseService : Service, IResolvableService |
{ |
private bool is_resolved = false; |
private bool resolve_pending = false; |
|
private Native.DNSServiceResolveReply resolve_reply_handler; |
private Native.DNSServiceQueryRecordReply query_record_reply_handler; |
|
public event ServiceResolvedEventHandler Resolved; |
|
public BrowseService() |
{ |
SetupCallbacks(); |
} |
|
public BrowseService(string name, string replyDomain, string regtype) : base(name, replyDomain, regtype) |
{ |
SetupCallbacks(); |
} |
|
private void SetupCallbacks() |
{ |
resolve_reply_handler = new Native.DNSServiceResolveReply(OnResolveReply); |
query_record_reply_handler = new Native.DNSServiceQueryRecordReply(OnQueryRecordReply); |
} |
|
public void Resolve() |
{ |
Resolve(false); |
} |
|
public void Resolve(bool requery) |
{ |
if(resolve_pending) { |
return; |
} |
|
is_resolved = false; |
resolve_pending = true; |
|
if(requery) { |
InterfaceIndex = 0; |
} |
|
ServiceRef sd_ref; |
ServiceError error = Native.DNSServiceResolve(out sd_ref, ServiceFlags.None, |
InterfaceIndex, Name, RegType, ReplyDomain, resolve_reply_handler, IntPtr.Zero); |
|
if(error != ServiceError.NoError) { |
throw new ServiceErrorException(error); |
} |
|
sd_ref.Process(); |
} |
|
public void RefreshTxtRecord() |
{ |
// Should probably make this async? |
|
ServiceRef sd_ref; |
ServiceError error = Native.DNSServiceQueryRecord(out sd_ref, ServiceFlags.None, 0, |
fullname, ServiceType.TXT, ServiceClass.IN, query_record_reply_handler, IntPtr.Zero); |
|
if(error != ServiceError.NoError) { |
throw new ServiceErrorException(error); |
} |
|
sd_ref.Process(); |
} |
|
private void OnResolveReply(ServiceRef sdRef, ServiceFlags flags, uint interfaceIndex, |
ServiceError errorCode, string fullname, string hosttarget, ushort port, ushort txtLen, |
IntPtr txtRecord, IntPtr contex) |
{ |
is_resolved = true; |
resolve_pending = false; |
|
InterfaceIndex = interfaceIndex; |
FullName = fullname; |
this.port = port; |
TxtRecord = new TxtRecord(txtLen, txtRecord); |
|
sdRef.Deallocate(); |
|
// Run an A query to resolve the IP address |
ServiceRef sd_ref; |
|
if (AddressProtocol == AddressProtocol.Any || AddressProtocol == AddressProtocol.IPv4) { |
ServiceError error = Native.DNSServiceQueryRecord(out sd_ref, ServiceFlags.None, interfaceIndex, |
hosttarget, ServiceType.A, ServiceClass.IN, query_record_reply_handler, IntPtr.Zero); |
|
if(error != ServiceError.NoError) { |
throw new ServiceErrorException(error); |
} |
|
sd_ref.Process(); |
} |
|
if (AddressProtocol == AddressProtocol.Any || AddressProtocol == AddressProtocol.IPv6) { |
ServiceError error = Native.DNSServiceQueryRecord(out sd_ref, ServiceFlags.None, interfaceIndex, |
hosttarget, ServiceType.AAAA, ServiceClass.IN, query_record_reply_handler, IntPtr.Zero); |
|
if(error != ServiceError.NoError) { |
throw new ServiceErrorException(error); |
} |
|
sd_ref.Process(); |
} |
} |
|
private void OnQueryRecordReply(ServiceRef sdRef, ServiceFlags flags, uint interfaceIndex, |
ServiceError errorCode, string fullname, ServiceType rrtype, ServiceClass rrclass, ushort rdlen, |
IntPtr rdata, uint ttl, IntPtr context) |
{ |
switch(rrtype) { |
case ServiceType.A: |
IPAddress address; |
|
if(rdlen == 4) { |
// ~4.5 times faster than Marshal.Copy into byte[4] |
uint address_raw = (uint)(Marshal.ReadByte (rdata, 3) << 24); |
address_raw |= (uint)(Marshal.ReadByte (rdata, 2) << 16); |
address_raw |= (uint)(Marshal.ReadByte (rdata, 1) << 8); |
address_raw |= (uint)Marshal.ReadByte (rdata, 0); |
|
address = new IPAddress(address_raw); |
} else if(rdlen == 16) { |
byte [] address_raw = new byte[rdlen]; |
Marshal.Copy(rdata, address_raw, 0, rdlen); |
address = new IPAddress(address_raw, interfaceIndex); |
} else { |
break; |
} |
|
if(hostentry == null) { |
hostentry = new IPHostEntry(); |
hostentry.HostName = hosttarget; |
} |
|
if(hostentry.AddressList != null) { |
ArrayList list = new ArrayList(hostentry.AddressList); |
list.Add(address); |
hostentry.AddressList = list.ToArray(typeof(IPAddress)) as IPAddress []; |
} else { |
hostentry.AddressList = new IPAddress [] { address }; |
} |
|
ServiceResolvedEventHandler handler = Resolved; |
if(handler != null) { |
handler(this, new ServiceResolvedEventArgs(this)); |
} |
|
break; |
case ServiceType.TXT: |
if(TxtRecord != null) { |
TxtRecord.Dispose(); |
} |
|
TxtRecord = new TxtRecord(rdlen, rdata); |
break; |
default: |
break; |
} |
|
sdRef.Deallocate(); |
} |
|
public bool IsResolved { |
get { return is_resolved; } |
} |
} |
} |
|
// |
// BrowseService.cs |
// |
// Authors: |
// Aaron Bockover <abockover@novell.com> |
// |
// Copyright (C) 2006-2008 Novell, Inc (http://www.novell.com) |
// |
// Permission is hereby granted, free of charge, to any person obtaining |
// a copy of this software and associated documentation files (the |
// "Software"), to deal in the Software without restriction, including |
// without limitation the rights to use, copy, modify, merge, publish, |
// distribute, sublicense, and/or sell copies of the Software, and to |
// permit persons to whom the Software is furnished to do so, subject to |
// the following conditions: |
// |
// The above copyright notice and this permission notice shall be |
// included in all copies or substantial portions of the Software. |
// |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
// |
|
using System; |
using System.Net; |
using System.Text; |
using System.Collections; |
using System.Runtime.InteropServices; |
|
namespace Mono.Zeroconf.Providers.Bonjour |
{ |
public sealed class BrowseService : Service, IResolvableService |
{ |
private bool is_resolved = false; |
private bool resolve_pending = false; |
|
private Native.DNSServiceResolveReply resolve_reply_handler; |
private Native.DNSServiceQueryRecordReply query_record_reply_handler; |
|
public event ServiceResolvedEventHandler Resolved; |
|
public BrowseService() |
{ |
SetupCallbacks(); |
} |
|
public BrowseService(string name, string replyDomain, string regtype) : base(name, replyDomain, regtype) |
{ |
SetupCallbacks(); |
} |
|
private void SetupCallbacks() |
{ |
resolve_reply_handler = new Native.DNSServiceResolveReply(OnResolveReply); |
query_record_reply_handler = new Native.DNSServiceQueryRecordReply(OnQueryRecordReply); |
resolveAction = new Action<bool>(Resolve); |
} |
|
private Action<bool> resolveAction; |
|
public void Resolve() |
{ |
Resolve(false); |
// If people call this in a ServiceAdded eventhandler (which they genreally do) |
// We need to invoke onto another thread otherwise we block processing any more results. |
|
resolveAction.BeginInvoke(false, null, null); |
} |
|
public void Resolve(bool requery) |
{ |
if(resolve_pending) { |
return; |
} |
|
is_resolved = false; |
resolve_pending = true; |
|
if(requery) { |
InterfaceIndex = 0; |
} |
|
ServiceRef sd_ref; |
ServiceError error = Native.DNSServiceResolve(out sd_ref, ServiceFlags.None, |
InterfaceIndex, Encoding.UTF8.GetBytes(Name), RegType, ReplyDomain, resolve_reply_handler, IntPtr.Zero); |
|
if(error != ServiceError.NoError) { |
throw new ServiceErrorException(error); |
} |
|
sd_ref.Process(); |
} |
|
public void RefreshTxtRecord() |
{ |
// Should probably make this async? |
|
ServiceRef sd_ref; |
ServiceError error = Native.DNSServiceQueryRecord(out sd_ref, ServiceFlags.None, 0, |
fullname, ServiceType.TXT, ServiceClass.IN, query_record_reply_handler, IntPtr.Zero); |
|
if(error != ServiceError.NoError) { |
throw new ServiceErrorException(error); |
} |
|
sd_ref.Process(); |
} |
|
private void OnResolveReply(ServiceRef sdRef, ServiceFlags flags, uint interfaceIndex, |
ServiceError errorCode, IntPtr fullname, string hosttarget, ushort port, ushort txtLen, |
IntPtr txtRecord, IntPtr contex) |
{ |
is_resolved = true; |
resolve_pending = false; |
|
InterfaceIndex = interfaceIndex; |
FullName = Native.Utf8toString(fullname); |
this.port = (ushort)IPAddress.NetworkToHostOrder((short)port); |
TxtRecord = new TxtRecord(txtLen, txtRecord); |
this.hosttarget = hosttarget; |
|
sdRef.Deallocate(); |
|
// Run an A query to resolve the IP address |
ServiceRef sd_ref; |
|
if (AddressProtocol == AddressProtocol.Any || AddressProtocol == AddressProtocol.IPv4) { |
ServiceError error = Native.DNSServiceQueryRecord(out sd_ref, ServiceFlags.None, interfaceIndex, |
hosttarget, ServiceType.A, ServiceClass.IN, query_record_reply_handler, IntPtr.Zero); |
|
if(error != ServiceError.NoError) { |
throw new ServiceErrorException(error); |
} |
|
sd_ref.Process(); |
} |
|
if (AddressProtocol == AddressProtocol.Any || AddressProtocol == AddressProtocol.IPv6) { |
ServiceError error = Native.DNSServiceQueryRecord(out sd_ref, ServiceFlags.None, interfaceIndex, |
hosttarget, ServiceType.AAAA, ServiceClass.IN, query_record_reply_handler, IntPtr.Zero); |
|
if(error != ServiceError.NoError) { |
throw new ServiceErrorException(error); |
} |
|
sd_ref.Process(); |
} |
|
if (hostentry.AddressList != null) |
{ |
ServiceResolvedEventHandler handler = Resolved; |
if (handler != null) |
{ |
handler(this, new ServiceResolvedEventArgs(this)); |
} |
} |
} |
|
private void OnQueryRecordReply(ServiceRef sdRef, ServiceFlags flags, uint interfaceIndex, |
ServiceError errorCode, string fullname, ServiceType rrtype, ServiceClass rrclass, ushort rdlen, |
IntPtr rdata, uint ttl, IntPtr context) |
{ |
switch(rrtype) { |
case ServiceType.A: |
case ServiceType.AAAA: |
IPAddress address; |
|
if(rdlen == 4) { |
// ~4.5 times faster than Marshal.Copy into byte[4] |
uint address_raw = (uint)(Marshal.ReadByte (rdata, 3) << 24); |
address_raw |= (uint)(Marshal.ReadByte (rdata, 2) << 16); |
address_raw |= (uint)(Marshal.ReadByte (rdata, 1) << 8); |
address_raw |= (uint)Marshal.ReadByte (rdata, 0); |
|
address = new IPAddress(address_raw); |
} else if(rdlen == 16) { |
byte [] address_raw = new byte[rdlen]; |
Marshal.Copy(rdata, address_raw, 0, rdlen); |
address = new IPAddress(address_raw, interfaceIndex); |
} else { |
break; |
} |
|
if(hostentry == null) { |
hostentry = new IPHostEntry(); |
hostentry.HostName = hosttarget; |
} |
|
if(hostentry.AddressList != null) { |
ArrayList list = new ArrayList(hostentry.AddressList); |
list.Add(address); |
hostentry.AddressList = list.ToArray(typeof(IPAddress)) as IPAddress []; |
} else { |
hostentry.AddressList = new IPAddress [] { address }; |
} |
|
ServiceResolvedEventHandler handler = Resolved; |
if(handler != null) { |
handler(this, new ServiceResolvedEventArgs(this)); |
} |
|
break; |
case ServiceType.TXT: |
if(TxtRecord != null) { |
TxtRecord.Dispose(); |
} |
|
TxtRecord = new TxtRecord(rdlen, rdata); |
break; |
default: |
break; |
} |
|
|
if ((flags & ServiceFlags.MoreComing) != ServiceFlags.MoreComing) |
{ |
sdRef.Deallocate(); |
} |
} |
|
public bool IsResolved { |
get { return is_resolved; } |
} |
} |
} |
|
/trunk/src/Mono.Zeroconf.Providers.Bonjour/Mono.Zeroconf.Providers.Bonjour/Native.cs |
@@ -27,6 +27,7 @@ |
// |
|
using System; |
using System.Text; |
using System.Runtime.InteropServices; |
|
namespace Mono.Zeroconf.Providers.Bonjour |
@@ -50,7 +51,7 @@ |
// DNSServiceBrowse |
|
public delegate void DNSServiceBrowseReply(ServiceRef sdRef, ServiceFlags flags, uint interfaceIndex, |
ServiceError errorCode, string serviceName, string regtype, string replyDomain, |
ServiceError errorCode, IntPtr serviceName, string regtype, string replyDomain, |
IntPtr context); |
|
[DllImport("dnssd.dll")] |
@@ -61,22 +62,23 @@ |
// DNSServiceResolve |
|
public delegate void DNSServiceResolveReply(ServiceRef sdRef, ServiceFlags flags, uint interfaceIndex, |
ServiceError errorCode, string fullname, string hosttarget, ushort port, ushort txtLen, |
ServiceError errorCode, IntPtr fullname, string hosttarget, ushort port, ushort txtLen, |
IntPtr txtRecord, IntPtr context); |
|
[DllImport("dnssd.dll")] |
public static extern ServiceError DNSServiceResolve(out ServiceRef sdRef, ServiceFlags flags, |
uint interfaceIndex, string name, string regtype, string domain, DNSServiceResolveReply callBack, |
uint interfaceIndex, byte[] name, string regtype, string domain, DNSServiceResolveReply callBack, |
IntPtr context); |
|
// DNSServiceRegister |
|
public delegate void DNSServiceRegisterReply(ServiceRef sdRef, ServiceFlags flags, ServiceError errorCode, |
string name, string regtype, string domain, IntPtr context); |
|
IntPtr name, string regtype, string domain, IntPtr context); |
|
|
[DllImport("dnssd.dll")] |
public static extern ServiceError DNSServiceRegister(out ServiceRef sdRef, ServiceFlags flags, |
uint interfaceIndex, string name, string regtype, string domain, string host, ushort port, |
uint interfaceIndex, byte[] name, string regtype, string domain, string host, ushort port, |
ushort txtLen, byte [] txtRecord, DNSServiceRegisterReply callBack, IntPtr context); |
|
// DNSServiceQueryRecord |
@@ -117,5 +119,17 @@ |
|
[DllImport("dnssd.dll")] |
public static extern ushort TXTRecordGetCount(ushort txtLen, IntPtr txtRecord); |
|
public static string Utf8toString(IntPtr ptr) |
{ |
int len = 0; |
while (Marshal.ReadByte(ptr, len) != 0) |
{ |
len++; |
} |
byte[] raw = new byte[len]; |
Marshal.Copy(ptr, raw, 0, len); |
return Encoding.UTF8.GetString(raw); |
} |
} |
} |