1 /*------------------------------------------------------------------------------
2  *
3  * Copyright (c) 2011-2021, EURid vzw. All rights reserved.
4  * The YADIFA TM software product is provided under the BSD 3-clause license:
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  *        * Redistributions of source code must retain the above copyright
11  *          notice, this list of conditions and the following disclaimer.
12  *        * Redistributions in binary form must reproduce the above copyright
13  *          notice, this list of conditions and the following disclaimer in the
14  *          documentation and/or other materials provided with the distribution.
15  *        * Neither the name of EURid nor the names of its contributors may be
16  *          used to endorse or promote products derived from this software
17  *          without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  *------------------------------------------------------------------------------
32  *
33  */
34 
35 /** @defgroup streaming Streams
36  *  @ingroup dnscore
37  *  @brief
38  *
39  *  Implementation of routines for the resource_record struct
40  *
41  * @{
42  */
43 /*------------------------------------------------------------------------------
44  *
45  * USE INCLUDES */
46 
47 #include "dnscore/dnscore-config.h"
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <arpa/inet.h>
51 #include <ctype.h>
52 
53 #include "dnscore/dnscore.h"
54 #include "dnscore/logger.h"
55 #include "dnscore/dnsname.h"
56 #include "dnscore/output_stream.h"
57 #include "dnscore/base64.h"
58 #include "dnscore/base32.h"
59 #include "dnscore/base32hex.h"
60 #include "dnscore/base16.h"
61 #include "dnscore/zalloc.h"
62 
63 #define MODULE_MSG_HANDLE g_system_logger
64 
65 #define OSTREAM_TAG 0x4d41455254534f
66 
67 static const char ESCAPE_CHARS[] = {'@', '$', '\\', ';'};
68 
69 ya_result
output_stream_write_nu32(output_stream * os,u32 value)70 output_stream_write_nu32(output_stream* os, u32 value)
71 {
72     u8 buffer[4];
73 
74     /*    ------------------------------------------------------------    */
75 
76     buffer[0] = value >> 24;
77     buffer[1] = value >> 16;
78     buffer[2] = value >> 8;
79     buffer[3] = value;
80 
81     return output_stream_write(os, buffer, 4);
82 }
83 
84 ya_result
output_stream_write_nu16(output_stream * os,u16 value)85 output_stream_write_nu16(output_stream* os, u16 value)
86 {
87     u8 buffer[2];
88 
89     /*    ------------------------------------------------------------    */
90 
91     buffer[0] = value >> 8;
92     buffer[1] = value;
93 
94     return output_stream_write(os, buffer, 2);
95 }
96 
97 ya_result
output_stream_decode_base64(output_stream * os,const char * string,u32 length)98 output_stream_decode_base64(output_stream* os, const char * string, u32 length)
99 {
100     char buffer[64];
101     u8 buffer_bin[48];
102 
103     u32 needle = 0;
104 
105     ya_result return_code = OK;
106 
107     /*    ------------------------------------------------------------    */
108 
109     while(length-- > 0)
110     {
111         char c = *string++;
112 
113         if(isspace(c))
114         {
115             continue;
116         }
117 
118         buffer[needle++] = c;
119         if(needle == 64)
120         {
121             if(FAIL(return_code = base64_decode(buffer, needle, buffer_bin)))
122             {
123                 return return_code;
124             }
125 
126             if(FAIL(return_code = output_stream_write(os, buffer_bin, return_code)))
127             {
128                 return return_code;
129             }
130 
131             needle = 0;
132         }
133     }
134 
135     if(needle > 0)
136     {
137         if((needle & 3) != 0)
138         {
139             return PARSEB64_ERROR;
140         }
141 
142         if(FAIL(return_code = base64_decode(buffer, needle, buffer_bin)))
143         {
144             return return_code;
145         }
146 
147         if(FAIL(return_code = output_stream_write(os, buffer_bin, return_code)))
148         {
149             return return_code;
150         }
151     }
152 
153     return return_code;
154 }
155 
156 ya_result
output_stream_decode_base32(output_stream * os,const char * string,u32 length)157 output_stream_decode_base32(output_stream* os, const char * string, u32 length)
158 {
159     char buffer[64];
160     u8 buffer_bin[40];
161 
162     u32 needle = 0;
163 
164     ya_result return_code = OK;
165 
166     /*    ------------------------------------------------------------    */
167 
168     while(length-- > 0)
169     {
170         char c = *string++;
171 
172         if(isspace(c))
173         {
174             continue;
175         }
176 
177         buffer[needle++] = c;
178         if(needle == sizeof(buffer))
179         {
180             if(FAIL(return_code = base32_decode(buffer, needle, buffer_bin)))
181             {
182                 return return_code;
183             }
184 
185             if(FAIL(return_code = output_stream_write(os, buffer_bin, return_code)))
186             {
187                 return return_code;
188             }
189 
190             needle = 0;
191         }
192     }
193 
194     if(needle > 0)
195     {
196         if((needle & 7) != 0)
197         {
198             return PARSEB32_ERROR;
199         }
200 
201         if(FAIL(return_code = base32_decode(buffer, needle, buffer_bin)))
202         {
203             return return_code;
204         }
205 
206         if(FAIL(return_code = output_stream_write(os, buffer_bin, return_code)))
207         {
208             return return_code;
209         }
210     }
211 
212     return return_code;
213 }
214 
215 ya_result
output_stream_decode_base32hex(output_stream * os,const char * string,u32 length)216 output_stream_decode_base32hex(output_stream* os, const char * string, u32 length)
217 {
218     char buffer[64];
219     u8 buffer_bin[40];
220 
221     u32 needle = 0;
222 
223     ya_result return_code = OK;
224 
225     /*    ------------------------------------------------------------    */
226 
227     while(length-- > 0)
228     {
229         char c = *string++;
230 
231         if(isspace(c))
232         {
233             continue;
234         }
235 
236         buffer[needle++] = c;
237 
238         if(needle == sizeof(buffer))
239         {
240             if(FAIL(return_code = base32hex_decode(buffer, needle, buffer_bin)))
241             {
242                 return return_code;
243             }
244 
245             if(FAIL(return_code = output_stream_write(os, buffer_bin, return_code)))
246             {
247                 return return_code;
248             }
249 
250             needle = 0;
251         }
252     }
253 
254     if(needle > 0)
255     {
256         if((needle & 7) != 0)
257         {
258             return PARSEB32H_ERROR;
259         }
260 
261         if(FAIL(return_code = base32hex_decode(buffer, needle, buffer_bin)))
262         {
263             return return_code;
264         }
265 
266         if(FAIL(return_code = output_stream_write(os, buffer_bin, return_code)))
267         {
268             return return_code;
269         }
270     }
271 
272     return return_code;
273 }
274 
275 ya_result
output_stream_decode_base16(output_stream * os,const char * string,u32 length)276 output_stream_decode_base16(output_stream* os, const char * string, u32 length)
277 {
278     const char *string_start = string;
279     u32 needle = 0;
280     ya_result return_code = OK;
281     char buffer[64];
282     u8 buffer_bin[32];
283 
284     /*    ------------------------------------------------------------    */
285 
286     while(length-- > 0)
287     {
288         char c = *string++;
289 
290         if(isspace(c))
291         {
292             continue;
293         }
294 
295         buffer[needle++] = c;
296         if(needle == sizeof(buffer))
297         {
298             if(FAIL(return_code = base16_decode(buffer, needle, buffer_bin)))
299             {
300                 return return_code;
301             }
302             if(FAIL(return_code = output_stream_write(os, buffer_bin, return_code)))
303             {
304                 return return_code;
305             }
306 
307             needle = 0;
308         }
309     }
310 
311     if(needle > 0)
312     {
313         if((needle & 1) != 0)
314         {
315             return PARSEB16_ERROR;
316         }
317 
318         if(FAIL(return_code = base16_decode(buffer, needle, buffer_bin)))
319         {
320             return return_code;
321         }
322 
323         if(FAIL(return_code = output_stream_write(os, buffer_bin, return_code)))
324         {
325             return return_code;
326         }
327     }
328 
329     /* return the number of bytes read, instead of the last write size
330      * this way something can be done about the input.
331      *
332      * alternatively we could just return "success"
333      */
334 
335     return string - string_start;
336 }
337 
338 ya_result
output_stream_write_pu32(output_stream * os,u32 value)339 output_stream_write_pu32(output_stream* os, u32 value)
340 {
341     u8 v;
342 
343     while(value > 127)
344     {
345         v = (u8)value;
346         value >>= 7;
347         v |= 0x80;
348 
349         /* I'll only check the error for the last byte */
350 
351         output_stream_write(os, &v, 1);
352     }
353 
354     v = (u8)value;
355 
356     return output_stream_write(os, &v, 1);
357 }
358 
359 ya_result
output_stream_write_pu64(output_stream * os,u64 value)360 output_stream_write_pu64(output_stream* os, u64 value)
361 {
362     u8 v;
363 
364     while(value > 127)
365     {
366         v = (u8)value;
367         value >>= 7;
368         v |= 0x80;
369 
370         /* I'll only check the error for the last byte */
371 
372         output_stream_write(os, &v, 1);
373     }
374 
375     v = (u8)value;
376 
377     return output_stream_write(os, &v, 1);
378 }
379 
380 ya_result
output_stream_write_dnsname(output_stream * os,const u8 * name)381 output_stream_write_dnsname(output_stream* os, const u8 *name)
382 {
383     u32 len = dnsname_len(name);
384     return output_stream_write(os, name, len);
385 }
386 
387 ya_result
output_stream_write_dnsname_text(output_stream * os,const u8 * name)388 output_stream_write_dnsname_text(output_stream* os, const u8 *name)
389 {
390     static char dot[1] = {'.'};
391 
392     const u8 *base = name;
393 
394     u8 label_len;
395     label_len = *name;
396 
397     if(label_len > 0)
398     {
399         do
400         {
401             output_stream_write(os, ++name, label_len);
402             output_stream_write(os, &dot, 1);
403             name += label_len;
404             label_len = *name;
405         }
406         while(label_len > 0);
407     }
408     else
409     {
410         output_stream_write(os, &dot, 1);
411     }
412 
413     return name - base + 1;
414 }
415 
416 ya_result
output_stream_write_dnslabel_text_escaped(output_stream * os,const u8 * label)417 output_stream_write_dnslabel_text_escaped(output_stream* os, const u8 *label)
418 {
419     static const char escape[1] = {'\\'};
420 
421     int len = *label++;
422 
423     u32 additional_len = 0;
424     for(int i = 0; i < len; ++i)
425     {
426         switch(label[i])
427         {
428             case '@':
429             case '$':
430             case ';':
431             case '\\':
432                 ++additional_len;
433                 output_stream_write(os, escape, 1);
434                 FALLTHROUGH // fall through
435             default:
436                 output_stream_write(os, &label[i], 1);
437         }
438     }
439 
440     return len + additional_len;
441 }
442 
443 static bool
output_stream_write_should_escape(const u8 * name,size_t name_len)444 output_stream_write_should_escape(const u8* name, size_t name_len)
445 {
446     for(size_t i = 0; i < name_len; ++i)
447     {
448         const char c = name[i];
449 
450         for(u32 j = 0; j < sizeof(ESCAPE_CHARS); ++j)
451         {
452             if(c == ESCAPE_CHARS[j])
453             {
454                 return TRUE;
455             }
456         }
457     }
458 
459     return FALSE;
460 }
461 
462 ya_result
output_stream_write_dnsname_text_escaped(output_stream * os,const u8 * name)463 output_stream_write_dnsname_text_escaped(output_stream* os, const u8 *name)
464 {
465     static const char dot[1] = {'.'};
466 
467     u8 label_len;
468     label_len = *name;
469     ya_result ret;
470 
471     if(label_len > 0)
472     {
473         ret = 0;
474 
475         do
476         {
477             ++name;
478 
479             if(!output_stream_write_should_escape(name, label_len))
480             {
481                 output_stream_write(os, name, label_len);
482                 ret += label_len;
483             }
484             else
485             {
486                 // write escaped
487                 ret += output_stream_write_dnslabel_text_escaped(os, name - 1);
488             }
489 
490             output_stream_write(os, dot, 1);
491             ++ret;
492 
493             name += label_len;
494             label_len = *name;
495         }
496         while(label_len > 0);
497     }
498     else
499     {
500         output_stream_write(os, dot, 1);
501         ret = 1;
502     }
503 
504     return ret;
505 }
506 
507 ya_result
output_stream_write_dnslabel_vector(output_stream * os,dnslabel_vector_reference labels,s32 top)508 output_stream_write_dnslabel_vector(output_stream* os, dnslabel_vector_reference labels, s32 top)
509 {
510     ya_result n = 0;
511     s32 i;
512 
513     for(i = 0; i <= top; i++)
514     {
515         ya_result err;
516         u8 len = labels[i][0] + 1;
517 
518         if(FAIL(err = output_stream_write(os, labels[i], len)))
519         {
520             return err;
521         }
522 
523         n += err;
524     }
525 
526     output_stream_write_u8(os, 0);
527 
528     return n;
529 }
530 
531 ya_result
output_stream_write_dnslabel_stack(output_stream * os,dnslabel_stack_reference labels,s32 top)532 output_stream_write_dnslabel_stack(output_stream* os, dnslabel_stack_reference labels, s32 top)
533 {
534     ya_result n = 0;
535     s32 i;
536 
537     for(i = top; i >= 0; i--)
538     {
539         ya_result err;
540         u8 len = labels[i][0] + 1;
541 
542         if(FAIL(err = output_stream_write(os, labels[i], len)))
543         {
544             return err;
545         }
546 
547         n += err;
548     }
549 
550     output_stream_write_u8(os, 0);
551 
552     return n;
553 }
554 
555 output_stream*
output_stream_alloc()556 output_stream_alloc()
557 {
558     output_stream* os;
559     ZALLOC_OBJECT_OR_DIE(os, output_stream, OSTREAM_TAG); /* OSTREAM */
560     os->data = NULL;
561     os->vtbl = NULL;
562     return os;
563 }
564 
565 static ya_result
void_output_stream_write(output_stream * stream,const u8 * buffer,u32 len)566 void_output_stream_write(output_stream* stream, const u8* buffer, u32 len)
567 {
568     (void)stream;
569     (void)buffer;
570     (void)len;
571     log_err("tried to write a closed stream");
572     return INVALID_STATE_ERROR;
573 }
574 
575 static ya_result
void_output_stream_flush(output_stream * stream)576 void_output_stream_flush(output_stream* stream)
577 {
578     (void)stream;
579     log_err("tried to flush a closed stream");
580     return INVALID_STATE_ERROR;
581 }
582 
583 static void
void_output_stream_close(output_stream * stream)584 void_output_stream_close(output_stream* stream)
585 {
586     (void)stream;
587     /*
588      * WARNING
589      */
590     log_err("tried to close a closed stream");
591 
592 #if DEBUG
593     abort();
594 #endif
595 }
596 
597 static const output_stream_vtbl void_output_stream_vtbl ={
598     void_output_stream_write,
599     void_output_stream_flush,
600     void_output_stream_close,
601     "void_output_stream",
602 };
603 
604 /**
605  * This tools allows a safer misuse (and detection) of closed streams
606  * It sets the stream to a sink that warns abouts its usage and for which every call that can fail fails.
607  */
608 
output_stream_set_void(output_stream * stream)609 void output_stream_set_void(output_stream* stream)
610 {
611     stream->data = NULL;
612     stream->vtbl = &void_output_stream_vtbl;
613 }
614 
615 static ya_result
sink_output_stream_write(output_stream * stream,const u8 * buffer,u32 len)616 sink_output_stream_write(output_stream* stream, const u8* buffer, u32 len)
617 {
618     (void)stream;
619     (void)buffer;
620     return len;
621 }
622 
623 static ya_result
sink_output_stream_flush(output_stream * stream)624 sink_output_stream_flush(output_stream* stream)
625 {
626     (void)stream;
627     return SUCCESS;
628 }
629 
630 static void
sink_output_stream_close(output_stream * stream)631 sink_output_stream_close(output_stream* stream)
632 {
633     (void)stream;
634 }
635 
636 static const output_stream_vtbl sink_output_stream_vtbl ={
637     sink_output_stream_write,
638     sink_output_stream_flush,
639     sink_output_stream_close,
640     "sink_output_stream",
641 };
642 
643 /**
644  * Used to temporarily initialise a stream with a sink that can be closed safely.
645  * Typically used as pre-init so the stream can be closed even if the function
646  * setup failed before reaching stream initialisation.
647  *
648  * @param os
649  */
650 
output_stream_set_sink(output_stream * os)651 void output_stream_set_sink(output_stream* os)
652 {
653     os->data = NULL;
654     os->vtbl = &sink_output_stream_vtbl;
655 }
656 
657 ya_result
output_stream_write_fully(output_stream * stream,const void * buffer_start,u32 len_start)658 output_stream_write_fully(output_stream* stream, const void* buffer_start, u32 len_start)
659 {
660     output_stream_write_method* writefunc = stream->vtbl->write;
661     u32 len = len_start;
662     u8* buffer = (u8*)buffer_start;
663     ya_result ret;
664 
665     while(len > 0)
666     {
667         if(FAIL(ret = writefunc(stream, buffer, len)))
668         {
669             return ret;
670         }
671 
672         if(ret == 0) /* eof */
673         {
674             break;
675         }
676 
677         buffer += ret;
678         len -= ret; // cppcheck: false positive
679     }
680 
681     /* If we only read a partial it's wrong.
682      * If we were asked to read nothing it's ok.
683      * If we read nothing at all we were on EOF and its still ok
684      */
685 
686     if(len > 0)
687     {
688         return UNABLE_TO_COMPLETE_FULL_WRITE;
689     }
690 
691     return buffer - (u8*)buffer_start;
692 }
693 
694 /** @} */
695