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