1 /*
2  * Print various SIE messages
3  *
4  *  Copyright (c) 2014-2018 by Farsight Security, Inc.
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */
18 
19 #include "sratool.h"
20 
21 /* extern: cmd.c */
22 extern EditLine *el_e;
23 extern axa_mode_t mode;
24 extern struct timeval no_reprompt;
25 
26 /* extern: infile.c */
27 extern int in_file_cur;
28 
29 /* extern: main.c */
30 extern axa_emsg_t emsg;
31 extern uint axa_debug;
32 extern int packet_count;
33 extern bool packet_counting;
34 extern int packet_count_total;
35 extern nmsg_input_t nmsg_input;
36 
37 /* extern: output.c */
38 extern bool out_on;
39 extern char *out_addr;
40 extern pcap_t *out_pcap;
41 extern int output_count;
42 extern bool output_counting;
43 extern int output_count_total;
44 extern nmsg_output_t out_nmsg_output;
45 
46 /* extern: server.c */
47 extern axa_client_t client;
48 
49 /* private */
50 static uint out_bar_idx;
51 static struct timeval out_bar_time;
52 static const char *out_bar_strs[] = {
53 	"|\b", "/\b", "-\b", "\\\b", "|\b", "/\b", "-\b", "\\\b"
54 };
55 #define PROGRESS_MS (1000/AXA_DIM(out_bar_strs)) /* 2 revolutions/second */
56 
57 bool					/* false=failed to print it */
print_dns_pkt(const uint8_t * data,size_t data_len,const char * str)58 print_dns_pkt(const uint8_t *data, size_t data_len, const char *str)
59 {
60 	wdns_message_t m;
61 	wdns_rr_t *q;
62 	const char *rcode, *class, *rtype;
63 	char wdns_resbuf[AXA_WDNS_RES_STRLEN];
64 	char class_buf[10], rtype_buf[10], rcode_buf[6];
65 	char qname[NS_MAXDNAME];
66 	char *msg_str, *p;
67 	bool eol;
68 	wdns_res wres;
69 
70 	fputs(NMSG_LEADER"DNS:", stdout);
71 
72 	wres = wdns_parse_message(&m, data, data_len);
73 	if (wres != wdns_res_success) {
74 		printf("  wdns_parse_message(%s): %s\n", str,
75 		       axa_wdns_res(wres, wdns_resbuf, sizeof(wdns_resbuf)));
76 		return (false);
77 	}
78 
79 	if (verbose != 0) {
80 		msg_str = wdns_message_to_str(&m);
81 		if (msg_str == NULL) {
82 			printf("  wdns_message_to_str(%s) failed\n", str);
83 		} else {
84 			fputs("\n", stdout);
85 			for (eol = true, p = msg_str; *p != '\0'; ++p) {
86 				if (eol) {
87 					eol = false;
88 					fputs(NMSG_LEADER2, stdout);
89 				}
90 				fputc(*p, stdout);
91 				if (*p == '\n')
92 					eol = true;
93 			}
94 			free(msg_str);
95 		}
96 		wdns_clear_message(&m);
97 		return (true);
98 	}
99 
100 	rcode = wdns_rcode_to_str(WDNS_FLAGS_RCODE(m));
101 	if (rcode == NULL) {
102 		snprintf(rcode_buf, sizeof(rcode_buf),
103 			 "%d ", WDNS_FLAGS_RCODE(m));
104 		rcode = rcode_buf;
105 	}
106 
107 	q = m.sections[0].rrs;
108 	if (q == NULL) {
109 		rtype = "?";
110 		class = "?";
111 		strlcpy(qname, "(empty QUESTION section)", sizeof(qname));
112 	} else {
113 		class = wdns_rrclass_to_str(q->rrclass);
114 		if (class == NULL) {
115 			snprintf(class_buf, sizeof(class_buf),
116 				 "CLASS %d", q->rrclass);
117 			class = class_buf;
118 		}
119 		rtype = axa_rtype_to_str(rtype_buf, sizeof(rtype_buf),
120 					 q->rrtype);
121 		axa_domain_to_str(q->name.data, q->name.len,
122 				  qname, sizeof(qname));
123 	}
124 
125 	printf(" %s %s %s"
126 	       "  %s%s%s%s%s%s%s",
127 	       qname, class, rtype,
128 	       WDNS_FLAGS_QR(m) ? " qr" : "",
129 	       WDNS_FLAGS_AA(m) ? " aa" : "",
130 	       WDNS_FLAGS_TC(m) ? " tc" : "",
131 	       WDNS_FLAGS_RD(m) ? " rd" : "",
132 	       WDNS_FLAGS_RA(m) ? " ra" : "",
133 	       WDNS_FLAGS_AD(m) ? " ad" : "",
134 	       WDNS_FLAGS_CD(m) ? " cd" : "");
135 
136 	if (WDNS_FLAGS_QR(m))
137 		printf("  %s"
138 		       "  %d ans, %d auth, %d add RRs",
139 		       rcode,
140 		       m.sections[1].n_rrs,
141 		       m.sections[2].n_rrs,
142 		       m.sections[3].n_rrs);
143 	fputc('\n', stdout);
144 
145 	wdns_clear_message(&m);
146 	return (true);
147 }
148 
149 void
print_raw(const uint8_t * pkt,size_t pkt_len)150 print_raw(const uint8_t *pkt, size_t pkt_len)
151 {
152 	char info_buf[64], *info;
153 	char chars_buf[18], *chars;
154 	size_t info_len, chars_len;
155 	uint pay_pos;
156 	u_char c;
157 
158 	info = info_buf;
159 	info_len = sizeof(info_buf);
160 	chars = chars_buf;
161 	chars_len = sizeof(chars_buf);
162 	for (pay_pos = 0; pay_pos < pkt_len; ++pay_pos) {
163 		if (info_len == sizeof(info_buf)) {
164 			if (pay_pos > 256) {
165 				fputs(NMSG_LEADER2"...\n", stdout);
166 				break;
167 			}
168 			axa_buf_print(&info, &info_len, "%7d:", pay_pos);
169 		}
170 		c = pkt[pay_pos];
171 		axa_buf_print(&info, &info_len, " %02x", c);
172 		if (c >= '!' && c <= '~')
173 			axa_buf_print(&chars, &chars_len, "%c", c);
174 		else
175 			axa_buf_print(&chars, &chars_len, ".");
176 		if (chars_len == sizeof(chars_buf) - 8) {
177 			axa_buf_print(&info, &info_len, " ");
178 			axa_buf_print(&chars, &chars_len, " ");
179 		} else if (chars_len == sizeof(chars_buf) - 17) {
180 			printf("%55s  %s\n", info_buf, chars_buf);
181 			info = info_buf;
182 			info_len = sizeof(info_buf);
183 			chars = chars_buf;
184 			chars_len = sizeof(chars_buf);
185 		}
186 	}
187 	if (info_len != sizeof(info_buf))
188 		printf("%-57s  %s\n", info_buf, chars_buf);
189 }
190 
191 void
print_raw_ip(const uint8_t * pkt_data,size_t caplen,axa_p_ch_t ch)192 print_raw_ip(const uint8_t *pkt_data, size_t caplen, axa_p_ch_t ch)
193 {
194 	axa_socku_t dst_su, src_su;
195 	char dst[AXA_SU_TO_STR_LEN], src[AXA_SU_TO_STR_LEN];
196 	char info[80];
197 
198 	clear_prompt();
199 
200 	if (axa_ipdg_parse(pkt_data, caplen, ch, &dst_su, &src_su,
201 			    info, sizeof(info))) {
202 		printf(" %20s > %-20s %s\n",
203 		       axa_su_to_str(src, sizeof(src), '.', &src_su),
204 		       axa_su_to_str(dst, sizeof(dst), '.', &dst_su),
205 		       info);
206 	} else {
207 		printf(" %s\n", info);
208 	}
209 
210 	if (verbose > 1)
211 		print_raw(pkt_data, caplen);
212 }
213 
214 static bool
get_nmsg_field(const nmsg_message_t msg,const char * fname,axa_nmsg_idx_t val_idx,void ** data,size_t * data_len,char * ebuf,size_t ebuf_size)215 get_nmsg_field(const nmsg_message_t msg, const char *fname,
216 	       axa_nmsg_idx_t val_idx, void **data, size_t *data_len,
217 	       char *ebuf, size_t ebuf_size)
218 {
219 	nmsg_res res;
220 
221 	res = nmsg_message_get_field(msg, fname, val_idx, data, data_len);
222 	if (res == nmsg_res_success)
223 		return (true);
224 
225 	snprintf(ebuf, ebuf_size, "nmsg_message_get_field(%s): %s",
226 		 fname, nmsg_res_lookup(res));
227 	*data = ebuf;
228 	*data_len = strlen(ebuf);
229 	return (false);
230 }
231 
232 static bool				/* false=returning error message */
get_nmsg_field_by_idx(const nmsg_message_t msg,axa_nmsg_idx_t field_idx,axa_nmsg_idx_t val_idx,void ** data,size_t * data_len,char * ebuf,size_t ebuf_size)233 get_nmsg_field_by_idx(const nmsg_message_t msg, axa_nmsg_idx_t field_idx,
234 		      axa_nmsg_idx_t val_idx, void **data, size_t *data_len,
235 		      char *ebuf, size_t ebuf_size)
236 {
237 	nmsg_res res;
238 
239 	res = nmsg_message_get_field_by_idx(msg, field_idx, val_idx,
240 					    data, data_len);
241 	if (res == nmsg_res_success)
242 		return (true);
243 
244 	snprintf(ebuf, ebuf_size, "nmsg_message_get_field(%d): %s",
245 		 field_idx, nmsg_res_lookup(res));
246 	*data = ebuf;
247 	*data_len = strlen(ebuf);
248 	return (false);
249 }
250 
251 static bool
enum_value_to_name(const nmsg_message_t msg,const char * fname,uint value,const char ** type_str,char * ebuf,size_t ebuf_size)252 enum_value_to_name(const nmsg_message_t msg, const char *fname, uint value,
253 		   const char **type_str, char *ebuf, size_t ebuf_size)
254 {
255 	nmsg_res res;
256 
257 	res = nmsg_message_enum_value_to_name(msg, fname, value, type_str);
258 	if (res == nmsg_res_success)
259 		return (true);
260 
261 	if (ebuf_size > 0) {
262 		snprintf(ebuf, ebuf_size,
263 			 "nmsg_message_enum_value_to_name(%s): %s",
264 			 fname, nmsg_res_lookup(res));
265 		*type_str = ebuf;
266 	} else {
267 		*type_str = NULL;
268 	}
269 	return (false);
270 }
271 
272 static bool
fname_enum_to_name(const nmsg_message_t msg,const char * fname,const char ** type_str,char * ebuf,size_t ebuf_size)273 fname_enum_to_name(const nmsg_message_t msg, const char *fname,
274 		   const char **type_str, char *ebuf, size_t ebuf_size)
275 {
276 	void *data;
277 	size_t data_len;
278 	uint value;
279 	nmsg_res res;
280 
281 	if (!get_nmsg_field(msg, fname, 0, &data, &data_len,
282 			    ebuf, ebuf_size)) {
283 		*type_str = data;
284 		return (false);
285 	}
286 	if (data_len != sizeof(value)) {
287 		*type_str = "wrong enum len from nmsg_message_get_field()";
288 		return (false);
289 	}
290 	memcpy(&value, data, sizeof(value));
291 	res = nmsg_message_enum_value_to_name(msg, fname, value, type_str);
292 	if (res == nmsg_res_success)
293 		return (true);
294 	*type_str = ebuf;
295 	return (false);
296 }
297 
298 static bool
data_to_ip(char * buf,size_t buf_len,const void * data,size_t data_len,const char * tag)299 data_to_ip(char *buf, size_t buf_len,
300 	   const void *data, size_t data_len, const char *tag)
301 {
302 	axa_socku_t su;
303 
304 	if (!axa_data_to_su(&su, data, data_len)) {
305 		snprintf(buf, buf_len, "%s IP length=%zd", tag, data_len);
306 		return (false);
307 	}
308 	axa_su_to_str(buf, buf_len, '.', &su);
309 	return (true);
310 }
311 
312 static bool
fname_to_ip(char * buf,size_t buf_len,const nmsg_message_t msg,const char * fname,axa_nmsg_idx_t val_idx)313 fname_to_ip(char *buf, size_t buf_len, const nmsg_message_t msg,
314 	    const char *fname, axa_nmsg_idx_t val_idx)
315 {
316 	void *data;
317 	size_t data_len;
318 
319 	if (!get_nmsg_field(msg, fname, val_idx, &data, &data_len,
320 			    buf, buf_len))
321 		return (buf);
322 	return (data_to_ip(buf, buf_len, data, data_len, fname));
323 }
324 
325 static void
print_verbose_nmsg(const nmsg_message_t msg,const char * eq,const char * val)326 print_verbose_nmsg(const nmsg_message_t msg, const char *eq, const char *val)
327 {
328 	char *pres_data;
329 	const char *line, *eol;
330 	nmsg_res res;
331 
332 	printf("%s%s\n", eq, val);
333 
334 	res = nmsg_message_to_pres(msg, &pres_data, "\n");
335 	if (res != nmsg_res_success) {
336 		printf(NMSG_LEADER"<UNKNOWN NMSG %u:%u>\n",
337 		       nmsg_message_get_vid(msg),
338 		       nmsg_message_get_msgtype(msg));
339 		return;
340 	}
341 
342 	for (line = pres_data; *line != '\0'; line = eol) {
343 		eol = strchr(line, '\n');
344 		AXA_ASSERT(eol != NULL);
345 		++eol;
346 		fputs(NMSG_LEADER, stdout);
347 		fwrite(line, eol-line, 1, stdout);
348 	}
349 	free(pres_data);
350 }
351 
352 typedef struct {
353 	char		*buf0;
354 	size_t		buf0_len;
355 	char		*buf;
356 	size_t		buf_len;
357 	nmsg_message_t	msg;
358 	const char	*rdata_name;
359 } rdata_ctxt_t;
360 
361 static void
rdata_error(void * ctxt0,const char * p,va_list args)362 rdata_error(void *ctxt0, const char *p, va_list args)
363 {
364 	rdata_ctxt_t *ctxt = ctxt0;
365 
366 	vsnprintf(ctxt->buf0, ctxt->buf0_len, p, args);
367 	ctxt->buf_len = 0;
368 }
369 
370 static bool
rdata_buf_alloc(rdata_ctxt_t * ctxt)371 rdata_buf_alloc(rdata_ctxt_t *ctxt)
372 {
373 	size_t len;
374 
375 	len = strlen(ctxt->buf);
376 	ctxt->buf += len;
377 	ctxt->buf_len -= len;
378 	return (ctxt->buf_len > 0);
379 }
380 
381 static bool
rdata_buf_cat(rdata_ctxt_t * ctxt,const char * str)382 rdata_buf_cat(rdata_ctxt_t *ctxt, const char *str)
383 {
384 	strlcpy(ctxt->buf, str, ctxt->buf_len);
385 	return (rdata_buf_alloc(ctxt));
386 }
387 
388 static bool
rdata_ip_to_buf(void * ctxt0,const uint8_t * ip,size_t ip_len,const char * str AXA_UNUSED)389 rdata_ip_to_buf(void *ctxt0, const uint8_t *ip, size_t ip_len,
390 		const char *str AXA_UNUSED)
391 {
392 	rdata_ctxt_t *ctxt = ctxt0;
393 	axa_socku_t su;
394 
395 	if (!rdata_buf_cat(ctxt, " "))
396 		return (false);
397 
398 	if (!axa_data_to_su(&su, ip, ip_len)) {
399 		snprintf(ctxt->buf0, ctxt->buf0_len, "%s IP length=%zd",
400 			 ctxt->rdata_name, ip_len);
401 		ctxt->buf_len = 0;
402 		return (false);
403 	}
404 	axa_su_to_str(ctxt->buf, ctxt->buf_len, '.', &su);
405 	return (rdata_buf_alloc(ctxt));
406 }
407 
408 static bool
rdata_domain_to_buf(void * ctxt0,const uint8_t * name,size_t name_len,axa_walk_dom_t dtype AXA_UNUSED,uint rtype AXA_UNUSED,const char * str AXA_UNUSED)409 rdata_domain_to_buf(void *ctxt0, const uint8_t *name, size_t name_len,
410 		    axa_walk_dom_t dtype AXA_UNUSED,
411 		    uint rtype AXA_UNUSED,
412 		    const char *str AXA_UNUSED)
413 {
414 	rdata_ctxt_t *ctxt = ctxt0;
415 	char wname[NS_MAXDNAME];
416 
417 	if (!rdata_buf_cat(ctxt, " "))
418 		return (false);
419 
420 	axa_domain_to_str(name, name_len, wname, sizeof(wname));
421 	strlcpy(ctxt->buf, wname, ctxt->buf_len);
422 	return (rdata_buf_alloc(ctxt));
423 }
424 
425 static axa_walk_ops_t rdata_ops = {
426 	.error = rdata_error,
427 	.ip = rdata_ip_to_buf,
428 	.domain = rdata_domain_to_buf,
429 };
430 
431 #define RDATA_BUF_LEN (32+NS_MAXDNAME+1+NS_MAXDNAME)
432 static const char *
rdata_to_buf(char * buf,size_t buf_len,const char * rdata_name,uint32_t rtype,uint8_t * rdata,size_t rdata_len)433 rdata_to_buf(char *buf, size_t buf_len,
434 	     const char *rdata_name, uint32_t rtype,
435 	     uint8_t *rdata, size_t rdata_len)
436 {
437 	rdata_ctxt_t ctxt;
438 
439 	ctxt.buf0 = buf;
440 	ctxt.buf0_len = buf_len;
441 	ctxt.buf = buf;
442 	ctxt.buf_len = buf_len;
443 	ctxt.rdata_name = rdata_name;
444 
445 	axa_rtype_to_str(ctxt.buf, ctxt.buf_len, rtype);
446 	if (!rdata_buf_alloc(&ctxt))
447 		return (buf);
448 
449 	axa_walk_rdata(&ctxt, &rdata_ops, NULL, 0, NULL, rdata+rdata_len,
450 		       rdata, rdata_len, rtype, "");
451 
452 	return (buf);
453 }
454 
455 /* Get a string for rdata specified by
456  *	rdata_name=nmsg field name for the data itself
457  *	rtype_idx=nmsg field index for the rtype of the data */
458 static const char *
rdata_nmsg_to_buf(char * buf,size_t buf_len,const nmsg_message_t msg,const axa_nmsg_field_t * field,axa_nmsg_idx_t val_idx)459 rdata_nmsg_to_buf(char *buf, size_t buf_len, const nmsg_message_t msg,
460 		  const axa_nmsg_field_t *field, axa_nmsg_idx_t val_idx)
461 {
462 	uint32_t rtype;
463 	void *data;
464 	size_t data_len;
465 
466 	/* Get the rdata type */
467 	if (!axa_get_helper(&emsg, msg, &field->rtype, 0,
468 			    &rtype, NULL, sizeof(rtype), sizeof(rtype), NULL)) {
469 		strlcpy(buf, emsg.c, buf_len);
470 		return (buf);
471 	}
472 
473 	/* get the rdata itself */
474 	if (!get_nmsg_field_by_idx(msg, field->idx, val_idx, &data, &data_len,
475 				   buf, buf_len))
476 		return (buf);
477 
478 	return (rdata_to_buf(buf, buf_len, field->name, rtype, data, data_len));
479 }
480 
481 static void
print_nmsg_base_dnsqr(const nmsg_message_t msg,const char * eq,const char * val,const axa_p_whit_t * whit)482 print_nmsg_base_dnsqr(const nmsg_message_t msg, const char *eq, const char *val,
483 		      const axa_p_whit_t *whit)
484 {
485 	const Nmsg__Base__DnsQR *dnsqr;
486 	char ebuf[80];
487 	const char *type_str;
488 
489 	if (verbose != 0) {
490 		print_verbose_nmsg(msg, eq, val);
491 		return;
492 	}
493 
494 	dnsqr = (Nmsg__Base__DnsQR *)nmsg_message_get_payload(msg);
495 	if (dnsqr == NULL) {
496 		print_verbose_nmsg(msg, eq, val);
497 		return;
498 	}
499 
500 	/* Punt on odd messages */
501 	if (dnsqr->n_query_packet == 0 && dnsqr->n_response_packet == 0) {
502 		print_verbose_nmsg(msg, eq, val);
503 		return;
504 	}
505 	if (dnsqr->type != NMSG__BASE__DNS_QRTYPE__UDP_QUERY_ONLY
506 	    && dnsqr->type != NMSG__BASE__DNS_QRTYPE__UDP_RESPONSE_ONLY
507 	    && dnsqr->type != NMSG__BASE__DNS_QRTYPE__UDP_QUERY_RESPONSE
508 	    && dnsqr->type != NMSG__BASE__DNS_QRTYPE__UDP_UNSOLICITED_RESPONSE
509 	    && dnsqr->type != NMSG__BASE__DNS_QRTYPE__UDP_UNANSWERED_QUERY) {
510 		print_verbose_nmsg(msg, eq, val);
511 		return;
512 	}
513 
514 	if (!enum_value_to_name(msg, "type", dnsqr->type, &type_str,
515 				ebuf, sizeof(ebuf))) {
516 		if (verbose > 1)
517 			printf("%s\n", ebuf);
518 		print_verbose_nmsg(msg, eq, val);
519 		return;
520 	}
521 	printf("%s%s  %s\n", eq, val, type_str);
522 	/* The query should be in the response,
523 	 * so print the query only without a response. */
524 	if (dnsqr->n_response_packet == 0) {
525 		print_raw_ip(dnsqr->query_packet[0].data,
526 			     dnsqr->query_packet[0].len,
527 			     AXA_P2H_CH(whit->hdr.ch));
528 	} else {
529 		print_raw_ip(dnsqr->response_packet[0].data,
530 			     dnsqr->response_packet[0].len,
531 			     AXA_P2H_CH(whit->hdr.ch));
532 	}
533 }
534 
535 static void
print_sie_dnsdedupe(const nmsg_message_t msg,const axa_nmsg_field_t * field,const char * eq,const char * val)536 print_sie_dnsdedupe(const nmsg_message_t msg, const axa_nmsg_field_t *field,
537 		    const char *eq, const char *val)
538 {
539 	const char *type_str;
540 	const Nmsg__Sie__DnsDedupe *dnsdedupe;
541 	char ebuf[80];
542 	char response_ip_buf[INET6_ADDRSTRLEN];
543 	char rdata_buf[RDATA_BUF_LEN];
544 	char rrname_buf[NS_MAXDNAME];
545 	bool need_nl;
546 
547 	if (verbose != 0) {
548 		print_verbose_nmsg(msg, eq, val);
549 		return;
550 	}
551 
552 	dnsdedupe = (Nmsg__Sie__DnsDedupe *)nmsg_message_get_payload(msg);
553 
554 	if (!dnsdedupe->has_type) {
555 		print_verbose_nmsg(msg, eq, val);
556 		return;
557 	}
558 	if (!enum_value_to_name(msg, "type", dnsdedupe->type, &type_str,
559 				ebuf, sizeof(ebuf))) {
560 		if (verbose > 1)
561 			printf("%s\n", ebuf);
562 		print_verbose_nmsg(msg, eq, val);
563 		return;
564 	}
565 
566 	printf("%s%s  %s\n", eq, val, type_str);
567 
568 	/* Print the response IP only if we did not print it as the trigger. */
569 	need_nl = false;
570 	if ((field == NULL || strcmp(field->name, "response_ip") != 0)
571 	    && dnsdedupe->has_response_ip) {
572 		data_to_ip(response_ip_buf, sizeof(response_ip_buf),
573 			   dnsdedupe->response_ip.data,
574 			   dnsdedupe->response_ip.len, "response_ip");
575 		printf(NMSG_LEADER"response_ip=%s", response_ip_buf);
576 		need_nl = true;
577 	}
578 	/* Print the rdata only if we will not print the response packet,
579 	 * we did not print it as the trigger,
580 	 * and we can. */
581 	if (!dnsdedupe->has_response
582 	    && (field == NULL || strcmp(field->name, "rdata") != 0)
583 	    && dnsdedupe->n_rdata >= 1 && dnsdedupe->has_rrtype) {
584                 /* handle strange case of null rdata contents */
585                 if (dnsdedupe->rdata->len == 0
586                     || dnsdedupe->rdata->data == NULL)
587                     printf(NMSG_LEADER"rdata=");
588                 else
589                     printf(NMSG_LEADER"rdata=%s",
590 		       rdata_to_buf(rdata_buf, sizeof(rdata_buf),
591 				    "rdata", dnsdedupe->rrtype,
592 				    dnsdedupe->rdata->data,
593 				    dnsdedupe->rdata->len));
594 		need_nl = true;
595 	}
596 	/* Print the domain name only if we will not print the response packet,
597 	 * we did not print it as the trigger,
598 	 * and we can. */
599 	if (!dnsdedupe->has_response
600 	    && (field == NULL || strcmp(field->name, "rrname") != 0)
601 	    && dnsdedupe->has_rrname) {
602 		axa_domain_to_str(dnsdedupe->rrname.data, dnsdedupe->rrname.len,
603 				  rrname_buf, sizeof(rrname_buf));
604 		printf(NMSG_LEADER"rrname=%s", rrname_buf);
605 		need_nl = true;
606 	}
607 	if (need_nl)
608 		fputc('\n', stdout);
609 
610 	if (dnsdedupe->has_response)
611 		print_dns_pkt(dnsdedupe->response.data,
612 			      dnsdedupe->response.len, "response");
613 }
614 
615 static void
print_sie_newdomain(const nmsg_message_t msg,const axa_nmsg_field_t * field AXA_UNUSED,const char * eq,const char * val)616 print_sie_newdomain(const nmsg_message_t msg,
617 		    const axa_nmsg_field_t *field AXA_UNUSED,
618 		    const char *eq, const char *val)
619 {
620 	const Nmsg__Sie__NewDomain *newdomain;
621 	char rrname_buf[NS_MAXDNAME];
622 	char domain_buf[NS_MAXDNAME];
623 	char rtype_buf[10];
624 
625 	if (verbose != 0) {
626 		print_verbose_nmsg(msg, eq, val);
627 		return;
628 	}
629 
630 	newdomain = (Nmsg__Sie__NewDomain *)nmsg_message_get_payload(msg);
631 
632 	axa_domain_to_str(newdomain->rrname.data, newdomain->rrname.len,
633 			  rrname_buf, sizeof(rrname_buf));
634 	axa_rtype_to_str(rtype_buf, sizeof(rtype_buf), newdomain->rrtype);
635 	if (newdomain->domain.data)
636 		axa_domain_to_str(newdomain->domain.data, newdomain->domain.len,
637 				domain_buf, sizeof(domain_buf));
638 	printf("%s%s\n %s/%s: %s\n",
639 	       eq, val, rrname_buf, rtype_buf,
640            newdomain->domain.data ? domain_buf : rrname_buf);
641 }
642 
643 static void
print_nmsg_base_http(const nmsg_message_t msg,const axa_nmsg_field_t * field,const char * eq,const char * val)644 print_nmsg_base_http(const nmsg_message_t msg, const axa_nmsg_field_t *field,
645 		     const char *eq, const char *val)
646 {
647 	char buf[NS_MAXDNAME];
648 	bool need_nl;
649 
650 	if (verbose != 0) {
651 		print_verbose_nmsg(msg, eq, val);
652 		return;
653 	}
654 
655 	/* Print the triggering field name and its value. */
656 	printf("%s%s\n", eq, val);
657 	need_nl = false;
658 
659 	/* Print the source and destination fields if not just now printed. */
660 	if (field == NULL || strcmp(field->name, "srcip") != 0) {
661 		fname_to_ip(buf, sizeof(buf), msg, "srcip", 0);
662 		printf(NMSG_LEADER"srcip=%s", buf);
663 		need_nl = true;
664 	}
665 	if (field == NULL || strcmp(field->name, "dstip") != 0) {
666 		fname_to_ip(buf, sizeof(buf), msg, "dstip", 0);
667 		printf(NMSG_LEADER"dstip=%s", buf);
668 		need_nl = true;
669 	}
670 	if (need_nl)
671 		fputc('\n', stdout);
672 }
673 
674 static void
print_text(const char * text,size_t text_len)675 print_text(const char *text, size_t text_len)
676 {
677 	int lines;
678 	size_t line_len, skip;
679 
680 	lines = 0;
681 	while (text_len > 0) {
682 		if (++lines >= 6) {
683 			fputs(NMSG_LEADER2"...\n", stdout);
684 			return;
685 		}
686 		line_len = 76;
687 		if (line_len > text_len) {
688 			line_len = text_len;
689 			skip = 0;
690 		} else {
691 			for (;;) {
692 				if (line_len < 60) {
693 					line_len = 76;
694 					skip = 0;
695 					break;
696 				}
697 				if (text[line_len] == ' '
698 				    || text[line_len] == '\t') {
699 					skip = 1;
700 					break;
701 				}
702 				--line_len;
703 			}
704 		}
705 		printf(NMSG_LEADER2"%.*s\n", (int)line_len, text);
706 		text += line_len+skip;
707 		text_len -= line_len+skip;
708 	}
709 }
710 
711 static void
print_nmsg_base_encode(const nmsg_message_t msg,const char * eq,const char * val)712 print_nmsg_base_encode(const nmsg_message_t msg,
713 		       const char *eq, const char *val)
714 {
715 	void *data;
716 	size_t data_len;
717 	const char *type_str;
718 	char ebuf[80];
719 	bool ok;
720 
721 	ok = fname_enum_to_name(msg, "type", &type_str, ebuf, sizeof(ebuf));
722 	printf("%s%s  %s\n", eq, val, type_str);
723 	if (!ok)
724 		return;
725 
726 	if (!get_nmsg_field(msg, "payload", 0,
727 			    &data, &data_len, ebuf, sizeof(ebuf))) {
728 		printf(NMSG_LEADER"%s\n", ebuf);
729 		return;
730 	}
731 
732 	if (strcmp(type_str, "JSON") == 0) {
733 		print_text(data, data_len);
734 	} else if (strcmp(type_str, "TEXT") == 0
735 		   || strcmp(type_str, "YAML") == 0
736 		   || strcmp(type_str, "XML") == 0) {
737 		if (verbose == 0)
738 			return;
739 		print_text(data, data_len);
740 	} else {
741 		/* MessagePack seems to be binary */
742 		if (verbose == 0)
743 			return;
744 		print_raw(data, data_len);
745 	}
746 }
747 
748 static void
print_nmsg_base_packet(const nmsg_message_t msg,const axa_p_whit_t * whit,const char * eq,const char * val)749 print_nmsg_base_packet(const nmsg_message_t msg, const axa_p_whit_t *whit,
750 		       const char *eq, const char *val)
751 {
752 	void *data;
753 	size_t data_len;
754 	const char *type_str;
755 	char ebuf[80];
756 	bool ok;
757 
758 	ok = fname_enum_to_name(msg, "payload_type",
759 				&type_str, ebuf, sizeof(ebuf));
760 	if (!ok) {
761 		printf("%s%s  %s\n", eq, val, type_str);
762 		return;
763 	}
764 	if (!get_nmsg_field(msg, "payload", 0,
765 			       &data, &data_len, ebuf, sizeof(ebuf))) {
766 		printf("%s%s  %s\n", eq, val, type_str);
767 		printf(NMSG_LEADER"%s\n", (char *)data);
768 		return;
769 	}
770 
771 	printf("%s%s\n", eq, val);
772 	print_raw_ip(data, data_len, AXA_P2H_CH(whit->hdr.ch));
773 }
774 
775 /*
776  * Convert field index to field name and value string.
777  */
778 static void
get_nm_eq_val(const nmsg_message_t msg,const axa_p_whit_t * whit,const axa_nmsg_field_t ** fieldp,const char ** nm,const char ** eq,const char ** val,char * buf,size_t buf_len)779 get_nm_eq_val(const nmsg_message_t msg, const axa_p_whit_t *whit,
780 	      const axa_nmsg_field_t **fieldp,
781 	      const char **nm, const char **eq, const char **val,
782 	      char *buf, size_t buf_len)
783 {
784 	const axa_nmsg_field_t *field;
785 	axa_nmsg_idx_t field_idx;
786 	void *data;
787 	size_t data_len;
788 	size_t n;
789 
790 	field_idx = AXA_P2H_IDX(whit->nmsg.hdr.field_idx);
791 	if (field_idx == AXA_NMSG_IDX_ERROR) {
792 		strlcpy(buf, "ERROR", buf_len);
793 		*nm = buf;
794 		*eq = "";
795 		*val = "";
796 		*fieldp = NULL;
797 		return;
798 	} else if (field_idx == AXA_NMSG_IDX_DARK) {
799 		*nm = "";
800 		*eq = "";
801 		*val = "";
802 		*fieldp = NULL;
803 		return;
804 	} else if (field_idx >= AXA_NMSG_IDX_RSVD) {
805 		strlcpy(buf, "? unrecognized message", buf_len);
806 		*nm = buf;
807 		*eq = "";
808 		*val = "";
809 		*fieldp = NULL;
810 		return;
811 	}
812 
813 	field = axa_msg_fields(msg);
814 	if (field == NULL) {
815 		strlcpy(buf, "? unrecognized message", buf_len);
816 		*nm = buf;
817 		*eq = "";
818 		*val = "";
819 		*fieldp = NULL;
820 		return;
821 	}
822 	for (;;) {
823 		if (field->idx == field_idx)
824 			break;
825 		field = field->next;
826 		if (field == NULL) {
827 			strlcpy(buf, "? unrecognized field", buf_len);
828 			*nm = buf;
829 			*eq = "";
830 			*val = "";
831 			return;
832 		}
833 	}
834 	*fieldp = field;
835 
836 	switch (field->fc) {
837 	case AXA_FC_IP:
838 		*nm = field->name;
839 		*eq = "=";
840 		fname_to_ip(buf, buf_len, msg, field->name,
841 			    AXA_P2H_IDX(whit->nmsg.hdr.val_idx));
842 		*val = buf;
843 		break;
844 
845 	case AXA_FC_DOM:
846 		*nm = field->name;
847 		*eq = "=";
848 		if (get_nmsg_field_by_idx(msg, field_idx,
849 					   AXA_P2H_IDX(whit->nmsg.hdr.val_idx),
850 					   &data, &data_len, buf, buf_len))
851 			axa_domain_to_str(data, data_len, buf, buf_len);
852 		*val = buf;
853 		break;
854 
855 	case AXA_FC_IP_ASCII:
856 	case AXA_FC_DOM_ASCII:
857 	case AXA_FC_HOST:
858 		*nm = field->name;
859 		*eq = "=";
860 		if (get_nmsg_field_by_idx(msg, field_idx,
861 					  AXA_P2H_IDX(whit->nmsg.hdr.val_idx),
862 					  &data, &data_len, buf, buf_len)) {
863 			n = min(buf_len-1, data_len);
864 			memcpy(buf, data, n);
865 			buf[n] = '\0';
866 		}
867 		*val = buf;
868 		break;
869 
870 	case AXA_FC_RDATA:
871 		*nm = field->name;
872 		*eq = "=";
873 		*val = rdata_nmsg_to_buf(buf, buf_len, msg, field,
874 					 AXA_P2H_IDX(whit->nmsg.hdr.val_idx));
875 		break;
876 
877 	case AXA_FC_DNS:
878 	case AXA_FC_JSON:
879 	case AXA_FC_IP_DGRAM:
880 		*nm = field->name;
881 		*eq = "";
882 		*val = "";
883 		break;
884 
885 	case AXA_FC_UNKNOWN:
886 		*nm = field->name;
887 		snprintf(buf, buf_len, " ? unknown field");
888 		*eq = buf;
889 		*val = "";
890 		break;
891 
892 #pragma clang diagnostic push
893  #pragma clang diagnostic ignored "-Wunreachable-code"
894 	default:
895 		*nm = field->name;
896 		snprintf(buf, buf_len, " ? strange field #%d", field->fc);
897 		*eq = buf;
898 		*val = "";
899 		break;
900 #pragma clang diagnostic pop
901 	}
902 }
903 
904 /* Create nmsg message from incoming watch hit containing a nmsg message */
905 axa_w2n_res_t
whit2nmsg(nmsg_message_t * msgp,axa_p_whit_t * whit,size_t whit_len)906 whit2nmsg(nmsg_message_t *msgp, axa_p_whit_t *whit, size_t whit_len)
907 {
908 	axa_w2n_res_t res;
909 
910 	res = axa_whit2nmsg(&emsg, nmsg_input, msgp, whit, whit_len);
911 	switch (res) {
912 		case AXA_W2N_RES_FAIL:
913 			clear_prompt();
914 			error_msg("%s", emsg.c);
915 			disconnect(true);
916 		case AXA_W2N_RES_SUCCESS:
917 		case AXA_W2N_RES_FRAGMENT:
918 			break;
919 	}
920 	return (res);
921 }
922 
923 static void
print_nmsg(axa_p_whit_t * whit,size_t whit_len,const char * title_sep,const char * title)924 print_nmsg(axa_p_whit_t *whit, size_t whit_len,
925 	   const char *title_sep, const char *title)
926 {
927 	axa_nmsg_idx_t vid, type;
928 	char tag_buf[AXA_TAG_STRLEN];
929 	const axa_nmsg_field_t *field;
930 	char vendor_buf[12], mname_buf[12], field_buf[RDATA_BUF_LEN];
931 	const char *vendor, *mname, *nm, *eq, *val;
932 	char group[40];
933 	const char *cp;
934 	nmsg_message_t msg;
935 	uint n;
936 
937 	if (whit2nmsg(&msg, whit, whit_len) == AXA_W2N_RES_FRAGMENT) {
938 		if (axa_debug != 0)
939 			printf("ignoring NMSG fragment from "
940 					AXA_OP_CH_PREFIX"%d",
941 					AXA_P2H_CH(whit->hdr.ch));
942 		return;
943 	}
944 	if (msg == NULL)
945 		return;
946 
947 	/* Convert binary vendor ID, message type, and field index to
948 	 * vendor name, message type string, field name, and field value. */
949 	vid = AXA_P2H_IDX(whit->nmsg.hdr.vid);
950 	type = AXA_P2H_IDX(whit->nmsg.hdr.type);
951 	vendor = nmsg_msgmod_vid_to_vname(vid);
952 	if (vendor == NULL) {
953 		snprintf(vendor_buf, sizeof(vendor_buf), "ID #%d", vid);
954 		vendor = vendor_buf;
955 	}
956 	mname = nmsg_msgmod_msgtype_to_mname(vid, type);
957 	if (mname == NULL) {
958 		snprintf(mname_buf, sizeof(mname_buf), "#%d", type);
959 		mname = mname_buf;
960 	}
961 	field = NULL;			/* silence gcc warning */
962 	get_nm_eq_val(msg, whit, &field, &nm, &eq, &val,
963 		      field_buf, sizeof(field_buf));
964 
965 	cp = NULL;
966 	n = nmsg_message_get_group(msg);
967 	if (n != 0)
968 		cp = nmsg_alias_by_key(nmsg_alias_group, n);
969 	if (cp == NULL)
970 		group[0] = '\0';
971 	else
972 		snprintf(group, sizeof(group), " %s", cp);
973 
974 	/* Print what we have so far,
975 	 * except the value which might be redundant */
976 	clear_prompt();
977 	printf("%s%s%s "AXA_OP_CH_PREFIX"%d  %s %s%s %s",
978 	       axa_tag_to_str(tag_buf, sizeof(tag_buf),
979 			      AXA_P2H_TAG(client.io.recv_hdr.tag)),
980 	       title_sep, title,
981 	       AXA_P2H_CH(whit->hdr.ch), vendor, mname, group, nm);
982 
983 	switch (vid) {
984 	case NMSG_VENDOR_BASE_ID:
985 		switch (type) {
986 		case NMSG_VENDOR_BASE_DNSQR_ID:
987 			print_nmsg_base_dnsqr(msg, eq, val, whit);
988 			break;
989 		case NMSG_VENDOR_BASE_HTTP_ID:
990 			print_nmsg_base_http(msg, field, eq, val);
991 			break;
992 		case NMSG_VENDOR_BASE_ENCODE_ID:
993 			print_nmsg_base_encode(msg, eq, val);
994 			break;
995 		case NMSG_VENDOR_BASE_PACKET_ID:
996 			print_nmsg_base_packet(msg, whit, eq, val);
997 			break;
998 		default:
999 			print_verbose_nmsg(msg, eq, val);
1000 			break;
1001 		}
1002 		break;
1003 
1004 	case NMSG_VENDOR_SIE_ID:
1005 		switch (type) {
1006 		case NMSG_VENDOR_SIE_DNSDEDUPE_ID:
1007 			print_sie_dnsdedupe(msg, field, eq, val);
1008 			break;
1009 		case NMSG_VENDOR_SIE_NEWDOMAIN_ID:
1010 			print_sie_newdomain(msg, field, eq, val);
1011 			break;
1012 		default:
1013 			print_verbose_nmsg(msg, eq, val);
1014 			break;
1015 		}
1016 		break;
1017 
1018 	default:
1019 		print_verbose_nmsg(msg, eq, val);
1020 		break;
1021 	}
1022 
1023 	nmsg_message_destroy(&msg);
1024 }
1025 
1026 void
print_whit(axa_p_whit_t * whit,size_t whit_len,const char * title_sep,const char * title)1027 print_whit(axa_p_whit_t *whit, size_t whit_len,
1028 	   const char *title_sep, const char *title)
1029 {
1030 	struct timeval now;
1031 	time_t ms;
1032 	char tag_buf[AXA_TAG_STRLEN];
1033 	bool fwded;
1034 
1035 	/* Forward binary packets if necessary. */
1036 	if (out_on) {
1037 		if (out_nmsg_output != NULL) {
1038 			fwded = out_whit_nmsg(whit, whit_len);
1039 		} else {
1040 			AXA_ASSERT(out_pcap != NULL);
1041 			fwded = out_whit_pcap(whit, whit_len);
1042 		}
1043 		if (fwded && --output_count == 0 && output_counting) {
1044 			clear_prompt();
1045 			printf("output %d packets to %s finished\n",
1046 			       output_count_total, out_addr);
1047 			out_close(true);
1048 		}
1049 	}
1050 
1051 	if (--packet_count < 0 && packet_counting) {
1052 		clear_prompt();
1053 		if (packet_count == -1) {
1054 			fputs("\npacket count limit exceeded\n", stdout);
1055 		} else if (in_file_cur == 0 && el_e != NULL) {
1056 			gettimeofday(&now, NULL);
1057 			ms = axa_elapsed_ms(&now, &out_bar_time);
1058 			if (ms >= PROGRESS_MS) {
1059 				fputs(out_bar_strs[out_bar_idx], stdout);
1060 				fflush(stdout);
1061 				no_reprompt = now;
1062 				++out_bar_idx;
1063 				out_bar_idx %= AXA_DIM(out_bar_strs);
1064 				out_bar_time = now;
1065 			}
1066 		}
1067 		return;
1068 	}
1069 
1070 	clear_prompt();
1071 	switch ((axa_p_whit_enum_t)whit->hdr.type) {
1072 	case AXA_P_WHIT_NMSG:
1073 		print_nmsg(whit, whit_len, title_sep, title);
1074 		return;
1075 	case AXA_P_WHIT_IP:
1076 		if (whit_len <= sizeof(whit->ip)) {
1077 			error_msg("truncated IP packet");
1078 			disconnect(true);
1079 			return;
1080 		}
1081 
1082 		printf("%s%s%s "AXA_OP_CH_PREFIX"%d\n",
1083 		       axa_tag_to_str(tag_buf, sizeof(tag_buf),
1084 				      AXA_P2H_TAG(client.io.recv_hdr.tag)),
1085 		       title_sep, title,
1086 		       AXA_P2H_CH(whit->hdr.ch));
1087 		print_raw_ip(whit->ip.b, whit_len - sizeof(whit->ip.hdr),
1088 			     AXA_P2H_CH(whit->hdr.ch));
1089 		return;
1090 	}
1091 #pragma clang diagnostic push
1092 #pragma clang diagnostic ignored "-Wunreachable-code"
1093 	error_msg("unrecognized message type %d", whit->hdr.type);
1094 	disconnect(true);
1095 #pragma clang diagnostic pop
1096 }
1097 
1098 void
print_ahit(void)1099 print_ahit(void)
1100 {
1101 	print_whit(&client.io.recv_body->ahit.whit,
1102 		   client.io.recv_hdr.len
1103 		   - (sizeof(client.io.recv_hdr)
1104 		      + sizeof(client.io.recv_body->ahit)
1105 		      - sizeof(client.io.recv_body->ahit.whit)),
1106 		   " ", client.io.recv_body->ahit.an.c);
1107 }
1108 
1109 void
print_channel(void)1110 print_channel(void)
1111 {
1112 	const axa_p_clist_t *clist;
1113 	axa_p_ch_buf_t buf;
1114 
1115 	clear_prompt();
1116 	clist = &client.io.recv_body->clist;
1117 	snprintf(buf.c, sizeof(buf),
1118 		 AXA_OP_CH_PREFIX"%d", AXA_P2H_CH(clist->ch));
1119 	printf(" %8s %3s %s\n",
1120 	       buf.c, clist->on != 0 ? "on" : "off", clist->spec.c);
1121 }
1122 
1123 void
wlist_alist(void)1124 wlist_alist(void)
1125 {
1126 	axa_tag_t list_tag;
1127 	char hdr_tag_buf[AXA_TAG_STRLEN];
1128 	char list_tag_buf[AXA_TAG_STRLEN];
1129 	char buf[AXA_P_STRLEN];
1130 
1131 	if (client.io.recv_hdr.op == AXA_P_OP_WLIST)
1132 		list_tag = client.io.recv_body->wlist.cur_tag;
1133 	else
1134 		list_tag = client.io.recv_body->alist.cur_tag;
1135 	axa_tag_to_str(list_tag_buf, sizeof(list_tag_buf),
1136 		       AXA_P2H_TAG(list_tag));
1137 
1138 	clear_prompt();
1139 	if (client.io.recv_hdr.tag != AXA_P2H_TAG(AXA_TAG_NONE)
1140 	    && client.io.recv_hdr.tag != list_tag)
1141 		printf("     nearest %s to %s is %s\n",
1142 		       mode == RAD ? "anomaly" : "watch",
1143 		       axa_tag_to_str(hdr_tag_buf, sizeof(hdr_tag_buf),
1144 				      client.io.recv_hdr.tag),
1145 		       list_tag_buf);
1146 
1147 	printf("%7s %s\n",
1148 	       list_tag_buf, axa_p_to_str(buf, sizeof(buf), false,
1149 					  &client.io.recv_hdr,
1150 					  client.io.recv_body));
1151 }
1152 
1153 void
count_print(bool always)1154 count_print(bool always)
1155 {
1156 	if (always && !packet_counting)
1157 		printf("    packet printing not limited by count\n"
1158 				"        %d packets recently printed\n",
1159 				0 - packet_count);
1160 	else if (packet_count < 0)
1161 		printf("    packet printing stopped by count %d packets ago\n",
1162 				0 - packet_count);
1163 	else
1164 		printf("    %d packets remaining to print of %d total\n",
1165 				packet_count, packet_count_total);
1166 
1167 	if (!out_on)
1168 		return;
1169 	if (!output_counting)
1170 		printf("    packet output or forwarding not limited by count\n"
1171 				"        %d packets recently output\n",
1172 				0 - output_count);
1173 	else if (output_count < 0)
1174 		printf("    packet output or forwarding stopped by count"
1175 				" %d packets ago\n",
1176 				0 - output_count);
1177 	else
1178 		printf("    %d packets remaining to output or forward of"
1179 				" %d total\n",
1180 				output_count, output_count_total);
1181 }
1182 
1183 static void
print_stats_sys(_axa_p_stats_sys_t * sys)1184 print_stats_sys(_axa_p_stats_sys_t *sys)
1185 {
1186 	int j, ch_cnt;
1187 	time_t t;
1188 	struct timeval tv;
1189 	struct tm *tm_info;
1190 	const char *server_type;
1191 	uint32_t user_cnt;
1192 	char time_buf[30];
1193 
1194 	if (sys->type != _AXA_P_STATS_TYPE_SYS) {
1195 		printf("expected system/server stats, got type \"%d\"\n",
1196 				sys->type);
1197 		return;
1198 	}
1199 
1200 	/* UINT32_MAX or UINT64_MAX == server error in gathering stat */
1201 	if (sys->uptime == UINT32_MAX) {
1202 		printf("    server uptime   : unavailable\n");
1203 	}
1204 	else {
1205 		gettimeofday(&tv, NULL);
1206 		tv.tv_sec -= AXA_P2H32(sys->uptime);
1207 		tm_info = gmtime(&t);
1208 		strftime(time_buf, sizeof (time_buf),
1209 				"%Y-%m-%dT%H:%M:%SZ", tm_info);
1210 		printf("    server uptime   : %s\n", convert_timeval(&tv));
1211 	}
1212 	if (sys->load[0] == UINT32_MAX && sys->load[1] == UINT32_MAX &&
1213 			sys->load[2] == UINT32_MAX) {
1214 		printf("    server load     : unavailable\n");
1215 	}
1216 	else {
1217 		printf("    server load     : %.2f %.2f %.2f\n",
1218 				(float)AXA_P2H32(sys->load[0]) * .0001,
1219 				(float)AXA_P2H32(sys->load[1]) * .0001,
1220 				(float)AXA_P2H32(sys->load[2]) * .0001);
1221 	}
1222 
1223 	server_type = sys->server_type == _AXA_STATS_SRVR_TYPE_SRA
1224 		? "sra" : "rad";
1225 	printf("    %s sys\n", server_type);
1226 	if (sys->cpu_usage == UINT32_MAX) {
1227 		printf("      CPU usage     : unavailable\n");
1228 	}
1229 	else {
1230 		printf("      CPU usage     : %.2f%%\n",
1231 				(float)AXA_P2H32(sys->cpu_usage) * .0001);
1232 	}
1233 	if (sys->starttime == UINT32_MAX) {
1234 		printf("      uptime        : unavailable\n");
1235 	}
1236 	else {
1237 		gettimeofday(&tv, NULL);
1238 		tv.tv_sec -= (AXA_P2H32(sys->uptime) -
1239 				AXA_P2H32(sys->starttime));
1240 		tm_info = gmtime(&t);
1241 		strftime(time_buf, sizeof (time_buf),
1242 				"%Y-%m-%dT%H:%M:%SZ", tm_info);
1243 		printf("      uptime        : %s\n", convert_timeval(&tv));
1244 	}
1245 	if (sys->vmsize == UINT64_MAX) {
1246 		printf("      VM size       : unavailable\n");
1247 	}
1248 	else {
1249 		printf("      VM size       : %"PRIu64"m\n",
1250 				AXA_P2H64(sys->vmsize) / (1024 * 1024));
1251 	}
1252 	if (sys->vmrss == UINT64_MAX) {
1253 		printf("      VM RSS        : unavailable\n");
1254 	}
1255 	else {
1256 		printf("      VM RSS        : %"PRIu64"kb\n",
1257 				AXA_P2H64(sys->vmrss) / 1024);
1258 	}
1259 	if (sys->thread_cnt == UINT32_MAX) {
1260 		printf("      thread cnt    : unavailable\n");
1261 	}
1262 	else {
1263 		printf("      thread cnt    : %"PRIu32"\n",
1264 				AXA_P2H32(sys->thread_cnt));
1265 	}
1266 	if (sys->server_type != _AXA_STATS_SRVR_TYPE_RAD) {
1267 		/* radd doesn't run as root so it can't read
1268 		 * /proc/[pid]/fdinfo or /proc/[pid]/io
1269 		 */
1270 		printf("    open file descriptors\n");
1271 		if (sys->fd_sockets == UINT32_MAX) {
1272 			printf("      socket        : unavailable\n");
1273 		}
1274 		else {
1275 			printf("      socket        : %"PRIu32"\n",
1276 					AXA_P2H32(sys->fd_sockets));
1277 		}
1278 		if (sys->fd_pipes == UINT32_MAX) {
1279 			printf("      pipe          : unavailable\n");
1280 		}
1281 		else {
1282 			printf("      pipe          : %"PRIu32"\n",
1283 					AXA_P2H32(sys->fd_pipes));
1284 		}
1285 		if (sys->fd_anon_inodes == UINT32_MAX) {
1286 			printf("      anon_inode    : unavailable\n");
1287 		}
1288 		else {
1289 			printf("      anon_inode    : %"PRIu32"\n",
1290 					AXA_P2H32(sys->fd_anon_inodes));
1291 		}
1292 		if (sys->fd_other == UINT32_MAX) {
1293 			printf("      other         : unavailable\n");
1294 		}
1295 		else {
1296 			printf("      other         : %"PRIu32"\n",
1297 					AXA_P2H32(sys->fd_other));
1298 		}
1299 		if (sys->rchar == UINT64_MAX) {
1300 			printf("    rchar           : unavailable\n");
1301 		}
1302 		else {
1303 			printf("    rchar           : %"PRIu64"\n",
1304 					AXA_P2H64(sys->rchar));
1305 		}
1306 		if (sys->wchar == UINT64_MAX) {
1307 			printf("    wchar           : unavailable\n");
1308 		}
1309 		else {
1310 			printf("    wchar           : %"PRIu64"\n",
1311 					AXA_P2H64(sys->wchar));
1312 		}
1313 	}
1314 	user_cnt = AXA_P2H32(sys->user_cnt);
1315 	printf("    users           : %d\n", user_cnt);
1316 
1317 	if (sys->server_type == _AXA_STATS_SRVR_TYPE_SRA) {
1318 		printf("    watches\n");
1319 		printf("      ipv4 watches  : %u\n",
1320 			AXA_P2H32(sys->srvr.sra.watches.ipv4_cnt));
1321 		printf("      ipv6 watches  : %u\n",
1322 			AXA_P2H32(sys->srvr.sra.watches.ipv6_cnt));
1323 		printf("      dns watches   : %u\n",
1324 			AXA_P2H32(sys->srvr.sra.watches.dns_cnt));
1325 		printf("      ch watches    : %u\n",
1326 			AXA_P2H32(sys->srvr.sra.watches.ch_cnt));
1327 		printf("      err watches   : %u\n",
1328 			AXA_P2H32(sys->srvr.sra.watches.err_cnt));
1329 		printf("      total watches : %u\n",
1330 			AXA_P2H32(sys->srvr.sra.watches.ipv4_cnt) +
1331 			AXA_P2H32(sys->srvr.sra.watches.ipv6_cnt) +
1332 			AXA_P2H32(sys->srvr.sra.watches.dns_cnt) +
1333 			AXA_P2H32(sys->srvr.sra.watches.ch_cnt) +
1334 			AXA_P2H32(sys->srvr.sra.watches.err_cnt));
1335 		printf("    channels        : ");
1336 		for (j = ch_cnt = 0; j <= AXA_NMSG_CH_MAX; j++) {
1337 			if (axa_get_bitwords(
1338 				sys->srvr.sra.ch_mask.m, j)) {
1339 					printf("%u ", j);
1340 					ch_cnt++;
1341 			}
1342 		}
1343 		if (ch_cnt == 0)
1344 			printf("none");
1345 		printf("\n");
1346 	}
1347 	else /* AXA_STATS_SRVR_TYPE_RAD */ {
1348 		printf("      anomalies     : %u\n",
1349 			AXA_P2H32(sys->srvr.rad.an_cnt));
1350 	}
1351 }
1352 
1353 static void
print_stats_user_an(_axa_p_stats_user_rad_an_t * an_obj)1354 print_stats_user_an(_axa_p_stats_user_rad_an_t *an_obj)
1355 {
1356 	int j, ch_cnt;
1357 	char ru_buf[sizeof("unlimited") + 1];
1358 
1359 	printf("        anomaly     : %s\n", an_obj->name);
1360 	printf("        options     : %s\n", an_obj->opt);
1361 
1362 	memset(&ru_buf, 0, sizeof (ru_buf));
1363 
1364 	if (an_obj->ru_original == INT_MAX)
1365 		strlcpy(ru_buf, "unlimited", sizeof(ru_buf));
1366 	else
1367 		snprintf(ru_buf, sizeof (ru_buf) - 1, "%d",
1368 				an_obj->ru_original);
1369 	printf("        RU (orig)   : %s\n", ru_buf);
1370 	if (an_obj->ru_current == INT_MAX)
1371 		strlcpy(ru_buf, "unlimited", sizeof(ru_buf));
1372 	else
1373 		snprintf(ru_buf, sizeof (ru_buf) - 1, "%d",
1374 				an_obj->ru_current);
1375 	printf("        RU (cur)    : %s\n", ru_buf);
1376 	printf("        RU (cost)   : %d\n", AXA_P2H32(an_obj->ru_cost));
1377 	printf("        channels    : ");
1378 	for (j = ch_cnt = 0; j <= AXA_NMSG_CH_MAX; j++) {
1379 		if (axa_get_bitwords(an_obj->ch_mask.m, j)) {
1380 				printf("%d ", j);
1381 				ch_cnt++;
1382 		}
1383 	}
1384 	if (ch_cnt == 0)
1385 		printf("none");
1386 	printf("\n\n");
1387 }
1388 
1389 static int
print_stats_user(_axa_p_stats_user_t * user)1390 print_stats_user(_axa_p_stats_user_t *user)
1391 {
1392 	uint8_t *p;
1393 	time_t t;
1394 	int j, ch_cnt, bytes_printed = 0, an_objs_cnt;
1395 	struct timeval connected_since, last_cnt_update;
1396 	struct tm *tm_info;
1397 	const char *io_type;
1398 	char time_buf[30];
1399 	char addr_str[INET6_ADDRSTRLEN];
1400 
1401 	if (user->type != _AXA_P_STATS_TYPE_USER) {
1402 		printf("expected user object, got type \"%d\"\n", user->type);
1403 		return (0);
1404 	}
1405 
1406 	printf("\n    user            : %s (%s)\n", user->user.name,
1407 			user->is_admin == 1 ? "admin" : "non-admin");
1408 	switch (user->addr_type)
1409 	{
1410 		case AXA_AF_INET:
1411 			inet_ntop(AF_INET, &user->ip.ipv4, addr_str,
1412 					sizeof(addr_str));
1413 			break;
1414 		case AXA_AF_INET6:
1415 			inet_ntop(AF_INET6, &user->ip.ipv6, addr_str,
1416 					sizeof(addr_str));
1417 			break;
1418 		case AXA_AF_UNKNOWN:
1419 			strlcpy(addr_str, "unknown", sizeof(addr_str));
1420 			break;
1421 	}
1422 	printf("      from          : %s\n", addr_str);
1423 	printf("      serial number : %u\n", AXA_P2H32(user->sn));
1424 	connected_since.tv_sec = user->connected_since.tv_sec;
1425 	connected_since.tv_usec = user->connected_since.tv_usec;
1426 	t = AXA_P2H32(connected_since.tv_sec);
1427 	tm_info = gmtime(&t);
1428 		strftime(time_buf, sizeof (time_buf),
1429 				"%Y-%m-%dT%H:%M:%SZ", tm_info);
1430 	printf("      since         : %s (%s)\n", time_buf,
1431 			convert_timeval(&connected_since));
1432 
1433 	switch (user->io_type) {
1434 		case AXA_IO_TYPE_UNIX:
1435 			io_type = AXA_IO_TYPE_UNIX_STR;
1436 			break;
1437 		case AXA_IO_TYPE_TCP:
1438 			io_type = AXA_IO_TYPE_TCP_STR;
1439 			break;
1440 		case AXA_IO_TYPE_SSH:
1441 			io_type = AXA_IO_TYPE_SSH_STR;
1442 			break;
1443 		case AXA_IO_TYPE_TLS:
1444 			io_type = AXA_IO_TYPE_TLS_STR;
1445 			break;
1446 		case AXA_IO_TYPE_APIKEY:
1447 			io_type = AXA_IO_TYPE_APIKEY_STR;
1448 			break;
1449 		case AXA_IO_TYPE_UNKN:
1450 		default:
1451 			io_type = "unknown";
1452 			break;
1453 	}
1454 	printf("      transport     : %s\n", io_type);
1455 	switch (mode) {
1456 		case SRA:
1457 			printf("      channels      : ");
1458 			for (j = ch_cnt = 0; j <= AXA_NMSG_CH_MAX; j++) {
1459 				if (axa_get_bitwords(
1460 					user->srvr.sra.ch_mask.m, j)) {
1461 						printf("%d ", j);
1462 						ch_cnt++;
1463 				}
1464 			}
1465 			if (ch_cnt == 0)
1466 				printf("none");
1467 			printf("\n");
1468 			printf("      ipv4 watches  : %d\n",
1469 				AXA_P2H32(
1470 					user->srvr.sra.watches.ipv4_cnt));
1471 			printf("      ipv6 watches  : %d\n",
1472 				AXA_P2H32(
1473 					user->srvr.sra.watches.ipv6_cnt));
1474 			printf("      dns watches   : %d\n",
1475 				AXA_P2H32(
1476 					user->srvr.sra.watches.dns_cnt));
1477 			printf("      ch watches    : %d\n",
1478 				AXA_P2H32(
1479 					user->srvr.sra.watches.ch_cnt));
1480 			printf("      err watches   : %d\n",
1481 				AXA_P2H32(
1482 					user->srvr.sra.watches.err_cnt));
1483 			break;
1484 		case RAD:
1485 			printf("      anomalies     : %d\n",
1486 				AXA_P2H32(user->srvr.rad.an_obj_cnt));
1487 			break;
1488 		case BOTH:
1489 			break;
1490 	}
1491 	printf("      rate-limiting : ");
1492 	if (AXA_P2H64(user->ratelimit) == AXA_RLIMIT_OFF)
1493 		printf("off\n");
1494 	else
1495 		printf("%"PRIu64"\n", AXA_P2H64(user->ratelimit));
1496 	printf("      sampling      : %.2f%%\n",
1497 			(float)AXA_P2H64(user->sample) * .0001);
1498 
1499 	if (mode == RAD && user->srvr.rad.an_obj_cnt > 0) {
1500 		if (user->srvr.rad.an_obj_cnt >
1501 			_AXA_STATS_MAX_USER_RAD_AN_OBJS) {
1502 			printf("invalid rad anomaly object count: %u",
1503 					user->srvr.rad.an_obj_cnt);
1504 			return (bytes_printed);
1505 		}
1506 
1507 		printf("      loaded modules\n");
1508 		p = (uint8_t *)user + sizeof (_axa_p_stats_user_t);
1509 		for (an_objs_cnt = user->srvr.rad.an_obj_cnt; an_objs_cnt;
1510 				an_objs_cnt--) {
1511 			print_stats_user_an((_axa_p_stats_user_rad_an_t *)p);
1512 			bytes_printed += sizeof(_axa_p_stats_user_rad_an_t);
1513 			p += sizeof(_axa_p_stats_user_rad_an_t);
1514 		}
1515 	}
1516 
1517 	printf("      packet counters\n");
1518 	last_cnt_update.tv_sec = user->last_cnt_update.tv_sec;
1519 	last_cnt_update.tv_usec = user->last_cnt_update.tv_usec;
1520 	t = AXA_P2H32(last_cnt_update.tv_sec);
1521 	tm_info = gmtime(&t);
1522 	strftime(time_buf, sizeof (time_buf), "%Y-%m-%dT%H:%M:%SZ", tm_info);
1523 	printf("      last updated  : %s (%s)\n", time_buf,
1524 			convert_timeval(&last_cnt_update));
1525 	printf("        filtered    : %"PRIu64"\n",
1526 			AXA_P2H64(user->filtered));
1527 	printf("        missed      : %"PRIu64"\n",
1528 			AXA_P2H64(user->missed));
1529 	printf("        collected   : %"PRIu64"\n",
1530 			AXA_P2H64(user->collected));
1531 	printf("        sent        : %"PRIu64"\n",
1532 			AXA_P2H64(user->sent));
1533 	printf("        rlimit      : %"PRIu64"\n",
1534 			AXA_P2H64(user->rlimit));
1535 	printf("        congested   : %"PRIu64"\n",
1536 			AXA_P2H64(user->congested));
1537 
1538 	bytes_printed += sizeof (_axa_p_stats_user_t);
1539 	return (bytes_printed);
1540 }
1541 
1542 void
print_stats(_axa_p_stats_rsp_t * stats,uint32_t len)1543 print_stats(_axa_p_stats_rsp_t *stats, uint32_t len)
1544 {
1545 	uint16_t user_objs_cnt;
1546 	uint8_t *p;
1547 	int q = 0;
1548 
1549 	if (stats->version != _AXA_STATS_VERSION_ONE) {
1550 		printf("server returned unknown stats protocol version %d\n",
1551 				stats->version);
1552 		return;
1553 	}
1554 
1555 	if (axa_debug != 0) {
1556 		printf("    stats_len       : %ub\n", AXA_P2H32(len));
1557 	}
1558 
1559 	switch (stats->result) {
1560 		case AXA_P_STATS_R_SUCCESS:
1561 			printf("    success\n");
1562 			break;
1563 		case AXA_P_STATS_R_FAIL_NF:
1564 			printf("    failed, user/sn not found\n");
1565 			return;
1566 		case AXA_P_STATS_R_FAIL_UNK:
1567 			printf("    failed, unknown reason\n");
1568 			return;
1569 		default:
1570 			printf("    unknown result code\n");
1571 			return;
1572 	}
1573 
1574 	if (stats->sys_objs_cnt > 1) {
1575 		printf("invalid stats response: too many sys objects (%u > 1)\n", stats->sys_objs_cnt);
1576 		return;
1577 	}
1578 
1579 	if (stats->user_objs_cnt > _AXA_STATS_MAX_USER_OBJS) {
1580 		printf("invalid stats response: too many user objects (%u > %u)\n", stats->user_objs_cnt, _AXA_STATS_MAX_USER_OBJS);
1581 		return;
1582 	}
1583 
1584 	p = (uint8_t *)stats + sizeof(_axa_p_stats_rsp_t);
1585 	if (stats->sys_objs_cnt == 1)
1586 		print_stats_sys((_axa_p_stats_sys_t *)p);
1587 
1588 	p += (stats->sys_objs_cnt * sizeof (_axa_p_stats_sys_t));
1589 	for (user_objs_cnt = stats->user_objs_cnt; user_objs_cnt;
1590 			user_objs_cnt--) {
1591 		q = print_stats_user((_axa_p_stats_user_t *)p);
1592 		p += q;
1593 	}
1594 }
1595 
1596 void
print_kill(_axa_p_kill_t * kill,size_t len AXA_UNUSED)1597 print_kill(_axa_p_kill_t *kill, size_t len AXA_UNUSED)
1598 {
1599 	switch (kill->result) {
1600 		case AXA_P_KILL_R_SUCCESS:
1601 			printf("    success\n");
1602 			break;
1603 		case AXA_P_KILL_R_FAIL_NF:
1604 			printf("    failed, %s not found\n",
1605 					kill->mode == AXA_P_KILL_M_SN ?
1606 					"serial number" : "user");
1607 			break;
1608 		case AXA_P_KILL_R_FAIL_UNK:
1609 			printf("    failed, unknown reason\n");
1610 			break;
1611 		default:
1612 			printf("    unknown result code\n");
1613 			break;
1614 	}
1615 }
1616