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.Net;
22 using System.Runtime.InteropServices;
23 using System.Text;
24 using Mono.Unix;
25 
26 namespace Avahi
27 {
28 
29     [Flags]
30     public enum PublishFlags {
31         None = 0,
32         Unique = 1,
33         NoProbe = 2,
34         NoAnnounce = 4,
35         AllowMultiple = 8,
36         NoReverse = 16,
37         NoCookie = 32,
38         Update = 64,
39         UseWideArea = 128,
40         UseMulticast = 256
41     }
42 
43     public enum EntryGroupState {
44         Uncommited,
45         Registering,
46         Established,
47         Collision,
48         Failure
49     }
50 
51     public class EntryGroupStateArgs : EventArgs
52     {
53         private EntryGroupState state;
54 
55         public EntryGroupState State
56         {
57             get { return state; }
58         }
59 
EntryGroupStateArgs(EntryGroupState state)60         public EntryGroupStateArgs (EntryGroupState state)
61         {
62             this.state = state;
63         }
64     }
65 
EntryGroupCallback(IntPtr group, EntryGroupState state, IntPtr userdata)66     internal delegate void EntryGroupCallback (IntPtr group, EntryGroupState state, IntPtr userdata);
EntryGroupStateHandler(object o, EntryGroupStateArgs args)67     public delegate void EntryGroupStateHandler (object o, EntryGroupStateArgs args);
68 
69     public class EntryGroup : IDisposable
70     {
71         private Client client;
72         private IntPtr handle;
73         private EntryGroupCallback cb;
74 
75         [DllImport ("avahi-client")]
avahi_entry_group_new(IntPtr client, EntryGroupCallback cb, IntPtr userdata)76         private static extern IntPtr avahi_entry_group_new (IntPtr client, EntryGroupCallback cb, IntPtr userdata);
77 
78         [DllImport ("avahi-client")]
avahi_entry_group_commit(IntPtr group)79         private static extern int avahi_entry_group_commit (IntPtr group);
80 
81         [DllImport ("avahi-client")]
avahi_entry_group_reset(IntPtr group)82         private static extern int avahi_entry_group_reset (IntPtr group);
83 
84         [DllImport ("avahi-client")]
avahi_entry_group_get_state(IntPtr group)85         private static extern EntryGroupState avahi_entry_group_get_state (IntPtr group);
86 
87         [DllImport ("avahi-client")]
avahi_entry_group_is_empty(IntPtr group)88         private static extern bool avahi_entry_group_is_empty (IntPtr group);
89 
90         [DllImport ("avahi-client")]
avahi_entry_group_add_service_strlst(IntPtr group, int iface, Protocol proto, PublishFlags flags, byte[] name, byte[] type, byte[] domain, byte[] host, UInt16 port, IntPtr strlst)91         private static extern int avahi_entry_group_add_service_strlst (IntPtr group, int iface, Protocol proto,
92                                                                         PublishFlags flags, byte[] name, byte[] type,
93                                                                         byte[] domain, byte[] host, UInt16 port,
94                                                                         IntPtr strlst);
95 
96         [DllImport ("avahi-client")]
avahi_entry_group_update_service_strlst(IntPtr group, int iface, Protocol proto, PublishFlags flags, byte[] name, byte[] type, byte[] domain, IntPtr strlst)97         private static extern int avahi_entry_group_update_service_strlst (IntPtr group, int iface, Protocol proto,
98                                                                            PublishFlags flags, byte[] name,
99                                                                            byte[] type, byte[] domain, IntPtr strlst);
100 
101         [DllImport ("avahi-client")]
avahi_entry_group_add_service_subtype(IntPtr group, int iface, Protocol proto, PublishFlags flags, byte[] name, byte[] type, byte[] domain, byte[] subtype)102         private static extern int avahi_entry_group_add_service_subtype (IntPtr group, int iface, Protocol proto,
103                                                                          PublishFlags flags, byte[] name, byte[] type,
104                                                                          byte[] domain, byte[] subtype);
105 
106         [DllImport ("avahi-client")]
avahi_entry_group_add_address(IntPtr group, int iface, Protocol proto, PublishFlags flags, byte[] name, IntPtr address)107         private static extern int avahi_entry_group_add_address (IntPtr group, int iface, Protocol proto,
108                                                                  PublishFlags flags, byte[] name, IntPtr address);
109 
110 
111         [DllImport ("avahi-client")]
avahi_entry_group_add_record(IntPtr group, int iface, Protocol proto, PublishFlags flags, byte[] name, RecordClass clazz, RecordType type, uint ttl, byte[] rdata, int size)112         private static extern int avahi_entry_group_add_record (IntPtr group, int iface, Protocol proto,
113                                                                 PublishFlags flags, byte[] name, RecordClass clazz,
114                                                                 RecordType type, uint ttl, byte[] rdata, int size);
115 
116         [DllImport ("avahi-client")]
avahi_entry_group_free(IntPtr group)117         private static extern void avahi_entry_group_free (IntPtr group);
118 
119         [DllImport ("avahi-common")]
avahi_string_list_new(IntPtr txt)120         private static extern IntPtr avahi_string_list_new (IntPtr txt);
121 
122         [DllImport ("avahi-common")]
avahi_string_list_add(IntPtr list, byte[] txt)123         private static extern IntPtr avahi_string_list_add (IntPtr list, byte[] txt);
124 
125         [DllImport ("avahi-common")]
avahi_string_list_free(IntPtr list)126         private static extern void avahi_string_list_free (IntPtr list);
127 
128         [DllImport ("avahi-common")]
avahi_alternative_service_name(byte[] name)129         private static extern IntPtr avahi_alternative_service_name (byte[] name);
130 
131         public event EntryGroupStateHandler StateChanged;
132 
133         public EntryGroupState State
134         {
135             get {
136                 lock (client) {
137                     return avahi_entry_group_get_state (handle);
138                 }
139             }
140         }
141 
142         public bool IsEmpty
143         {
144             get {
145                 lock (client) {
146                     return avahi_entry_group_is_empty (handle);
147                 }
148             }
149         }
150 
EntryGroup(Client client)151         public EntryGroup (Client client)
152         {
153             this.client = client;
154             cb = OnEntryGroupCallback;
155 
156             lock (client) {
157                 handle = avahi_entry_group_new (client.Handle, cb, IntPtr.Zero);
158                 if (handle == IntPtr.Zero)
159                     client.ThrowError ();
160             }
161         }
162 
~EntryGroup()163         ~EntryGroup ()
164         {
165             Dispose ();
166         }
167 
Dispose()168         public void Dispose ()
169         {
170             if (client.Handle != IntPtr.Zero && handle != IntPtr.Zero) {
171                 lock (client) {
172                     avahi_entry_group_free (handle);
173                     handle = IntPtr.Zero;
174                 }
175             }
176         }
177 
Commit()178         public void Commit ()
179         {
180             lock (client) {
181                 if (avahi_entry_group_commit (handle) < 0)
182                     client.ThrowError ();
183             }
184         }
185 
Reset()186         public void Reset ()
187         {
188             lock (client) {
189                 if (avahi_entry_group_reset (handle) < 0)
190                     client.ThrowError ();
191             }
192         }
193 
AddService(string name, string type, string domain, UInt16 port, params string[] txt)194         public void AddService (string name, string type, string domain,
195                                 UInt16 port, params string[] txt)
196         {
197             AddService (PublishFlags.None, name, type, domain, port, txt);
198         }
199 
AddService(PublishFlags flags, string name, string type, string domain, UInt16 port, params string[] txt)200         public void AddService (PublishFlags flags, string name, string type, string domain,
201                                 UInt16 port, params string[] txt)
202         {
203             AddService (-1, Protocol.Unspecified, flags, name, type, domain, null, port, txt);
204         }
205 
AddService(int iface, Protocol proto, PublishFlags flags, string name, string type, string domain, string host, UInt16 port, params string[] txt)206         public void AddService (int iface, Protocol proto, PublishFlags flags, string name, string type, string domain,
207                                 string host, UInt16 port, params string[] txt)
208         {
209             IntPtr list = avahi_string_list_new (IntPtr.Zero);
210 
211             if (txt != null) {
212                 foreach (string item in txt) {
213                     list = avahi_string_list_add (list, Utility.StringToBytes (item));
214                 }
215             }
216 
217             AddService (iface, proto, flags, name, type, domain, host, port, list);
218         }
219 
AddService(int iface, Protocol proto, PublishFlags flags, string name, string type, string domain, string host, UInt16 port, params byte[][] txt)220         public void AddService (int iface, Protocol proto, PublishFlags flags, string name, string type, string domain,
221                                 string host, UInt16 port, params byte[][] txt)
222         {
223             IntPtr list = avahi_string_list_new (IntPtr.Zero);
224 
225             if (txt != null) {
226                 foreach (byte[] item in txt) {
227                     list = avahi_string_list_add (list, item);
228                 }
229             }
230 
231             AddService (iface, proto, flags, name, type, domain, host, port, list);
232         }
233 
AddService(int iface, Protocol proto, PublishFlags flags, string name, string type, string domain, string host, UInt16 port, IntPtr list)234         private void AddService (int iface, Protocol proto, PublishFlags flags, string name, string type,
235                                  string domain, string host, UInt16 port, IntPtr list)
236         {
237             int ret;
238 
239             lock (client) {
240                 ret = avahi_entry_group_add_service_strlst (handle, iface, proto, flags,
241                                                             Utility.StringToBytes (name),
242                                                             Utility.StringToBytes (type),
243                                                             Utility.StringToBytes (domain),
244                                                             Utility.StringToBytes (host), port, list);
245             }
246 
247             avahi_string_list_free (list);
248 
249             if (ret < 0) {
250                 client.ThrowError ();
251             }
252         }
253 
UpdateService(string name, string type, string domain, params string[] txt)254         public void UpdateService (string name, string type, string domain, params string[] txt)
255         {
256             UpdateService (-1, Protocol.Unspecified, PublishFlags.None, name, type, domain, txt);
257         }
258 
UpdateService(int iface, Protocol proto, PublishFlags flags, string name, string type, string domain, params string[] txt)259         public void UpdateService (int iface, Protocol proto, PublishFlags flags, string name, string type,
260                                    string domain, params string[] txt)
261         {
262             IntPtr list = avahi_string_list_new (IntPtr.Zero);
263 
264             if (txt != null) {
265                 foreach (string item in txt) {
266                     list = avahi_string_list_add (list, Utility.StringToBytes (item));
267                 }
268             }
269 
270             UpdateService (iface, proto, flags, name, type, domain, list);
271         }
272 
UpdateService(int iface, Protocol proto, PublishFlags flags, string name, string type, string domain, params byte[][] txt)273         public void UpdateService (int iface, Protocol proto, PublishFlags flags, string name, string type,
274                                    string domain, params byte[][] txt)
275         {
276             IntPtr list = avahi_string_list_new (IntPtr.Zero);
277 
278             if (txt != null) {
279                 foreach (byte[] item in txt) {
280                     list = avahi_string_list_add (list, item);
281                 }
282             }
283 
284             UpdateService (iface, proto, flags, name, type, domain, list);
285         }
286 
UpdateService(int iface, Protocol proto, PublishFlags flags, string name, string type, string domain, IntPtr list)287         private void UpdateService (int iface, Protocol proto, PublishFlags flags, string name, string type,
288                                     string domain, IntPtr list)
289         {
290             lock (client) {
291                 int ret = avahi_entry_group_update_service_strlst (handle, iface, proto, flags,
292                                                                    Utility.StringToBytes (name),
293                                                                    Utility.StringToBytes (type),
294                                                                    Utility.StringToBytes (domain),
295                                                                    list);
296 
297                 avahi_string_list_free (list);
298 
299                 if (ret < 0) {
300                     client.ThrowError ();
301                 }
302             }
303         }
304 
AddServiceSubtype(string name, string type, string domain, string subtype)305         public void AddServiceSubtype (string name, string type, string domain, string subtype)
306         {
307             AddServiceSubtype (-1, Protocol.Unspecified, PublishFlags.None, name, type, domain, subtype);
308         }
309 
AddServiceSubtype(int iface, Protocol proto, PublishFlags flags, string name, string type, string domain, string subtype)310         public void AddServiceSubtype (int iface, Protocol proto, PublishFlags flags, string name,
311                                        string type, string domain, string subtype)
312         {
313             lock (client) {
314                 int ret = avahi_entry_group_add_service_subtype (handle, iface, proto, flags,
315                                                                  Utility.StringToBytes (name),
316                                                                  Utility.StringToBytes (type),
317                                                                  Utility.StringToBytes (domain),
318                                                                  Utility.StringToBytes (subtype));
319 
320                 if (ret < 0) {
321                     client.ThrowError ();
322                 }
323             }
324         }
325 
AddAddress(string name, IPAddress address)326         public void AddAddress (string name, IPAddress address)
327         {
328             AddAddress (-1, Protocol.Unspecified, PublishFlags.None, name, address);
329         }
330 
AddAddress(int iface, Protocol proto, PublishFlags flags, string name, IPAddress address)331         public void AddAddress (int iface, Protocol proto, PublishFlags flags, string name, IPAddress address)
332         {
333             IntPtr addressPtr = Utility.AddressToPtr (address);
334 
335             lock (client) {
336                 int ret = avahi_entry_group_add_address (handle, iface, proto, flags,
337                                                          Utility.StringToBytes (name), addressPtr);
338 
339                 Utility.Free (addressPtr);
340 
341                 if (ret < 0) {
342                     client.ThrowError ();
343                 }
344             }
345         }
346 
AddRecord(string name, RecordClass clazz, RecordType type, uint ttl, byte[] rdata, int length)347         public void AddRecord (string name, RecordClass clazz, RecordType type, uint ttl, byte[] rdata, int length)
348         {
349             AddRecord (-1, Protocol.Unspecified, PublishFlags.None, name, clazz, type, ttl, rdata, length);
350         }
351 
AddRecord(int iface, Protocol proto, PublishFlags flags, string name, RecordClass clazz, RecordType type, uint ttl, byte[] rdata, int length)352         public void AddRecord (int iface, Protocol proto, PublishFlags flags, string name,
353                                RecordClass clazz, RecordType type, uint ttl, byte[] rdata, int length)
354         {
355             lock (client) {
356                 int ret = avahi_entry_group_add_record (handle, iface, proto, flags,
357                                                         Utility.StringToBytes (name),
358                                                         clazz, type, ttl, rdata, length);
359 
360                 if (ret < 0) {
361                     client.ThrowError ();
362                 }
363             }
364         }
365 
GetAlternativeServiceName(string name)366         public static string GetAlternativeServiceName (string name) {
367             return Utility.PtrToStringFree (avahi_alternative_service_name (Utility.StringToBytes (name)));
368         }
369 
OnEntryGroupCallback(IntPtr group, EntryGroupState state, IntPtr userdata)370         private void OnEntryGroupCallback (IntPtr group, EntryGroupState state, IntPtr userdata)
371         {
372             if (StateChanged != null)
373                 StateChanged (this, new EntryGroupStateArgs (state));
374         }
375     }
376 }
377