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 yadifa
36  *  @ingroup ###
37  *  @brief
38  */
39 #include "client-config.h"
40 
41 #include <dnscore/output_stream.h>
42 #include <dnscore/counter_output_stream.h>
43 #include <dnscore/format.h>
44 #include <dnscore/message.h>
45 #include <dnscore/message-viewer.h>
46 
47 //#include message-viewer-xml.h"
48 
49 
50 
51 static char *message_record_names[4] =
52         {
53                 "question", "answer", "authority", "additional"
54         };
55 
56 static char *message_section_names[4] =
57         {
58                 "questions", "answers", "authorities", "additionals"
59         };
60 
61 static char *message_section_update_names[4] =
62         {
63                 "ZONE", "PREREQUISITES", "UPDATE RECORDS", "ADDITIONAL RECORDS"
64         };
65 
66 static char *record_name;
67 
68 
69 #define PRETTY_PRINT_WORD "|    "
70 #define PRETTY_PRINT_CR   '\n'
71 
72 static char pretty_print_tab[32];
73 static char pretty_print_cr[2];
74 
75 /*----------------------------------------------------------------------------*/
76 #pragma mark STATIC PROTOTYPES
77 
level_up(message_viewer * mv)78 static void level_up(message_viewer *mv)
79 {
80     strcat(pretty_print_tab, PRETTY_PRINT_WORD);
81 }
82 
level_down(message_viewer * mv)83 static void level_down(message_viewer *mv)
84 {
85     size_t l = strlen(PRETTY_PRINT_WORD);
86     size_t length = strlen(pretty_print_tab);
87 
88     pretty_print_tab[length -l] = '\0';
89 }
90 
level_finalize(message_viewer * mv)91 static void level_finalize(message_viewer *mv)
92 {
93     pretty_print_cr[0] = '\0';
94     pretty_print_tab[0] = '\0';
95 }
96 
level_init(message_viewer * mv)97 static void level_init(message_viewer *mv)
98 {
99     pretty_print_cr[0] = PRETTY_PRINT_CR;
100     pretty_print_tab[0] = '\0';
101 }
102 
103 static void
message_viewer_xml_start(message_viewer * mv)104 message_viewer_xml_start(message_viewer *mv)
105 {
106     output_stream *os = mv->os;
107 
108     /* init if pretty print outpu */
109     level_init(mv);
110 
111     /*    ------------------------------------------------------------    */
112 
113     osformatln(os,
114                "; ( server found)");         /// @todo 20150709 gve -- still need to find a way to show amount of servers
115     osformatln(os,
116                ";; global options: ");       /// @todo 20150709 gve -- no global options given output_stream                                         *os  = mv->os;
117 
118 
119     /*
120      * print xml encoding and start of the response container
121      *
122      * <?xml version="1.0" encoding="UTF-8"?>
123      * <response>
124      */
125 
126     osformat(os,"<?xml version=\"1.0\" encoding=\"UTF-8\"?>%s", pretty_print_cr);
127     osformat(os, "<response>%s", pretty_print_cr);
128 
129     /* pretty print output level of tabs +1 */
130     level_up(mv);
131 }
132 
133 
134 static void
message_viewer_xml_end(message_viewer * mv,long time_duration)135 message_viewer_xml_end(message_viewer *mv, long time_duration)
136 {
137     output_stream *os = mv->os;
138     time_t timep;
139 
140     time(&timep);
141 
142     level_down(mv);
143     osformat(os, "</response>%s", pretty_print_cr);
144 
145     osformat(os, "%s<meta-data>%s", pretty_print_tab, pretty_print_cr);
146     level_up(mv);
147     osformat(os, "%s<query-time unit=\"msec\">%ld<query-time>%s", pretty_print_tab, time_duration, pretty_print_cr);
148 
149     /** @todo 20150716 gve -- still need to implemented the server viewable line */
150 //    osformat(os, ";; SERVER: %{hostaddr}(%{hostaddr})\n", config->server, config->server);
151 
152 
153     /// @todo 20150716 gve -- remove carriage return in timep
154 
155     osformat(os, "%s<date>%s</date>%s", pretty_print_tab, ctime(&timep), pretty_print_cr);
156     // osformat(os, "<when>%s</when>", date_stamp);
157 
158     if(mv->view_mode_with & MESSAGE_VIEWER_WITH_XFR)
159     {
160         osformat(os, "%s<records>%lu</records>%s", pretty_print_tab, mv->resource_records_total[1], pretty_print_cr);
161         osformat(os, "%s<messages>%lu</messages>%s", pretty_print_tab, mv->messages, pretty_print_cr);
162         osformat(os, "%s<bytes>%lu</bytes>%s", pretty_print_tab, mv->bytes, pretty_print_cr);
163     }
164     else
165     {
166         osformatln(os, "%s<msg-size kind=\"rcvd\">%ld<msg-size>%s", pretty_print_tab, mv->bytes, pretty_print_tab);
167     }
168 
169 
170     /* pretty print output level of tabs -1 */
171     level_down(mv);
172     osformat(os, "%s</meta-data>%s", pretty_print_tab, pretty_print_cr);
173 
174     /* clear pretty print output */
175     level_finalize(mv);
176 }
177 
178 
179 static void
message_viewer_xml_header(message_viewer * mv,const u8 * buffer)180 message_viewer_xml_header(message_viewer *mv, const u8 *buffer)
181 {
182     /* 1. get the output stream */
183     output_stream *os = mv->os;
184 
185 
186     /* 2. get values of the different sections: QUESTION, ANSWER, AUTHORITY and ADDITIONAL */
187     u16 count[4];
188     count[0] = ntohs(MESSAGE_QD(buffer));
189     count[1] = ntohs(MESSAGE_AN(buffer));
190     count[2] = ntohs(MESSAGE_NS(buffer));
191     count[3] = ntohs(MESSAGE_AR(buffer));
192 
193 
194     /* 3. add the amount of section resource records into a total */
195     message_viewer_resource_record_total_update(mv, count);
196 
197 
198     /* 4. get message id */
199     u16 id = MESSAGE_ID(buffer);
200 
201 
202     /* 5. get opcode and rcode.
203      *    opcode is needed for for knowing the difference between a regular message and a update message
204      */
205     u8 opcode = MESSAGE_OP(buffer);
206     opcode >>= OPCODE_SHIFT;
207 
208     u8 rcode = MESSAGE_RCODE(buffer);
209 
210     const char *opcode_txt = dns_message_opcode_get_name(opcode);
211     const char *status_txt = dns_message_rcode_get_name(rcode);
212 
213     mv->section_name = (opcode != OPCODE_UPDATE) ? message_section_names : message_section_update_names;
214 
215 
216     /* if no view with header then inmediately return,
217      * xml axfr has no header information so --> return
218      */
219     if(mv->view_mode_with & MESSAGE_VIEWER_WITH_XFR)
220     {
221         return;
222     }
223 
224 
225     /* 6. we have all the information, fill the stream */
226     osformat(os, "%s<opcode>%s</opcode>%s", pretty_print_tab, opcode_txt, pretty_print_cr);
227     osformat(os, "%s<status>%s</status>%s", pretty_print_tab, status_txt, pretty_print_cr);
228     osformat(os, "%s<id format=\"dec\">%hd</id>%s", pretty_print_tab, id, pretty_print_cr);
229 
230     osformat(os, "%s<flags>%s", pretty_print_tab, pretty_print_cr);
231 
232     /* pretty print output level of tabs +1 */
233     level_up(mv);
234 
235     if(MESSAGE_QR(buffer) != 0) osformat(os, "%s<flag>qr</flag>%s", pretty_print_tab, pretty_print_cr);
236     if(MESSAGE_AA(buffer) != 0) osformat(os, "%s<flag>aa</flag>%s", pretty_print_tab, pretty_print_cr);
237     if(MESSAGE_TC(buffer) != 0) osformat(os, "%s<flag>tc</flag>%s", pretty_print_tab, pretty_print_cr);
238     if(MESSAGE_RD(buffer) != 0) osformat(os, "%s<flag>rd</flag>%s", pretty_print_tab, pretty_print_cr);
239     if(MESSAGE_RA(buffer) != 0) osformat(os, "%s<flag>ra</flag>%s", pretty_print_tab, pretty_print_cr);
240     if(MESSAGE_ZF(buffer) != 0) osformat(os, "%s<flag>zf</flag>%s", pretty_print_tab, pretty_print_cr);
241     if(MESSAGE_AD(buffer) != 0) osformat(os, "%s<flag>ad</flag>%s", pretty_print_tab, pretty_print_cr);
242     if(MESSAGE_CD(buffer) != 0) osformat(os, "%s<flag>cd</flag>%s", pretty_print_tab, pretty_print_cr);
243 
244     /* pretty print output level of tabs -1 */
245     level_down(mv);
246 
247     osformat(os, "%s</flags>%s", pretty_print_tab, pretty_print_cr);
248 }
249 
250 
251 static void
message_viewer_xml_section_header(message_viewer * mv,u32 section_idx,u16 count)252 message_viewer_xml_section_header(message_viewer *mv, u32 section_idx, u16 count)
253 {
254     if(mv->view_mode_with & MESSAGE_VIEWER_WITH_XFR)
255     {
256         return;
257     }
258 
259 //    u16 view_mode_with = mv->view_mode_with;
260     output_stream *os = mv->os;
261 
262     const char *section_name = mv->section_name[section_idx];
263 
264     record_name = message_record_names[section_idx];
265 
266 //    if(message_viewer_requires_section(section_idx, view_mode_with) && count)
267     {
268         osformat(os, "%s<%s amount=\"%d\">%s", pretty_print_tab, section_name , count, pretty_print_cr);
269     }
270 
271 
272     /* pretty print output level of tabs +1 */
273     level_up(mv);
274 }
275 
276 
277 static void
message_viewer_xml_section_footer(message_viewer * mv,u32 section_idx,u16 count)278 message_viewer_xml_section_footer(message_viewer *mv, u32 section_idx, u16 count)
279 {
280     if(mv->view_mode_with & MESSAGE_VIEWER_WITH_XFR)
281     {
282         return;
283     }
284 
285 //    u16 view_mode_with = mv->view_mode_with;
286     output_stream *os = mv->os;
287 
288     const char *section_name = mv->section_name[section_idx];
289 
290     /* pretty print output level of tabs -1 */
291     level_down(mv);
292 
293 //    if(message_viewer_requires_section(section_idx, view_mode_with))
294     {
295         osformat(os, "%s</%s>%s", pretty_print_tab, section_name, pretty_print_cr);
296     }
297 }
298 
299 
300 static void
message_viewer_xml_question_record(message_viewer * mv,const u8 * record_wire,u16 rclass,u16 rtype)301 message_viewer_xml_question_record(message_viewer *mv, const u8 *record_wire, u16 rclass, u16 rtype)
302 {
303     if(mv->view_mode_with & MESSAGE_VIEWER_WITH_XFR)
304     {
305         return;
306     }
307 
308     output_stream *os_ = mv->os;
309 
310     /*
311      * There is no padding support for formats on complex types (padding is ignored)
312      * Doing it would be relatively expensive for it's best doing it manually when needed (afaik: only here)
313      */
314 
315     counter_output_stream_data counters;
316     output_stream cos;
317     counter_output_stream_init(os_, &cos, &counters);
318 
319     output_stream *os = &cos;
320 
321     /* A. print container begin */
322     osformat(os, "%s<%s>%s", pretty_print_tab, record_name, pretty_print_cr);
323 
324     /* pretty print output level of tabs +1 */
325     level_up(mv);
326 
327 
328     /* B. write resource record: FQDN + attribute 'ClASS' and 'TYPE' */
329     osformat(os, "%s<name class=\"%{dnsclass}\" type=\"%{dnstype}\">%{dnsname}</name>%s",
330              pretty_print_tab, &rclass, &rtype, record_wire, pretty_print_cr);
331 
332 
333     /* C. print container end */
334 
335     /* pretty print output level of tabs -1 */
336     level_down(mv);
337 
338     osformat(os, "%s</%s>%s", pretty_print_tab, record_name, pretty_print_cr);
339 
340 
341     flushout();
342 }
343 
344 
345 static void
message_viewer_xml_section_record(message_viewer * mv,const u8 * record_wire,u8 section_idx)346 message_viewer_xml_section_record(message_viewer *mv, const u8 *record_wire, u8 section_idx)
347 {
348     (void)section_idx;
349 
350     /*
351      * there is no padding support for formats on complex types (padding is ignored)
352      * doing it would be relatively expensive for it's best doing it manually when needed (afaik: only here)
353      */
354 
355     counter_output_stream_data counters;
356     output_stream cos;
357     output_stream *os_ = mv->os;
358     counter_output_stream_init(os_, &cos, &counters);
359 
360     output_stream *os = &cos; /* final output stream */
361 
362     /*    ------------------------------------------------------------    */
363 
364 
365     /* 1. get the needed parameters: FQDN, TYPE, CLASS, TTL, RDATA size */
366     const u8 *rname = record_wire;
367     const u8 *rdata = rname + dnsname_len(rname);
368     u16 rtype = GET_U16_AT(rdata[0]);
369     u16 rclass = GET_U16_AT(rdata[2]);
370     u32 rttl = ntohl(GET_U32_AT(rdata[4]));
371     u16 rdata_size = ntohs(GET_U16_AT(rdata[8]));
372 
373     /** @todo 20150716 gve -- test that rdata_size matches the record size */
374 
375     /* move pointer to RDATA information in the record_wire */
376     rdata += 10;
377 
378 
379     /* 2. write the retrieved info into the stream:
380      *    e.g.
381      *
382      *  <answer>
383      *      <name class="IN" type="NS" ttl"86400">somedomain.eu</name>
384      *      <rdata length="19">ns1.somedomain.eu.</rdata>
385      *  </answer>
386      */
387 
388     /* A. print container begin */
389     osformat(os, "%s<%s>%s", pretty_print_tab, record_name, pretty_print_cr);
390 
391     /* pretty print output level of tabs +1 */
392     level_up(mv);
393 
394 
395     /* B. write resource record: FQDN + attribute 'ClASS', 'TYPE' and 'TTL' */
396     osformat(os, "%s<name class=\"%{dnsclass}\" type=\"%{dnstype}\" ttl\"%d\">%{dnsname}</name>%s", pretty_print_tab, &rclass, &rtype, rttl, rname, pretty_print_cr);
397 
398 
399     /* C. write RDATA with attribute rdata size */
400     //osformat(os, "%s<rdata size=\"%d\">", pretty_print_tab, rdata_size);
401     osformat(os, "%s<rdata>", pretty_print_tab);
402     osprint_rdata(os, rtype, rdata, rdata_size);
403     osformat(os, "</rdata>%s", pretty_print_cr);
404 
405 
406     /* D. print container end */
407 
408     /* pretty print output level of tabs -1 */
409     level_down(mv);
410 
411     osformat(os, "%s</%s>%s", pretty_print_tab, record_name, pretty_print_cr);
412 
413 
414     flushout();
415 }
416 
417 static ya_result
message_viewer_xml_pseudosection_record(message_viewer * mv,const u8 * record_wire)418 message_viewer_xml_pseudosection_record(message_viewer *mv, const u8 *record_wire)
419 {
420     (void)mv;
421     (void)record_wire;
422     return SUCCESS;
423 }
424 
425 static const message_viewer_vtbl xml_viewer_vtbl = {
426         message_viewer_xml_header,
427         message_viewer_xml_start,
428         message_viewer_xml_end,
429         message_viewer_xml_section_header,
430         message_viewer_xml_section_footer,
431         message_viewer_xml_question_record,
432         message_viewer_xml_section_record,
433         message_viewer_xml_pseudosection_record,
434         "message_viewer_xml",
435 };
436 
437 
438 /*----------------------------------------------------------------------------*/
439 #pragma mark FUNCTIONS
440 
441 void
message_viewer_xml_set(message_viewer * mv)442 message_viewer_xml_set(message_viewer *mv)
443 {
444     mv->vtbl = &xml_viewer_vtbl;
445 }
446 
447 
448 void
message_viewer_xml_init(message_viewer * mv,output_stream * os,u16 view_mode_with)449 message_viewer_xml_init(message_viewer *mv, output_stream *os, u16 view_mode_with)
450 {
451     message_viewer_init(mv);
452 
453     mv->vtbl = &xml_viewer_vtbl;
454     mv->os = os;
455     mv->view_mode_with = view_mode_with;
456 }
457