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