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