Merge pull request #3769 from evincarofautumn/fix-verify-before-allocs
[mono.git] / mcs / class / System / Mono.Btls / MonoBtlsBio.cs
1 //
2 // MonoBtlsBio.cs
3 //
4 // Author:
5 //       Martin Baulig <martin.baulig@xamarin.com>
6 //
7 // Copyright (c) 2016 Xamarin Inc. (http://www.xamarin.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 //
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 //
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // THE SOFTWARE.
26 #if SECURITY_DEP && MONO_FEATURE_BTLS
27 using System;
28 using System.IO;
29 using System.Text;
30 using System.Runtime.CompilerServices;
31 using System.Runtime.InteropServices;
32
33 #if MONOTOUCH
34 using MonoTouch;
35 #endif
36
37 namespace Mono.Btls
38 {
39         class MonoBtlsBio : MonoBtlsObject
40         {
41                 internal MonoBtlsBio (BoringBioHandle handle)
42                         : base (handle)
43                 {
44                 }
45
46                 new protected internal BoringBioHandle Handle {
47                         get { return (BoringBioHandle)base.Handle; }
48                 }
49
50                 protected internal class BoringBioHandle : MonoBtlsHandle
51                 {
52                         public BoringBioHandle (IntPtr handle)
53                                 : base (handle, true)
54                         {
55                         }
56
57                         protected override bool ReleaseHandle ()
58                         {
59                                 if (handle != IntPtr.Zero) {
60                                         mono_btls_bio_free (handle);
61                                         handle = IntPtr.Zero;
62                                 }
63                                 return true;
64                         }
65
66                 }
67
68                 public static MonoBtlsBio CreateMonoStream (Stream stream)
69                 {
70                         return MonoBtlsBioMono.CreateStream (stream, false);
71                 }
72
73                 [DllImport (BTLS_DYLIB)]
74                 extern static int mono_btls_bio_read (IntPtr bio, IntPtr data, int len);
75
76                 [DllImport (BTLS_DYLIB)]
77                 extern static int mono_btls_bio_write (IntPtr bio, IntPtr data, int len);
78
79                 [DllImport (BTLS_DYLIB)]
80                 extern static int mono_btls_bio_flush (IntPtr bio);
81
82                 [DllImport (BTLS_DYLIB)]
83                 extern static int mono_btls_bio_indent (IntPtr bio, uint indent, uint max_indent);
84
85                 [DllImport (BTLS_DYLIB)]
86                 extern static int mono_btls_bio_hexdump (IntPtr bio, IntPtr data, int len, uint indent);
87
88                 [DllImport (BTLS_DYLIB)]
89                 extern static void mono_btls_bio_print_errors (IntPtr bio);
90
91                 [DllImport (BTLS_DYLIB)]
92                 extern static void mono_btls_bio_free (IntPtr handle);
93
94                 public int Read (byte[] buffer, int offset, int size)
95                 {
96                         CheckThrow ();
97                         var data = Marshal.AllocHGlobal (size);
98                         if (data == IntPtr.Zero)
99                                 throw new OutOfMemoryException ();
100
101                         bool release = false;
102                         try {
103                                 Handle.DangerousAddRef (ref release);
104                                 var ret = mono_btls_bio_read (Handle.DangerousGetHandle (), data, size);
105                                 if (ret > 0)
106                                         Marshal.Copy (data, buffer,offset, ret);
107                                 return ret;
108                         } finally {
109                                 if (release)
110                                         Handle.DangerousRelease ();
111                                 Marshal.FreeHGlobal (data);
112                         }
113                 }
114
115                 public int Write (byte[] buffer, int offset, int size)
116                 {
117                         CheckThrow ();
118                         var data = Marshal.AllocHGlobal (size);
119                         if (data == IntPtr.Zero)
120                                 throw new OutOfMemoryException ();
121
122                         bool release = false;
123                         try {
124                                 Handle.DangerousAddRef (ref release);
125                                 Marshal.Copy (buffer, offset, data, size);
126                                 return mono_btls_bio_write (Handle.DangerousGetHandle (), data, size);
127                         } finally {
128                                 if (release)
129                                         Handle.DangerousRelease ();
130                                 Marshal.FreeHGlobal (data);
131                         }
132                 }
133
134                 public int Flush ()
135                 {
136                         CheckThrow ();
137                         bool release = false;
138                         try {
139                                 Handle.DangerousAddRef (ref release);
140                                 return mono_btls_bio_flush (Handle.DangerousGetHandle ());
141                         } finally {
142                                 if (release)
143                                         Handle.DangerousRelease ();
144                         }
145                 }
146
147                 public int Indent (uint indent, uint max_indent)
148                 {
149                         CheckThrow ();
150                         bool release = false;
151                         try {
152                                 Handle.DangerousAddRef (ref release);
153                                 return mono_btls_bio_indent (Handle.DangerousGetHandle (), indent, max_indent);
154                         } finally {
155                                 if (release)
156                                         Handle.DangerousRelease ();
157                         }
158                 }
159
160                 public int HexDump (byte[] buffer, uint indent)
161                 {
162                         CheckThrow ();
163                         var data = Marshal.AllocHGlobal (buffer.Length);
164                         if (data == IntPtr.Zero)
165                                 throw new OutOfMemoryException ();
166
167                         bool release = false;
168                         try {
169                                 Handle.DangerousAddRef (ref release);
170                                 Marshal.Copy (buffer, 0, data, buffer.Length);
171                                 return mono_btls_bio_hexdump (Handle.DangerousGetHandle (), data, buffer.Length, indent);
172                         } finally {
173                                 if (release)
174                                         Handle.DangerousRelease ();
175                                 Marshal.FreeHGlobal (data);
176                         }
177                 }
178
179                 public void PrintErrors ()
180                 {
181                         CheckThrow ();
182                         bool release = false;
183                         try {
184                                 Handle.DangerousAddRef (ref release);
185                                 mono_btls_bio_print_errors (Handle.DangerousGetHandle ());
186                         } finally {
187                                 if (release)
188                                         Handle.DangerousRelease ();
189                         }
190                 }
191         }
192
193         class MonoBtlsBioMemory : MonoBtlsBio
194         {
195                 [DllImport (BTLS_DYLIB)]
196                 extern static IntPtr mono_btls_bio_mem_new ();
197
198                 [DllImport (BTLS_DYLIB)]
199                 extern static int mono_btls_bio_mem_get_data (IntPtr handle, out IntPtr data);
200
201                 public MonoBtlsBioMemory ()
202                         : base (new BoringBioHandle (mono_btls_bio_mem_new ()))
203                 {
204                 }
205
206                 public byte[] GetData ()
207                 {
208                         IntPtr data;
209                         bool release = false;
210                         try {
211                                 Handle.DangerousAddRef (ref release);
212                                 var size = mono_btls_bio_mem_get_data (Handle.DangerousGetHandle (), out data);
213                                 CheckError (size > 0);
214                                 var buffer = new byte[size];
215                                 Marshal.Copy (data, buffer, 0, size);
216                                 return buffer;
217                         } finally {
218                                 if (release)
219                                         Handle.DangerousRelease ();
220                         }
221                 }
222         }
223
224         interface IMonoBtlsBioMono
225         {
226                 int Read (byte[] buffer, int offset, int size, out bool wantMore);
227
228                 bool Write (byte[] buffer, int offset, int size);
229
230                 void Flush ();
231
232                 void Close ();
233         }
234
235         class MonoBtlsBioMono : MonoBtlsBio
236         {
237                 GCHandle handle;
238                 IntPtr instance;
239                 BioReadFunc readFunc;
240                 BioWriteFunc writeFunc;
241                 BioControlFunc controlFunc;
242                 IntPtr readFuncPtr;
243                 IntPtr writeFuncPtr;
244                 IntPtr controlFuncPtr;
245                 IMonoBtlsBioMono backend;
246
247                 public MonoBtlsBioMono (IMonoBtlsBioMono backend)
248                         : base (new BoringBioHandle (mono_btls_bio_mono_new ()))
249                 {
250                         this.backend = backend;
251                         handle = GCHandle.Alloc (this);
252                         instance = GCHandle.ToIntPtr (handle);
253                         readFunc = OnRead;
254                         writeFunc = OnWrite;
255                         controlFunc = Control;
256                         readFuncPtr = Marshal.GetFunctionPointerForDelegate (readFunc);
257                         writeFuncPtr = Marshal.GetFunctionPointerForDelegate (writeFunc);
258                         controlFuncPtr = Marshal.GetFunctionPointerForDelegate (controlFunc);
259                         mono_btls_bio_mono_initialize (Handle.DangerousGetHandle (), instance, readFuncPtr, writeFuncPtr, controlFuncPtr);
260                 }
261
262                 public static MonoBtlsBioMono CreateStream (Stream stream, bool ownsStream)
263                 {
264                         return new MonoBtlsBioMono (new StreamBackend (stream, ownsStream));
265                 }
266
267                 public static MonoBtlsBioMono CreateString (StringWriter writer)
268                 {
269                         return new MonoBtlsBioMono (new StringBackend (writer));
270                 }
271
272                 enum ControlCommand
273                 {
274                         Flush = 1
275                 }
276
277                 delegate int BioReadFunc (IntPtr bio, IntPtr data, int dataLength, out int wantMore);
278                 delegate int BioWriteFunc (IntPtr bio, IntPtr data, int dataLength);
279                 delegate long BioControlFunc (IntPtr bio, ControlCommand command, long arg);
280
281                 [DllImport (BTLS_DYLIB)]
282                 extern static IntPtr mono_btls_bio_mono_new ();
283
284                 [DllImport (BTLS_DYLIB)]
285                 extern static void mono_btls_bio_mono_initialize (IntPtr handle, IntPtr instance, IntPtr readFunc, IntPtr writeFunc, IntPtr controlFunc);
286
287                 long Control (ControlCommand command, long arg)
288                 {
289                         switch (command) {
290                         case ControlCommand.Flush:
291                                 backend.Flush ();
292                                 return 1;
293
294                         default:
295                                 throw new NotImplementedException ();
296                         }
297                 }
298
299                 int OnRead (IntPtr data, int dataLength, out int wantMore)
300                 {
301                         bool wantMoreBool;
302                         var buffer = new byte[dataLength];
303                         var ret = backend.Read (buffer, 0, dataLength, out wantMoreBool);
304                         wantMore = wantMoreBool ? 1 : 0;
305                         if (ret <= 0)
306                                 return ret;
307                         Marshal.Copy (buffer, 0, data, ret);
308                         return ret;
309                 }
310
311 #if MONOTOUCH
312                 [MonoPInvokeCallback (typeof (BioReadFunc))]
313 #endif
314                 static int OnRead (IntPtr instance, IntPtr data, int dataLength, out int wantMore)
315                 {
316                         var c = (MonoBtlsBioMono)GCHandle.FromIntPtr (instance).Target;
317                         try {
318                                 return c.OnRead (data, dataLength, out wantMore);
319                         } catch (Exception ex) {
320                                 c.SetException (ex);
321                                 wantMore = 0;
322                                 return -1;
323                         }
324                 }
325
326                 int OnWrite (IntPtr data, int dataLength)
327                 {
328                         var buffer = new byte[dataLength];
329                         Marshal.Copy (data, buffer, 0, dataLength);
330                         var ok = backend.Write (buffer, 0, dataLength);
331                         return ok ? dataLength : -1;
332                 }
333
334 #if MONOTOUCH
335                 [MonoPInvokeCallback (typeof (BioWriteFunc))]
336 #endif
337                 static int OnWrite (IntPtr instance, IntPtr data, int dataLength)
338                 {
339                         var c = (MonoBtlsBioMono)GCHandle.FromIntPtr (instance).Target;
340                         try {
341                                 return c.OnWrite (data, dataLength);
342                         } catch (Exception ex) {
343                                 c.SetException (ex);
344                                 return -1;
345                         }
346                 }
347
348 #if MONOTOUCH
349                 [MonoPInvokeCallback (typeof (BioControlFunc))]
350 #endif
351                 static long Control (IntPtr instance, ControlCommand command, long arg)
352                 {
353                         var c = (MonoBtlsBioMono)GCHandle.FromIntPtr (instance).Target;
354                         try {
355                                 return c.Control (command, arg);
356                         } catch (Exception ex) {
357                                 c.SetException (ex);
358                                 return -1;
359                         }
360                 }
361
362                 protected override void Close ()
363                 {
364                         try {
365                                 if (backend != null) {
366                                         backend.Close ();
367                                         backend = null;
368                                 }
369                                 if (handle.IsAllocated)
370                                         handle.Free ();
371                         } finally {
372                                 base.Close ();
373                         }
374                 }
375
376                 class StreamBackend : IMonoBtlsBioMono
377                 {
378                         Stream stream;
379                         bool ownsStream;
380
381                         public Stream InnerStream {
382                                 get { return stream; }
383                         }
384
385                         public StreamBackend (Stream stream, bool ownsStream)
386                         {
387                                 this.stream = stream;
388                                 this.ownsStream = ownsStream;
389                         }
390
391                         public int Read (byte[] buffer, int offset, int size, out bool wantMore)
392                         {
393                                 wantMore = false;
394                                 return stream.Read (buffer, offset, size);
395                         }
396
397                         public bool Write (byte[] buffer, int offset, int size)
398                         {
399                                 stream.Write (buffer, offset, size);
400                                 return true;
401                         }
402
403                         public void Flush ()
404                         {
405                                 stream.Flush ();
406                         }
407
408                         public void Close ()
409                         {
410                                 if (ownsStream && stream != null)
411                                         stream.Dispose ();
412                                 stream = null;
413                         }
414                 }
415
416                 class StringBackend : IMonoBtlsBioMono
417                 {
418                         StringWriter writer;
419                         Encoding encoding = new UTF8Encoding ();
420
421                         public StringBackend (StringWriter writer)
422                         {
423                                 this.writer = writer;
424                         }
425
426                         public int Read (byte[] buffer, int offset, int size, out bool wantMore)
427                         {
428                                 wantMore = false;
429                                 return -1;
430                         }
431
432                         public bool Write (byte[] buffer, int offset, int size)
433                         {
434                                 var text = encoding.GetString (buffer, offset, size);
435                                 writer.Write (text);
436                                 return true;
437                         }
438
439                         public void Flush ()
440                         {
441                         }
442
443                         public void Close ()
444                         {
445                         }
446                 }
447         }
448 }
449 #endif