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