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