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 using System;
21 using System.Collections;
22 using System.Net;
23 using System.Runtime.InteropServices;
24 using System.Text;
25 using Mono.Unix;
26 
27 namespace Avahi
28 {
29 
ServiceResolverCallback(IntPtr resolver, int iface, Protocol proto, ResolverEvent revent, IntPtr name, IntPtr type, IntPtr domain, IntPtr host, IntPtr address, UInt16 port, IntPtr txt, LookupResultFlags flags, IntPtr userdata)30     internal delegate void ServiceResolverCallback (IntPtr resolver, int iface, Protocol proto,
31                                                     ResolverEvent revent, IntPtr name, IntPtr type,
32                                                     IntPtr domain, IntPtr host, IntPtr address,
33                                                     UInt16 port, IntPtr txt, LookupResultFlags flags,
34                                                     IntPtr userdata);
35 
36     public class ServiceResolver : ResolverBase, IDisposable
37     {
38         private IntPtr handle;
39         private ServiceInfo currentInfo;
40         private Client client;
41         private int iface;
42         private Protocol proto;
43         private string name;
44         private string type;
45         private string domain;
46         private Protocol aproto;
47         private LookupFlags flags;
48         private ServiceResolverCallback cb;
49 
50         private ArrayList foundListeners = new ArrayList ();
51         private ArrayList timeoutListeners = new ArrayList ();
52 
53         [DllImport ("avahi-client")]
avahi_service_resolver_new(IntPtr client, int iface, Protocol proto, byte[] name, byte[] type, byte[] domain, Protocol aproto, LookupFlags flags, ServiceResolverCallback cb, IntPtr userdata)54         private static extern IntPtr avahi_service_resolver_new (IntPtr client, int iface, Protocol proto,
55                                                                  byte[] name, byte[] type, byte[] domain,
56                                                                  Protocol aproto, LookupFlags flags,
57                                                                  ServiceResolverCallback cb,
58                                                                  IntPtr userdata);
59 
60         [DllImport ("avahi-common")]
avahi_string_list_get_next(IntPtr list)61         private static extern IntPtr avahi_string_list_get_next (IntPtr list);
62 
63         [DllImport ("avahi-common")]
avahi_string_list_get_text(IntPtr list)64         private static extern IntPtr avahi_string_list_get_text (IntPtr list);
65 
66         [DllImport ("avahi-common")]
avahi_string_list_get_size(IntPtr list)67         private static extern int avahi_string_list_get_size (IntPtr list);
68 
69         [DllImport ("avahi-client")]
avahi_service_resolver_free(IntPtr handle)70         private static extern void avahi_service_resolver_free (IntPtr handle);
71 
72         public event ServiceInfoHandler Found
73         {
74             add {
75                 foundListeners.Add (value);
76                 Start ();
77             }
78             remove {
79                 foundListeners.Remove (value);
80                 Stop (false);
81             }
82         }
83 
84         public event EventHandler Timeout
85         {
86             add {
87                 timeoutListeners.Add (value);
88                 Start ();
89             }
90             remove {
91                 timeoutListeners.Remove (value);
92                 Stop (false);
93             }
94         }
95 
96         public ServiceInfo Service
97         {
98             get { return currentInfo; }
99         }
100 
ServiceResolver(Client client, string name, string type, string domain)101         public ServiceResolver (Client client, string name, string type, string domain) : this (client, -1,
102                                                                                                 Protocol.Unspecified,
103                                                                                                 name, type, domain,
104                                                                                                 Protocol.Unspecified,
105                                                                                                 LookupFlags.None)
106         {
107         }
108 
ServiceResolver(Client client, ServiceInfo service)109         public ServiceResolver (Client client, ServiceInfo service) : this (client, service.NetworkInterface,
110                                                                             service.Protocol, service.Name,
111                                                                             service.ServiceType, service.Domain,
112                                                                             Protocol.Unspecified,
113                                                                             GetLookupFlags (service.Flags))
114         {
115         }
116 
ServiceResolver(Client client, int iface, Protocol proto, string name, string type, string domain, Protocol aproto, LookupFlags flags)117         public ServiceResolver (Client client, int iface, Protocol proto, string name,
118                                 string type, string domain, Protocol aproto, LookupFlags flags)
119         {
120             this.client = client;
121             this.iface = iface;
122             this.proto = proto;
123             this.name = name;
124             this.type = type;
125             this.domain = domain;
126             this.aproto = aproto;
127             this.flags = flags;
128             cb = OnServiceResolverCallback;
129         }
130 
~ServiceResolver()131         ~ServiceResolver ()
132         {
133             Dispose ();
134         }
135 
Dispose()136         public void Dispose ()
137         {
138             Stop (true);
139         }
140 
Start()141         private void Start ()
142         {
143             if (client.Handle == IntPtr.Zero || handle != IntPtr.Zero ||
144                 (foundListeners.Count == 0 && timeoutListeners.Count == 0))
145                 return;
146 
147             lock (client) {
148                 handle = avahi_service_resolver_new (client.Handle, iface, proto,
149                                                      Utility.StringToBytes (name), Utility.StringToBytes (type),
150                                                      Utility.StringToBytes (domain), aproto, flags, cb, IntPtr.Zero);
151 
152                 if (handle == IntPtr.Zero)
153                     client.ThrowError ();
154             }
155         }
156 
Stop(bool force)157         private void Stop (bool force)
158         {
159             if (client.Handle != IntPtr.Zero && handle != IntPtr.Zero &&
160                 (force || (foundListeners.Count == 0 && timeoutListeners.Count == 0))) {
161 
162                 lock (client) {
163                     avahi_service_resolver_free (handle);
164                     handle = IntPtr.Zero;
165                 }
166             }
167         }
168 
OnServiceResolverCallback(IntPtr resolver, int iface, Protocol proto, ResolverEvent revent, IntPtr name, IntPtr type, IntPtr domain, IntPtr host, IntPtr address, UInt16 port, IntPtr txt, LookupResultFlags flags, IntPtr userdata)169         private void OnServiceResolverCallback (IntPtr resolver, int iface, Protocol proto,
170                                                 ResolverEvent revent, IntPtr name, IntPtr type,
171                                                 IntPtr domain, IntPtr host, IntPtr address,
172                                                 UInt16 port, IntPtr txt, LookupResultFlags flags,
173                                                 IntPtr userdata)
174         {
175             ServiceInfo info;
176             info.NetworkInterface = iface;
177             info.Protocol = proto;
178             info.Domain = Utility.PtrToString (domain);
179             info.ServiceType = Utility.PtrToString (type);
180             info.Name = Utility.PtrToString (name);
181             info.HostName = Utility.PtrToString (host);
182             info.Address = Utility.PtrToAddress (address);
183             info.Port = port;
184 
185             if (proto == Protocol.IPv6)
186               info.Address.ScopeId = iface;
187 
188             ArrayList txtlist = new ArrayList ();
189             for (IntPtr l = txt; l != IntPtr.Zero; l = avahi_string_list_get_next (l)) {
190                 IntPtr buf = avahi_string_list_get_text (l);
191                 int len = avahi_string_list_get_size (l);
192 
193                 byte[] txtbuf = new byte[len];
194                 Marshal.Copy (buf, txtbuf, 0, len);
195                 txtlist.Add (txtbuf);
196             }
197 
198             info.Text = (byte[][]) txtlist.ToArray (typeof (byte[]));
199             info.Flags = flags;
200 
201             switch (revent) {
202             case ResolverEvent.Found:
203                 currentInfo = info;
204 
205                 foreach (ServiceInfoHandler handler in foundListeners)
206                     handler (this, new ServiceInfoArgs (info));
207                 break;
208             case ResolverEvent.Failure:
209                 EmitFailure (client.LastError);
210                 break;
211             }
212         }
213 
GetLookupFlags(LookupResultFlags rflags)214         private static LookupFlags GetLookupFlags (LookupResultFlags rflags) {
215             LookupFlags ret = LookupFlags.None;
216 
217             if ((rflags & LookupResultFlags.Multicast) > 0)
218                 ret |= LookupFlags.UseMulticast;
219             if ((rflags & LookupResultFlags.WideArea) > 0)
220                 ret |= LookupFlags.UseWideArea;
221 
222             return ret;
223         }
224     }
225 }
226