/*** This file is part of avahi. avahi is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. avahi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with avahi; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ using System; using System.Threading; using System.Collections; using System.Runtime.InteropServices; using Mono.Unix; using Mono.Unix.Native; using Stdlib = Mono.Unix.Native.Stdlib; namespace Avahi { internal enum ResolverEvent { Found, Failure } internal enum BrowserEvent { Added, Removed, CacheExhausted, AllForNow, Failure } internal delegate int PollCallback (IntPtr ufds, uint nfds, int timeout); internal delegate void ClientCallback (IntPtr client, ClientState state, IntPtr userData); public delegate void ClientStateHandler (object o, ClientStateArgs state); public class ClientStateArgs : EventArgs { private ClientState state; private ErrorCode error; public ClientState State { get { return state; } } public ErrorCode Error { get { return error; } } public ClientStateArgs (ClientState state, ErrorCode error) { this.state = state; this.error = error; } } public enum Protocol { Unspecified = -1, IPv4 = 0, IPv6 = 1 } internal enum ServerState { Invalid, Registering, Running, Collision } public enum ClientState { Registering = ServerState.Registering, Running = ServerState.Running, Collision = ServerState.Collision, Failure = 100, Connecting = 101 } [Flags] public enum LookupFlags { None = 0, UseWideArea = 1, UseMulticast = 2, NoTxt = 4, NoAddress = 8 } [Flags] public enum LookupResultFlags { None = 0, Cached = 1, WideArea = 2, Multicast = 4, Local = 8, OurOwn = 16, } [Flags] public enum ClientFlags { None = 0, IgnoreUserConfig = 1, NoFail = 2 } public class Client : IDisposable { private IntPtr handle; private ClientCallback cb; private PollCallback pollcb; private IntPtr spoll; private Thread thread; [DllImport ("avahi-client")] private static extern IntPtr avahi_client_new (IntPtr poll, ClientFlags flags, ClientCallback 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-common")] private static extern IntPtr avahi_simple_poll_new (); [DllImport ("avahi-common")] private static extern IntPtr avahi_simple_poll_get (IntPtr spoll); [DllImport ("avahi-common")] private static extern void avahi_simple_poll_free (IntPtr spoll); [DllImport ("avahi-common")] private static extern int avahi_simple_poll_loop (IntPtr spoll); [DllImport ("avahi-common")] private static extern void avahi_simple_poll_set_func (IntPtr spoll, PollCallback cb); [DllImport ("avahi-common")] private static extern void avahi_simple_poll_quit (IntPtr spoll); [DllImport ("avahi-client")] private static extern uint avahi_client_get_local_service_cookie (IntPtr client); [DllImport ("avahi-common")] private static extern int avahi_service_name_join (IntPtr buf, int len, byte[] name, byte[] type, byte[] domain); [DllImport ("avahi-common")] private static extern int avahi_service_name_split (byte[] service, IntPtr name, int name_len, IntPtr type, int type_len, IntPtr domain, int domain_len); [DllImport ("libc")] private static extern int poll(IntPtr ufds, uint nfds, int timeout); public event ClientStateHandler StateChanged; internal IntPtr Handle { get { return handle; } } public string Version { get { lock (this) { return Utility.PtrToString (avahi_client_get_version_string (handle)); } } } public string HostName { get { lock (this) { return Utility.PtrToString (avahi_client_get_host_name (handle)); } } } public string DomainName { get { lock (this) { return Utility.PtrToString (avahi_client_get_domain_name (handle)); } } } public string HostNameFqdn { get { lock (this) { return Utility.PtrToString (avahi_client_get_host_name_fqdn (handle)); } } } public ClientState State { get { lock (this) { return (ClientState) avahi_client_get_state (handle); } } } public uint LocalServiceCookie { get { lock (this) { return avahi_client_get_local_service_cookie (handle); } } } internal ErrorCode LastError { get { lock (this) { return (ErrorCode) avahi_client_errno (handle); } } } public Client (ClientFlags flags) { spoll = avahi_simple_poll_new (); pollcb = OnPollCallback; avahi_simple_poll_set_func (spoll, pollcb); IntPtr poll = avahi_simple_poll_get (spoll); cb = OnClientCallback; int error; handle = avahi_client_new (poll, flags, cb, IntPtr.Zero, out error); if (error != 0) throw new ClientException (error); thread = new Thread (PollLoop); thread.IsBackground = true; thread.Start (); } public Client () : this (ClientFlags.None) { } ~Client () { Dispose (); } public void Dispose () { if (handle != IntPtr.Zero) { lock (this) { avahi_client_free (handle); handle = IntPtr.Zero; avahi_simple_poll_quit (spoll); Monitor.Wait (this); avahi_simple_poll_free (spoll); } } } public static string JoinServiceName (string name, string type, string domain) { int len = 4 * (name.Length + type.Length + domain.Length) + 4; IntPtr buf = Stdlib.malloc ((ulong) len); int ret = avahi_service_name_join (buf, len, Utility.StringToBytes (name), Utility.StringToBytes (type), Utility.StringToBytes (domain)); if (ret < 0) { Utility.Free (buf); return null; // FIXME, should throw exception } string service = Utility.PtrToString (buf); Utility.Free (buf); return service; } public static void SplitServiceName (string service, out string name, out string type, out string domain) { int len = 1024; IntPtr namePtr = Stdlib.malloc ((ulong) len); IntPtr typePtr = Stdlib.malloc ((ulong) len); IntPtr domainPtr = Stdlib.malloc ((ulong) len); int ret = avahi_service_name_split (Utility.StringToBytes (service), namePtr, len, typePtr, len, domainPtr, len); if (ret < 0) { Utility.Free (namePtr); Utility.Free (typePtr); Utility.Free (domainPtr); name = null; type = null; domain = null; return; } name = Utility.PtrToString (namePtr); type = Utility.PtrToString (typePtr); domain = Utility.PtrToString (domainPtr); Utility.Free (namePtr); Utility.Free (typePtr); Utility.Free (domainPtr); } internal void ThrowError () { ErrorCode error = LastError; if (error != ErrorCode.Ok) throw new ClientException (error); } private void OnClientCallback (IntPtr client, ClientState state, IntPtr userData) { if (StateChanged != null) StateChanged (this, new ClientStateArgs (state, LastError)); } private int OnPollCallback (IntPtr ufds, uint nfds, int timeout) { Monitor.Exit (this); int result = poll (ufds, nfds, timeout); Monitor.Enter (this); return result; } private void PollLoop () { try { lock (this) { avahi_simple_poll_loop (spoll); Monitor.Pulse (this); } } catch (Exception e) { Console.Error.WriteLine ("Error in avahi-sharp event loop: " + e); } } } }