From ba12decc4413dedf22c06545d1ec5938efa8954a Mon Sep 17 00:00:00 2001 From: James Willcox Date: Fri, 9 Sep 2005 17:12:57 +0000 Subject: add initial mono bindings git-svn-id: file:///home/lennart/svn/public/avahi/trunk@538 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- avahi-sharp/AddressResolver.cs | 132 +++++++++++++++++++++++++++++++ avahi-sharp/AvahiTest.cs | 70 +++++++++++++++++ avahi-sharp/Client.cs | 141 +++++++++++++++++++++++++++++++++ avahi-sharp/ClientException.cs | 30 +++++++ avahi-sharp/DomainBrowser.cs | 143 ++++++++++++++++++++++++++++++++++ avahi-sharp/EntryGroup.cs | 137 ++++++++++++++++++++++++++++++++ avahi-sharp/HostNameResolver.cs | 134 +++++++++++++++++++++++++++++++ avahi-sharp/Makefile.am | 20 +++++ avahi-sharp/ServiceBrowser.cs | 160 ++++++++++++++++++++++++++++++++++++++ avahi-sharp/ServiceResolver.cs | 158 +++++++++++++++++++++++++++++++++++++ avahi-sharp/ServiceTypeBrowser.cs | 139 +++++++++++++++++++++++++++++++++ avahi-sharp/Utility.cs | 70 +++++++++++++++++ configure.ac | 1 + 13 files changed, 1335 insertions(+) create mode 100644 avahi-sharp/AddressResolver.cs create mode 100644 avahi-sharp/AvahiTest.cs create mode 100644 avahi-sharp/Client.cs create mode 100644 avahi-sharp/ClientException.cs create mode 100644 avahi-sharp/DomainBrowser.cs create mode 100644 avahi-sharp/EntryGroup.cs create mode 100644 avahi-sharp/HostNameResolver.cs create mode 100644 avahi-sharp/Makefile.am create mode 100644 avahi-sharp/ServiceBrowser.cs create mode 100644 avahi-sharp/ServiceResolver.cs create mode 100644 avahi-sharp/ServiceTypeBrowser.cs create mode 100644 avahi-sharp/Utility.cs diff --git a/avahi-sharp/AddressResolver.cs b/avahi-sharp/AddressResolver.cs new file mode 100644 index 0000000..730645f --- /dev/null +++ b/avahi-sharp/AddressResolver.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections; +using System.Net; +using System.Runtime.InteropServices; +using Mono.Unix; + +namespace Avahi +{ + + internal delegate void AddressResolverCallback (IntPtr resolver, int iface, Protocol proto, + ResolverEvent revent, Protocol aproto, IntPtr address, + IntPtr hostname, IntPtr userdata); + + public delegate void HostAddressHandler (object o, string host, IPAddress address); + + public class AddressResolver : IDisposable + { + private IntPtr handle; + private Client client; + private int iface; + private Protocol proto; + private IPAddress address; + + private IPAddress currentAddress; + private string currentHost; + + private ArrayList foundListeners = new ArrayList (); + private ArrayList timeoutListeners = new ArrayList (); + + [DllImport ("avahi-client")] + private static extern IntPtr avahi_address_resolver_new (IntPtr client, int iface, Protocol proto, + IntPtr address, AddressResolverCallback cb, + IntPtr userdata); + + [DllImport ("avahi-client")] + private static extern void avahi_address_resolver_free (IntPtr handle); + + public event HostAddressHandler Found + { + add { + foundListeners.Add (value); + Start (); + } + remove { + foundListeners.Remove (value); + Stop (false); + } + } + + public event EventHandler Timeout + { + add { + timeoutListeners.Add (value); + Start (); + } + remove { + timeoutListeners.Remove (value); + Stop (false); + } + } + + public IPAddress Address + { + get { return currentAddress; } + } + + public string HostName + { + get { return currentHost; } + } + + public AddressResolver (Client client, IPAddress address) : this (client, -1, Protocol.Unspecified, address) + { + } + + public AddressResolver (Client client, int iface, Protocol proto, IPAddress address) + { + this.client = client; + this.iface = iface; + this.proto = proto; + this.address = address; + } + + ~AddressResolver () + { + Dispose (); + } + + public void Dispose () + { + Stop (true); + } + + private void Start () + { + if (handle != IntPtr.Zero || (foundListeners.Count == 0 && timeoutListeners.Count == 0)) + return; + + IntPtr addrPtr = Utility.StringToPtr (address.ToString ()); + handle = avahi_address_resolver_new (client.Handle, iface, proto, addrPtr, + OnAddressResolverCallback, IntPtr.Zero); + Utility.Free (addrPtr); + } + + private void Stop (bool force) + { + if (handle != IntPtr.Zero && (force || (foundListeners.Count == 0 && timeoutListeners.Count == 0))) { + avahi_address_resolver_free (handle); + handle = IntPtr.Zero; + } + } + + private void OnAddressResolverCallback (IntPtr resolver, int iface, Protocol proto, + ResolverEvent revent, Protocol aproto, IntPtr address, + IntPtr hostname, IntPtr userdata) + { + if (revent == ResolverEvent.Found) { + currentAddress = Utility.PtrToAddress (address); + currentHost = Utility.PtrToString (hostname); + + foreach (HostAddressHandler handler in foundListeners) + handler (this, currentHost, currentAddress); + } else { + currentAddress = null; + currentHost = null; + + foreach (EventHandler handler in timeoutListeners) + handler (this, new EventArgs ()); + } + } + } +} diff --git a/avahi-sharp/AvahiTest.cs b/avahi-sharp/AvahiTest.cs new file mode 100644 index 0000000..0507204 --- /dev/null +++ b/avahi-sharp/AvahiTest.cs @@ -0,0 +1,70 @@ +using System; +using System.Net; +using Gtk; +using Avahi; + +public class AvahiTest { + private static Client client; + + public static void Main () { + client = new Client (); + + EntryGroup eg = new EntryGroup (client); + eg.StateChanged += OnEntryGroupChanged; + eg.AddService ("foobar2", "_daap._tcp", client.DomainName, + 444, new string[] { "foo", "bar", "baz" }); + eg.Commit (); + + Application.Run (); + } + + private static void OnEntryGroupChanged (object o, EntryGroupState state) + { + Console.WriteLine ("Entry group status: " + state); + + if (state == EntryGroupState.Established) { + DomainBrowser browser = new DomainBrowser (client); + browser.DomainAdded += OnDomainAdded; + } + } + + private static void OnDomainAdded (object o, DomainInfo info) + { + Console.WriteLine ("Got domain added: " + info.Domain); + ServiceTypeBrowser stb = new ServiceTypeBrowser (client, info.Domain); + stb.ServiceTypeAdded += OnServiceTypeAdded; + } + + private static void OnServiceTypeAdded (object o, ServiceTypeInfo info) + { + Console.WriteLine ("Got service type: " + info.ServiceType); + ServiceBrowser sb = new ServiceBrowser (client, info.ServiceType, info.Domain); + sb.ServiceAdded += OnServiceAdded; + } + + private static void OnServiceAdded (object o, ServiceInfo info) + { + // Console.WriteLine ("Got service: " + info.Name); + ServiceResolver resolver = new ServiceResolver (client, info); + resolver.Found += OnServiceResolved; + } + + private static void OnServiceResolved (object o, ServiceInfo info) + { + Console.WriteLine ("Service '{0}' at {1}:{2}", info.Name, info.Host, info.Port); + AddressResolver ar = new AddressResolver (client, info.Address); + ar.Found += OnAddressResolved; + } + + private static void OnAddressResolved (object o, string host, IPAddress address) + { + Console.WriteLine ("Resolved {0} to {1}", address, host); + HostNameResolver hr = new HostNameResolver (client, host); + hr.Found += OnHostNameResolved; + } + + private static void OnHostNameResolved (object o, string host, IPAddress address) + { + Console.WriteLine ("Resolved {0} to {1}", host, address); + } +} diff --git a/avahi-sharp/Client.cs b/avahi-sharp/Client.cs new file mode 100644 index 0000000..99651cd --- /dev/null +++ b/avahi-sharp/Client.cs @@ -0,0 +1,141 @@ + +using System; +using System.Collections; +using System.Runtime.InteropServices; + +namespace Avahi +{ + internal enum ResolverEvent { + Found, + Timeout + } + + internal enum BrowserEvent { + Added, + Removed + } + + internal delegate void ClientHandler (IntPtr client, ClientState state, IntPtr userData); + + public enum Protocol { + Unspecified = 0, + IPv4 = 2, + IPv6 = 10 + } + + public enum ClientState { + Invalid, + Registering, + Running, + Collision, + Disconnected = 100 + } + + public class Client : IDisposable + { + private IntPtr handle; + + [DllImport ("avahi-client")] + private static extern IntPtr avahi_client_new (IntPtr poll, ClientHandler handler, + IntPtr userData, out int error); + + [DllImport ("avahi-client")] + private static extern void avahi_client_free (IntPtr handle); + + [DllImport ("avahi-client")] + private static extern IntPtr avahi_client_get_version_string (IntPtr handle); + + [DllImport ("avahi-client")] + private static extern IntPtr avahi_client_get_host_name (IntPtr handle); + + [DllImport ("avahi-client")] + private static extern IntPtr avahi_client_get_domain_name (IntPtr handle); + + [DllImport ("avahi-client")] + private static extern IntPtr avahi_client_get_host_name_fqdn (IntPtr handle); + + [DllImport ("avahi-client")] + private static extern ClientState avahi_client_get_state (IntPtr handle); + + [DllImport ("avahi-client")] + private static extern int avahi_client_errno (IntPtr handle); + + [DllImport ("avahi-glib")] + private static extern IntPtr avahi_glib_poll_new (IntPtr context, int priority); + + [DllImport ("avahi-glib")] + private static extern IntPtr avahi_glib_poll_get (IntPtr gpoll); + + internal IntPtr Handle + { + get { return handle; } + } + + public string Version + { + get { return Utility.PtrToString (avahi_client_get_version_string (handle)); } + } + + public string HostName + { + get { return Utility.PtrToString (avahi_client_get_host_name (handle)); } + } + + public string DomainName + { + get { return Utility.PtrToString (avahi_client_get_domain_name (handle)); } + } + + public string HostNameFqdn + { + get { return Utility.PtrToString (avahi_client_get_host_name_fqdn (handle)); } + } + + public ClientState State + { + get { return (ClientState) avahi_client_get_state (handle); } + } + + internal int LastError + { + get { return avahi_client_errno (handle); } + } + + public Client () + { + IntPtr gpoll = avahi_glib_poll_new (IntPtr.Zero, 0); + IntPtr poll = avahi_glib_poll_get (gpoll); + + int error; + handle = avahi_client_new (poll, OnClientCallback, IntPtr.Zero, out error); + if (error != 0) + throw new ClientException (error); + } + + ~Client () + { + Dispose (); + } + + public void Dispose () + { + if (handle != IntPtr.Zero) { + avahi_client_free (handle); + handle = IntPtr.Zero; + } + } + + internal void CheckError () + { + int error = LastError; + + if (error != 0) + throw new ClientException (error); + } + + private void OnClientCallback (IntPtr client, ClientState state, IntPtr userData) + { + Console.WriteLine ("Got new state: " + state); + } + } +} diff --git a/avahi-sharp/ClientException.cs b/avahi-sharp/ClientException.cs new file mode 100644 index 0000000..f38f438 --- /dev/null +++ b/avahi-sharp/ClientException.cs @@ -0,0 +1,30 @@ + +using System; +using System.Runtime.InteropServices; + +namespace Avahi +{ + public class ClientException : ApplicationException + { + private int code; + + [DllImport ("avahi-common")] + private static extern IntPtr avahi_strerror (int code); + + public int ErrorCode + { + get { return code; } + } + + internal ClientException (int code) : base (GetErrorString (code)) + { + this.code = code; + } + + private static string GetErrorString (int code) + { + IntPtr str = avahi_strerror (code); + return Utility.PtrToString (str); + } + } +} diff --git a/avahi-sharp/DomainBrowser.cs b/avahi-sharp/DomainBrowser.cs new file mode 100644 index 0000000..deed9b8 --- /dev/null +++ b/avahi-sharp/DomainBrowser.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections; +using System.Runtime.InteropServices; + +namespace Avahi +{ + internal delegate void DomainBrowserCallback (IntPtr browser, int iface, Protocol proto, BrowserEvent bevent, + IntPtr domain, IntPtr userdata); + + public enum DomainBrowserType { + Register, + RegisterDefault, + Browse, + BrowseDefault, + BrowseLegacy + } + + public struct DomainInfo + { + public int NetworkInterface; + public Protocol Protocol; + public string Domain; + } + + public delegate void DomainInfoHandler (object o, DomainInfo info); + + public class DomainBrowser : IDisposable + { + private IntPtr handle; + private ArrayList infos = new ArrayList (); + private Client client; + private int iface; + private Protocol proto; + private string domain; + private DomainBrowserType btype; + + private ArrayList addListeners = new ArrayList (); + private ArrayList removeListeners = new ArrayList (); + + [DllImport ("avahi-client")] + private static extern IntPtr avahi_domain_browser_new (IntPtr client, int iface, int proto, + IntPtr domain, int btype, DomainBrowserCallback cb, + IntPtr userdata); + + [DllImport ("avahi-client")] + private static extern void avahi_domain_browser_free (IntPtr handle); + + public event DomainInfoHandler DomainAdded + { + add { + addListeners.Add (value); + Start (); + } + remove { + addListeners.Remove (value); + Stop (false); + } + } + + public event DomainInfoHandler DomainRemoved + { + add { + removeListeners.Add (value); + Start (); + } + remove { + removeListeners.Remove (value); + Stop (false); + } + } + + public DomainInfo[] Domains + { + get { return (DomainInfo[]) infos.ToArray (typeof (DomainInfo)); } + } + + public DomainBrowser (Client client) : this (client, -1, Protocol.Unspecified, client.DomainName, + DomainBrowserType.Browse) { + } + + public DomainBrowser (Client client, int iface, Protocol proto, string domain, DomainBrowserType btype) + { + this.client = client; + this.iface = iface; + this.proto = proto; + this.domain = domain; + this.btype = btype; + } + + ~DomainBrowser () + { + Dispose (); + } + + public void Dispose () + { + Stop (true); + } + + private void Start () + { + if (handle != IntPtr.Zero || (addListeners.Count == 0 && removeListeners.Count == 0)) + return; + + IntPtr domainPtr = Utility.StringToPtr (domain); + handle = avahi_domain_browser_new (client.Handle, iface, (int) proto, domainPtr, (int) btype, + OnDomainBrowserCallback, IntPtr.Zero); + Utility.Free (domainPtr); + } + + private void Stop (bool force) + { + if (handle != IntPtr.Zero && (force || (addListeners.Count == 0 && removeListeners.Count == 0))) { + avahi_domain_browser_free (handle); + handle = IntPtr.Zero; + } + } + + private void OnDomainBrowserCallback (IntPtr browser, int iface, Protocol proto, BrowserEvent bevent, + IntPtr domain, IntPtr userdata) + { + + DomainInfo info; + info.NetworkInterface = iface; + info.Protocol = proto; + info.Domain = Utility.PtrToString (domain); + + infos.Add (info); + + if (bevent == BrowserEvent.Added) { + infos.Add (info); + + foreach (DomainInfoHandler handler in addListeners) + handler (this, info); + } else { + infos.Remove (info); + + foreach (DomainInfoHandler handler in removeListeners) + handler (this, info); + } + } + } +} diff --git a/avahi-sharp/EntryGroup.cs b/avahi-sharp/EntryGroup.cs new file mode 100644 index 0000000..74a127c --- /dev/null +++ b/avahi-sharp/EntryGroup.cs @@ -0,0 +1,137 @@ +using System; +using System.Runtime.InteropServices; + + +namespace Avahi +{ + + public enum EntryGroupState { + Uncommited, + Registering, + Established, + Collision + } + + internal delegate void EntryGroupCallback (IntPtr group, EntryGroupState state, IntPtr userdata); + public delegate void EntryGroupStateHandler (object o, EntryGroupState state); + + public class EntryGroup : IDisposable + { + private Client client; + private IntPtr handle; + + [DllImport ("avahi-client")] + private static extern IntPtr avahi_entry_group_new (IntPtr client, EntryGroupCallback cb, IntPtr userdata); + + [DllImport ("avahi-client")] + private static extern void avahi_entry_group_commit (IntPtr group); + + [DllImport ("avahi-client")] + private static extern void avahi_entry_group_reset (IntPtr group); + + [DllImport ("avahi-client")] + private static extern EntryGroupState avahi_entry_group_get_state (IntPtr group); + + [DllImport ("avahi-client")] + private static extern bool avahi_entry_group_is_empty (IntPtr group); + + [DllImport ("avahi-client")] + private static extern void avahi_entry_group_add_service_strlst (IntPtr group, int iface, Protocol proto, + IntPtr name, IntPtr type, IntPtr domain, + IntPtr host, UInt16 port, IntPtr strlst); + + [DllImport ("avahi-client")] + private static extern void avahi_entry_group_free (IntPtr group); + + [DllImport ("avahi-common")] + private static extern IntPtr avahi_string_list_new (IntPtr txt); + + [DllImport ("avahi-common")] + private static extern void avahi_string_list_add (IntPtr list, IntPtr txt); + + [DllImport ("avahi-common")] + private static extern void avahi_string_list_free (IntPtr list); + + public event EntryGroupStateHandler StateChanged; + + public EntryGroupState State + { + get { return avahi_entry_group_get_state (handle); } + } + + public bool IsEmpty + { + get { return avahi_entry_group_is_empty (handle); } + } + + public EntryGroup (Client client) + { + this.client = client; + handle = avahi_entry_group_new (client.Handle, OnEntryGroupCallback, IntPtr.Zero); + client.CheckError (); + } + + ~EntryGroup () + { + Dispose (); + } + + public void Dispose () + { + if (handle != IntPtr.Zero) { + avahi_entry_group_free (handle); + handle = IntPtr.Zero; + } + } + + public void Commit () + { + avahi_entry_group_commit (handle); + client.CheckError (); + } + + public void Reset () + { + avahi_entry_group_reset (handle); + client.CheckError (); + } + + public void AddService (string name, string type, string domain, + UInt16 port, string[] txt) + { + AddService (-1, Protocol.Unspecified, name, type, domain, null, port, txt); + } + + public void AddService (int iface, Protocol proto, string name, string type, string domain, + string host, UInt16 port, string[] txt) + { + IntPtr list = avahi_string_list_new (IntPtr.Zero); + + if (txt != null) { + foreach (string item in txt) { + IntPtr itemPtr = Utility.StringToPtr (item); + avahi_string_list_add (list, itemPtr); + Utility.Free (itemPtr); + } + } + + IntPtr namePtr = Utility.StringToPtr (name); + IntPtr typePtr = Utility.StringToPtr (type); + IntPtr domainPtr = Utility.StringToPtr (domain); + IntPtr hostPtr = Utility.StringToPtr (host); + avahi_entry_group_add_service_strlst (handle, iface, proto, namePtr, typePtr, domainPtr, + hostPtr, port, list); + avahi_string_list_free (list); + + client.CheckError (); + + Console.WriteLine ("Added service: {0}, {1}, {2}, {3}, {4}", name, type, domain, host, port); + } + + private void OnEntryGroupCallback (IntPtr group, EntryGroupState state, IntPtr userdata) + { + if (StateChanged != null) + StateChanged (this, state); + } + } +} diff --git a/avahi-sharp/HostNameResolver.cs b/avahi-sharp/HostNameResolver.cs new file mode 100644 index 0000000..62f4149 --- /dev/null +++ b/avahi-sharp/HostNameResolver.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections; +using System.Net; +using System.Runtime.InteropServices; +using Mono.Unix; + +namespace Avahi +{ + + internal delegate void HostNameResolverCallback (IntPtr resolver, int iface, Protocol proto, + ResolverEvent revent, IntPtr hostname, IntPtr address, + IntPtr userdata); + + public class HostNameResolver : IDisposable + { + private IntPtr handle; + private Client client; + private int iface; + private Protocol proto; + private string hostname; + private Protocol aproto; + + private IPAddress currentAddress; + private string currentHost; + + private ArrayList foundListeners = new ArrayList (); + private ArrayList timeoutListeners = new ArrayList (); + + [DllImport ("avahi-client")] + private static extern IntPtr avahi_host_name_resolver_new (IntPtr client, int iface, Protocol proto, + IntPtr hostname, Protocol aproto, + HostNameResolverCallback cb, IntPtr userdata); + + [DllImport ("avahi-client")] + private static extern void avahi_host_name_resolver_free (IntPtr handle); + + public event HostAddressHandler Found + { + add { + foundListeners.Add (value); + Start (); + } + remove { + foundListeners.Remove (value); + Stop (false); + } + } + + public event EventHandler Timeout + { + add { + timeoutListeners.Add (value); + Start (); + } + remove { + timeoutListeners.Remove (value); + Stop (false); + } + } + + public IPAddress Address + { + get { return currentAddress; } + } + + public string HostName + { + get { return currentHost; } + } + + public HostNameResolver (Client client, string hostname) : this (client, -1, Protocol.Unspecified, + hostname, Protocol.Unspecified) + { + } + + public HostNameResolver (Client client, int iface, Protocol proto, string hostname, + Protocol aproto) + { + this.client = client; + this.iface = iface; + this.proto = proto; + this.hostname = hostname; + this.aproto = aproto; + } + + ~HostNameResolver () + { + Dispose (); + } + + public void Dispose () + { + Stop (true); + } + + private void Start () + { + if (handle != IntPtr.Zero || (foundListeners.Count == 0 && timeoutListeners.Count == 0)) + return; + + IntPtr hostPtr = Utility.StringToPtr (hostname); + handle = avahi_host_name_resolver_new (client.Handle, iface, proto, hostPtr, aproto, + OnHostNameResolverCallback, IntPtr.Zero); + Utility.Free (hostPtr); + } + + private void Stop (bool force) + { + if (handle != IntPtr.Zero && (force || (foundListeners.Count == 0 && timeoutListeners.Count == 0))) { + avahi_host_name_resolver_free (handle); + handle = IntPtr.Zero; + } + } + + private void OnHostNameResolverCallback (IntPtr resolver, int iface, Protocol proto, + ResolverEvent revent, IntPtr hostname, IntPtr address, + IntPtr userdata) + { + if (revent == ResolverEvent.Found) { + currentAddress = Utility.PtrToAddress (address); + currentHost = Utility.PtrToString (hostname); + + foreach (HostAddressHandler handler in foundListeners) + handler (this, currentHost, currentAddress); + } else { + currentAddress = null; + currentHost = null; + + foreach (EventHandler handler in timeoutListeners) + handler (this, new EventArgs ()); + } + } + } +} diff --git a/avahi-sharp/Makefile.am b/avahi-sharp/Makefile.am new file mode 100644 index 0000000..a82f4e6 --- /dev/null +++ b/avahi-sharp/Makefile.am @@ -0,0 +1,20 @@ + + +all: avahi-sharp.dll + +AVAHISOURCES = \ + $(srcdir)/AddressResolver.cs \ + $(srcdir)/Client.cs \ + $(srcdir)/ClientException.cs \ + $(srcdir)/DomainBrowser.cs \ + $(srcdir)/EntryGroup.cs \ + $(srcdir)/HostNameResolver.cs \ + $(srcdir)/ServiceBrowser.cs \ + $(srcdir)/ServiceResolver.cs \ + $(srcdir)/ServiceTypeBrowser.cs \ + Utility.cs + +avahi-sharp.dll: $(AVAHISOURCES) + mcs -target:library -o $@ -debug $(AVAHISOURCES) -r:Mono.Posix + +CLEANFILES = avahi-sharp.dll avahi-sharp.dll.mdb diff --git a/avahi-sharp/ServiceBrowser.cs b/avahi-sharp/ServiceBrowser.cs new file mode 100644 index 0000000..975746b --- /dev/null +++ b/avahi-sharp/ServiceBrowser.cs @@ -0,0 +1,160 @@ +using System; +using System.Net; +using System.Collections; +using System.Runtime.InteropServices; + +namespace Avahi +{ + internal delegate void ServiceBrowserCallback (IntPtr browser, int iface, Protocol proto, BrowserEvent bevent, + IntPtr name, IntPtr type, IntPtr domain, IntPtr userdata); + + public struct ServiceInfo + { + public int NetworkInterface; + public Protocol Protocol; + public string Domain; + public string ServiceType; + public string Name; + + public string Host; + public IPAddress Address; + public UInt16 Port; + public byte[][] Text; + + public static ServiceInfo Zero = new ServiceInfo (); + } + + public delegate void ServiceInfoHandler (object o, ServiceInfo info); + + public class ServiceBrowser : IDisposable + { + private IntPtr handle; + private ArrayList infos = new ArrayList (); + private Client client; + private int iface; + private Protocol proto; + private string domain; + private string type; + + private ArrayList addListeners = new ArrayList (); + private ArrayList removeListeners = new ArrayList (); + + [DllImport ("avahi-client")] + private static extern IntPtr avahi_service_browser_new (IntPtr client, int iface, int proto, IntPtr type, + IntPtr domain, ServiceBrowserCallback cb, + IntPtr userdata); + + [DllImport ("avahi-client")] + private static extern void avahi_service_browser_free (IntPtr handle); + + public event ServiceInfoHandler ServiceAdded + { + add { + addListeners.Add (value); + Start (); + } + remove { + addListeners.Remove (value); + Stop (false); + } + } + + public event ServiceInfoHandler ServiceRemoved + { + add { + removeListeners.Add (value); + Start (); + } + remove { + removeListeners.Remove (value); + Stop (false); + } + } + + public ServiceInfo[] Services + { + get { return (ServiceInfo[]) infos.ToArray (typeof (ServiceInfo)); } + } + + public ServiceBrowser (Client client, string type) : this (client, type, client.DomainName) + { + } + + public ServiceBrowser (Client client, string type, string domain) : this (client, -1, Protocol.Unspecified, + type, domain) + { + } + + public ServiceBrowser (Client client, int iface, Protocol proto, string type, string domain) + { + this.client = client; + this.iface = iface; + this.proto = proto; + this.domain = domain; + this.type = type; + + + } + + ~ServiceBrowser () + { + Dispose (); + } + + public void Dispose () + { + Stop (true); + } + + private void Start () + { + if (handle != IntPtr.Zero || (addListeners.Count == 0 && removeListeners.Count == 0)) + return; + + IntPtr domainPtr = Utility.StringToPtr (domain); + IntPtr typePtr = Utility.StringToPtr (type); + handle = avahi_service_browser_new (client.Handle, iface, (int) proto, typePtr, domainPtr, + OnServiceBrowserCallback, IntPtr.Zero); + Utility.Free (domainPtr); + Utility.Free (typePtr); + } + + private void Stop (bool force) + { + if (handle != IntPtr.Zero && (force || (addListeners.Count == 0 && removeListeners.Count == 0))) { + avahi_service_browser_free (handle); + handle = IntPtr.Zero; + } + } + + private void OnServiceBrowserCallback (IntPtr browser, int iface, Protocol proto, BrowserEvent bevent, + IntPtr name, IntPtr type, IntPtr domain, IntPtr userdata) + { + + ServiceInfo info; + info.NetworkInterface = iface; + info.Protocol = proto; + info.Domain = Utility.PtrToString (domain); + info.ServiceType = Utility.PtrToString (type); + info.Name = Utility.PtrToString (name); + info.Host = null; + info.Address = null; + info.Port = 0; + info.Text = null; + + infos.Add (info); + + if (bevent == BrowserEvent.Added) { + infos.Add (info); + + foreach (ServiceInfoHandler handler in addListeners) + handler (this, info); + } else { + infos.Remove (info); + + foreach (ServiceInfoHandler handler in removeListeners) + handler (this, info); + } + } + } +} diff --git a/avahi-sharp/ServiceResolver.cs b/avahi-sharp/ServiceResolver.cs new file mode 100644 index 0000000..af3bf5b --- /dev/null +++ b/avahi-sharp/ServiceResolver.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections; +using System.Net; +using System.Runtime.InteropServices; +using Mono.Unix; + +namespace Avahi +{ + + internal delegate void ServiceResolverCallback (IntPtr resolver, int iface, Protocol proto, + ResolverEvent revent, IntPtr name, IntPtr type, + IntPtr domain, IntPtr host, IntPtr address, + UInt16 port, IntPtr txt, IntPtr userdata); + + public class ServiceResolver : IDisposable + { + private IntPtr handle; + private ServiceInfo currentInfo; + private Client client; + private int iface; + private Protocol proto; + private string name; + private string type; + private string domain; + private Protocol aproto; + + private ArrayList foundListeners = new ArrayList (); + private ArrayList timeoutListeners = new ArrayList (); + + [DllImport ("avahi-client")] + private static extern IntPtr avahi_service_resolver_new (IntPtr client, int iface, Protocol proto, + IntPtr name, IntPtr type, IntPtr domain, + Protocol aproto, ServiceResolverCallback cb, + IntPtr userdata); + + [DllImport ("avahi-client")] + private static extern void avahi_service_resolver_free (IntPtr handle); + + public event ServiceInfoHandler Found + { + add { + foundListeners.Add (value); + Start (); + } + remove { + foundListeners.Remove (value); + Stop (false); + } + } + + public event EventHandler Timeout + { + add { + timeoutListeners.Add (value); + Start (); + } + remove { + timeoutListeners.Remove (value); + Stop (false); + } + } + + public ServiceInfo Service + { + get { return currentInfo; } + } + + public ServiceResolver (Client client, string name, string type, string domain) : this (client, -1, + Protocol.Unspecified, + name, type, domain, + Protocol.Unspecified) + { + } + + public ServiceResolver (Client client, ServiceInfo service) : this (client, service.NetworkInterface, + service.Protocol, service.Name, + service.ServiceType, service.Domain, + Protocol.Unspecified) + { + } + + public ServiceResolver (Client client, int iface, Protocol proto, string name, + string type, string domain, Protocol aproto) + { + this.client = client; + this.iface = iface; + this.proto = proto; + this.name = name; + this.type = type; + this.domain = domain; + this.aproto = aproto; + + + } + + ~ServiceResolver () + { + Dispose (); + } + + public void Dispose () + { + Stop (true); + } + + private void Start () + { + if (handle != IntPtr.Zero || (foundListeners.Count == 0 && timeoutListeners.Count == 0)) + return; + + IntPtr namePtr = Utility.StringToPtr (name); + IntPtr typePtr = Utility.StringToPtr (type); + IntPtr domainPtr = Utility.StringToPtr (domain); + handle = avahi_service_resolver_new (client.Handle, iface, proto, namePtr, typePtr, domainPtr, + aproto, OnServiceResolverCallback, IntPtr.Zero); + Utility.Free (namePtr); + Utility.Free (typePtr); + Utility.Free (domainPtr); + } + + private void Stop (bool force) + { + if (handle != IntPtr.Zero && (force || (foundListeners.Count == 0 && timeoutListeners.Count == 0))) { + avahi_service_resolver_free (handle); + handle = IntPtr.Zero; + } + } + + private void OnServiceResolverCallback (IntPtr resolver, int iface, Protocol proto, + ResolverEvent revent, IntPtr name, IntPtr type, + IntPtr domain, IntPtr host, IntPtr address, + UInt16 port, IntPtr txt, IntPtr userdata) + { + ServiceInfo info; + info.NetworkInterface = iface; + info.Protocol = proto; + info.Domain = Utility.PtrToString (domain); + info.ServiceType = Utility.PtrToString (type); + info.Name = Utility.PtrToString (name); + info.Host = Utility.PtrToString (host); + info.Address = Utility.PtrToAddress (address); + info.Port = port; + info.Text = null; + + if (revent == ResolverEvent.Found) { + currentInfo = info; + + foreach (ServiceInfoHandler handler in foundListeners) + handler (this, info); + } else { + currentInfo = ServiceInfo.Zero; + + foreach (EventHandler handler in timeoutListeners) + handler (this, new EventArgs ()); + } + } + } +} diff --git a/avahi-sharp/ServiceTypeBrowser.cs b/avahi-sharp/ServiceTypeBrowser.cs new file mode 100644 index 0000000..0334b96 --- /dev/null +++ b/avahi-sharp/ServiceTypeBrowser.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections; +using System.Runtime.InteropServices; + +namespace Avahi +{ + internal delegate void ServiceTypeBrowserCallback (IntPtr browser, int iface, Protocol proto, BrowserEvent bevent, + IntPtr type, IntPtr domain, IntPtr userdata); + + public struct ServiceTypeInfo + { + public int NetworkInterface; + public Protocol Protocol; + public string Domain; + public string ServiceType; + } + + public delegate void ServiceTypeInfoHandler (object o, ServiceTypeInfo info); + + public class ServiceTypeBrowser : IDisposable + { + private IntPtr handle; + private ArrayList infos = new ArrayList (); + private Client client; + private int iface; + private Protocol proto; + private string domain; + + private ArrayList addListeners = new ArrayList (); + private ArrayList removeListeners = new ArrayList (); + + [DllImport ("avahi-client")] + private static extern IntPtr avahi_service_type_browser_new (IntPtr client, int iface, int proto, + IntPtr domain, ServiceTypeBrowserCallback cb, + IntPtr userdata); + + [DllImport ("avahi-client")] + private static extern void avahi_service_type_browser_free (IntPtr handle); + + public event ServiceTypeInfoHandler ServiceTypeAdded + { + add { + addListeners.Add (value); + Start (); + } + remove { + addListeners.Remove (value); + Stop (false); + } + } + + public event ServiceTypeInfoHandler ServiceTypeRemoved + { + add { + removeListeners.Add (value); + Start (); + } + remove { + removeListeners.Remove (value); + Stop (false); + } + } + + public ServiceTypeInfo[] ServiceTypes + { + get { return (ServiceTypeInfo[]) infos.ToArray (typeof (ServiceTypeInfo)); } + } + + public ServiceTypeBrowser (Client client) : this (client, client.DomainName) + { + } + + public ServiceTypeBrowser (Client client, string domain) : this (client, -1, Protocol.Unspecified, domain) + { + } + + public ServiceTypeBrowser (Client client, int iface, Protocol proto, string domain) + { + this.client = client; + this.iface = iface; + this.proto = proto; + this.domain = domain; + } + + ~ServiceTypeBrowser () + { + Dispose (); + } + + public void Dispose () + { + Stop (true); + } + + private void Start () + { + if (handle != IntPtr.Zero || (addListeners.Count == 0 && removeListeners.Count == 0)) + return; + + IntPtr domainPtr = Utility.StringToPtr (domain); + handle = avahi_service_type_browser_new (client.Handle, iface, (int) proto, domainPtr, + OnServiceTypeBrowserCallback, IntPtr.Zero); + Utility.Free (domainPtr); + } + + private void Stop (bool force) + { + if (handle != IntPtr.Zero && (force || (addListeners.Count == 0 && removeListeners.Count == 0))) { + avahi_service_type_browser_free (handle); + handle = IntPtr.Zero; + } + } + + private void OnServiceTypeBrowserCallback (IntPtr browser, int iface, Protocol proto, BrowserEvent bevent, + IntPtr type, IntPtr domain, IntPtr userdata) + { + + ServiceTypeInfo info; + info.NetworkInterface = iface; + info.Protocol = proto; + info.Domain = Utility.PtrToString (domain); + info.ServiceType = Utility.PtrToString (type); + + infos.Add (info); + + if (bevent == BrowserEvent.Added) { + infos.Add (info); + + foreach (ServiceTypeInfoHandler handler in addListeners) + handler (this, info); + } else { + infos.Remove (info); + + foreach (ServiceTypeInfoHandler handler in removeListeners) + handler (this, info); + } + } + } +} diff --git a/avahi-sharp/Utility.cs b/avahi-sharp/Utility.cs new file mode 100644 index 0000000..c621bed --- /dev/null +++ b/avahi-sharp/Utility.cs @@ -0,0 +1,70 @@ +using System; +using System.Net; +using System.Text; +using System.Runtime.InteropServices; +using Mono.Unix; + + +namespace Avahi +{ + internal class Utility + { + [DllImport ("libc")] + private static extern int strlen (IntPtr ptr); + + [DllImport ("avahi-common")] + private static extern IntPtr avahi_address_snprint (IntPtr buf, int size, IntPtr address); + + public static string PtrToString (IntPtr ptr) + { + if (ptr == IntPtr.Zero) + return null; + + int len = strlen (ptr); + byte[] bytes = new byte[len]; + Marshal.Copy (ptr, bytes, 0, len); + return Encoding.UTF8.GetString (bytes); + } + + public static string PtrToStringFree (IntPtr ptr) + { + if (ptr == IntPtr.Zero) + return null; + + string ret = PtrToString (ptr); + Free (ptr); + return ret; + } + + public static IntPtr StringToPtr (string str) + { + if (str == null) + return IntPtr.Zero; + + byte[] bytes = Encoding.UTF8.GetBytes (str); + IntPtr buf = Stdlib.malloc ((uint) bytes.Length + 1); + Marshal.Copy (bytes, 0, buf, bytes.Length); + Marshal.WriteByte (buf, bytes.Length, 0); + return buf; + } + + public static void Free (IntPtr ptr) + { + Stdlib.free (ptr); + } + + public static IPAddress PtrToAddress (IntPtr ptr) + { + IPAddress address = null; + + if (ptr != IntPtr.Zero) { + IntPtr buf = Stdlib.malloc (256); + IntPtr addrPtr = avahi_address_snprint (buf, 256, ptr); + address = IPAddress.Parse (Utility.PtrToString (addrPtr)); + Utility.Free (addrPtr); + } + + return address; + } + } +} diff --git a/configure.ac b/configure.ac index 8e63c4d..2986334 100644 --- a/configure.ac +++ b/configure.ac @@ -488,6 +488,7 @@ common/Makefile man/Makefile tests/Makefile service-type-database/Makefile +avahi-sharp/Makefile ]) AC_OUTPUT -- cgit