1 /*
2 * SIE Remote Access (SRA) ASCII tool
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: infile.c */
22 extern in_files_t in_files[];
23 extern int in_file_cur;
24
25 /* extern: main.c */
26 extern uint verbose;
27 extern bool quiet;
28 extern bool out_on;
29 extern bool out_on_nmsg;
30 extern bool nmsg_zlib;
31 extern axa_client_t client;
32 extern uint axa_debug;
33 extern axa_emsg_t emsg;
34 extern int packet_count;
35 extern int packet_count_total;
36 extern bool packet_counting;
37
38 /* extern: output.c */
39 extern int out_fd;
40 extern pcap_t *out_pcap;
41 extern int out_pcap_datalink;
42 extern int8_t *out_buf;
43 extern size_t out_buf_base;
44 extern int output_count;
45 extern int output_count_total;
46 extern bool output_counting;
47 extern bool output_buffering;
48 extern bool out_on;
49 extern char *out_addr;
50 extern int output_errno;
51 extern int out_sock_type;
52 extern nmsg_output_t out_nmsg_output;
53 extern pcap_dumper_t *out_pcap_dumper;
54
55 /* extern: signal.c */
56 extern bool interrupted;
57 extern bool terminated;
58
59 /* extern: server.c */
60 extern axa_client_t client;
61
62 /* global */
63 bool eclose = false; /* disconnect on error */
64 History *el_history; /* command history */
65 HistEvent el_event; /* command history event */
66 char *history_savefile = NULL; /* fq path to history savefile */
67 EditLine *el_e = NULL; /* editline context */
68 struct timeval cmd_input; /* timestamp of last input from user */
69 bool no_prompt = false; /* true == sra> or rad> prompt */
70 struct timeval no_reprompt; /* server has been active recently */
71 struct timeval prompt_cleared; /* !=0 if prompt & user input erased */
72 struct timeval last_output; /* timestamp of last output to user */
73 size_t prompt_len; /* length of visible prompt */
74 cmd_t version_cmd; /* version command exported to main */
75 axa_mode_t mode; /* sratool or radtool */
76
77 /* private */
78 static int out_cmd_pcap_if(const char *ifname);
79 static int out_cmd_pcap_file(const char *addr, bool want_fifo);
80
81 static struct timeval connect_time;
82 static struct {
83 struct ether_addr dst;
84 struct ether_addr src;
85 uint16_t etype;
86 } out_mac;
87
88 /* commands */
89 static cmd_t help_cmd;
90 static cmd_t exit_cmd;
91 static cmd_t error_mode_cmd;
92 static cmd_t debug_cmd;
93 static cmd_t verbose_cmd;
94 static cmd_t source_cmd;
95 static cmd_t disconnect_cmd;
96 static cmd_t connect_cmd;
97 static cmd_t count_cmd;
98 static cmd_t ciphers_cmd;
99 static cmd_t out_cmd;
100 static cmd_t nop_cmd;
101 static cmd_t mode_cmd;
102 static cmd_t sra_mode_cmd;
103 static cmd_t rad_mode_cmd;
104 static cmd_t user_cmd;
105 static cmd_t sra_watch_cmd;
106 static cmd_t rad_watch_cmd;
107 static cmd_t list_cmd;
108 static cmd_t delete_cmd;
109 static cmd_t channel_cmd;
110 static cmd_t anom_cmd;
111 static cmd_t rlimits_cmd;
112 static cmd_t sample_cmd;
113 static cmd_t sndbuf_cmd;
114 static cmd_t acct_cmd;
115 static cmd_t pause_cmd;
116 static cmd_t trace_cmd;
117 static cmd_t go_cmd;
118 static cmd_t sleep_cmd;
119 static cmd_t radunit_cmd;
120 static cmd_t nmsg_zlib_cmd;
121 static cmd_t stats_req_cmd;
122 static cmd_t kill_cmd;
123 static cmd_t alias_cmd;
124 static cmd_t buffer_cmd;
125
126 typedef enum {
127 NO,
128 MB,
129 YES
130 } ternary_t;
131
132 /* -1 = display help message, 0 = command failed, 1 = success */
133 struct cmd_tbl_entry {
134 const char *cmd; /* common name of cmd */
135 cmd_t (*fnc); /* function that runs cmd */
136 axa_mode_t mode; /* SRA|RAD|BOTH */
137 ternary_t need_args; /* YES|NO|MB (maybe) */
138 bool need_sock; /* server connection req'd? YES|NO */
139 const char *help_str; /* one-liner synopsis */
140 const char *usage_str; /* detailed usage */
141 };
142
143 const cmd_tbl_entry_t cmds_tbl[] = {
144 {"?", help_cmd, BOTH, MB, NO,
145 "? [cmd]",
146 "List all commands or get more information about a command."
147 },
148 {"accounting", acct_cmd, BOTH, NO, YES,
149 "accounting",
150 "Ask the server to report total message counts."
151 },
152 {"alias", alias_cmd, BOTH, NO, NO,
153 "alias",
154 "List available connection aliases."
155 },
156 {"anomaly", anom_cmd, RAD, YES, YES,
157 "tag anomaly name [parameters]",
158 "Start the named anomaly detector module.\n"
159 " \"Tag\" is the number labeling the module."
160 },
161 {"buffering", buffer_cmd, BOTH, NO, NO,
162 "nmsg output buffering",
163 "Toggle nmsg container buffering.\nFor this option to have any"
164 " effect, output mode must be enabled in nmsg socket mode. "
165 "When enabled, (by default) nmsg containers will fill with payloads"
166 " before being emitted. When disabled, nmsg payloads will be emitted as"
167 " rapidly as possible.\n"
168 "Note, for this command to take effect, it must be set before using"
169 " the 'forward' command."
170 },
171 {"ciphers", ciphers_cmd, BOTH, MB, NO,
172 "ciphers [cipher-list]",
173 "Specify the ciphers to be used with future connections."
174 " Disconnect the current connection if it uses TLS."
175 },
176 {"channels", channel_cmd, SRA, YES, YES,
177 "channel {list | {on | off} {all | chN}}",
178 "List available SRA channels or enable or disable"
179 " one or all SIE channels."
180 },
181 {"connect", connect_cmd, BOTH, MB, NO,
182 "connect [server]",
183 "Show the current connection"
184 " or connect with 'tcp:user@host,port',"
185 " 'unix:[user@]/socket'] through a UNIX domain socket,"
186 " 'ssh:[user@]host' via SSH"
187 " or with 'tls:user@host,port."
188 },
189 {"count", count_cmd, BOTH, MB, NO,
190 "count [#packets | off]",
191 "Set terminal output to stop displaying packets after a"
192 " number of packets (including immediately with a number of 0),"
193 " show the currently remaining count,"
194 " or turn off the packet count limit."
195 },
196 {"debug", debug_cmd, BOTH, MB, NO,
197 "debug [on | off | quiet | N]",
198 "increases, decreases, or shows the level of debugging and tracing messages"
199 " that is also controlled by -d."
200 " \"Debug quiet\" turns off reports of successful AXA commands."
201 },
202 {"delete", delete_cmd, BOTH,MB, YES,
203 "delete",
204 "Delete a watch or anomaly."
205 },
206 {"delete watches", delete_cmd, SRA, MB, YES,
207 "[tag] delete watches [all]",
208 "With a tag, stop or delete the specified watch.\n"
209 " With \"all\", delete all watches"
210 },
211 {"delete anomaly", delete_cmd, RAD, MB, YES,
212 "[tag] delete anomaly [all]",
213 "Delete an anomaly detector module specified by tag"
214 " or all anomaly detector modules."
215 },
216 {"disconnect", disconnect_cmd, BOTH, NO, NO,
217 "disconnect",
218 "Disconnect from the server."
219 },
220 {"error mode", error_mode_cmd, BOTH, MB, NO,
221 "error mode [disconnect | off]",
222 "\"error mode disconnect\" disconnects from the server and exits"
223 " when the server reports an error or the connection breaks."
224 " In the default mode, \"error mode off\", errors are only reported."
225 },
226 {"exit", exit_cmd, BOTH, NO, NO,
227 "exit",
228 "Quit the program."
229 },
230 {"forward", out_cmd, BOTH, MB, NO,
231 "forward [off | nmsg:[tcp:|udp:]host,port [count] | nmsg:file:path [count]\n"
232 " | pcap[-fifo]:file [count] | pcap-if:[dst/]ifname] [count]",
233 "Start, stop or show the state of forwarding packets received from"
234 " the server."
235 " Received NMSG messages and IP packets can be"
236 " forwarded as NMSG messages to a TCP or UDP port."
237 " Received IP packets can be forwarded as a pcap stream"
238 " to a file, to a FIFO created separately with `mkfifo`,"
239 " or in Ethernet frames on a named network interface to a 48-bit address"
240 " (default 0)."
241 " Stop forwarding after count messages."
242 },
243 {"get", list_cmd, RAD, NO, YES,
244 "[tag] get",
245 "With a tag, list the set of watches and anomaly detection modules with"
246 " that tag."
247 " Without a tag, list all active as well as available anomaly detection"
248 " modules."
249 },
250 {"get channels", list_cmd, SRA, MB, YES,
251 "get channels",
252 "List all SIE channels available to the user on the SRA server."
253 },
254 {"get watches", list_cmd, SRA, MB, YES,
255 "[tag] get watches",
256 "With a tag, list the specified watch."
257 " List all watches without a tag."
258 },
259 {"go", go_cmd, BOTH, NO, YES,
260 "go",
261 "Tell the server to resume sending data."
262 },
263 {"help", help_cmd, BOTH, MB, NO,
264 "help [cmd]",
265 "List all commands or get more information about a command."
266 },
267 {"kill", kill_cmd, BOTH, YES, YES,
268 "kill user_name | serial_number",
269 "Kill off user session (admin users only). If serial number is specified"
270 " kill a single session; if user name is specified, kill all sessions"
271 " belonging to that user."
272 },
273 {"list channels", list_cmd, SRA, MB, YES,
274 "list channels",
275 "List all SIE channels available to the user on the SRA server."
276 },
277 {"list", list_cmd, RAD, NO, YES,
278 "[tag] list",
279 "With a tag, list the set of watches and anomaly detection modules with"
280 " that tag."
281 " Without a tag, list all active as well as available anomaly detection"
282 " modules."
283 },
284 {"list watches", list_cmd, SRA, MB, YES,
285 "[tag] list watches",
286 "With a tag, list the specified watch."
287 " List all watches without a tag."
288 },
289 {"stats", stats_req_cmd, BOTH, MB, YES,
290 "stats [all | user_name | serial_number]",
291 "Get current system, server, and user statistics (admin users only)."
292 " If no argument is provided, return a top-level summary containing"
293 " system and server statistics."
294 " If a user name or serial number is provided, proceed summary with"
295 " information on all current sessions for that user."
296 " If the keyword \"all\" is specified, proceed summary with information"
297 " on all current sessions for all logged in users."
298 },
299 {"mode", mode_cmd, BOTH, MB, NO,
300 "mode [SRA | RAD]",
301 "Show the current command mode or"
302 " expect to connect to an SRA or RAD server. Mode cannot be changed"
303 " while connected to server."
304 },
305 {"nop", nop_cmd, BOTH, NO,YES,
306 "nop",
307 "Send a command to the server that does nothing but test the connection"
308 },
309 {"pause", pause_cmd, BOTH, NO, YES,
310 "pause",
311 "Tell the server to stop sending data."
312 },
313 {"quit", exit_cmd, BOTH, NO, NO,
314 "quit",
315 "Quit the program."
316 },
317 {"radd", rad_mode_cmd, BOTH, MB, NO,
318 "radd",
319 "Change to RAD mode (must not be connected to a server)."
320 },
321 {"rate limits", rlimits_cmd, BOTH, MB, YES,
322 "rate limits [-|MAX|per-sec] [-|NEVER|report-secs]",
323 "Ask the server to report its rate limits"
324 " or to set rate limits and the interval between rate limit reports."
325 },
326 {"runits", radunit_cmd, RAD, NO, YES,
327 "runits",
328 "Ask the server for my RAD Unit balance."
329 },
330 {"sample", sample_cmd, BOTH, MB, YES,
331 "sample [percent]",
332 "Ask the server to report its current output sampling rate"
333 " or to set its sampling rate."
334 },
335 {"sleep", sleep_cmd, BOTH, YES, NO,
336 "sleep x.y",
337 "Stop accepting commands or displaying server output for a while."
338 },
339 {"source", source_cmd, BOTH, YES, NO,
340 "source filename",
341 "Read and execute commands commands from a file."
342 },
343 {"status", connect_cmd, BOTH, NO, NO,
344 "status",
345 "get server status"
346 },
347 {"srad", sra_mode_cmd, BOTH, MB, NO,
348 "srad",
349 "Change to RAD mode (must not be connected to a server)."
350 },
351 {"stop", delete_cmd, BOTH, MB, YES,
352 "stop",
353 "Stop watches or anomalies."
354 },
355 {"watch", rad_watch_cmd, RAD, MB, YES,
356 "tag watch {ip=IP[/n] | dns=[*.]dom}",
357 "Tell the RAD server about address and domains of interest."
358 },
359 {"watch", sra_watch_cmd, SRA, MB, YES,
360 "tag watch {ip=IP[/n] | dns=[*.]dom | ch=chN | errors}",
361 "Tell the SRA server to send nmsg messages or IP packets that are to,"
362 " from, or contain the specified IP addresses,"
363 " that contain the specified domain name,"
364 " that arrived at the server on the specified SIE channel,"
365 " or are SIE messages that could not be decoded."
366 " The \"tag\" is the integer labeling the watch."
367 },
368 {"trace", trace_cmd, BOTH, YES, YES,
369 "trace N",
370 "Set server trace level."
371 },
372 {"user", user_cmd, BOTH, YES, YES,
373 "user name",
374 "Send the user name required by the server on a TCP/IP connection or"
375 " a UNIX domain socket.\n"
376 " TLS/SSH connections do not use this command but use the"
377 " name negotiated with the tls or ssh protocol."
378 },
379 {"verbose", verbose_cmd, BOTH, MB, NO,
380 "verbose [on | off | N]",
381 "controls the length of SIE message and IP packet descriptions."
382 " The default, \"verbose off\", generally displays one line summaries."
383 },
384 {"version", version_cmd, BOTH, NO, NO,
385 "version",
386 "shows the software and protocol version."
387 },
388 {"window", sndbuf_cmd, BOTH, MB, YES,
389 "window [bytes]",
390 "Ask the server to report its current TCP output buffer size on TLS and"
391 " TCP connections or to set its output buffer size."
392 },
393 {"zlib", nmsg_zlib_cmd, BOTH, NO, NO,
394 "zlib",
395 "Toggle nmsg container compression.\nFor this option to have any"
396 " effect, output mode must be enabled in nmsg file or socket mode."
397 },
398 };
399
400
401 const char *
el_prompt(EditLine * e AXA_UNUSED)402 el_prompt(EditLine *e AXA_UNUSED)
403 {
404 static const char null_prompt[] = "";
405 static const char rad_std_prompt[] = "rad> ";
406 static const char sra_std_prompt[] = "sra> ";
407 static const char rad_out_prompt[] = "output-rad> ";
408 static const char sra_out_prompt[] = "output-sra> ";
409 const char *prompt;
410
411 if (interrupted)
412 return (null_prompt);
413
414 if (no_prompt)
415 prompt = null_prompt;
416 else if (out_on)
417 prompt = mode == RAD ? rad_out_prompt : sra_out_prompt;
418 else
419 prompt = mode == RAD ? rad_std_prompt : sra_std_prompt;
420
421 prompt_cleared.tv_sec = 0;
422 no_reprompt.tv_sec = 0;
423 prompt_len = strlen(prompt);
424 return (prompt);
425 }
426
427 static int
get_cols(void)428 get_cols(void)
429 {
430 int cols = 0;
431
432 #ifdef TIOCGWINSZ
433 if (cols == 0) {
434 struct winsize ws;
435
436 if (ioctl(el->el_infd, TIOCGWINSZ, (void *)&ws) != -1)
437 col = ws.ws_col
438 }
439 #endif
440 #ifdef TIOCGSIZE
441 if (cols == 0) {
442 struct ttysize ts;
443 if (ioctl(el->el_infd, TIOCGSIZE, (void *)&ts) != -1)
444 cols = ts.ts_cols;
445 }
446 #endif
447
448 if (cols == 0)
449 cols = 80;
450
451 return (cols);
452 }
453
454 /* Clear the prompt and any partial command to make room output from the
455 * server. The history library will eventually be told to restore it. */
456 void
clear_prompt(void)457 clear_prompt(void)
458 {
459 const LineInfo *li;
460 int cols, llen, i;
461
462 cmd_input.tv_sec = 0;
463 gettimeofday(&last_output, NULL);
464
465 if (el_e == NULL)
466 return;
467
468 fflush(stderr);
469 fflush(stdout);
470
471 if (prompt_cleared.tv_sec == 0 && prompt_len != 0) {
472 /* We do not catch SIGWINCH,
473 * and so must always get the screen (line) size. */
474 cols = get_cols();
475
476 /* get user's partial command */
477 li = el_line(el_e);
478 llen = li->lastchar - li->buffer;
479 llen += prompt_len;
480
481 if (llen > 0) {
482 /* Erase the prompt and the user's command.
483 * '\b' generally does not wrap to the previous line,
484 * so mess around with the window size. */
485 for (i = (li->cursor - li->buffer + prompt_len)/cols;
486 i > 0;
487 --i) {
488 el_set(el_e, EL_ECHOTC, "up", NULL);
489 }
490 fputc('\r', stdout);
491 /* Cover everything with blanks. */
492 for (i = llen; i > 0; --i)
493 fputc(' ', stdout);
494 /* Back to the start of the (first) line. */
495 for (i = llen/cols; i > 0; --i) {
496 el_set(el_e, EL_ECHOTC, "up", NULL);
497 }
498 }
499 fputc('\r', stdout);
500 fflush(stdout);
501 }
502
503 prompt_len = 0;
504 prompt_cleared = last_output;
505 }
506
507 /* Restore the prompt */
508 void
reprompt(void)509 reprompt(void)
510 {
511 prompt_cleared.tv_sec = 0;
512 no_reprompt.tv_sec = 0;
513 el_set(el_e, EL_REFRESH);
514 }
515
516 void
error_help_cmd(axa_tag_t tag,const char * arg)517 error_help_cmd(axa_tag_t tag, const char *arg)
518 {
519 error_close(true);
520 help_cmd(tag, arg, NULL);
521 }
522
523 void
history_get_savefile(void)524 history_get_savefile(void)
525 {
526 int n;
527 struct passwd *pw;
528 const char *histfile_name = ".sratool_history";
529 static char buf[MAXPATHLEN + 1];
530
531 pw = getpwuid(getuid());
532 if (pw == NULL)
533 return;
534
535 n = snprintf(buf, sizeof(buf), "%s", pw->pw_dir);
536 snprintf(buf + n, sizeof(buf) - n, "/%s", histfile_name);
537
538 history_savefile = buf;
539 }
540
541
542 /* Compare command names ignoring case. */
543 static const char * /* NULL or mismatch in user string */
cmd_cmp(const char * user,const char * op,bool * iss)544 cmd_cmp(const char *user, /* from the user */
545 const char *op, /* from command table entry */
546 bool *iss) /* mere initial substring match */
547 {
548 char op_c, user_c;
549 int len;
550
551 if (iss != NULL)
552 *iss = false;
553
554 len = 0;
555 for (;;) {
556 op_c = AXA_TO_LOWER(*op);
557
558 user_c = AXA_TO_LOWER(*user);
559 if (user_c == '\t')
560 user_c = ' ';
561
562 if (op_c != user_c) {
563 /* compress bursts of blanks */
564 if (user_c == ' ' && len != 0 && *(op-1) == ' ') {
565 ++user;
566 continue;
567 }
568
569 /* Treat an initial substring match without an arg
570 * as a complete match. */
571 if (user_c == '\0') {
572 if (iss != NULL)
573 *iss = true;
574 return (NULL);
575 }
576 return (user);
577 }
578
579 /* stop at an exact match */
580 if (op_c == '\0')
581 return (NULL);
582
583 ++op;
584 ++user;
585 ++len;
586 }
587 }
588
589 /* Look for (the start of) a word. */
590 static bool
word_cmp(const char ** argp,const char * tgt)591 word_cmp(const char **argp, const char *tgt)
592 {
593 const char *arg1, *arg2;
594 int i;
595
596 arg1 = *argp;
597 arg1 += strspn(arg1, AXA_WHITESPACE);
598
599 if (arg1[0] == '\0')
600 return (false);
601
602 arg2 = cmd_cmp(arg1, tgt, NULL);
603 if (arg2 == arg1)
604 return (false);
605 if (arg2 == NULL) {
606 *argp = arg1 + strlen(arg1);
607 return (true);
608 }
609 i = strspn(arg2, AXA_WHITESPACE);
610 if (i != 0) {
611 *argp = arg2+i;
612 return (true);
613 }
614 return (false);
615 }
616
617 static bool
run_cmd(axa_tag_t tag,const char * op,const char * arg,const cmd_tbl_entry_t * ce)618 run_cmd(axa_tag_t tag, const char *op, const char *arg,
619 const cmd_tbl_entry_t *ce)
620 {
621 int i;
622
623 if (ce->need_sock && !AXA_CLIENT_OPENED(&client)) {
624 error_msg("\"%s\" requires a connection to a server", op);
625 return (0);
626 }
627
628 if ((ce->need_args == YES && *arg == '\0')
629 || (ce->need_args == NO && *arg != '\0')) {
630 error_help_cmd(tag, op);
631 return (false);
632 }
633
634 i = ce->fnc(tag, arg, ce);
635 if (i > 0)
636 return (true);
637
638 if (i < 0)
639 error_help_cmd(tag, op);
640 else
641 error_close(true);
642 return (false);
643 }
644
645 static bool /* true=ok false=bad command */
cmd(axa_tag_t tag,const char * op)646 cmd(axa_tag_t tag, const char *op)
647 {
648 const char *arg, *best_argp = NULL;
649 int j;
650 const cmd_tbl_entry_t *ce, *ce1, *best_ce = NULL;
651 bool iss;
652 int num_iss;
653
654 /* Look for the string as a command and execute it if we find it. */
655 ce1 = NULL;
656 num_iss = 0;
657 for (ce = cmds_tbl; ce <= AXA_LAST(cmds_tbl); ++ce) {
658 if (ce->mode != mode && ce->mode != BOTH)
659 continue;
660 arg = cmd_cmp(op, ce->cmd, &iss);
661 if (arg == op)
662 continue;
663
664 /* If the command table entry and the command completely
665 * matched, then infer a null argument. */
666 if (arg == NULL) {
667 if (iss) {
668 ++num_iss;
669
670 /* Continue searching after
671 * the 1st initial substring match,
672 * a duplicate initial substring match,
673 * or a synonym initial substring match. */
674 if (ce1 == NULL) {
675 ce1 = ce;
676 continue;
677 }
678 if (ce1->fnc == ce->fnc)
679 continue;
680 if (ce->help_str == NULL)
681 continue;
682 error_help_cmd(tag, op);
683 return (false);
684 }
685 if (ce->need_args != YES)
686 return (run_cmd(tag, op, "", ce));
687 error_help_cmd(tag, op);
688 return (false);
689 }
690 /* If the command table entry is an initial substring of
691 * the user's command, then the rest of the command must
692 * start with white space. */
693 j = strspn(arg, AXA_WHITESPACE);
694 if (j == 0) {
695 /* Handle blanks part of the command and before
696 * the mismatch. */
697 j = strspn(--arg, AXA_WHITESPACE);
698 }
699 /* Trim blanks & use the rest of the string as the argument. */
700 if (j != 0) {
701 if (ce->need_args == NO) {
702 /* arg not allowed */
703 error_help_cmd(tag, op);
704 return (false);
705 }
706
707 if (best_ce) {
708 error_help_cmd(tag, op);
709 return (false);
710 }
711
712 best_argp = arg+j;
713 best_ce = ce;
714 continue;
715 }
716 }
717
718 if (best_ce)
719 return (run_cmd(tag, op, best_argp, best_ce));
720
721 /* run an unambiguous partial command */
722 if (ce1 != NULL && (ce1->help_str != NULL || num_iss <= 1))
723 return (run_cmd(tag, op, "", ce1));
724
725 if (op[0] == '?') {
726 help_cmd(tag, op+1, NULL);
727 return (true);
728 }
729 error_msg("unrecognized command \"%s\"", op);
730 return (false);
731 }
732
733 /*
734 * Get an leading tag on a command
735 */
736 static axa_tag_t
cmd_tag(const char ** cur_cmdp)737 cmd_tag(const char **cur_cmdp)
738 {
739 axa_tag_t tag;
740 const char *cur_cmd;
741 char *p;
742
743 cur_cmd = *cur_cmdp;
744 if (cur_cmd[0] == '*' && AXA_IS_WHITE(cur_cmd[1])) {
745 tag = AXA_TAG_NONE;
746 cur_cmd += 2;
747 } else {
748 tag = strtoul(cur_cmd, &p, 10);
749 if (tag == AXA_TAG_NONE || !AXA_IS_WHITE(*p)) {
750 tag = AXA_TAG_NONE;
751 } else {
752 cur_cmd = p;
753 }
754 }
755 cur_cmd += strspn(cur_cmd, AXA_WHITESPACE);
756 *cur_cmdp = cur_cmd;
757 return (tag);
758 }
759
760
761 static size_t
help_cmd_snprint(char * buf,size_t buf_len,const cmd_tbl_entry_t * help_ce)762 help_cmd_snprint(char *buf, size_t buf_len, const cmd_tbl_entry_t *help_ce)
763 {
764 if (help_ce->help_str == NULL)
765 return (0);
766
767 snprintf(buf, buf_len, " %s", help_ce->help_str);
768 return (strlen(buf));
769 }
770
771 static void
help_usage_print(const cmd_tbl_entry_t * ce)772 help_usage_print(const cmd_tbl_entry_t *ce)
773 {
774 const char *bol, *p, *eol;
775
776 if (ce == NULL)
777 return;
778 bol = ce->usage_str;
779 if (bol == NULL)
780 return;
781
782 while (*bol != '\0') {
783 while (*bol == ' ' || *bol == '\n')
784 ++bol;
785 eol = bol;
786 p = bol;
787 for (;;) {
788 if (*p == ' ' || *p == '\n' || *p == '\0') {
789 if (p - bol >= 64)
790 break;
791 eol = p;
792 if (*p == '\0' || *p == '\n')
793 break;
794 }
795 ++p;
796 }
797 if (*bol != '\0')
798 printf("%8s%.*s\n", "", (int)(eol-bol), bol);
799 bol = eol;
800 }
801 }
802
803 int
help_cmd(axa_tag_t tag AXA_UNUSED,const char * arg,const cmd_tbl_entry_t * ce0 AXA_UNUSED)804 help_cmd(axa_tag_t tag AXA_UNUSED, const char *arg,
805 const cmd_tbl_entry_t *ce0 AXA_UNUSED)
806 {
807 bool iss;
808 int found; /* 0=partial ISS, 1=iss, 2=exact */
809 int found_len; /* best partial ISS */
810 bool stealth; /* true=hidden command is only match */
811 const cmd_tbl_entry_t *ce;
812 const cmd_tbl_entry_t *help_ce, *usage_ce;
813 int num_help;
814 char buf[160];
815 size_t hlen;
816 const char *p;
817
818 /* Ignore a tag. */
819 cmd_tag(&arg);
820
821 /* See if the string matches one or more commands. */
822 found = -1;
823 found_len = -1;
824 stealth = true;
825 if (arg != NULL && *arg != '\0') {
826 for (ce = cmds_tbl; ce <= AXA_LAST(cmds_tbl); ++ce) {
827 if (ce->mode != mode && ce->mode != BOTH)
828 continue;
829 p = cmd_cmp(arg, ce->cmd, &iss);
830 if (p == NULL) {
831 if (!iss) {
832 /* complete match */
833 found = 2;
834 } else if (found < 1) {
835 /* target is initial substring of
836 * command; note if it is best so far */
837 found = 1;
838 }
839 if (ce->help_str != NULL)
840 stealth = false;
841 } else if (p != arg && found <= 0) {
842 /* target has an initial substring that
843 * matches initial substring of command */
844 found = 0;
845 found_len = max(found_len, p-arg);
846 }
847 }
848 }
849 /* If we found something, show it */
850 if (found > 0) {
851 help_ce = NULL;
852 usage_ce = NULL;
853 num_help = 0;
854 for (ce = cmds_tbl; ce <= AXA_LAST(cmds_tbl); ++ce) {
855 if (ce->mode != mode && ce->mode != BOTH)
856 continue;
857 if (ce->help_str != NULL) {
858 /* Show help for a matching synonym. */
859 help_ce = ce;
860 } else if (!stealth) {
861 /* don't show hidden command if we have better */
862 continue;
863 }
864 if (help_ce == NULL)
865 continue;
866 p = cmd_cmp(arg, ce->cmd, &iss);
867 /* don't show a command that does not match at all */
868 if (p == arg)
869 continue;
870 /* don't show commands that share initial substring
871 * with target if we have better */
872 if (p != NULL && found > 0)
873 continue;
874
875 /* show commands for which the target is an initial
876 * substring only if we do not have better */
877 if (found > 1 && iss)
878 continue;
879 if (p != NULL && found_len > p-arg)
880 continue;
881
882 help_cmd_snprint(buf, sizeof(buf), help_ce);
883 printf(" %s%s\n", "", buf);
884 ++num_help;
885 usage_ce = help_ce;\
886 help_ce = NULL;
887 }
888 if (num_help == 1)
889 help_usage_print(usage_ce);
890 return (1);
891 } else if (arg && strlen(arg)) {
892 printf("No matching help topic could be found\n");
893 return (1);
894 }
895
896 /* talk about all of the commands */
897 printf(" %s AXA protocol %d\n", axa_get_version(), AXA_P_PVERS);
898
899 for (ce = cmds_tbl; ce <= AXA_LAST(cmds_tbl); ++ce) {
900 if (ce->mode != mode && ce->mode != BOTH)
901 continue;
902 hlen = help_cmd_snprint(buf, sizeof(buf), ce);
903 if (hlen == 0)
904 continue;
905 printf(" %s\n", buf);
906 }
907 fputc('\n', stdout);
908
909 return (1);
910 }
911
912 static int AXA_NORETURN
exit_cmd(axa_tag_t tag AXA_UNUSED,const char * arg AXA_UNUSED,const cmd_tbl_entry_t * ce AXA_UNUSED)913 exit_cmd(axa_tag_t tag AXA_UNUSED, const char *arg AXA_UNUSED,
914 const cmd_tbl_entry_t *ce AXA_UNUSED)
915 {
916 stop(EX_OK);
917 }
918
919 static int
error_mode_cmd(axa_tag_t tag AXA_UNUSED,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)920 error_mode_cmd(axa_tag_t tag AXA_UNUSED, const char *arg,
921 const cmd_tbl_entry_t *ce AXA_UNUSED)
922 {
923 const char *setting;
924
925 setting = arg;
926 word_cmp(&setting, "mode");
927 if (setting[0] != '\0') {
928 if (word_cmp(&setting, "off")) {
929 eclose = false;
930 } else if (word_cmp(&setting, "disconnect")
931 || word_cmp(&setting, "on")
932 || word_cmp(&setting, "close")) {
933 eclose = true;
934 } else {
935 return (-1);
936 }
937 }
938 if (verbose > 0 || arg[0] == '\0') {
939 if (eclose)
940 printf(" error mode disconnect\n");
941 else
942 printf(" error mode off\n");
943 }
944 return (1);
945 }
946
947 static int
debug_cmd(axa_tag_t tag AXA_UNUSED,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)948 debug_cmd(axa_tag_t tag AXA_UNUSED, const char *arg,
949 const cmd_tbl_entry_t *ce AXA_UNUSED)
950 {
951 const char *setting;
952 u_long l;
953 char *p;
954
955 setting = arg;
956 if (setting[0] != '\0') {
957 if (word_cmp(&setting, "quiet")) {
958 axa_debug = 0;
959 quiet = true;
960 } else if (word_cmp(&setting, "off")) {
961 axa_debug = 0;
962 quiet = false;
963 } else if (word_cmp(&setting, "on")) {
964 quiet = false;
965 ++axa_debug;
966 } else {
967 l = strtoul(setting, &p, 10);
968 if (*p != '\0')
969 return (-1);
970 axa_debug = l <= AXA_DEBUG_MAX ? l :
971 AXA_DEBUG_MAX;
972 quiet = false;
973 }
974 AXA_DEBUG_TO_NMSG(axa_debug);
975 }
976 if (axa_debug > 1 || arg[0] == '\0') {
977 if (axa_debug == 0) {
978 if (quiet)
979 printf(" debug quiet\n");
980 else
981 printf(" debug off\n");
982 } else if (axa_debug == 1) {
983 printf(" debug on\n");
984 } else {
985 printf(" debug on+%d\n", axa_debug-1);
986 }
987 }
988 return (1);
989 }
990
991 static int
verbose_cmd(axa_tag_t tag AXA_UNUSED,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)992 verbose_cmd(axa_tag_t tag AXA_UNUSED, const char *arg,
993 const cmd_tbl_entry_t *ce AXA_UNUSED)
994 {
995 const char *setting;
996 u_long l;
997 char *p;
998
999 setting = arg;
1000 if (setting[0] != '\0') {
1001 if (word_cmp(&setting, "off")) {
1002 verbose = 0;
1003 } else if (word_cmp(&setting, "on")) {
1004 ++verbose;
1005 } else {
1006 l = strtoul(setting, &p, 10);
1007 if (*p != '\0')
1008 return (-1);
1009 verbose = l;
1010 }
1011 }
1012 if (verbose > 1 || arg[0] == '\0') {
1013 if (verbose == 0)
1014 printf(" verbose off\n");
1015 else if (verbose == 1)
1016 printf(" verbose on\n");
1017 else
1018 printf(" verbose on+%d\n", verbose-1);
1019 }
1020 return (1);
1021 }
1022
1023 int
version_cmd(axa_tag_t tag AXA_UNUSED,const char * arg AXA_UNUSED,const cmd_tbl_entry_t * ce AXA_UNUSED)1024 version_cmd(axa_tag_t tag AXA_UNUSED, const char *arg AXA_UNUSED,
1025 const cmd_tbl_entry_t *ce AXA_UNUSED)
1026 {
1027 axa_p_pvers_t pvers;
1028 char *out = NULL;
1029 const char *origin = ((mode == RAD) ? "radtool" : "sratool");
1030
1031 pvers = client.io.pvers == 0 ? AXA_P_PVERS : client.io.pvers;
1032 #if AXA_P_PVERS_MIN != AXA_P_PVERS_MAX
1033 printf("%s built using AXA library %s, supporting AXA protocols v%d to v%d; currently using v%d\n",
1034 axa_prog_name, axa_get_version(),
1035 AXA_P_PVERS_MIN, AXA_P_PVERS_MAX, pvers);
1036 #else
1037 printf("%s built using AXA library: %s, supporting AXA protocol v%d\n",
1038 axa_prog_name, axa_get_version(), pvers);
1039 #endif
1040 if (!axa_client_get_hello_string(&emsg, origin, &client, &out))
1041 axa_error_msg("error retrieving client HELLO: %s", emsg.c);
1042 else {
1043 printf("client HELLO: %s\n", out);
1044 free(out);
1045 }
1046 return (1);
1047 }
1048
1049 static int
source_cmd(axa_tag_t tag AXA_UNUSED,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1050 source_cmd(axa_tag_t tag AXA_UNUSED, const char *arg,
1051 const cmd_tbl_entry_t *ce AXA_UNUSED)
1052 {
1053 if (in_file_cur >= MAX_IN_FILES-1) {
1054 error_msg("\"source\" nesting too deep");
1055 return (0);
1056 }
1057
1058 in_files[in_file_cur+1].lineno = 0;
1059 in_files[in_file_cur+1].f = fopen(arg, "r");
1060 if (in_files[in_file_cur+1].f == NULL) {
1061 error_msg("fopen(%s): %s", arg, strerror(errno));
1062 return (0);
1063 }
1064 in_files[in_file_cur+1].name = axa_strdup(arg);
1065 ++in_file_cur;
1066
1067 return (1);
1068 }
1069
1070 static int
disconnect_cmd(axa_tag_t tag AXA_UNUSED,const char * arg AXA_UNUSED,const cmd_tbl_entry_t * ce AXA_UNUSED)1071 disconnect_cmd(axa_tag_t tag AXA_UNUSED, const char *arg AXA_UNUSED,
1072 const cmd_tbl_entry_t *ce AXA_UNUSED)
1073 {
1074 if (!AXA_CLIENT_OPENED(&client)) {
1075 fputs("not connected to a server\n", stdout);
1076 } else {
1077 disconnect(true);
1078 }
1079 return (1);
1080 }
1081
1082 static int
alias_cmd(axa_tag_t tag AXA_UNUSED,const char * arg0 AXA_UNUSED,const cmd_tbl_entry_t * ce AXA_UNUSED)1083 alias_cmd(axa_tag_t tag AXA_UNUSED, const char *arg0 AXA_UNUSED,
1084 const cmd_tbl_entry_t *ce AXA_UNUSED)
1085 {
1086 /* Check for config-file-specified alias first. */
1087 axa_client_config_alias_print();
1088
1089 return (1);
1090 }
1091
1092
1093 static int
connect_cmd(axa_tag_t tag AXA_UNUSED,const char * arg0,const cmd_tbl_entry_t * ce AXA_UNUSED)1094 connect_cmd(axa_tag_t tag AXA_UNUSED, const char *arg0,
1095 const cmd_tbl_entry_t *ce AXA_UNUSED)
1096 {
1097 const char *arg;
1098
1099 if (arg0[0] == '\0') {
1100 if (!AXA_CLIENT_OPENED(&client)) {
1101 fputs("not connected to a server\n", stdout);
1102 } else if (client.hello == NULL) {
1103 printf("connecting to %s\n", client.io.label);
1104 } else {
1105 printf("connected to: %s @ %s using AXA protocol %d\n",
1106 client.io.is_rad == true ? "radd" : "srad",
1107 client.io.addr, client.io.pvers);
1108 printf(" connected for: %s\n",
1109 convert_timeval(&connect_time));
1110 if (client.io.tls_info != NULL)
1111 printf(" %s\n", client.io.tls_info);
1112 count_print(false);
1113 if (mode == RAD)
1114 return (srvr_send(tag, AXA_P_OP_RADU, NULL, 0));
1115 }
1116 return (1);
1117 }
1118
1119 /* Check for config-file-specified alias first. */
1120 arg = axa_client_config_alias_chk(arg0);
1121 arg = arg ? arg : arg0;
1122
1123 if (AXA_CLIENT_OPENED(&client))
1124 disconnect(false);
1125
1126 axa_client_backoff_reset(&client);
1127 switch (axa_client_open(&emsg, &client, arg, mode == RAD,
1128 axa_debug > AXA_DEBUG_TRACE,
1129 256*1024, true)) {
1130 case AXA_CONNECT_ERR:
1131 case AXA_CONNECT_TEMP:
1132 error_msg("%s", emsg.c);
1133 return (0);
1134 case AXA_CONNECT_DONE:
1135 break;
1136 case AXA_CONNECT_NOP:
1137 case AXA_CONNECT_USER:
1138 if (axa_debug >= AXA_DEBUG_TRACE) {
1139 clear_prompt();
1140 printf("send %s\n", emsg.c);
1141 }
1142 break;
1143 case AXA_CONNECT_INCOM:
1144 /* Wait here until the connection is complete or fails,
1145 * because user commands that would send AXA messages will
1146 * fail before the connection is complete. */
1147 while (AXA_CLIENT_OPENED(&client)
1148 && !AXA_CLIENT_CONNECTED(&client)) {
1149 if (interrupted) {
1150 disconnect(true);
1151 return (1);
1152 }
1153 io_wait(false, true, INT_MAX);
1154 }
1155 }
1156
1157 if (packet_counting)
1158 packet_count = packet_count_total;
1159
1160 gettimeofday(&connect_time, NULL);
1161 return (1);
1162 }
1163
1164 static int
out_cmd_pcap_if(const char * ifname)1165 out_cmd_pcap_if(const char *ifname)
1166 {
1167 char ether[32];
1168 char errbuf[PCAP_ERRBUF_SIZE];
1169 const struct ether_addr *eaddr;
1170 const char *p;
1171 int i;
1172
1173 memset(&out_mac, 0, sizeof(out_mac));
1174 p = strchr(ifname, '/');
1175 memset(ether, 0, sizeof(ether));
1176 if (p != NULL) {
1177 memcpy(ether, ifname, min(sizeof(ether), (size_t)(p-ifname)));
1178 ifname = p+1;
1179 }
1180
1181 out_pcap = pcap_create(ifname, errbuf);
1182 if (out_pcap == NULL) {
1183 error_msg("pcap_create(%s): %s", ifname, errbuf);
1184 return (0);
1185 }
1186 i = pcap_activate(out_pcap);
1187 if (i != 0) {
1188 error_msg("pcap_activate(%s): %s",
1189 ifname, pcap_geterr(out_pcap));
1190 pcap_close(out_pcap);
1191
1192 out_pcap = NULL;
1193 return (0);
1194 }
1195 out_pcap_datalink = pcap_datalink(out_pcap);
1196 switch (out_pcap_datalink) {
1197 case DLT_EN10MB:
1198 if (ether[0] != '\0') {
1199 /* ether_aton_r() is not available on all systems,
1200 * and ether_aton() is safe here. */
1201 eaddr = ether_aton(ether);
1202 if (eaddr != NULL) {
1203 out_mac.dst = *eaddr;
1204 } else if (ether_hostton(ether, &out_mac.dst) != 0) {
1205 error_msg("cannot convert \"%s\""
1206 " to an address; using 0:0:0:0:0:0",
1207 ether);
1208 memset(&out_mac, 0, sizeof(out_mac));
1209 }
1210 }
1211 out_mac.etype = htons(0x800);
1212 memcpy(out_buf, &out_mac, sizeof(out_mac));
1213 out_buf_base = sizeof(out_mac);
1214 break;
1215 case DLT_NULL:
1216 case DLT_LOOP:
1217 if (ether[0] != '\0')
1218 error_msg("ignoring MAC address \"%s\""
1219 " for loopback interface %s",
1220 ether, ifname);
1221 out_buf_base = sizeof(uint32_t);
1222 break;
1223 default:
1224 error_msg("cannot output to %s"
1225 " with unknown datalink type %d",
1226 ifname, out_pcap_datalink);
1227 pcap_close(out_pcap);
1228 return (0);
1229 }
1230
1231 return (1);
1232 }
1233
1234 static int
out_cmd(axa_tag_t tag AXA_UNUSED,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1235 out_cmd(axa_tag_t tag AXA_UNUSED, const char *arg,
1236 const cmd_tbl_entry_t *ce AXA_UNUSED)
1237 {
1238 u_long l;
1239 char *count, *p;
1240 int result;
1241
1242 if (arg[0] == '\0') {
1243 if (!out_on) {
1244 fputs("not forwarding messages\n", stdout);
1245 } else if (output_counting) {
1246 printf("forwarding %d of total %d messages to %s\n",
1247 output_count, output_count_total, out_addr);
1248 } else {
1249 printf("forwarding messages to %s\n", out_addr);
1250 }
1251 if (out_on_nmsg == true)
1252 printf("output buffering is %s\n",
1253 output_buffering == true ? "on" : "off");
1254 return (1);
1255 }
1256
1257 out_close(verbose > 0);
1258
1259 if (word_cmp(&arg, "off") || word_cmp(&arg, "stop"))
1260 return (1);
1261
1262 out_addr = axa_strdup(arg);
1263
1264 count = strpbrk(out_addr, AXA_WHITESPACE);
1265 if (count != NULL) {
1266 *count++ = '\0';
1267 count += strspn(count, AXA_WHITESPACE);
1268 l = strtoul(count, &p, 10);
1269 if (p == count) {
1270 out_close(false);
1271 return (-1);
1272 }
1273 p += strspn(p, AXA_WHITESPACE);
1274 if (*p != '\0') {
1275 out_close(false);
1276 return (-1);
1277 }
1278 output_counting = true;
1279 output_count = l;
1280 output_count_total = l;
1281 }
1282 output_errno = -1;
1283
1284 if (AXA_CLITCMP(out_addr, "pcap:")) {
1285 result = out_cmd_pcap_file(strchr(out_addr, ':')+1, false);
1286 } else if (AXA_CLITCMP(out_addr, "pcap-fifo:")) {
1287 result = out_cmd_pcap_file(strchr(out_addr, ':')+1, true);
1288 } else if (AXA_CLITCMP(out_addr, "pcap-if:")) {
1289 result = out_cmd_pcap_if(strchr(out_addr, ':')+1);
1290 } else if (AXA_CLITCMP(out_addr, "nmsg:")) {
1291 result = axa_open_nmsg_out(&emsg, &out_nmsg_output,
1292 &out_sock_type,
1293 strchr(out_addr, ':')+1,
1294 output_buffering);
1295 if (result <= 0)
1296 error_msg("%s", emsg.c);
1297 out_on_nmsg = true;
1298 } else {
1299 result = -1;
1300 }
1301
1302 if (result > 0) {
1303 out_on = true;
1304 } else {
1305 out_close(false);
1306 }
1307 return (result);
1308 }
1309
1310 static int
count_cmd(axa_tag_t tag AXA_UNUSED,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1311 count_cmd(axa_tag_t tag AXA_UNUSED, const char *arg,
1312 const cmd_tbl_entry_t *ce AXA_UNUSED)
1313 {
1314 u_long l;
1315 char *p;
1316
1317 if (arg[0] != '\0') {
1318 if (word_cmp(&arg, "off")) {
1319 if (packet_counting) {
1320 packet_counting = false;
1321 packet_count = 0;
1322 packet_count_total = 0;
1323 }
1324 } else {
1325 l = strtoul(arg, &p, 10);
1326 if (p == arg)
1327 return (-1);
1328 p += strspn(p, AXA_WHITESPACE);
1329 if (*p != '\0')
1330 return (-1);
1331 packet_counting = true;
1332 packet_count = l;
1333 packet_count_total = l;
1334 }
1335 return (1);
1336 }
1337
1338 count_print(true);
1339 return (1);
1340 }
1341
1342 static int
ciphers_cmd(axa_tag_t tag AXA_UNUSED,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1343 ciphers_cmd(axa_tag_t tag AXA_UNUSED, const char *arg,
1344 const cmd_tbl_entry_t *ce AXA_UNUSED)
1345 {
1346 const char *cipher;
1347
1348 if (arg[0] == '\0') {
1349 cipher = axa_tls_cipher_list(&emsg, NULL);
1350 if (cipher == NULL || *cipher == '\0')
1351 printf("next TLS cipher: \"\"\n");
1352 else
1353 printf("next TLS cipher: %s\n",
1354 cipher);
1355 if (client.io.tls_info != NULL)
1356 printf(" current: %s\n",
1357 client.io.tls_info);
1358
1359 } else if (axa_tls_cipher_list(&emsg, arg) == NULL) {
1360 error_msg("%s", emsg.c);
1361 return (0);
1362 }
1363 return (1);
1364 }
1365
1366 static int
nop_cmd(axa_tag_t tag,const char * arg AXA_UNUSED,const cmd_tbl_entry_t * ce AXA_UNUSED)1367 nop_cmd(axa_tag_t tag, const char *arg AXA_UNUSED,
1368 const cmd_tbl_entry_t *ce AXA_UNUSED)
1369 {
1370 return (srvr_send(tag, AXA_P_OP_NOP, NULL, 0));
1371 }
1372
1373 static int
mode_cmd(axa_tag_t tag AXA_UNUSED,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1374 mode_cmd(axa_tag_t tag AXA_UNUSED, const char *arg,
1375 const cmd_tbl_entry_t *ce AXA_UNUSED)
1376 {
1377 const char *setting;
1378
1379 setting = arg;
1380 if (setting[0] != '\0') {
1381 if (word_cmp(&setting, "sra")) {
1382 if (mode == RAD && AXA_CLIENT_CONNECTED(&client)) {
1383 printf(" can't change mode while connected to server\n");
1384 return (-1);
1385 }
1386 mode = SRA;
1387 } else if (word_cmp(&setting, "rad")) {
1388 if (mode == SRA && AXA_CLIENT_CONNECTED(&client)) {
1389 printf(" can't change mode while connected to server\n");
1390 return (-1);
1391 }
1392 mode = RAD;
1393 } else {
1394 return (-1);
1395 }
1396 }
1397
1398 if (verbose > 0 || arg[0] == '\0') {
1399 switch (mode) {
1400 case SRA:
1401 printf(" SRA mode\n");
1402 break;
1403 case RAD:
1404 printf(" RAD mode\n");
1405 break;
1406 case BOTH:
1407 default:
1408 AXA_FAIL("impossible mode");
1409 }
1410 }
1411 return (1);
1412 }
1413
1414 static int
sra_mode_cmd(axa_tag_t tag,const char * arg,const cmd_tbl_entry_t * ce)1415 sra_mode_cmd(axa_tag_t tag, const char *arg,
1416 const cmd_tbl_entry_t *ce)
1417 {
1418 word_cmp(&arg, "mode");
1419 if (arg[0] != '\0')
1420 return (-1);
1421 return (mode_cmd(tag, "sra", ce));
1422 }
1423
1424 static int
rad_mode_cmd(axa_tag_t tag,const char * arg,const cmd_tbl_entry_t * ce)1425 rad_mode_cmd(axa_tag_t tag, const char *arg,
1426 const cmd_tbl_entry_t *ce)
1427 {
1428 word_cmp(&arg, "mode");
1429 if (arg[0] != '\0')
1430 return (-1);
1431 return (mode_cmd(tag, "rad", ce));
1432 }
1433
1434 static int
user_cmd(axa_tag_t tag,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1435 user_cmd(axa_tag_t tag, const char *arg,
1436 const cmd_tbl_entry_t *ce AXA_UNUSED)
1437 {
1438 axa_p_user_t user;
1439
1440 if (strlen(arg) >= sizeof(user.name)) {
1441 error_msg("user name \"%s\" is too long", arg);
1442 return (0);
1443 }
1444 strncpy(user.name, arg, sizeof(user.name));
1445 return (srvr_send(tag, AXA_P_OP_USER, &user, sizeof(user)));
1446 }
1447
1448 static int
sra_watch_cmd(axa_tag_t tag,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1449 sra_watch_cmd(axa_tag_t tag, const char *arg,
1450 const cmd_tbl_entry_t *ce AXA_UNUSED)
1451 {
1452 axa_p_watch_t watch;
1453 size_t watch_len;
1454
1455 if (arg[0] == '\0' || word_cmp(&arg, "get") || word_cmp(&arg, "list"))
1456 return (srvr_send(tag, AXA_P_OP_WGET, NULL, 0));
1457
1458 if (tag == AXA_TAG_NONE) {
1459 error_msg("\"watch\" requires a tag");
1460 return (0);
1461 }
1462
1463 if (axa_parse_watch(&emsg, &watch, &watch_len, arg))
1464 return (srvr_send(tag, AXA_P_OP_WATCH, &watch, watch_len));
1465
1466 if (emsg.c[0] == '\0')
1467 return (-1);
1468 error_msg("%s", emsg.c);
1469 return (0);
1470 }
1471
1472 static int
rad_watch_cmd(axa_tag_t tag,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1473 rad_watch_cmd(axa_tag_t tag, const char *arg,
1474 const cmd_tbl_entry_t *ce AXA_UNUSED)
1475 {
1476 axa_p_watch_t watch;
1477 size_t watch_len;
1478
1479 if (tag == AXA_TAG_NONE) {
1480 error_msg("\"watch\" requires a tag");
1481 return (0);
1482 }
1483
1484 if (axa_parse_rad_watch(&emsg, &watch, &watch_len, arg))
1485 return (srvr_send(tag, AXA_P_OP_WATCH, &watch, watch_len));
1486
1487 if (emsg.c[0] == '\0')
1488 return (-1);
1489 error_msg("%s", emsg.c);
1490 return (0);
1491 }
1492
1493 static int
list_cmd(axa_tag_t tag,const char * arg,const cmd_tbl_entry_t * ce)1494 list_cmd(axa_tag_t tag, const char *arg, const cmd_tbl_entry_t *ce)
1495 {
1496 if (mode == RAD) {
1497 return (srvr_send(tag, AXA_P_OP_AGET, NULL, 0));
1498 }
1499
1500 if (word_cmp(&arg, "watches")
1501 || (arg[0] == '\0'
1502 && ce != NULL && strcmp(ce->cmd, "list watches") == 0))
1503 return (srvr_send(tag, AXA_P_OP_WGET, NULL, 0));
1504 if (word_cmp(&arg, "channels")
1505 || (arg[0] == '\0'
1506 && ce != NULL && (strcmp(ce->cmd, "list channels") == 0
1507 || strcmp(ce->cmd, "get channels") == 0)))
1508 return (srvr_send(tag, AXA_P_OP_CGET, NULL, 0));
1509
1510 return (-1);
1511 }
1512
1513 static int
delete_cmd(axa_tag_t tag,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1514 delete_cmd(axa_tag_t tag, const char *arg,
1515 const cmd_tbl_entry_t *ce AXA_UNUSED)
1516 {
1517 if (mode == SRA) {
1518 (void)word_cmp(&arg, "watches");
1519 } else {
1520 /* ignore "anomaly" but take "a" to mean "all" and so
1521 * do not match "a" as if it were "anomaly". */
1522 if (strcasecmp(arg, "a") != 0)
1523 (void)(word_cmp(&arg, "anomaly"));
1524 }
1525 if (word_cmp(&arg, "all"))
1526 return (srvr_send(tag, AXA_P_OP_ALL_STOP, NULL, 0));
1527 if (*arg == '\0') {
1528 if (tag == AXA_TAG_NONE)
1529 return (srvr_send(tag, AXA_P_OP_ALL_STOP, NULL, 0));
1530 return (srvr_send(tag, AXA_P_OP_STOP, NULL, 0));
1531 }
1532 return (-1);
1533 }
1534
1535 static int
channel_on_off(axa_tag_t tag,const char * arg,bool on)1536 channel_on_off(axa_tag_t tag, const char *arg, bool on)
1537 {
1538 axa_p_channel_t channel;
1539 axa_p_ch_t ch;
1540
1541 ch = 0;
1542 memset(&channel, 0, sizeof(channel));
1543 if (!axa_parse_ch(&emsg, &ch, arg, strlen(arg), true, true)) {
1544 error_msg("%s", emsg.c);
1545 return (0);
1546 }
1547 channel.ch = ch;
1548 channel.ch = AXA_H2P_CH(channel.ch);
1549 channel.on = on ? 1 : 0;
1550 return (srvr_send(tag, AXA_P_OP_CHANNEL, &channel, sizeof(channel)));
1551 }
1552
1553 static int
channel_cmd(axa_tag_t tag,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1554 channel_cmd(axa_tag_t tag, const char *arg,
1555 const cmd_tbl_entry_t *ce AXA_UNUSED)
1556 {
1557 axa_p_ch_buf_t arg1_buf, arg2_buf;
1558 const char *arg1, *arg2;
1559
1560 if (arg[0] == '\0' || word_cmp(&arg, "get") || word_cmp(&arg, "list"))
1561 return (srvr_send(tag, AXA_P_OP_CGET, NULL, 0));
1562
1563 memset(&arg1_buf, 0, sizeof(arg1_buf));
1564 memset(&arg2_buf, 0, sizeof(arg2_buf));
1565 if (0 > axa_get_token(arg1_buf.c, sizeof(arg1_buf),
1566 &arg, AXA_WHITESPACE)
1567 || 0 > axa_get_token(arg2_buf.c, sizeof(arg2_buf),
1568 &arg, AXA_WHITESPACE)
1569 || *arg != '\0')
1570 return (-1);
1571 arg1 = arg1_buf.c;
1572 arg2 = arg2_buf.c;
1573
1574 if (word_cmp(&arg1, "off") || word_cmp(&arg1, "stop"))
1575 return (channel_on_off(tag, arg2, false));
1576 if (word_cmp(&arg1, "on"))
1577 return (channel_on_off(tag, arg2, true));
1578
1579 if (word_cmp(&arg2, "off") || word_cmp(&arg2, "stop"))
1580 return (channel_on_off(tag, arg1, false));
1581 if (word_cmp(&arg2, "on"))
1582 return (channel_on_off(tag, arg1, true));
1583
1584 return (-1);
1585 }
1586
1587 static int
anom_cmd(axa_tag_t tag,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1588 anom_cmd(axa_tag_t tag, const char *arg,
1589 const cmd_tbl_entry_t *ce AXA_UNUSED)
1590 {
1591 axa_p_anom_t anom;
1592 size_t anom_len;
1593
1594 if (tag == AXA_TAG_NONE) {
1595 error_msg("\"anomaly\" requires a tag");
1596 return (0);
1597 }
1598
1599 if (axa_parse_anom(&emsg, &anom, &anom_len, arg))
1600 return (srvr_send(tag, AXA_P_OP_ANOM, &anom, anom_len));
1601
1602 if (emsg.c[0] == '\0')
1603 return (-1);
1604 return (0);
1605 }
1606
1607 static bool /* false=bad value */
get_rlimit(axa_cnt_t * rlimit,const char * word)1608 get_rlimit(axa_cnt_t *rlimit, const char *word)
1609 {
1610 unsigned long n;
1611 char *p;
1612
1613 if (*word == '\0' || strcmp("-", word) == 0) {
1614 *rlimit = AXA_H2P64(AXA_RLIMIT_NA);
1615 return (true);
1616 }
1617 if (word_cmp(&word, "MAXIMUM") || word_cmp(&word, "NEVER")
1618 || word_cmp(&word, "OFF") || word_cmp(&word, "NONE")) {
1619 *rlimit = AXA_H2P64(AXA_RLIMIT_OFF);
1620 return (true);
1621 }
1622 n = strtoul(word, &p, 10);
1623 if (*p != '\0' || n < 1 || n > AXA_RLIMIT_MAX)
1624 return (false);
1625
1626 *rlimit = AXA_H2P64(n);
1627 return (true);
1628 }
1629
1630 static int
rlimits_cmd(axa_tag_t tag,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1631 rlimits_cmd(axa_tag_t tag, const char *arg,
1632 const cmd_tbl_entry_t *ce AXA_UNUSED)
1633 {
1634 char sec_buf[32];
1635 char report_secs_buf[32];
1636 axa_p_opt_t opt;
1637 axa_cnt_t rlimit;
1638
1639 rlimit = 0;
1640 memset(&opt, 0, sizeof(opt));
1641 opt.type = AXA_P_OPT_RLIMIT;
1642
1643 /* Ignore " limits" if present. */
1644 word_cmp(&arg, "limits");
1645
1646 if (0 > axa_get_token(sec_buf, sizeof(sec_buf),
1647 &arg, AXA_WHITESPACE)
1648 || 0 > axa_get_token(report_secs_buf, sizeof(report_secs_buf),
1649 &arg, AXA_WHITESPACE)
1650 || *arg != '\0')
1651 return (-1);
1652
1653 if (!get_rlimit(&rlimit, sec_buf))
1654 return (-1);
1655 else
1656 opt.u.rlimit.max_pkts_per_sec = rlimit;
1657 if (!get_rlimit(&rlimit, report_secs_buf))
1658 return (-1);
1659 else
1660 opt.u.rlimit.report_secs = rlimit;
1661
1662 opt.u.rlimit.cur_pkts_per_sec = AXA_RLIMIT_NA;
1663 return (srvr_send(tag, AXA_P_OP_OPT, &opt,
1664 sizeof(opt) - sizeof(opt.u) + sizeof(opt.u.rlimit)));
1665 }
1666
1667 static int
sample_cmd(axa_tag_t tag,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1668 sample_cmd(axa_tag_t tag, const char *arg,
1669 const cmd_tbl_entry_t *ce AXA_UNUSED)
1670 {
1671 axa_p_opt_t opt;
1672 double d;
1673 char *p;
1674
1675 memset(&opt, 0, sizeof(opt));
1676 opt.type = AXA_P_OPT_SAMPLE;
1677
1678 if (*arg == '\0') {
1679 opt.u.sample = AXA_H2P32(AXA_P_OPT_SAMPLE_REQ);
1680
1681 } else {
1682 d = strtod(arg, &p);
1683 if (*p != '\0' && *p != '%' && p[1] != '\0')
1684 return (-1);
1685 if (d <= 0.0 || d > 100.0) {
1686 error_msg("\"%s\" is an invalid sampling rate", arg);
1687 return (0);
1688 }
1689 opt.u.sample = AXA_H2P32(d * AXA_P_OPT_SAMPLE_SCALE);
1690 }
1691
1692 return (srvr_send(tag, AXA_P_OP_OPT, &opt,
1693 sizeof(opt) - sizeof(opt.u) + sizeof(opt.u.sample)));
1694 }
1695
1696 static int
sndbuf_cmd(axa_tag_t tag,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1697 sndbuf_cmd(axa_tag_t tag, const char *arg,
1698 const cmd_tbl_entry_t *ce AXA_UNUSED)
1699 {
1700 axa_p_opt_t opt;
1701 char *p;
1702
1703 memset(&opt, 0, sizeof(opt));
1704 opt.type = AXA_P_OPT_SNDBUF;
1705
1706 if (client.io.type != AXA_IO_TYPE_TCP
1707 && client.io.type != AXA_IO_TYPE_TLS) {
1708 error_msg("cannot change the buffer size on %s connections",
1709 axa_io_type_to_str(client.io.type));
1710 return (0);
1711 }
1712
1713 if (*arg == '\0') {
1714 opt.u.bufsize = AXA_H2P32(AXA_P_OPT_SNDBUF_REQ);
1715
1716 } else {
1717 opt.u.bufsize = strtoul(arg, &p, 0);
1718 if (*p != '\0')
1719 return (-1);
1720 if (opt.u.bufsize < AXA_P_OPT_SNDBUF_MIN) {
1721 error_msg("invalid output window size of %s", arg);
1722 return (0);
1723 }
1724 opt.u.bufsize = AXA_H2P32(opt.u.bufsize);
1725 }
1726
1727 return (srvr_send(tag, AXA_P_OP_OPT, &opt,
1728 sizeof(opt) - sizeof(opt.u) + sizeof(opt.u.bufsize)));
1729 }
1730
1731 static int
pause_cmd(axa_tag_t tag,const char * arg AXA_UNUSED,const cmd_tbl_entry_t * ce AXA_UNUSED)1732 pause_cmd(axa_tag_t tag, const char *arg AXA_UNUSED,
1733 const cmd_tbl_entry_t *ce AXA_UNUSED)
1734 {
1735 return (srvr_send(tag, AXA_P_OP_PAUSE, NULL, 0));
1736 }
1737
1738 static int
go_cmd(axa_tag_t tag,const char * arg AXA_UNUSED,const cmd_tbl_entry_t * ce AXA_UNUSED)1739 go_cmd(axa_tag_t tag, const char *arg AXA_UNUSED,
1740 const cmd_tbl_entry_t *ce AXA_UNUSED)
1741 {
1742 return (srvr_send(tag, AXA_P_OP_GO, NULL, 0));
1743 }
1744
1745 static int
sleep_cmd(axa_tag_t tag AXA_UNUSED,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1746 sleep_cmd(axa_tag_t tag AXA_UNUSED, const char *arg,
1747 const cmd_tbl_entry_t *ce AXA_UNUSED)
1748 {
1749 double s;
1750 char *p;
1751
1752 s = strtod(arg, &p);
1753 if (*p != '\0' || s < 0.001 || s > 1000)
1754 return (-1);
1755 io_wait(false, false, s*1000.0);
1756 return (1);
1757 }
1758
1759 static int
trace_cmd(axa_tag_t tag,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1760 trace_cmd(axa_tag_t tag, const char *arg,
1761 const cmd_tbl_entry_t *ce AXA_UNUSED)
1762 {
1763 axa_p_opt_t opt;
1764 u_long l;
1765 char *p;
1766
1767 l = strtoul(arg, &p, 10);
1768 if (*p != '\0') {
1769 if (strcasecmp(arg, "off") == 0)
1770 l = 0;
1771 else if (strcasecmp(arg, "on") == 0)
1772 l = AXA_DEBUG_TRACE;
1773 else
1774 return (-1);
1775 }
1776 memset(&opt, 0, sizeof(opt));
1777 opt.type = AXA_P_OPT_TRACE;
1778 opt.u.trace = AXA_H2P32(l);
1779 return (srvr_send(tag, AXA_P_OP_OPT, &opt,
1780 sizeof(opt) - sizeof(opt.u) + sizeof(opt.u.trace)));
1781 }
1782
1783 static int
acct_cmd(axa_tag_t tag,const char * arg AXA_UNUSED,const cmd_tbl_entry_t * ce AXA_UNUSED)1784 acct_cmd(axa_tag_t tag, const char *arg AXA_UNUSED,
1785 const cmd_tbl_entry_t *ce AXA_UNUSED)
1786 {
1787 return (srvr_send(tag, AXA_P_OP_ACCT, NULL, 0));
1788 }
1789
1790 static int
radunit_cmd(axa_tag_t tag,const char * arg AXA_UNUSED,const cmd_tbl_entry_t * ce AXA_UNUSED)1791 radunit_cmd(axa_tag_t tag, const char *arg AXA_UNUSED,
1792 const cmd_tbl_entry_t *ce AXA_UNUSED)
1793 {
1794 return (srvr_send(tag, AXA_P_OP_RADU, NULL, 0));
1795 }
1796
1797 static int
nmsg_zlib_cmd(axa_tag_t tag AXA_UNUSED,const char * arg AXA_UNUSED,const cmd_tbl_entry_t * ce AXA_UNUSED)1798 nmsg_zlib_cmd(axa_tag_t tag AXA_UNUSED, const char *arg AXA_UNUSED,
1799 const cmd_tbl_entry_t *ce AXA_UNUSED)
1800 {
1801 if (out_on == false) {
1802 printf(" output mode not enabled\n");
1803 return (0);
1804 }
1805 if (out_on_nmsg == false) {
1806 printf(" output mode not emitting nmsgs\n");
1807 return (0);
1808 }
1809 if (nmsg_zlib == false) {
1810 nmsg_zlib = true;
1811 nmsg_output_set_zlibout(out_nmsg_output, true);
1812 printf(" enabled\n");
1813 }
1814 else if (nmsg_zlib == true) {
1815 nmsg_zlib = false;
1816 nmsg_output_set_zlibout(out_nmsg_output, false);
1817 printf(" disabled\n");
1818 }
1819 return (1);
1820 }
1821
1822 static int
stats_req_cmd(axa_tag_t tag,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1823 stats_req_cmd(axa_tag_t tag, const char *arg,
1824 const cmd_tbl_entry_t *ce AXA_UNUSED)
1825 {
1826 char *p;
1827 uint32_t sn;
1828 _axa_p_stats_req_t stats_req;
1829
1830 memset(&stats_req, 0, sizeof (stats_req));
1831
1832 stats_req.version = _AXA_STATS_VERSION;
1833
1834 /* no argument == summary */
1835 if (*arg == '\0') {
1836 printf(" sending stats summary request to server\n");
1837 stats_req.type = AXA_P_STATS_M_M_SUM;
1838 }
1839 /* all == everything */
1840 else if (word_cmp(&arg,"all")) {
1841 printf(" sending stats all request to server\n");
1842 stats_req.type = AXA_P_STATS_M_M_ALL;
1843 }
1844 /* username or serial number */
1845 else {
1846 sn = strtoul(arg, &p, 0);
1847 if (*p != '\0') {
1848 printf(" sending stats request to server for"
1849 " user \"%s\"\n", arg);
1850 strlcpy(stats_req.user.name, arg,
1851 sizeof(stats_req.user.name));
1852 stats_req.type = AXA_P_STATS_M_M_U;
1853 }
1854 else {
1855 stats_req.sn = AXA_H2P32(sn);
1856 stats_req.type = AXA_P_STATS_M_M_SN;
1857 printf(" sending stats request to server for"
1858 " serial number \"%u\"\n",
1859 stats_req.sn);
1860 }
1861 }
1862 return (srvr_send(tag, _AXA_P_OP_STATS_REQ, &stats_req,
1863 sizeof (stats_req)));
1864 }
1865
1866 static int
kill_cmd(axa_tag_t tag,const char * arg,const cmd_tbl_entry_t * ce AXA_UNUSED)1867 kill_cmd(axa_tag_t tag, const char *arg,
1868 const cmd_tbl_entry_t *ce AXA_UNUSED)
1869 {
1870 char *p;
1871 uint32_t sn;
1872 _axa_p_kill_t kill;
1873
1874 if (*arg == '\0') {
1875 error_msg("kill command requires a valid user name or"
1876 " serial number");
1877 return (0);
1878 }
1879
1880 memset(&kill, 0, sizeof (kill));
1881 sn = strtoul(arg, &p, 0);
1882 if (*p != '\0') {
1883 printf(" sending kill request to server"
1884 " (kill all sessions belonging to %s)...\n",
1885 arg);
1886 strlcpy(kill.user.name, arg, sizeof(kill.user.name));
1887 kill.mode = AXA_P_KILL_M_U;
1888 }
1889 else {
1890 printf(" sending kill request to server"
1891 " (kill session serial number %d)...\n", sn);
1892 kill.sn = AXA_H2P32(sn);
1893 kill.mode = AXA_P_KILL_M_SN;
1894 }
1895
1896 return (srvr_send(tag, _AXA_P_OP_KILL_REQ, &kill, sizeof (kill)));
1897 }
1898
1899 static int
out_cmd_pcap_file(const char * addr,bool want_fifo)1900 out_cmd_pcap_file(const char *addr, bool want_fifo)
1901 {
1902 FILE *f;
1903 struct stat sb;
1904 bool have_file, have_fifo;
1905
1906 if (*addr == '\0')
1907 return (-1);
1908
1909 if (0 <= stat(addr, &sb)) {
1910 have_file = true;
1911 have_fifo = S_ISFIFO(sb.st_mode);
1912 } else {
1913 if (errno != ENOENT) {
1914 error_msg("stat(%s): %s", addr, strerror(errno));
1915 return (0);
1916 }
1917 have_file = false;
1918 have_fifo = false;
1919 }
1920
1921 if (want_fifo && !have_fifo) {
1922 if (have_file) {
1923 error_msg("\"%s\" exists but is not a FIFO", addr);
1924 return (0);
1925 }
1926 if (0 > mkfifo(addr, 0600)) {
1927 error_msg("mkfifo(%s): %s", addr, strerror(errno));
1928 return (0);
1929 }
1930 have_fifo = true;
1931 }
1932
1933 /* Create the stdio FILE manually to avoid blocking in the
1934 * libpcap fopen() when the file is a pre-existing FIFO. */
1935 out_fd = open(addr, O_RDWR|O_CREAT|O_TRUNC|O_NONBLOCK|O_CLOEXEC, 0666);
1936 if (out_fd < 0) {
1937 error_msg("open(%s): %s", addr, strerror(errno));
1938 return (0);
1939 }
1940
1941 /* drain old bits from what might be a FIFO */
1942 if (have_fifo) {
1943 ssize_t rlen;
1944 size_t n;
1945
1946 n = 0;
1947 for (;;) {
1948 rlen = read(out_fd, out_buf, sizeof(out_buf));
1949 if (rlen < 0) {
1950 if (errno == EAGAIN || errno == EWOULDBLOCK
1951 || errno == EINTR)
1952 break;
1953 error_msg("read(%s): %s",
1954 addr, strerror(errno));
1955 close(out_fd);
1956 return (0);
1957 }
1958 if (rlen == 0)
1959 break;
1960 if (++n >= (1024*1024)/sizeof(out_buf)) {
1961 error_msg("\"%s\" seems to be an active fifo",
1962 addr);
1963 close(out_fd);
1964 return (0);
1965 }
1966 }
1967 }
1968
1969 f = fdopen(out_fd, "w");
1970 if (f == NULL) {
1971 error_msg("fdopen(%s): %s", addr, strerror(errno));
1972 close(out_fd);
1973 return (0);
1974 }
1975 out_pcap = pcap_open_dead(DLT_RAW, AXA_P_WHIT_IP_MAX);
1976 if (out_pcap == NULL) {
1977 error_msg("pcap_open_dead() failed");
1978 fclose(f);
1979 return (0);
1980 }
1981 out_pcap_dumper = pcap_dump_fopen(out_pcap, f);
1982 if (out_pcap_dumper == NULL) {
1983 error_msg("pcap_dump_open(%s): %s",
1984 addr, pcap_geterr(out_pcap));
1985 fclose(f);
1986 return (0);
1987 }
1988
1989 /* Cajole the pcap library into writing its header, but we will
1990 * write the packets themselves to allow non-blocking output
1991 * to tcpdump. */
1992 if (0 > pcap_dump_flush(out_pcap_dumper)) {
1993 error_msg("pcap_dump_flush(forward): %s",
1994 pcap_geterr(out_pcap));
1995 out_close(false);
1996 return (0);
1997 }
1998
1999 return (1);
2000 }
2001
2002 static int
buffer_cmd(axa_tag_t tag AXA_UNUSED,const char * arg AXA_UNUSED,const cmd_tbl_entry_t * ce AXA_UNUSED)2003 buffer_cmd(axa_tag_t tag AXA_UNUSED, const char *arg AXA_UNUSED,
2004 const cmd_tbl_entry_t *ce AXA_UNUSED)
2005 {
2006 if (out_on == false) {
2007 printf(" output mode not enabled\n");
2008 return (0);
2009 }
2010 if (out_on_nmsg == false) {
2011 printf(" output mode not emitting nmsgs\n");
2012 return (0);
2013 }
2014 if (output_buffering == false) {
2015 output_buffering = true;
2016 nmsg_output_set_buffered(out_nmsg_output, true);
2017 printf(" enabled\n");
2018 }
2019 else if (output_buffering == true) {
2020 output_buffering = false;
2021 nmsg_output_set_buffered(out_nmsg_output, false);
2022 printf(" disabled\n");
2023 }
2024 return (1);
2025 }
2026
2027 bool /* true=ok false=bad command */
do_cmds(const char * str)2028 do_cmds(const char *str)
2029 {
2030 char buf[2048];
2031 const char *cur;
2032 ssize_t cur_len;
2033 axa_tag_t tag;
2034
2035 for (;;) {
2036 str += strspn(str, AXA_WHITESPACE";");
2037 if (*str == '#' || *str == '\0')
2038 return (true);
2039
2040 /* Get the next tag, command, and args from the buffer. */
2041 cur_len = axa_get_token(buf, sizeof(buf), &str, ";\r\n");
2042 /* command too long */
2043 if (0 > cur_len)
2044 return (false);
2045
2046 cur = buf;
2047 if (*cur == '#' || *cur == '\0')
2048 return (true);
2049
2050 /* Trim trailing whitespace. */
2051 while (cur_len > 0
2052 && strchr(AXA_WHITESPACE, buf[--cur_len]) != NULL) {
2053 buf[cur_len] = '\0';
2054 }
2055
2056 tag = cmd_tag(&cur);
2057
2058 /* Ignore null command with a tag. */
2059 if (*cur == '\0')
2060 continue;
2061
2062 if (!cmd(tag, cur))
2063 return (false);
2064 }
2065 }
2066
2067 int
2068 #if LIBEDIT_IS_UNICODE
getcfn(EditLine * e AXA_UNUSED,wchar_t * buf)2069 getcfn(EditLine *e AXA_UNUSED, wchar_t *buf)
2070 #else
2071 getcfn(EditLine *e AXA_UNUSED, char *buf)
2072 #endif
2073 {
2074 int i;
2075 char c = '\0';
2076
2077 /* Wait until the user types something or a redisplay is faked */
2078 for (;;) {
2079 io_wait(true, false, INT_MAX);
2080 if (interrupted) {
2081 if (terminated)
2082 stop(1);
2083 /* Return with '\0' in the buffer to tell editline(3)
2084 * to return immediately
2085 * so that the interrupt can be acknowledged. */
2086 el_set(el_e, EL_UNBUFFERED, 1);
2087 #if LIBEDIT_IS_UNICODE
2088 *buf = btowc('\0');
2089 #else
2090 *buf = '\0';
2091 #endif
2092 return (1);
2093 }
2094
2095 /* After EOF from the input,
2096 * wait until the server connection breaks
2097 * or until we cannot output. */
2098 if (in_file_cur < 0) {
2099 if (!AXA_CLIENT_OPENED(&client))
2100 stop(EX_OK);
2101 continue;
2102 }
2103
2104 AXA_ASSERT(in_file_cur == 0);
2105 /* Restore the prompt before echoing user's input. */
2106 if (prompt_cleared.tv_sec != 0)
2107 reprompt();
2108 i = read(STDIN_FILENO, &c, 1);
2109 if (i == 1) {
2110 gettimeofday(&cmd_input, NULL);
2111 #if LIBEDIT_IS_UNICODE
2112 *buf = btowc(c);
2113 #else
2114 *buf = c;
2115 #endif
2116 return (1);
2117 }
2118 close(STDIN_FILENO);
2119 --in_file_cur;
2120 }
2121 }
2122
2123 void AXA_NORETURN
usage(void)2124 usage(void)
2125 {
2126 const char *sra = "SIE Remote Access Tool (sratool)\n";
2127 const char *rad = "Real-time Anomaly Detection Tool (radtool)\n";
2128
2129 printf("%s", mode == SRA ? sra : rad);
2130 printf("(c) 2013-2018 Farsight Security, Inc.\n");
2131 printf("%s [options] [commands]\n", axa_prog_name);
2132 printf("[-c file]\t\tspecify commands file\n");
2133 printf("[-d]\t\t\tincrement debug level, -ddd > -dd > -d\n");
2134 printf("[-E ciphers]\t\tuse these TLS ciphers\n");
2135 printf("[-F file]\t\tspecify AXA fields file\n");
2136 printf("[-n file]\t\tspecify AXA config file\n");
2137 printf("[-N]\t\t\tdisable command-line prompt\n");
2138 printf("[-S dir]\t\tspecify TLS certificates directory\n");
2139 printf("[-V]\t\t\tprint version and quit\n");
2140 printf("[commands]\t\tquoted string of commands to execute\n");
2141 exit(EX_USAGE);
2142 }
2143