[Nsd]Fixed ArgumentException and implemented IDisposable in INsdService
[platform/core/csapi/nsd.git] / Tizen.Network.Nsd / Tizen.Network.Nsd / DnssdService.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 using System;
18 using System.Text;
19 using System.Threading;
20 using System.Net;
21
22 namespace Tizen.Network.Nsd
23 {
24     internal class DnssdInitializer
25     {
26         internal DnssdInitializer()
27         {
28             Globals.DnssdInitialize();
29         }
30
31         ~DnssdInitializer()
32         {
33             int ret = Interop.Nsd.Dnssd.Deinitialize();
34             if (ret != (int)DnssdError.None)
35             {
36                 Log.Error(Globals.LogTag, "Failed to deinitialize Dnssd, Error - " + (DnssdError)ret);
37             }
38         }
39     }
40     /// <summary>
41     /// This class is used for managing local service registration and its properties using DNSSD.
42     /// </summary>
43     /// <since_tizen> 4 </since_tizen>
44     public class DnssdService : INsdService
45     {
46         private uint _serviceHandle;
47         private string _serviceType;
48         private ushort _dnsRecordtype = 16;
49         private Interop.Nsd.Dnssd.ServiceRegisteredCallback _serviceRegisteredCallback;
50
51         /// <summary>
52         /// Constructor to create DnssdService instance that sets the serviceType to a given value.
53         /// </summary>
54         /// <since_tizen> 4 </since_tizen>
55         /// <param name="serviceType">The DNSSD service type. It is expressed as type followed by protocol, separated by a dot(e.g. "_ftp._tcp").
56         /// It must begin with an underscore, followed by 1-15 characters which may be letters, digits or hyphens.
57         /// </param>
58         /// <feature>http://tizen.org/feature/network.dnssd</feature>
59         public DnssdService(string serviceType)
60         {
61             _serviceType = serviceType;
62             DnssdInitializeCreateService();
63         }
64
65         internal DnssdService(uint service)
66         {
67             _serviceHandle = service;
68         }
69
70         internal void DnssdInitializeCreateService()
71         {
72             DnssdInitializer dnssdInit = Globals.s_threadDns.Value;
73             Log.Info(Globals.LogTag, "Initialize ThreadLocal<DnssdInitializer> instance = " + dnssdInit);
74             int ret = Interop.Nsd.Dnssd.CreateService(_serviceType, out _serviceHandle);
75             if (ret != (int)DnssdError.None)
76             {
77                 Log.Error(Globals.LogTag, "Failed to create a local Dnssd service handle, Error - " + (DnssdError)ret);
78                 NsdErrorFactory.ThrowDnssdException(ret);
79             }
80         }
81
82         /// <summary>
83         /// Name of DNSSD service.
84         /// </summary>
85         /// <remarks>
86         /// Set Name for only unregistered service created locally.
87         /// It may be up to 63 bytes.
88         /// In case of error, null will be returned during get and exception will be thrown during set.
89         /// </remarks>
90         /// <since_tizen> 4 </since_tizen>
91         /// <feature>http://tizen.org/feature/network.dnssd</feature>
92         /// <exception cref="NotSupportedException">Thrown while setting this property when DNSSD is not supported.</exception>
93         /// <exception cref="ArgumentException">Thrown when Name value is set to null.</exception>
94         /// <exception cref="InvalidOperationException">Thrown while setting this property when any other error occurred.</exception>
95         public string Name
96         {
97             get
98             {
99                 string name;
100                 int ret = Interop.Nsd.Dnssd.GetName(_serviceHandle, out name);
101                 if (ret != (int)DnssdError.None)
102                 {
103                     Log.Error(Globals.LogTag, "Failed to get name of service, Error: " + (DnssdError)ret);
104                     return null;
105                 }
106
107                 return name;
108             }
109
110             set
111             {
112                 if (!Globals.s_threadDns.IsValueCreated)
113                 {
114                     DnssdInitializeCreateService();
115                 }
116
117                 int ret = Interop.Nsd.Dnssd.SetName(_serviceHandle, value);
118                 if (ret != (int)DnssdError.None)
119                 {
120                     Log.Error(Globals.LogTag, "Failed to set name of service, Error: " + (DnssdError)ret);
121                     NsdErrorFactory.ThrowDnssdException(ret);
122                 }
123             }
124         }
125
126         /// <summary>
127         /// Type of DNSSD local/remote service.
128         /// </summary>
129         /// <remarks>
130         /// It is expressed as type followed by protocol, separated by a dot(e.g. "_ftp._tcp").
131         /// It must begin with an underscore, followed by 1-15 characters which may be letters, digits or hyphens.
132         /// In case of error, null will be returned.
133         /// </remarks>
134         /// <since_tizen> 4 </since_tizen>
135         public string Type
136         {
137             get
138             {
139                 string type;
140                 int ret = Interop.Nsd.Dnssd.GetType(_serviceHandle, out type);
141                 if (ret != (int)DnssdError.None)
142                 {
143                     Log.Error(Globals.LogTag, "Failed to get type of service, Error: " + (DnssdError)ret);
144                     return null;
145                 }
146
147                 return type;
148             }
149         }
150
151         /// <summary>
152         /// Port number of DNSSD local/remote service.
153         /// </summary>
154         /// <remarks>
155         /// Set Port for only unregistered service created locally. The default value of Port is 0.
156         /// In case of error, -1 will be returned during get and exception will be thrown during set.
157         /// </remarks>
158         /// <since_tizen> 4 </since_tizen>
159         /// <feature>http://tizen.org/feature/network.dnssd</feature>
160         /// <exception cref="NotSupportedException">Thrown while setting this property when DNSSD is not supported.</exception>
161         /// <exception cref="ArgumentException">Thrown if value of Port is set to less than 0 or more than 65535.</exception>
162         /// <exception cref="InvalidOperationException">Thrown while setting this property when any other error occurred.</exception>
163         public int Port
164         {
165             get
166             {
167                 int port;
168                 int ret = Interop.Nsd.Dnssd.GetPort(_serviceHandle, out port);
169                 if (ret != (int)DnssdError.None)
170                 {
171                     Log.Error(Globals.LogTag, "Failed to get port number of Dnssd service, Error: " + (DnssdError)ret);
172                     return -1;
173                 }
174
175                 return port;
176             }
177
178             set
179             {
180                 if (!Globals.s_threadDns.IsValueCreated)
181                 {
182                     DnssdInitializeCreateService();
183                 }
184
185                 int ret = Interop.Nsd.Dnssd.SetPort(_serviceHandle, value);
186                 if (ret != (int)DnssdError.None)
187                 {
188                     Log.Error(Globals.LogTag, "Failed to set port number of Dnssd service, Error: " + (DnssdError)ret);
189                     NsdErrorFactory.ThrowDnssdException(ret);
190                 }
191             }
192         }
193
194         /// <summary>
195         /// IP address of DNSSD remote service.
196         /// </summary>
197         /// <remarks>
198         /// If the remote service has no IPv4 Address, then IPv4Address would contain null and if it has no IPv6 Address, then IPv6Address would contain null.
199         /// In case of error, null object will be returned.
200         /// </remarks>
201         /// <since_tizen> 4 </since_tizen>
202         public IPAddressInformation IP
203         {
204             get
205             {
206                 string IPv4, IPv6;
207                 int ret = Interop.Nsd.Dnssd.GetIP(_serviceHandle, out IPv4, out IPv6);
208                 if (ret != (int)DnssdError.None)
209                 {
210                     Log.Error(Globals.LogTag, "Failed to get the IP of Dnssd remote service, Error: " + (DnssdError)ret);
211                     return null;
212                 }
213
214                 IPAddressInformation IPAddressInstance = new IPAddressInformation(IPv4, IPv6);
215                 return IPAddressInstance;
216             }
217         }
218
219         private void GetTxtRecord(out ushort length, out byte[] value)
220         {
221             int ret = Interop.Nsd.Dnssd.GetAllTxtRecord(_serviceHandle, out length, out value);
222             if (ret != (int)DnssdError.None)
223             {
224                 Log.Error(Globals.LogTag, "Failed to get the TXT record, Error: " + (DnssdError)ret);
225                 NsdErrorFactory.ThrowDnssdException(ret);
226             }
227         }
228
229         /// <summary>
230         /// Adds the TXT record.
231         /// </summary>
232         /// <remarks>
233         /// TXT record should be added after registering local service using RegisterService().
234         /// </remarks>
235         /// <since_tizen> 4 </since_tizen>
236         /// <param name="key">The key of the TXT record. It must be a null-terminated string with 9 characters or fewer excluding null. It is case insensitive.</param>
237         /// <param name="value">The value of the TXT record.If null, then "key" will be added with no value. If non-null but value_length is zero, then "key=" will be added with empty value.</param>
238         /// <feature>http://tizen.org/feature/network.dnssd</feature>
239         /// <exception cref="NotSupportedException">Thrown when DNSSD is not supported.</exception>
240         /// <exception cref="ArgumentException">Thrown when value of key is null.</exception>
241         /// <exception cref="InvalidOperationException">Thrown when any other error occurred.</exception>
242         public void AddTXTRecord(string key, string value)
243         {
244             byte[] byteValue = Encoding.UTF8.GetBytes(value);
245             ushort length = Convert.ToUInt16(byteValue.Length);
246             int ret = Interop.Nsd.Dnssd.AddTxtRecord(_serviceHandle, key, length, byteValue);
247             if (ret != (int)DnssdError.None)
248             {
249                 Log.Error(Globals.LogTag, "Failed to add the TXT record, Error: " + (DnssdError)ret);
250                 NsdErrorFactory.ThrowDnssdException(ret);
251             }
252
253             byte[] txtValue;
254             ushort txtLength;
255             GetTxtRecord(out txtLength, out txtValue);
256
257             ret = Interop.Nsd.Dnssd.SetRecord(_serviceHandle, _dnsRecordtype, txtLength, txtValue);
258             if (ret != (int)DnssdError.None)
259             {
260                 Log.Error(Globals.LogTag, "Failed to set the DNS resource record, Error: " + (DnssdError)ret);
261                 NsdErrorFactory.ThrowDnssdException(ret);
262             }
263         }
264
265         /// <summary>
266         /// Removes the TXT record.
267         /// </summary>
268         /// <since_tizen> 4 </since_tizen>
269         /// <param name="key">The key of the TXT record to be removed.</param>
270         /// <feature>http://tizen.org/feature/network.dnssd</feature>
271         /// <exception cref="NotSupportedException">Thrown when DNSSD is not supported.</exception>
272         /// <exception cref="ArgumentException">Thrown when value of key is null.</exception>
273         /// <exception cref="InvalidOperationException">Thrown when any other error occurred.</exception>
274         public void RemoveTXTRecord(string key)
275         {
276             int ret = Interop.Nsd.Dnssd.RemoveTxtRecord(_serviceHandle, key);
277             if (ret != (int)DnssdError.None)
278             {
279                 Log.Error(Globals.LogTag, "Failed to remove the TXT record, Error: " + (DnssdError)ret);
280                 NsdErrorFactory.ThrowDnssdException(ret);
281             }
282
283             byte[] txtValue;
284             ushort txtLength;
285             GetTxtRecord(out txtLength, out txtValue);
286             if (txtLength == 0)
287             {
288                 ret = Interop.Nsd.Dnssd.UnsetRecord(_serviceHandle, _dnsRecordtype);
289                 if (ret != (int)DnssdError.None)
290                 {
291                     Log.Error(Globals.LogTag, "Failed to unset the DNS resource record, Error: " + (DnssdError)ret);
292                     NsdErrorFactory.ThrowDnssdException(ret);
293                 }
294             }
295         }
296
297         /// <summary>
298         /// Registers the DNSSD local service for publishing.
299         /// </summary>
300         /// Name of the service must be set.
301         /// <since_tizen> 4 </since_tizen>
302         /// <privilege>http://tizen.org/privilege/internet</privilege>
303         /// <feature>http://tizen.org/feature/network.dnssd</feature>
304         /// <exception cref="InvalidOperationException">Thrown when any other error occurred.</exception>
305         /// <exception cref="NotSupportedException">Thrown when DNSSD is not supported.</exception>
306         /// <exception cref="UnauthorizedAccessException">Thrown when permission is denied.</exception>
307         public void RegisterService()
308         {
309             if (!Globals.s_threadDns.IsValueCreated)
310             {
311                 DnssdInitializeCreateService();
312             }
313
314             _serviceRegisteredCallback = (DnssdError result, uint service, IntPtr userData) =>
315             {
316                 if (result != DnssdError.None)
317                 {
318                     Log.Error(Globals.LogTag, "Failed to finish the registration of Dnssd local service, Error: " + result);
319                     NsdErrorFactory.ThrowDnssdException((int)result);
320                 }
321             };
322
323             int ret = Interop.Nsd.Dnssd.RegisterService(_serviceHandle, _serviceRegisteredCallback, IntPtr.Zero);
324             if (ret != (int)DnssdError.None)
325             {
326                 Log.Error(Globals.LogTag, "Failed to register the Dnssd local service, Error: " + (DnssdError)ret);
327                 NsdErrorFactory.ThrowDnssdException(ret);
328             }
329         }
330
331         /// <summary>
332         /// Deregisters the DNSSD local service.
333         /// </summary>
334         /// <remarks>
335         /// A local service registered using RegisterService() must be passed.
336         /// </remarks>
337         /// <since_tizen> 4 </since_tizen>
338         /// <feature>http://tizen.org/feature/network.dnssd</feature>
339         /// <exception cref="InvalidOperationException">Thrown when any other error occurred.</exception>
340         /// <exception cref="NotSupportedException">Thrown when DNSSD is not supported.</exception>
341         public void DeregisterService()
342         {
343             int ret = Interop.Nsd.Dnssd.DeregisterService(_serviceHandle);
344             if (ret != (int)DnssdError.None)
345             {
346                 Log.Error(Globals.LogTag, "Failed to deregister the Dnssd local service, Error: " + (DnssdError)ret);
347                 NsdErrorFactory.ThrowDnssdException(ret);
348             }
349         }
350
351         #region IDisposable Support
352         private bool _disposedValue = false; // To detect redundant calls
353
354         protected virtual void Dispose(bool disposing)
355         {
356             if (!_disposedValue)
357             {
358                 if (disposing)
359                 {
360                     if (_serviceHandle != 0)
361                     {
362                         int ret = Interop.Nsd.Dnssd.DestroyService(_serviceHandle);
363                         if (ret != (int)DnssdError.None)
364                         {
365                             Log.Error(Globals.LogTag, "Failed to destroy the local Dnssd service handle, Error - " + (DnssdError)ret);
366                         }
367                     }
368                 }
369
370                 _disposedValue = true;
371             }
372         }
373
374          ~DnssdService()
375         {
376             Dispose(false);
377         }
378
379         /// <summary>
380         /// Disposes the memory allocated to unmanaged resources.
381         /// </summary>
382         /// <since_tizen> 4 </since_tizen>
383         public void Dispose()
384         {
385             Dispose(true);
386             GC.SuppressFinalize(this);
387         }
388         #endregion
389     }
390
391     /// <summary>
392     /// This class manages the IP address properties of DNSSD service.
393     /// </summary>
394     /// <since_tizen> 4 </since_tizen>
395     public class IPAddressInformation
396     {
397         private string _ipv4, _ipv6;
398         internal IPAddressInformation()
399         {
400         }
401
402         internal IPAddressInformation(string ipv4, string ipv6)
403         {
404             _ipv4 = ipv4;
405             _ipv6 = ipv6;
406         }
407
408         /// <summary>
409         /// The IP version 4 address of DNSSD service.
410         /// </summary>
411         /// <since_tizen> 4 </since_tizen>
412         public IPAddress IPv4Address
413         {
414             get
415             {
416                 return IPAddress.Parse(_ipv4);
417             }
418         }
419
420         /// <summary>
421         /// The IP version 6 address of DNSSD service.
422         /// </summary>
423         /// <since_tizen> 4 </since_tizen>
424         public IPAddress IPv6Address
425         {
426             get
427             {
428                 return IPAddress.Parse(_ipv6);
429             }
430         }
431     }
432 }