1/*** 2 This file is part of avahi. 3 4 avahi is free software; you can redistribute it and/or modify it 5 under the terms of the GNU Lesser General Public License as 6 published by the Free Software Foundation; either version 2.1 of the 7 License, or (at your option) any later version. 8 9 avahi is distributed in the hope that it will be useful, but WITHOUT 10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General 12 Public License for more details. 13 14 You should have received a copy of the GNU Lesser General Public 15 License along with avahi; if not, write to the Free Software 16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 17 USA. 18***/ 19 20 21using System; 22using System.Threading; 23using System.Collections; 24using System.Runtime.InteropServices; 25using Mono.Unix; 26using Mono.Unix.Native; 27 28using Stdlib = Mono.Unix.Native.Stdlib; 29 30namespace Avahi 31{ 32 internal enum ResolverEvent { 33 Found, 34 Failure 35 } 36 37 internal enum BrowserEvent { 38 Added, 39 Removed, 40 CacheExhausted, 41 AllForNow, 42 Failure 43 } 44 45 internal delegate int PollCallback (IntPtr ufds, uint nfds, int timeout); 46 internal delegate void ClientCallback (IntPtr client, ClientState state, IntPtr userData); 47 48 public delegate void ClientStateHandler (object o, ClientStateArgs state); 49 50 public class ClientStateArgs : EventArgs 51 { 52 private ClientState state; 53 private ErrorCode error; 54 55 public ClientState State 56 { 57 get { return state; } 58 } 59 60 public ErrorCode Error 61 { 62 get { return error; } 63 } 64 65 public ClientStateArgs (ClientState state, ErrorCode error) 66 { 67 this.state = state; 68 this.error = error; 69 } 70 } 71 72 public enum Protocol { 73 Unspecified = -1, 74 IPv4 = 0, 75 IPv6 = 1 76 } 77 78 internal enum ServerState { 79 Invalid, 80 Registering, 81 Running, 82 Collision 83 } 84 85 public enum ClientState { 86 Registering = ServerState.Registering, 87 Running = ServerState.Running, 88 Collision = ServerState.Collision, 89 Failure = 100, 90 Connecting = 101 91 } 92 93 [Flags] 94 public enum LookupFlags { 95 None = 0, 96 UseWideArea = 1, 97 UseMulticast = 2, 98 NoTxt = 4, 99 NoAddress = 8 100 } 101 102 [Flags] 103 public enum LookupResultFlags { 104 None = 0, 105 Cached = 1, 106 WideArea = 2, 107 Multicast = 4, 108 Local = 8, 109 OurOwn = 16, 110 } 111 112 [Flags] 113 public enum ClientFlags { 114 None = 0, 115 IgnoreUserConfig = 1, 116 NoFail = 2 117 } 118 119 public class Client : IDisposable 120 { 121 private IntPtr handle; 122 private ClientCallback cb; 123 private PollCallback pollcb; 124 private IntPtr spoll; 125 126 private Thread thread; 127 128 [DllImport ("avahi-client")] 129 private static extern IntPtr avahi_client_new (IntPtr poll, ClientFlags flags, ClientCallback handler, 130 IntPtr userData, out int error); 131 132 [DllImport ("avahi-client")] 133 private static extern void avahi_client_free (IntPtr handle); 134 135 [DllImport ("avahi-client")] 136 private static extern IntPtr avahi_client_get_version_string (IntPtr handle); 137 138 [DllImport ("avahi-client")] 139 private static extern IntPtr avahi_client_get_host_name (IntPtr handle); 140 141 [DllImport ("avahi-client")] 142 private static extern IntPtr avahi_client_get_domain_name (IntPtr handle); 143 144 [DllImport ("avahi-client")] 145 private static extern IntPtr avahi_client_get_host_name_fqdn (IntPtr handle); 146 147 [DllImport ("avahi-client")] 148 private static extern ClientState avahi_client_get_state (IntPtr handle); 149 150 [DllImport ("avahi-client")] 151 private static extern int avahi_client_errno (IntPtr handle); 152 153 [DllImport ("avahi-common")] 154 private static extern IntPtr avahi_simple_poll_new (); 155 156 [DllImport ("avahi-common")] 157 private static extern IntPtr avahi_simple_poll_get (IntPtr spoll); 158 159 [DllImport ("avahi-common")] 160 private static extern void avahi_simple_poll_free (IntPtr spoll); 161 162 [DllImport ("avahi-common")] 163 private static extern int avahi_simple_poll_loop (IntPtr spoll); 164 165 [DllImport ("avahi-common")] 166 private static extern void avahi_simple_poll_set_func (IntPtr spoll, PollCallback cb); 167 168 [DllImport ("avahi-common")] 169 private static extern void avahi_simple_poll_quit (IntPtr spoll); 170 171 [DllImport ("avahi-client")] 172 private static extern uint avahi_client_get_local_service_cookie (IntPtr client); 173 174 [DllImport ("avahi-common")] 175 private static extern int avahi_service_name_join (IntPtr buf, int len, byte[] name, byte[] type, 176 byte[] domain); 177 178 [DllImport ("avahi-common")] 179 private static extern int avahi_service_name_split (byte[] service, IntPtr name, int name_len, 180 IntPtr type, int type_len, 181 IntPtr domain, int domain_len); 182 183 184 [DllImport ("libc")] 185 private static extern int poll(IntPtr ufds, uint nfds, int timeout); 186 187 public event ClientStateHandler StateChanged; 188 189 internal IntPtr Handle 190 { 191 get { return handle; } 192 } 193 194 public string Version 195 { 196 get { 197 lock (this) { 198 return Utility.PtrToString (avahi_client_get_version_string (handle)); 199 } 200 } 201 } 202 203 public string HostName 204 { 205 get { 206 lock (this) { 207 return Utility.PtrToString (avahi_client_get_host_name (handle)); 208 } 209 } 210 } 211 212 public string DomainName 213 { 214 get { 215 lock (this) { 216 return Utility.PtrToString (avahi_client_get_domain_name (handle)); 217 } 218 } 219 } 220 221 public string HostNameFqdn 222 { 223 get { 224 lock (this) { 225 return Utility.PtrToString (avahi_client_get_host_name_fqdn (handle)); 226 } 227 } 228 } 229 230 public ClientState State 231 { 232 get { 233 lock (this) { 234 return (ClientState) avahi_client_get_state (handle); 235 } 236 } 237 } 238 239 public uint LocalServiceCookie 240 { 241 get { 242 lock (this) { 243 return avahi_client_get_local_service_cookie (handle); 244 } 245 } 246 } 247 248 internal ErrorCode LastError 249 { 250 get { 251 lock (this) { 252 return (ErrorCode) avahi_client_errno (handle); 253 } 254 } 255 } 256 257 public Client (ClientFlags flags) 258 { 259 spoll = avahi_simple_poll_new (); 260 261 pollcb = OnPollCallback; 262 avahi_simple_poll_set_func (spoll, pollcb); 263 IntPtr poll = avahi_simple_poll_get (spoll); 264 cb = OnClientCallback; 265 266 int error; 267 handle = avahi_client_new (poll, flags, cb, IntPtr.Zero, out error); 268 if (error != 0) 269 throw new ClientException (error); 270 271 thread = new Thread (PollLoop); 272 thread.IsBackground = true; 273 thread.Start (); 274 } 275 276 public Client () : this (ClientFlags.None) { 277 } 278 279 ~Client () 280 { 281 Dispose (); 282 } 283 284 public void Dispose () 285 { 286 if (handle != IntPtr.Zero) { 287 lock (this) { 288 avahi_client_free (handle); 289 handle = IntPtr.Zero; 290 291 avahi_simple_poll_quit (spoll); 292 Monitor.Wait (this); 293 294 avahi_simple_poll_free (spoll); 295 } 296 } 297 } 298 299 public static string JoinServiceName (string name, string type, string domain) 300 { 301 int len = 4 * (name.Length + type.Length + domain.Length) + 4; 302 IntPtr buf = Stdlib.malloc ((ulong) len); 303 304 int ret = avahi_service_name_join (buf, len, 305 Utility.StringToBytes (name), 306 Utility.StringToBytes (type), 307 Utility.StringToBytes (domain)); 308 309 if (ret < 0) { 310 Utility.Free (buf); 311 return null; // FIXME, should throw exception 312 } 313 314 string service = Utility.PtrToString (buf); 315 Utility.Free (buf); 316 317 return service; 318 } 319 320 public static void SplitServiceName (string service, out string name, out string type, out string domain) 321 { 322 int len = 1024; 323 324 IntPtr namePtr = Stdlib.malloc ((ulong) len); 325 IntPtr typePtr = Stdlib.malloc ((ulong) len); 326 IntPtr domainPtr = Stdlib.malloc ((ulong) len); 327 328 int ret = avahi_service_name_split (Utility.StringToBytes (service), namePtr, len, typePtr, len, 329 domainPtr, len); 330 331 if (ret < 0) { 332 Utility.Free (namePtr); 333 Utility.Free (typePtr); 334 Utility.Free (domainPtr); 335 336 name = null; 337 type = null; 338 domain = null; 339 return; 340 } 341 342 name = Utility.PtrToString (namePtr); 343 type = Utility.PtrToString (typePtr); 344 domain = Utility.PtrToString (domainPtr); 345 346 Utility.Free (namePtr); 347 Utility.Free (typePtr); 348 Utility.Free (domainPtr); 349 } 350 351 internal void ThrowError () 352 { 353 ErrorCode error = LastError; 354 355 if (error != ErrorCode.Ok) 356 throw new ClientException (error); 357 } 358 359 private void OnClientCallback (IntPtr client, ClientState state, IntPtr userData) 360 { 361 if (StateChanged != null) 362 StateChanged (this, new ClientStateArgs (state, LastError)); 363 } 364 365 private int OnPollCallback (IntPtr ufds, uint nfds, int timeout) { 366 Monitor.Exit (this); 367 int result = poll (ufds, nfds, timeout); 368 Monitor.Enter (this); 369 return result; 370 } 371 372 private void PollLoop () { 373 try { 374 lock (this) { 375 avahi_simple_poll_loop (spoll); 376 Monitor.Pulse (this); 377 } 378 } catch (Exception e) { 379 Console.Error.WriteLine ("Error in avahi-sharp event loop: " + e); 380 } 381 } 382 } 383} 384