1 /*
2  * AXA protocol utilities
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 <axa/axa_endian.h>
20 #include <axa/wire.h>
21 
22 #include <nmsg.h>
23 #include <wdns.h>
24 
25 #include <arpa/inet.h>
26 #include <arpa/nameser.h>
27 #include <errno.h>
28 #include <limits.h>
29 #include <netinet/ip.h>
30 #include <netinet/ip6.h>
31 #define __FAVOR_BSD			/* for Debian tcp.h and udp.h */
32 #include <netinet/udp.h>
33 #include <netinet/tcp.h>
34 #include <signal.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #ifdef __linux
38 #include <bsd/string.h>			/* for strlcpy() */
39 #include <time.h>			/* for localtime() and strftime() */
40 #endif
41 #include <sys/uio.h>
42 #include <sys/wait.h>
43 #include <unistd.h>
44 
45 
46 /* Point to the value string after "ip=", "dns=", or "ch=".
47  * Allow whitespace around '=' or instead of '='.
48  */
49 static const char *
get_value(const char * arg,const char * type,size_t type_len)50 get_value(const char *arg,
51 	  const char *type, size_t type_len)	/* "ip", "dns", or "ch" */
52 {
53 	size_t sep_len;
54 
55 	if (strncasecmp(arg, type, type_len) != 0)
56 		return (NULL);
57 	arg += strlen(type);
58 	sep_len = strspn(arg, AXA_WHITESPACE);
59 	arg += sep_len;
60 	if (*arg == '=') {
61 		++arg;
62 		arg += strspn(arg, AXA_WHITESPACE);
63 	} else if (sep_len == 0) {
64 		return (NULL);
65 	}
66 	return (arg);
67 }
68 
69 /*
70  * Parse an AXA watch definition.
71  *	false=problem
72  *	    with emsg->c a relevant error message except when the watch
73  *	    makes no sense.  In that case, emsg->c[0] == '\0'.
74  */
75 bool
axa_parse_watch(axa_emsg_t * emsg,axa_p_watch_t * watch,size_t * watch_len,const char * arg)76 axa_parse_watch(axa_emsg_t *emsg,
77 		axa_p_watch_t *watch,	/* parsed result */
78 		size_t *watch_len,
79 		const char *arg)	/* null terminated input */
80 {
81 	const char *value;
82 	int prefix;
83 	axa_socku_t su;
84 	axa_p_ch_t ch;
85 	wdns_name_t name;
86 
87 	memset(watch, 0, sizeof(*watch));
88 	*watch_len = sizeof(*watch) - sizeof(watch->pat);
89 
90 	if ((value = get_value(arg, "ip", sizeof("ip")-1)) != NULL) {
91 		if (*value == '\0') {
92 			axa_pemsg(emsg, "missing IP address");
93 			return (false);
94 		}
95 		prefix = axa_str_to_cidr(emsg, &su, value);
96 		if (prefix <= 0)
97 			return (false);
98 		watch->prefix = prefix;
99 		if (su.sa.sa_family == AF_INET) {
100 			watch->type = AXA_P_WATCH_IPV4;
101 			if (watch->prefix < 32)
102 				watch->flags |= AXA_P_WATCH_FG_WILD;
103 			watch->pat.addr = su.ipv4.sin_addr;
104 			/* Be conservative in what we send by not trimming
105 			 * the address. */
106 			*watch_len += sizeof(watch->pat.addr);
107 		} else {
108 			watch->type = AXA_P_WATCH_IPV6;
109 			if (watch->prefix < 128)
110 				watch->flags |= AXA_P_WATCH_FG_WILD;
111 			watch->pat.addr6 = su.ipv6.sin6_addr;
112 			/* Be conservative in what we send by not trimming
113 			 * the address. */
114 			*watch_len += sizeof(watch->pat.addr6);
115 		}
116 		return (true);
117 	}
118 
119 	if ((value = get_value(arg, "dns", sizeof("dns")-1)) != NULL) {
120 		watch->type = AXA_P_WATCH_DNS;
121 		if (*value == '*') {
122 			watch->flags |= AXA_P_WATCH_FG_WILD;
123 			if (*++value == '.' && value[1] != '\0')
124 				++value;
125 		}
126 		if (*value == '\0') {
127 			axa_pemsg(emsg, "missing domain name");
128 			return (false);
129 		}
130 		name.data = NULL;
131 		if (wdns_str_to_name(value, &name) != wdns_res_success) {
132 			axa_pemsg(emsg, "invalid DNS name \"%s\"", value);
133 			return (false);
134 		}
135 		memcpy(watch->pat.dns, name.data, name.len);
136 		*watch_len += name.len;
137 		free(name.data);
138 		return (true);
139 	}
140 
141 	if ((value = get_value(arg, AXA_OP_CH_PREFIX,
142 			       sizeof(AXA_OP_CH_PREFIX)-1)) != NULL) {
143 		if (*value == '\0') {
144 			axa_pemsg(emsg, "missing channel");
145 			return (false);
146 		}
147 		watch->type = AXA_P_WATCH_CH;
148 		if (!axa_parse_ch(emsg, &ch,
149 				  value, strlen(value), false, true))
150 			return (false);
151 		watch->pat.ch = AXA_H2P_CH(ch);
152 		*watch_len += sizeof(watch->pat.ch);
153 		return (true);
154 	}
155 
156 	if (AXA_CLITCMP(arg, "errors")) {
157 		arg += sizeof("errors")-1;
158 		arg += strspn(arg, AXA_WHITESPACE);
159 		if (*arg == '\0') {
160 			watch->type = AXA_P_WATCH_ERRORS;
161 			return (true);
162 		}
163 	}
164 	if (AXA_CLITCMP(arg, "error")) {
165 		arg += sizeof("error")-1;
166 		arg += strspn(arg, AXA_WHITESPACE);
167 		if (*arg == '\0') {
168 			watch->type = AXA_P_WATCH_ERRORS;
169 			return (true);
170 		}
171 	}
172 
173 	/* Let the caller handle nonsense watches */
174 	emsg->c[0] = '\0';
175 	return (false);
176 }
177 
178 static bool
get_flag(axa_p_watch_t * watch,u_int bit,char ** strp,const char * flag,size_t flag_len)179 get_flag(axa_p_watch_t *watch, u_int bit,
180 	 char **strp, const char *flag, size_t flag_len)
181 {
182 	char *str;
183 
184 	str = *strp;
185 	if (strncasecmp(str, flag, flag_len) != 0)
186 		return (false);
187 	str += flag_len;
188 	if (*str == ',') {
189 		++str;
190 	} else if (*str != ')') {
191 		return (false);
192 	}
193 	watch->flags |= bit;
194 	*strp = str;
195 	return (true);
196 }
197 
198 /*
199  * Parse an RAD watch definition.
200  *	An empty emsg->c indicates an unrecognized type of watch.
201  */
202 bool
axa_parse_rad_watch(axa_emsg_t * emsg,axa_p_watch_t * watch,size_t * watch_len,const char * arg)203 axa_parse_rad_watch(axa_emsg_t *emsg,
204 		    axa_p_watch_t *watch,   /* parsed result */
205 		    size_t *watch_len,
206 		    const char *arg)	/* null terminated */
207 {
208 	char *str, *flags;
209 
210 	str = axa_strdup(arg);
211 	flags = strchr(str, '(');
212 	if (flags != NULL)
213 		*flags++ = '\0';
214 
215 	if (!axa_parse_watch(emsg, watch, watch_len, str)) {
216 		free(str);
217 		return (false);
218 	}
219 
220 	switch ((axa_p_watch_type_t)watch->type) {
221 	case AXA_P_WATCH_IPV4:
222 	case AXA_P_WATCH_IPV6:
223 	case AXA_P_WATCH_DNS:
224 		break;
225 	case AXA_P_WATCH_CH:
226 		axa_pemsg(emsg, "channel watches not available");
227 		free(str);
228 		return (false);
229 	case AXA_P_WATCH_ERRORS:
230 		axa_pemsg(emsg, "error watches not available");
231 		free(str);
232 		return (false);
233 	default:
234 		AXA_FAIL("impossible message type");
235 	}
236 
237 	if (flags != NULL && *flags != '\0') {
238 		do {
239 			if (get_flag(watch, AXA_P_WATCH_FG_SHARED,
240 				     &flags, AXA_P_WATCH_STR_SHARED,
241 				     sizeof(AXA_P_WATCH_STR_SHARED)-1))
242 				continue;
243 			axa_pemsg(emsg, "unrecognized flag \"(%s\"", flags);
244 			free(str);
245 			return (false);
246 		} while (strcmp(flags, ")") != 0);
247 	}
248 	free(str);
249 	return (true);
250 }
251 
252 /* Parse an AXA anomaly detection module definition.
253  *	false=problem
254  *	    emsg->c is a relevant error message except when the watch
255  *	    makes no sense.  In that case, emsg->c[0] == '\0'. */
256 bool
axa_parse_anom(axa_emsg_t * emsg,axa_p_anom_t * anom,size_t * anom_len,const char * arg)257 axa_parse_anom(axa_emsg_t *emsg,
258 	       axa_p_anom_t *anom,	/* parsed result */
259 	       size_t *anom_len,	/* parsed result length */
260 	       const char *arg)		/* null terminated input */
261 {
262 	const char *parms;
263 	size_t an_len, parms_len;
264 
265 	memset(anom, 0, sizeof(*anom));
266 
267 	/* require "name[ parameters]" */
268 	if (arg[0] == '\0') {
269 		axa_pemsg(emsg, "missing name");
270 		return (false);
271 	}
272 	parms = strpbrk(arg, AXA_WHITESPACE);
273 	if (parms == NULL) {
274 		an_len = strlen(arg);
275 		parms = arg+an_len;
276 	} else {
277 		an_len = parms - arg;
278 	}
279 	if (an_len >= sizeof(anom->an)) {
280 		axa_pemsg(emsg, "name \"%.*s\" too long",
281 			  (int)an_len, arg);
282 		return (false);
283 	}
284 	memcpy(&anom->an, arg, an_len);
285 
286 	parms += strspn(parms, AXA_WHITESPACE);
287 	parms_len = strlen(parms)+1;
288 	if (parms_len >= sizeof(anom->parms)) {
289 		axa_pemsg(emsg, "parameters \"%s\" too long", parms);
290 		return (false);
291 	}
292 	memcpy(&anom->parms, parms, parms_len);
293 	*anom_len = sizeof(*anom) - sizeof(anom->parms) + parms_len;
294 
295 	return (true);
296 }
297 
298 const char *
axa_tag_to_str(char * buf,size_t buf_len,axa_tag_t tag)299 axa_tag_to_str(char *buf, size_t buf_len,   /* should be AXA_TAG_STRLEN */
300 	       axa_tag_t tag)
301 {
302 	if (tag == AXA_TAG_NONE)
303 		strlcpy(buf, "*", buf_len);
304 	else
305 		snprintf(buf, buf_len, "%d", tag);
306 	return (buf);
307 }
308 
309 const char *
axa_op_to_str(char * buf,size_t buflen,axa_p_op_t op)310 axa_op_to_str(char *buf, size_t buflen,	/* should be AXA_P_OP_STRLEN */
311 	      axa_p_op_t op)
312 {
313 	switch (op) {
314 	case AXA_P_OP_HELLO:	strlcpy(buf, "HELLO",		buflen); break;
315 	case AXA_P_OP_NOP:	strlcpy(buf, "NOP",		buflen); break;
316 	case AXA_P_OP_OK:	strlcpy(buf, "OK",		buflen); break;
317 	case AXA_P_OP_ERROR:	strlcpy(buf, "ERROR",		buflen); break;
318 	case AXA_P_OP_WHIT:	strlcpy(buf, "WATCH HIT",	buflen); break;
319 	case AXA_P_OP_AHIT:	strlcpy(buf, "ANOMALY HIT",	buflen); break;
320 	case AXA_P_OP_MISSED:	strlcpy(buf, "MISSED",		buflen); break;
321 	case AXA_P_OP_WLIST:	strlcpy(buf, "WATCH LIST",	buflen); break;
322 	case AXA_P_OP_ALIST:	strlcpy(buf, "ANOMALY LIST",	buflen); break;
323 	case AXA_P_OP_CLIST:	strlcpy(buf, "CHANNEL LIST",	buflen); break;
324 	case AXA_P_OP_MISSED_RAD: strlcpy(buf, "RAD MISSED",	buflen); break;
325 
326 	case AXA_P_OP_USER:	strlcpy(buf, "USER",		buflen); break;
327 	case AXA_P_OP_JOIN:	strlcpy(buf, "JOIN",		buflen); break;
328 	case AXA_P_OP_PAUSE:	strlcpy(buf, "PAUSE",		buflen); break;
329 	case AXA_P_OP_GO:	strlcpy(buf, "GO",		buflen); break;
330 	case AXA_P_OP_WATCH:	strlcpy(buf, "WATCH",		buflen); break;
331 	case AXA_P_OP_WGET:	strlcpy(buf, "WATCH GET",	buflen); break;
332 	case AXA_P_OP_ANOM:	strlcpy(buf, "ANOMALY",		buflen); break;
333 	case AXA_P_OP_AGET:	strlcpy(buf, "ANOMALY GET",	buflen); break;
334 	case AXA_P_OP_STOP:	strlcpy(buf, "STOP",		buflen); break;
335 	case AXA_P_OP_ALL_STOP:	strlcpy(buf, "ALL STOP",	buflen); break;
336 	case AXA_P_OP_CHANNEL:	strlcpy(buf, "CHANNEL ON/OFF",	buflen); break;
337 	case AXA_P_OP_CGET:	strlcpy(buf, "CHANNEL GET",	buflen); break;
338 	case AXA_P_OP_OPT:	strlcpy(buf, "OPTION",		buflen); break;
339 	case AXA_P_OP_ACCT:	strlcpy(buf, "ACCOUNTING",	buflen); break;
340 	case AXA_P_OP_RADU:	strlcpy(buf, "RAD UNITS GET",	buflen); break;
341 	case AXA_P_OP_MGMT_GET:	strlcpy(buf, "MGMT GET",	buflen); break;
342 	case AXA_P_OP_MGMT_GETRSP:strlcpy(buf, "MGMT GET RSP",	buflen); break;
343 	case _AXA_P_OP_KILL_REQ:strlcpy(buf, "KILL REQ",	buflen); break;
344 	case _AXA_P_OP_KILL_RSP:strlcpy(buf, "KILL RSP",	buflen); break;
345 	case _AXA_P_OP_STATS_REQ:strlcpy(buf,"STATS REQ",	buflen); break;
346 	case _AXA_P_OP_STATS_RSP:strlcpy(buf,"STATS RSP",	buflen); break;
347 #pragma clang diagnostic push
348 #pragma clang diagnostic ignored "-Wunreachable-code"
349 	default:
350 		snprintf(buf, buflen, "unknown op #%d", op);
351 #pragma clang diagnostic pop
352 	}
353 	return (buf);
354 }
355 
356 const char *
axa_opt_to_str(char * buf,size_t buflen,axa_p_opt_type_t opt)357 axa_opt_to_str(char *buf, size_t buflen, axa_p_opt_type_t opt)
358 {
359 	switch (opt) {
360 	case AXA_P_OPT_TRACE:	strlcpy(buf, "TRACE",	    buflen); break;
361 	case AXA_P_OPT_RLIMIT:	strlcpy(buf, "RATE LIMIT",  buflen); break;
362 	case AXA_P_OPT_SAMPLE:	strlcpy(buf, "SAMPLE",	    buflen); break;
363 	case AXA_P_OPT_SNDBUF:	strlcpy(buf, "SNDBUF",	    buflen); break;
364 #pragma clang diagnostic push
365 #pragma clang diagnostic ignored "-Wunreachable-code"
366 	default:
367 		snprintf(buf, buflen, "unknown option type #%d", opt);
368 #pragma clang diagnostic pop
369 	}
370 	return (buf);
371 }
372 
373 const char *
axa_tag_op_to_str(char * buf,size_t buf_len,axa_tag_t tag,axa_p_op_t op)374 axa_tag_op_to_str(char *buf, size_t buf_len,
375 		  axa_tag_t tag, axa_p_op_t op)
376 {
377 	size_t tag_len;
378 	char *op_buf;
379 
380 	axa_tag_to_str(buf, buf_len, tag);
381 	tag_len = strlen(buf);
382 	if (tag_len+1+1+1 >= buf_len)
383 		return (buf);
384 	buf_len -= tag_len+2;
385 	op_buf = buf + tag_len;
386 	*op_buf++ = ' ';
387 	axa_op_to_str(op_buf, buf_len - (tag_len+1), op);
388 
389 	return (buf);
390 }
391 
392 char *
axa_watch_ip_to_str(char * buf,size_t buf_len,int af,const void * addr,size_t alen,uint prefix)393 axa_watch_ip_to_str(char *buf, size_t buf_len,
394 		int af, const void *addr, size_t alen, uint prefix)
395 {
396 	union {
397 		struct in_addr	ipv4;
398 		struct in6_addr	ipv6;
399 		uint8_t		b[0];
400 	} abuf;
401 	char ip_str[INET6_ADDRSTRLEN];
402 	char prefix_str[1+3+1];
403 	size_t cplen;
404 
405 	if (af == AF_INET) {
406 		/* Watch IP address lengths are checked in input */
407 		if (prefix == 0 || prefix > 32) {
408 			snprintf(buf, buf_len,
409 				 "invalid IPv4 prefix of %d", prefix);
410 			return (buf);
411 		}
412 		if (prefix == 32) {
413 			prefix_str[0] = '\0';
414 		} else {
415 			snprintf(prefix_str, sizeof(prefix_str),
416 				 "/%d", prefix);
417 		}
418 	} else {
419 		if (prefix == 0 || prefix > 128) {
420 			snprintf(buf, buf_len,
421 				 "invalid IPv6 prefix of %d", prefix);
422 			return (buf);
423 		}
424 		if (prefix == 128) {
425 			prefix_str[0] = '\0';
426 		} else {
427 			snprintf(prefix_str, sizeof(prefix_str), "/%d", prefix);
428 		}
429 	}
430 
431 	/* allow truncation to the prefix */
432 	memset(&abuf, 0, sizeof(abuf));
433 	cplen = alen;
434 	if (alen > sizeof(abuf))
435 		cplen = sizeof(abuf);
436 	memcpy(&abuf, addr, cplen);
437 
438 	if (NULL == inet_ntop(af, &abuf, ip_str, sizeof(ip_str))) {
439 		snprintf(buf, buf_len,
440 			 "inet_ntop(%c): %s",
441 			 af == AF_INET ? '4' : '6', strerror(errno));
442 		return (buf);
443 	}
444 	snprintf(buf, buf_len, "IP=%s%s", ip_str, prefix_str);
445 	return (buf);
446 }
447 
448 char *
axa_watch_to_str(char * buf,size_t buf_len,const axa_p_watch_t * watch,size_t watch_len)449 axa_watch_to_str(char *buf, size_t buf_len,
450 		 const axa_p_watch_t *watch, size_t watch_len)
451 {
452 	char domain[NS_MAXDNAME];
453 	const char *star;
454 	ssize_t pat_len;
455 
456 	pat_len = watch_len - (sizeof(*watch) - sizeof(watch->pat));
457 	AXA_ASSERT(pat_len >= 0);
458 	switch ((axa_p_watch_type_t)watch->type) {
459 	case AXA_P_WATCH_IPV4:
460 		axa_watch_ip_to_str(buf, buf_len, AF_INET,
461 				&watch->pat.addr, pat_len, watch->prefix);
462 		break;
463 	case AXA_P_WATCH_IPV6:
464 		axa_watch_ip_to_str(buf, buf_len, AF_INET6,
465 				&watch->pat.addr6, pat_len, watch->prefix);
466 		break;
467 	case AXA_P_WATCH_DNS:
468 		axa_domain_to_str(watch->pat.dns, pat_len,
469 				  domain, sizeof(domain));
470 		if ((watch->flags & AXA_P_WATCH_FG_WILD) == 0) {
471 			star = "";
472 		} else if (domain[0] == '.') {
473 				star = "*";
474 		} else {
475 			star = "*.";
476 		}
477 		snprintf(buf, buf_len, "dns=%s%s", star, domain);
478 		break;
479 	case AXA_P_WATCH_CH:
480 		snprintf(buf, buf_len,
481 			 AXA_OP_CH_PREFIX"="AXA_OP_CH_PREFIX"%d",
482 			 AXA_P2H_CH(watch->pat.ch));
483 		break;
484 	case AXA_P_WATCH_ERRORS:
485 		snprintf(buf, buf_len, "ERRORS");
486 		break;
487 #pragma clang diagnostic push
488 #pragma clang diagnostic ignored "-Wunreachable-code"
489 	default:
490 		snprintf(buf, buf_len, "unknown watch type %d", watch->type);
491 		break;
492 #pragma clang diagnostic pop
493 	}
494 
495 	if ((watch->flags & AXA_P_WATCH_FG_SHARED) != 0)
496 		strlcat(buf, "("AXA_P_WATCH_STR_SHARED")", buf_len);
497 
498 	return (buf);
499 }
500 
501 static void
watch_add_str(char ** bufp,size_t * buf_lenp,const char * op_sp,const axa_p_watch_t * watch,size_t watch_len)502 watch_add_str(char **bufp, size_t *buf_lenp,
503 	      const char *op_sp, const axa_p_watch_t *watch, size_t watch_len)
504 {
505 	size_t len;
506 
507 	axa_buf_print(bufp, buf_lenp, "%s", op_sp);
508 	axa_watch_to_str(*bufp, *buf_lenp, watch, watch_len);
509 	len = strlen(*bufp);
510 	*bufp += len;
511 	*buf_lenp -= len;
512 }
513 
514 static void
whit_add_str(char ** bufp,size_t * buf_lenp,const char * op_sp,const axa_p_whit_t * whit,size_t whit_len)515 whit_add_str(char **bufp, size_t *buf_lenp,
516 	     const char *op_sp, const axa_p_whit_t *whit, size_t whit_len)
517 {
518 	char ip_str[INET6_ADDRSTRLEN];
519 
520 	if (whit->hdr.type == AXA_P_WHIT_NMSG) {
521 		axa_buf_print(bufp, buf_lenp, "%s"AXA_OP_CH_PREFIX"%d nmsg",
522 			      op_sp, whit->hdr.ch);
523 		return;
524 	}
525 
526 	if (whit->hdr.type != AXA_P_WHIT_IP) {
527 		axa_buf_print(bufp, buf_lenp, "%s"AXA_OP_CH_PREFIX"%d ???",
528 			      op_sp, whit->hdr.ch);
529 		return;
530 	}
531 
532 	if (whit_len >= sizeof(struct ip)
533 	    && (whit->ip.b[0] & 0xf0) == 0x40) {
534 		axa_watch_ip_to_str(ip_str, sizeof(ip_str), AF_INET,
535 				AXA_OFFSET(whit->ip.b, struct ip, ip_src),
536 				4, 32);
537 		axa_buf_print(bufp, buf_lenp, "%s"AXA_OP_CH_PREFIX"%d src %s",
538 			      op_sp, whit->hdr.ch, ip_str);
539 
540 	} else if (whit_len >= sizeof(struct ip6_hdr)
541 	    && (whit->ip.b[0] & 0xf0) == 0x60) {
542 		axa_watch_ip_to_str(ip_str, sizeof(ip_str), AF_INET6,
543 				AXA_OFFSET(whit->ip.b, struct ip6_hdr, ip6_src),
544 				16, 128);
545 		axa_buf_print(bufp, buf_lenp, "%s"AXA_OP_CH_PREFIX"%d src %s",
546 			      op_sp, whit->hdr.ch, ip_str);
547 
548 	} else {
549 		axa_buf_print(bufp, buf_lenp, "%s"AXA_OP_CH_PREFIX"%d ???",
550 			      op_sp, whit->hdr.ch);
551 	}
552 }
553 
554 static void
rlimit_add_str(char ** bufp,size_t * buf_lenp,axa_cnt_t limit,axa_cnt_t cur,const char * str)555 rlimit_add_str(char **bufp, size_t *buf_lenp,
556 	       axa_cnt_t limit, axa_cnt_t cur, const char *str)
557 {
558 	/* buffers sized largest sane number of packets */
559 	char limit_buf[sizeof("9,999,999,999,999,999,999")];
560 	char cur_buf[sizeof("; current value=9,999,999,999,999,999,999")];
561 	const char *limit_str;
562 
563 	limit = AXA_P2H64(limit);
564 	cur = AXA_P2H64(cur);
565 	if (cur == AXA_RLIMIT_NA) {
566 		if (limit == AXA_RLIMIT_NA)
567 			return;
568 		cur_buf[0] = '\0';
569 	} else {
570 		snprintf(cur_buf, sizeof(cur_buf),
571 			 "; current value=%"PRIu64, cur);
572 	}
573 	if (limit == AXA_RLIMIT_NA) {
574 		limit_str = "no change";
575 	} else if (limit == AXA_RLIMIT_OFF) {
576 		limit_str = "unlimited";
577 	} else {
578 		snprintf(limit_buf, sizeof(limit_buf), "%"PRIu64, limit);
579 		limit_str = limit_buf;
580 	}
581 	axa_buf_print(bufp, buf_lenp, "\n    %s per %s%s",
582 		      limit_str, str, cur_buf);
583 }
584 
585 static void
missed_add_str(char ** bufp,size_t * buf_lenp,const char * op_nl,const axa_p_missed_t * missed)586 missed_add_str(char **bufp, size_t *buf_lenp,
587 	       const char *op_nl, const axa_p_missed_t *missed)
588 {
589 	time_t epoch;
590 	char time_buf[32];
591 
592 	epoch = AXA_P2H32(missed->last_report);
593 	strftime(time_buf, sizeof(time_buf), "%Y/%m/%d %T",
594 		 localtime(&epoch));
595 
596 	axa_buf_print(bufp, buf_lenp,
597 		      "%s"
598 		      "    missed %"PRIu64" input packets,"
599 		      " dropped %"PRIu64" for congestion,\n"
600 		      "\tdropped %"PRIu64" for rate limit,"
601 		      " filtered %"PRIu64"\n"
602 		      "\tsince %s",
603 		      op_nl,
604 		      AXA_P2H64(missed->missed),
605 		      AXA_P2H64(missed->dropped),
606 		      AXA_P2H64(missed->rlimit),
607 		      AXA_P2H64(missed->filtered),
608 		      time_buf);
609 }
610 
611 static void
missed_rad_add_str(char ** bufp,size_t * buf_lenp,const char * op_nl,const axa_p_missed_rad_t * missed)612 missed_rad_add_str(char **bufp, size_t *buf_lenp,
613 		   const char *op_nl, const axa_p_missed_rad_t *missed)
614 {
615 	time_t epoch;
616 	char time_buf[32];
617 
618 	epoch = AXA_P2H32(missed->last_report);
619 	strftime(time_buf, sizeof(time_buf), "%Y/%m/%d %T",
620 		 localtime(&epoch));
621 
622 	axa_buf_print(bufp, buf_lenp,
623 		      "%s"
624 		      "    missed %"PRIu64" input packets at SRA server,"
625 		      " dropped %"PRIu64" for SRA->RAD congestion,\n"
626 		      "\tdropped %"PRIu64" for SRA->RAD rate limit,"
627 		      " filtered %"PRIu64" by SRA\n"
628 		      "\tdropped %"PRIu64" for RAD->client congestion,"
629 		      " dropped %"PRIu64" for RAD rate limit,\n"
630 		      "\tfiltered %"PRIu64" by RAD modules"
631 		      " since %s",
632 		      op_nl,
633 		      AXA_P2H64(missed->sra_missed),
634 		      AXA_P2H64(missed->sra_dropped),
635 		      AXA_P2H64(missed->sra_rlimit),
636 		      AXA_P2H64(missed->sra_filtered),
637 		      AXA_P2H64(missed->dropped),
638 		      AXA_P2H64(missed->rlimit),
639 		      AXA_P2H64(missed->filtered),
640 		      time_buf);
641 }
642 
643 static bool				/* false=message added to err_buf */
ck_ipdg(const void * ptr,size_t actual_len,bool is_ip,char ** err_buf,size_t * err_buf_len,const char * proto_nm,size_t min_len,axa_p_ch_t ch)644 ck_ipdg(const void *ptr, size_t actual_len, bool is_ip,
645 	char **err_buf, size_t *err_buf_len,
646 	const char *proto_nm, size_t min_len, axa_p_ch_t ch)
647 {
648 	if (ptr == NULL) {
649 		axa_buf_print(err_buf, err_buf_len,
650 			      "%smissing %s header from "
651 			      AXA_OP_CH_PREFIX"%d",
652 			      is_ip ? "" : "\n",
653 			      proto_nm, ch);
654 		return (false);
655 	}
656 	if (actual_len < min_len) {
657 		axa_buf_print(err_buf, err_buf_len,
658 			      "%struncated %s header of %zd bytes from "
659 			      AXA_OP_CH_PREFIX"%d",
660 			      is_ip ? "" : "\n",
661 			      proto_nm, actual_len, ch);
662 		return (false);
663 	}
664 	return (true);
665 }
666 
667 /* Convert a raw IP datagram to a string. */
668 bool					/* true=dst and src set */
axa_ipdg_parse(const uint8_t * pkt_data,size_t caplen,axa_p_ch_t ch,axa_socku_t * dst_su,axa_socku_t * src_su,char * cmt,size_t cmt_len)669 axa_ipdg_parse(const uint8_t *pkt_data, size_t caplen, axa_p_ch_t ch,
670 	       axa_socku_t *dst_su, axa_socku_t *src_su,
671 	       char *cmt, size_t cmt_len)
672 {
673 	struct nmsg_ipdg dg;
674 	struct ip ip_hdr;
675 	uint ttl;
676 	struct ip6_hdr ip6_hdr;
677 	struct tcphdr tcp_hdr;
678 	struct udphdr udp_hdr;
679 	uint uh_ulen;
680 	nmsg_res res;
681 
682 	/* quell static analyzer complaints when dg.proto_network is AF_INET6 */
683 	ip_hdr.ip_len = 0;
684 
685 	memset(dst_su, 0, sizeof(*dst_su));
686 	memset(src_su, 0, sizeof(*src_su));
687 	if (cmt_len > 0)
688 		*cmt = '\0';
689 
690 	memset(&dg, 0, sizeof(dg));
691 	res = nmsg_ipdg_parse_pcap_raw(&dg, DLT_RAW, pkt_data, caplen);
692 	if (res != nmsg_res_success || dg.len_network == 0) {
693 		axa_buf_print(&cmt, &cmt_len, " unknown packet");
694 		return (false);
695 	}
696 
697 	switch (dg.proto_network) {
698 	case AF_INET:
699 		if (!ck_ipdg(dg.network, dg.len_network, false,
700 			     &cmt, &cmt_len, "IP", sizeof(ip_hdr), ch))
701 			return (false);
702 		memcpy(&ip_hdr, dg.network, sizeof(ip_hdr));
703 		axa_ip_to_su(dst_su, &ip_hdr.ip_dst, AF_INET);
704 		axa_ip_to_su(src_su, &ip_hdr.ip_src, AF_INET);
705 		ttl = ip_hdr.ip_ttl;
706 		break;
707 	case AF_INET6:
708 		if (!ck_ipdg(dg.network, dg.len_network, false,
709 			     &cmt, &cmt_len, "IPv6", sizeof(ip6_hdr), ch))
710 			return (false);
711 		memcpy(&ip6_hdr, dg.network, sizeof(ip6_hdr));
712 		axa_ip_to_su(dst_su, &ip6_hdr.ip6_dst, AF_INET6);
713 		axa_ip_to_su(src_su, &ip6_hdr.ip6_src, AF_INET6);
714 		ttl = ip6_hdr.ip6_hlim;
715 		break;
716 	default:
717 		axa_buf_print(&cmt, &cmt_len, "unknown AF %d from "
718 			      AXA_OP_CH_PREFIX"%d",
719 			      dg.proto_network, AXA_P2H_CH(ch));
720 		return (false);
721 	}
722 
723 	switch (dg.proto_transport) {
724 	case IPPROTO_ICMP:
725 		axa_buf_print(&cmt, &cmt_len, "TTL=%d ICMP", ttl);
726 		if (dg.transport == NULL)
727 			axa_buf_print(&cmt, &cmt_len, " later fragment");
728 		else
729 			axa_buf_print(&cmt, &cmt_len,
730 				      " %d bytes", ntohs(ip_hdr.ip_len));
731 		break;
732 
733 	case IPPROTO_ICMPV6:
734 		axa_buf_print(&cmt, &cmt_len, "TTL=%d ICMPv6", ttl);
735 		if (dg.transport == NULL)
736 			axa_buf_print(&cmt, &cmt_len, " later fragment");
737 		break;
738 
739 	case IPPROTO_TCP:
740 		axa_buf_print(&cmt, &cmt_len, "TTL=%d TCP", ttl);
741 		if (dg.transport == NULL) {
742 			axa_buf_print(&cmt, &cmt_len, " later fragment");
743 		} else if (ck_ipdg(dg.transport, dg.len_transport, true,
744 				    &cmt, &cmt_len, "TCP",
745 				    sizeof(tcp_hdr), ch )) {
746 			memcpy(&tcp_hdr, dg.transport, sizeof(tcp_hdr));
747 			AXA_SU_PORT(dst_su) = tcp_hdr.th_dport;
748 			AXA_SU_PORT(src_su) = tcp_hdr.th_sport;
749 			if ((tcp_hdr.th_flags & TH_FIN) != 0)
750 				axa_buf_print(&cmt, &cmt_len, " FIN");
751 			if ((tcp_hdr.th_flags & TH_SYN) != 0)
752 				axa_buf_print(&cmt, &cmt_len, " SYN");
753 			if ((tcp_hdr.th_flags & TH_ACK) != 0)
754 				axa_buf_print(&cmt, &cmt_len, " ACK");
755 			if ((tcp_hdr.th_flags & TH_RST) != 0)
756 				axa_buf_print(&cmt, &cmt_len, " RST");
757 		}
758 		break;
759 
760 	case IPPROTO_UDP:
761 		axa_buf_print(&cmt, &cmt_len, "TTL=%d UDP", ttl);
762 		if (dg.transport == NULL) {
763 			axa_buf_print(&cmt, &cmt_len, " later fragment");
764 		} else if (ck_ipdg(dg.transport, dg.len_transport, true,
765 				    &cmt, &cmt_len, "UDP",
766 				    sizeof(udp_hdr), ch)) {
767 			memcpy(&udp_hdr, dg.transport, sizeof(udp_hdr));
768 			AXA_SU_PORT(dst_su) = udp_hdr.uh_dport;
769 			AXA_SU_PORT(src_su) = udp_hdr.uh_sport;
770 			uh_ulen = ntohs(udp_hdr.uh_ulen);
771 			axa_buf_print(&cmt, &cmt_len, " %d bytes", uh_ulen);
772 			if (uh_ulen != dg.len_payload+sizeof(udp_hdr))
773 				axa_buf_print(&cmt, &cmt_len, "  fragment");
774 		}
775 		break;
776 
777 	default:
778 		axa_buf_print(&cmt, &cmt_len, " IP protocol %d",
779 			      dg.proto_transport);
780 		break;
781 	}
782 
783 	return (true);
784 }
785 
786 /* Convert some AXA protocol messages to strings. */
787 char *					/* input parameter buf0 */
axa_p_to_str(char * buf0,size_t buf_len,bool print_op,const axa_p_hdr_t * hdr,const axa_p_body_t * body)788 axa_p_to_str(char *buf0, size_t buf_len,    /* should be AXA_P_STRLEN */
789 	     bool print_op,
790 	     const axa_p_hdr_t *hdr,	/* protocol byte order */
791 	     const axa_p_body_t *body)
792 {
793 	char tag_op_buf[AXA_TAG_STRLEN+AXA_P_OP_STRLEN];
794 	char *buf;
795 	char opt_buf[AXA_P_OP_STRLEN];
796 	const char *op_sp, *op_nl;
797 	uint32_t sample, bufsize;
798 
799 	buf = buf0;
800 	buf[0] = '\0';
801 	if (print_op) {
802 		axa_buf_print(&buf, &buf_len, "%s",
803 			      axa_tag_op_to_str(tag_op_buf, sizeof(tag_op_buf),
804 						AXA_P2H_TAG(hdr->tag),
805 						hdr->op));
806 		op_sp = " ";
807 		op_nl = "\n";
808 	} else {
809 		op_sp = "";
810 		op_nl = "";
811 	}
812 
813 	switch ((axa_p_op_t)hdr->op) {
814 	case AXA_P_OP_NOP:
815 		break;
816 
817 	case AXA_P_OP_HELLO:
818 		axa_buf_print(&buf, &buf_len, "%s%s", op_sp, body->hello.str);
819 		break;
820 
821 	case AXA_P_OP_OK:
822 	case AXA_P_OP_ERROR:
823 		if (body->result.orig_op == AXA_P_OP_OK
824 		    || body->result.orig_op == AXA_P_OP_NOP
825 		    || body->result.orig_op == AXA_P_OP_ERROR) {
826 			axa_buf_print(&buf, &buf_len, "%s%s",
827 				      op_sp, body->result.str);
828 		} else {
829 			axa_buf_print(&buf, &buf_len, "%s%s %s",
830 				      op_sp,
831 				      axa_op_to_str(tag_op_buf,
832 						    sizeof(tag_op_buf),
833 						    body->result.orig_op),
834 				      body->result.str);
835 		}
836 		break;
837 
838 	case AXA_P_OP_MISSED:
839 		missed_add_str(&buf, &buf_len, op_nl, &body->missed);
840 		break;
841 
842 	case AXA_P_OP_MISSED_RAD:
843 		missed_rad_add_str(&buf, &buf_len, op_nl, &body->missed_rad);
844 		break;
845 
846 	case AXA_P_OP_WHIT:
847 		whit_add_str(&buf, &buf_len, op_sp, &body->whit,
848 			      (AXA_P2H32(hdr->len) - sizeof(*hdr)));
849 		break;
850 
851 	case AXA_P_OP_WATCH:
852 		watch_add_str(&buf, &buf_len, op_sp, &body->watch,
853 			      (AXA_P2H32(hdr->len) - sizeof(*hdr)));
854 		break;
855 
856 	case AXA_P_OP_ANOM:
857 		axa_buf_print(&buf, &buf_len, "%s%s", op_sp, body->anom.an.c);
858 		if (AXA_P2H32(hdr->len)-sizeof(*hdr) > sizeof(body->anom.an.c)
859 		    && body->anom.parms[0] != '\0')
860 			axa_buf_print(&buf, &buf_len, " %s", body->anom.parms);
861 		break;
862 
863 	case AXA_P_OP_CHANNEL:
864 		if (body->channel.ch == AXA_P2H_CH(AXA_OP_CH_ALL)) {
865 			snprintf(buf, buf_len, "%s"AXA_OP_CH_ALLSTR" %s",
866 				 op_sp,
867 				 (body->channel.on != 0) ? "on" : "off");
868 		} else {
869 			snprintf(buf, buf_len, "%s"AXA_OP_CH_PREFIX"%d %s",
870 				 op_sp,
871 				 AXA_P2H_CH(body->channel.ch),
872 				 (body->channel.on != 0) ? "on" : "off");
873 		}
874 		break;
875 
876 	case AXA_P_OP_WLIST:
877 		if (print_op)
878 			axa_buf_print(&buf, &buf_len, " %5s",
879 				      axa_tag_to_str(tag_op_buf,
880 						     sizeof(tag_op_buf),
881 						     AXA_P2H_TAG(body
882 							 ->wlist.cur_tag)));
883 		watch_add_str(&buf, &buf_len, op_sp, &body->wlist.w,
884 			      (AXA_P2H32(hdr->len) - sizeof(*hdr)
885 			       - (sizeof(body->wlist)
886 				  - sizeof(body->wlist.w))));
887 		break;
888 
889 	case AXA_P_OP_AHIT:
890 		axa_buf_print(&buf, &buf_len, "%s%s ", op_sp, body->ahit.an.c);
891 		whit_add_str(&buf, &buf_len, op_sp, &body->ahit.whit,
892 			      (AXA_P2H32(hdr->len) - sizeof(*hdr)));
893 		break;
894 
895 	case AXA_P_OP_ALIST:
896 		if (print_op)
897 			axa_buf_print(&buf, &buf_len, " %5s ",
898 				      axa_tag_to_str(tag_op_buf,
899 						     sizeof(tag_op_buf),
900 						     AXA_P2H_TAG(body
901 							 ->alist.cur_tag)));
902 		axa_buf_print(&buf, &buf_len, "%5s %s",
903 			      body->alist.anom.an.c,
904 			      body->alist.anom.parms);
905 		break;
906 
907 	case AXA_P_OP_CLIST:
908 		break;
909 
910 	case AXA_P_OP_USER:
911 		axa_buf_print(&buf, &buf_len, "%s'%s'",
912 			      op_sp, body->user.name);
913 		break;
914 
915 	case AXA_P_OP_OPT:
916 		switch ((axa_p_opt_type_t)body->opt.type) {
917 		case AXA_P_OPT_TRACE:
918 			axa_buf_print(&buf, &buf_len, "%strace=%d", op_sp,
919 				      AXA_P2H32(body->opt.u.trace));
920 			break;
921 		case AXA_P_OPT_RLIMIT:
922 			axa_buf_print(&buf, &buf_len, "%s%s", op_sp,
923 				      axa_opt_to_str(opt_buf, sizeof(opt_buf),
924 						     body->opt.type));
925 			rlimit_add_str(&buf, &buf_len,
926 				       body->opt.u.rlimit.max_pkts_per_sec,
927 				       body->opt.u.rlimit.cur_pkts_per_sec,
928 				       "second");
929 			if (AXA_P2H64(body->opt.u.rlimit.report_secs)
930 			    == AXA_RLIMIT_OFF)
931 				axa_buf_print(&buf, &buf_len,
932 					      "    no regular reports");
933 			else if (AXA_P2H64(body->opt.u.rlimit.report_secs)
934 				 != AXA_RLIMIT_NA)
935 				axa_buf_print(&buf, &buf_len,
936 					      "\n    %"PRIu64
937 					      " seconds between reports",
938 					      AXA_P2H64(body->opt.u.
939 							rlimit.report_secs));
940 			break;
941 		case AXA_P_OPT_SAMPLE:
942 			sample = AXA_P2H32(body->opt.u.sample);
943 			if (sample == AXA_P_OPT_SAMPLE_REQ)
944 				axa_buf_print(&buf, &buf_len,
945 					      "%srequest sample rate",
946 					      op_sp);
947 			else
948 				axa_buf_print(&buf, &buf_len,
949 					      "%ssample %.2f%%",
950 					      op_sp,
951 					      sample
952 					      / (AXA_P_OPT_SAMPLE_SCALE*1.0));
953 			break;
954 		case AXA_P_OPT_SNDBUF:
955 			bufsize = AXA_P2H32(body->opt.u.bufsize);
956 			if (bufsize == AXA_P_OPT_SNDBUF_REQ)
957 				axa_buf_print(&buf, &buf_len,
958 					      "%srequest bufsize",
959 					      op_sp);
960 			else
961 				axa_buf_print(&buf, &buf_len, "%sbufsize=%d",
962 				      op_sp, bufsize);
963 			break;
964 #pragma clang diagnostic push
965 #pragma clang diagnostic ignored "-Wunreachable-code"
966 		default:
967 			if (print_op)
968 				axa_buf_print(&buf, &buf_len, " ");
969 			axa_buf_print(&buf, &buf_len,
970 				      "unrecognized type %d", body->opt.type);
971 			break;
972 #pragma clang diagnostic pop
973 		}
974 		break;
975 
976 	case AXA_P_OP_JOIN:
977 	case AXA_P_OP_PAUSE:
978 	case AXA_P_OP_GO:
979 	case AXA_P_OP_WGET:
980 	case AXA_P_OP_AGET:
981 	case AXA_P_OP_STOP:
982 	case AXA_P_OP_ALL_STOP:
983 	case AXA_P_OP_CGET:
984 	case AXA_P_OP_ACCT:
985 	case AXA_P_OP_RADU:
986 	case AXA_P_OP_MGMT_GET:
987 	case AXA_P_OP_MGMT_GETRSP:
988 	case _AXA_P_OP_KILL_REQ:
989 	case _AXA_P_OP_KILL_RSP:
990 	case _AXA_P_OP_STATS_REQ:
991 	case _AXA_P_OP_STATS_RSP:
992 	default:
993 		break;
994 	}
995 
996 	return (buf0);
997 }
998 
999 /* Check the header of an AXA message. */
1000 bool				/* false=bad */
axa_ck_hdr(axa_emsg_t * emsg,const axa_p_hdr_t * hdr,const char * label,axa_p_direction_t dir)1001 axa_ck_hdr(axa_emsg_t *emsg, const axa_p_hdr_t *hdr,
1002        const char *label, axa_p_direction_t dir)
1003 {
1004 	size_t max_len, min_len;
1005 	bool dir_ok;
1006 	const char *dir1_str, *dir2_str;
1007 	int tagged;			/* -1=never 0=sometimes 1=always */
1008 	char op_buf[AXA_P_OP_STRLEN];
1009 	axa_p_body_t *body;
1010 	axa_tag_t tag;
1011 	uint32_t len;
1012 
1013 	body = NULL;
1014 	len = AXA_P2H32(hdr->len);
1015 	if (len < sizeof(*hdr)) {
1016 		axa_pemsg(emsg, "AXA header length of %d is too small"
1017 			  " from %s", len, label);
1018 		return (false);
1019 	}
1020 	if (hdr->pvers < AXA_P_PVERS_MIN || hdr->pvers > AXA_P_PVERS_MAX) {
1021 		axa_pemsg(emsg, "unknown protocol version #%d for %s from %s",
1022 			  hdr->pvers,
1023 			  axa_op_to_str(op_buf, sizeof(op_buf), hdr->op),
1024 			  label);
1025 		return (false);
1026 	}
1027 	len -= sizeof(*hdr);
1028 	if (len > AXA_P_MAX_BODY_LEN) {
1029 		axa_pemsg(emsg, "impossible body length %d from %s",
1030 			  len, label);
1031 		return (false);
1032 	}
1033 
1034 	switch ((axa_p_op_t)hdr->op) {
1035 	case AXA_P_OP_NOP:
1036 		max_len = min_len = 0;
1037 		tagged = 0;
1038 		dir_ok = true;
1039 		break;
1040 	case AXA_P_OP_HELLO:
1041 		max_len = sizeof(body->hello);
1042 		min_len = max_len - sizeof(body->hello.str);
1043 		tagged = -1;
1044 		dir_ok = true;
1045 		break;
1046 	case AXA_P_OP_OK:
1047 	case AXA_P_OP_ERROR:
1048 		max_len = sizeof(body->result);
1049 		min_len = max_len - sizeof(body->result.str);
1050 		tagged = 0;
1051 		dir_ok = (dir == AXA_P_FROM_SRA || dir == AXA_P_FROM_RAD);
1052 		break;
1053 	case AXA_P_OP_MISSED:
1054 		max_len = min_len = sizeof(body->missed);
1055 		tagged = -1;
1056 		dir_ok = (dir == AXA_P_FROM_SRA);
1057 		break;
1058 	case AXA_P_OP_MISSED_RAD:
1059 		max_len = min_len = sizeof(body->missed_rad);
1060 		tagged = -1;
1061 		dir_ok = (dir == AXA_P_FROM_RAD);
1062 		break;
1063 	case AXA_P_OP_WHIT:
1064 		min_len = AXA_WHIT_MIN_LEN;
1065 		max_len = AXA_WHIT_MAX_LEN;
1066 		tagged = 1;
1067 		dir_ok = (dir == AXA_P_FROM_SRA || dir == AXA_P_FROM_RAD);
1068 		break;
1069 	case AXA_P_OP_WLIST:
1070 		max_len = sizeof(body->wlist);
1071 		min_len = max_len - sizeof(body->wlist.w.pat);
1072 		tagged = 0;
1073 		dir_ok = (dir == AXA_P_FROM_SRA || dir == AXA_P_FROM_RAD);
1074 		break;
1075 	case AXA_P_OP_AHIT:
1076 		min_len = (sizeof(body->ahit) - sizeof(body->ahit.whit)
1077 			   + AXA_WHIT_MIN_LEN);
1078 		max_len = (sizeof(body->ahit) - sizeof(body->ahit.whit)
1079 			   + AXA_WHIT_MAX_LEN);
1080 		tagged = 1;
1081 		dir_ok = (dir == AXA_P_FROM_RAD);
1082 		break;
1083 	case AXA_P_OP_ALIST:
1084 		max_len = sizeof(body->alist);
1085 		min_len = max_len - sizeof(body->alist.anom.parms);
1086 		tagged = 0;
1087 		dir_ok = (dir == AXA_P_FROM_RAD);
1088 		break;
1089 	case AXA_P_OP_CLIST:
1090 		max_len = sizeof(body->clist);
1091 		min_len = max_len;
1092 		tagged = 0;
1093 		dir_ok = (dir == AXA_P_FROM_SRA || dir == AXA_P_FROM_RAD);
1094 		break;
1095 
1096 	case AXA_P_OP_USER:
1097 		max_len = min_len = sizeof(body->user);
1098 		tagged = 0;
1099 		dir_ok = (dir == AXA_P_TO_SRA || dir == AXA_P_TO_RAD);
1100 		break;
1101 	case AXA_P_OP_JOIN:
1102 		max_len = min_len = sizeof(body->join);
1103 		tagged = 0;
1104 		dir_ok = (dir == AXA_P_TO_SRA || dir == AXA_P_TO_RAD);
1105 		break;
1106 	case AXA_P_OP_PAUSE:
1107 	case AXA_P_OP_GO:
1108 		max_len = min_len = 0;
1109 		tagged = 0;
1110 		dir_ok = (dir == AXA_P_TO_SRA || dir == AXA_P_TO_RAD);
1111 		break;
1112 	case AXA_P_OP_WATCH:
1113 		max_len = sizeof(body->watch);
1114 		min_len = max_len - sizeof(body->watch.pat);
1115 		tagged = 1;
1116 		dir_ok = (dir == AXA_P_TO_SRA || dir == AXA_P_TO_RAD);
1117 		break;
1118 	case AXA_P_OP_WGET:
1119 		max_len = min_len = 0;
1120 		tagged = 0;
1121 		dir_ok = (dir == AXA_P_TO_SRA || dir == AXA_P_TO_RAD);
1122 		break;
1123 	case AXA_P_OP_ANOM:
1124 		max_len = sizeof(body->anom);
1125 		min_len = max_len - sizeof(body->anom.parms);
1126 		tagged = 1;
1127 		dir_ok = (dir == AXA_P_TO_RAD);
1128 		break;
1129 	case AXA_P_OP_AGET:
1130 		max_len = min_len = 0;
1131 		tagged = 0;
1132 		dir_ok = (dir == AXA_P_TO_RAD);
1133 		break;
1134 	case AXA_P_OP_STOP:
1135 		max_len = min_len = 0;
1136 		tagged = 1;
1137 		dir_ok = (dir == AXA_P_TO_SRA || dir == AXA_P_TO_RAD);
1138 		break;
1139 	case AXA_P_OP_ALL_STOP:
1140 		max_len = min_len = 0;
1141 		tagged = 0;
1142 		dir_ok = (dir == AXA_P_TO_SRA || dir == AXA_P_TO_RAD);
1143 		break;
1144 	case AXA_P_OP_CHANNEL:
1145 		max_len = sizeof(axa_p_channel_t);
1146 		min_len = max_len;
1147 		tagged = 0;
1148 		dir_ok = (dir == AXA_P_TO_SRA);
1149 		break;
1150 	case AXA_P_OP_CGET:
1151 		max_len = min_len = 0;
1152 		tagged = 0;
1153 		dir_ok = (dir == AXA_P_TO_SRA || dir == AXA_P_TO_RAD);
1154 		break;
1155 	case AXA_P_OP_OPT:
1156 		min_len = sizeof(body->opt) - sizeof(body->opt.u);
1157 		max_len = min_len + 1024;
1158 		tagged = 0;
1159 		dir_ok = true;
1160 		break;
1161 	case AXA_P_OP_ACCT:
1162 		max_len = min_len = 0;
1163 		tagged = 0;
1164 		dir_ok = (dir == AXA_P_TO_SRA || dir == AXA_P_TO_RAD);
1165 		break;
1166 	case AXA_P_OP_RADU:
1167 		max_len = min_len = 0;
1168 		tagged = 0;
1169 		dir_ok = (dir == AXA_P_TO_RAD);
1170 		break;
1171 	case AXA_P_OP_MGMT_GET:
1172 		max_len = min_len = 0;
1173 		tagged = 0;
1174 		dir_ok = (dir == AXA_P_TO_SRA || dir == AXA_P_TO_RAD);
1175 		break;
1176 	case AXA_P_OP_MGMT_GETRSP:
1177 		min_len = max_len =
1178 			strlen("mgmt is deprecated, please upgrade and use \"stats\"");
1179 		tagged = 0;
1180 		dir_ok = (dir == AXA_P_FROM_SRA || dir == AXA_P_FROM_RAD);
1181 		break;
1182 	case _AXA_P_OP_KILL_REQ:
1183 		max_len = min_len = sizeof(_axa_p_kill_t);
1184 		tagged = 0;
1185 		dir_ok = (dir == AXA_P_TO_SRA || dir == AXA_P_TO_RAD);
1186 		break;
1187 	case _AXA_P_OP_KILL_RSP:
1188 		min_len = max_len = sizeof(_axa_p_kill_t);
1189 		tagged = 0;
1190 		dir_ok = (dir == AXA_P_FROM_SRA || dir == AXA_P_FROM_RAD);
1191 		break;
1192 	case _AXA_P_OP_STATS_REQ:
1193 		max_len = min_len = sizeof(_axa_p_stats_req_t);
1194 		tagged = 0;
1195 		dir_ok = (dir == AXA_P_TO_SRA || dir == AXA_P_TO_RAD);
1196 		break;
1197 	case _AXA_P_OP_STATS_RSP:
1198 		/* stats response header */
1199 		min_len = sizeof(_axa_p_stats_rsp_t);
1200 		/* stats response header + sys object + max user objs + max
1201 		 * rad an objs */
1202 		max_len = sizeof(_axa_p_stats_rsp_t) +
1203 			sizeof(_axa_p_stats_sys_t) +
1204 			(_AXA_STATS_MAX_USER_OBJS *
1205 			 sizeof(_axa_p_stats_user_t)) +
1206 			(_AXA_STATS_MAX_USER_OBJS *
1207 			 ((_AXA_STATS_MAX_USER_RAD_AN_OBJS *
1208                          sizeof(_axa_p_stats_user_rad_an_t))));
1209 		tagged = 0;
1210 		dir_ok = (dir == AXA_P_FROM_SRA || dir == AXA_P_FROM_RAD);
1211 		break;
1212 
1213 #pragma clang diagnostic push
1214 #pragma clang diagnostic ignored "-Wunreachable-code"
1215 	default:
1216 		max_len = 0;
1217 		min_len = INT_MAX;
1218 		tagged = 1;
1219 		dir_ok = false;
1220 		break;
1221 #pragma clang diagnostic pop
1222 	}
1223 
1224 	tag = AXA_P2H_TAG(hdr->tag);
1225 	if (tagged == 1) {
1226 		if (tag == AXA_TAG_NONE) {
1227 			axa_pemsg(emsg, "missing tag for %s from %s",
1228 				  axa_op_to_str(op_buf, sizeof(op_buf),
1229 						hdr->op),
1230 				  label);
1231 			return (false);
1232 		}
1233 	} else if (tagged == -1) {
1234 		if (tag != AXA_TAG_NONE) {
1235 			axa_pemsg(emsg, "unexpected tag %d for %s from %s",
1236 				  tag,
1237 				  axa_op_to_str(op_buf, sizeof(op_buf),
1238 						hdr->op),
1239 				  label);
1240 			return (false);
1241 		}
1242 	}
1243 
1244 	if (!dir_ok) {
1245 		switch (dir) {
1246 		case AXA_P_TO_SRA:
1247 			dir1_str = label;
1248 			dir2_str = "SRA client";
1249 			break;
1250 		case AXA_P_FROM_SRA:
1251 			dir1_str = "SRA";
1252 			dir2_str = label;
1253 			break;
1254 		case AXA_P_TO_RAD:
1255 			dir1_str = label;
1256 			dir2_str = "RAD client";
1257 			break;
1258 		case AXA_P_FROM_RAD:
1259 			dir1_str = "RAD";
1260 			dir2_str = label;
1261 			break;
1262 #pragma clang diagnostic push
1263 #pragma clang diagnostic ignored "-Wunreachable-code"
1264 		default:
1265 			dir1_str = "?";
1266 			dir2_str = label;
1267 			break;
1268 #pragma clang diagnostic pop
1269 		}
1270 		axa_pemsg(emsg, "%s illegal from %s to %s",
1271 			  axa_op_to_str(op_buf, sizeof(op_buf), hdr->op),
1272 			  dir1_str, dir2_str);
1273 		return (false);
1274 	}
1275 
1276 	if (len > max_len) {
1277 		axa_pemsg(emsg, "length %d for %s from %s should be at most %zu",
1278 			  len, axa_op_to_str(op_buf, sizeof(op_buf), hdr->op),
1279 			  label, max_len);
1280 		return (false);
1281 	}
1282 	if (len < min_len) {
1283 		axa_pemsg(emsg, "length %d for %s from %s must be at least %zu",
1284 			  len, axa_op_to_str(op_buf, sizeof(op_buf), hdr->op),
1285 			  label, min_len);
1286 		return (false);
1287 	}
1288 
1289 	return (true);
1290 }
1291 
1292 /* Check that an AXA message is null terminated. */
1293 static bool
ck_field_null(axa_emsg_t * emsg,axa_p_op_t op,const void * field,size_t field_len,const char * field_name)1294 ck_field_null(axa_emsg_t *emsg, axa_p_op_t op, const void *field,
1295 	      size_t field_len, const char *field_name)
1296 {
1297 	char op_buf[AXA_P_OP_STRLEN];
1298 
1299 	if (field_len == 0) {
1300 		axa_pemsg(emsg, "%s %s truncated",
1301 			  axa_op_to_str(op_buf, sizeof(op_buf), op),
1302 			  field_name);
1303 		return (false);
1304 	}
1305 	if (((uint8_t *)field)[field_len-1] != '\0') {
1306 		axa_pemsg(emsg, "%s %s not null terminated",
1307 			  axa_op_to_str(op_buf, sizeof(op_buf), op),
1308 			  field_name);
1309 		return (false);
1310 	}
1311 	return (true);
1312 }
1313 
1314 /* Check a binary channel number. */
1315 static bool
ck_ch(axa_emsg_t * emsg,axa_p_op_t op,axa_p_ch_t ch,bool all_ok)1316 ck_ch(axa_emsg_t *emsg, axa_p_op_t op,
1317       axa_p_ch_t ch,			/* protocol byte order */
1318       bool all_ok)
1319 {
1320 	char op_buf[AXA_P_OP_STRLEN];
1321 
1322 	ch = AXA_P2H_CH(ch);
1323 	if (ch == AXA_OP_CH_ALL && all_ok)
1324 		return (true);
1325 	if (ch > AXA_OP_CH_MAX) {
1326 		axa_pemsg(emsg, "%s "AXA_OP_CH_PREFIX"%d is an invalid channel",
1327 			  axa_op_to_str(op_buf, sizeof(op_buf), op), ch);
1328 		return (false);
1329 	}
1330 	return (true);
1331 }
1332 
1333 /* Check anomaly name */
1334 static bool
ck_an(axa_emsg_t * emsg,axa_p_op_t op,const axa_p_an_t * an)1335 ck_an(axa_emsg_t *emsg, axa_p_op_t op, const axa_p_an_t *an)
1336 {
1337 	char op_buf[AXA_P_OP_STRLEN];
1338 
1339 	if (an->c[sizeof(an->c)-1] != '\0') {
1340 		axa_pemsg(emsg, "%s \"%.*s\" name not null terminated",
1341 			  axa_op_to_str(op_buf, sizeof(op_buf), op),
1342 			  (int)sizeof(*an), an->c);
1343 		return (false);
1344 	}
1345 	return (true);
1346 }
1347 
1348 /* Check anomaly specification. */
1349 static bool
ck_anom(axa_emsg_t * emsg,axa_p_op_t op,const axa_p_anom_t * anom,size_t anom_len)1350 ck_anom(axa_emsg_t *emsg, axa_p_op_t op,
1351 	const axa_p_anom_t *anom, size_t anom_len)
1352 {
1353 	size_t parms_len;
1354 
1355 	parms_len = anom_len - sizeof(anom->an);
1356 	return (ck_field_null(emsg, op, anom->an.c, sizeof(anom->an), "name")
1357 		&& (parms_len == 0
1358 		    || ck_field_null(emsg, op, anom->parms, parms_len,
1359 				     "parameters")));
1360 }
1361 
1362 /* Check a watch in AXA_P_OP_WATCH or AXA_P_OP_WLIST. */
1363 static bool
ck_watch(axa_emsg_t * emsg,axa_p_op_t op,const axa_p_watch_t * w,size_t watch_len)1364 ck_watch(axa_emsg_t *emsg, axa_p_op_t op,
1365 	 const axa_p_watch_t *w, size_t watch_len)
1366 {
1367 	char op_buf[AXA_P_OP_STRLEN];
1368 	ssize_t pat_len;
1369 	int name_len;
1370 
1371 	if (w->pad != 0) {
1372 		axa_pemsg(emsg, "%s bad watch byte %#x",
1373 			  axa_op_to_str(op_buf, sizeof(op_buf), op),
1374 			  w->pad);
1375 		return (false);
1376 	}
1377 	if (0 != (w->flags & ~(AXA_P_WATCH_FG_WILD
1378 			       | AXA_P_WATCH_FG_SHARED))) {
1379 		axa_pemsg(emsg, "%s bad watch flags %#x",
1380 			  axa_op_to_str(op_buf, sizeof(op_buf), op),
1381 			  w->flags);
1382 		return (false);
1383 	}
1384 
1385 	pat_len = watch_len - (sizeof(*w) - sizeof(w->pat));
1386 	switch ((axa_p_watch_type_t)w->type) {
1387 	case AXA_P_WATCH_IPV4:
1388 		if (pat_len <= 0 || pat_len > (ssize_t)sizeof(w->pat.addr)) {
1389 			axa_pemsg(emsg, "%s bad IPv4 length %zd",
1390 				  axa_op_to_str(op_buf, sizeof(op_buf), op),
1391 				  watch_len);
1392 			return (false);
1393 		}
1394 		if (w->prefix == 0 || (w->prefix+7)/8 > pat_len) {
1395 			axa_pemsg(emsg, "%s bad prefix length"
1396 				  " %d for address length %zd",
1397 				  axa_op_to_str(op_buf, sizeof(op_buf), op),
1398 				  w->prefix, pat_len);
1399 			return (false);
1400 		}
1401 		break;
1402 	case AXA_P_WATCH_IPV6:
1403 		if (pat_len <= 0 || pat_len > (ssize_t)sizeof(w->pat.addr6)) {
1404 			axa_pemsg(emsg, "%s bad IPv6 length %zd",
1405 				  axa_op_to_str(op_buf, sizeof(op_buf), op),
1406 				  watch_len);
1407 			return (false);
1408 		}
1409 		if (w->prefix == 0 || (w->prefix+7)/8 > pat_len) {
1410 			axa_pemsg(emsg, "%s bad prefix length"
1411 				  " %d for address length %zd",
1412 				  axa_op_to_str(op_buf, sizeof(op_buf), op),
1413 				  w->prefix, pat_len);
1414 			return (false);
1415 		}
1416 		break;
1417 	case AXA_P_WATCH_DNS:
1418 		if (pat_len <= 0 || pat_len > (int)sizeof(w->pat.dns)) {
1419 			axa_pemsg(emsg, "%s bad dns length %zd",
1420 				  axa_op_to_str(op_buf, sizeof(op_buf), op),
1421 				  watch_len);
1422 			return (false);
1423 		}
1424 		name_len = 0;
1425 		while (w->pat.dns[name_len] != 0) {
1426 			name_len += 1+w->pat.dns[name_len];
1427 			if (name_len > pat_len)
1428 				break;
1429 		}
1430 		if (name_len+1 != pat_len) {
1431 			axa_pemsg(emsg, "%s bad dns label lengths",
1432 				  axa_op_to_str(op_buf, sizeof(op_buf), op));
1433 			return (false);
1434 		}
1435 		break;
1436 	case AXA_P_WATCH_CH:
1437 		if (pat_len != sizeof(w->pat.ch)) {
1438 			axa_pemsg(emsg, "%s bad channel watch length %zd",
1439 				  axa_op_to_str(op_buf, sizeof(op_buf), op),
1440 				  watch_len);
1441 			return (false);
1442 		}
1443 		return (ck_ch(emsg, op, w->pat.ch, false));
1444 	case AXA_P_WATCH_ERRORS:
1445 		if (pat_len != 0) {
1446 			axa_pemsg(emsg, "%s bad error watch length %zd",
1447 				  axa_op_to_str(op_buf, sizeof(op_buf), op),
1448 				  watch_len);
1449 			return (false);
1450 		}
1451 		break;
1452 #pragma clang diagnostic push
1453 #pragma clang diagnostic ignored "-Wunreachable-code"
1454 	default:
1455 		axa_pemsg(emsg, "%s unknown watch type %d",
1456 			  axa_op_to_str(op_buf, sizeof(op_buf), op),
1457 			  w->type);
1458 		return (false);
1459 #pragma clang diagnostic pop
1460 	}
1461 
1462 	return (true);
1463 }
1464 
1465 static bool
ck_whit(axa_emsg_t * emsg,axa_p_op_t op,const axa_p_whit_t * whit,size_t whit_len)1466 ck_whit(axa_emsg_t *emsg, axa_p_op_t op,
1467 	const axa_p_whit_t *whit, size_t whit_len)
1468 {
1469 	char op_buf[AXA_P_OP_STRLEN];
1470 
1471 	if (!ck_ch(emsg, op, whit->hdr.ch, false))
1472 		return (false);
1473 
1474 	if (whit->hdr.type == AXA_P_WHIT_NMSG) {
1475 		if (whit_len < sizeof(axa_p_whit_nmsg_t)) {
1476 			axa_pemsg(emsg, "%s bad nmsg watch hit length %zd",
1477 				  axa_op_to_str(op_buf, sizeof(op_buf), op),
1478 				  whit_len);
1479 			return (false);
1480 		}
1481 
1482 	} else if (whit->hdr.type == AXA_P_WHIT_IP) {
1483 		if (whit_len < sizeof(axa_p_whit_ip_t)) {
1484 			axa_pemsg(emsg, "%s bad IP watch hit length %zd",
1485 				  axa_op_to_str(op_buf, sizeof(op_buf), op),
1486 				  whit_len);
1487 			return (false);
1488 		}
1489 
1490 	} else {
1491 		axa_pemsg(emsg, "%s bad watch hit type %d",
1492 			  axa_op_to_str(op_buf, sizeof(op_buf), op),
1493 			  whit->hdr.type);
1494 		return (false);
1495 	}
1496 
1497 	return (true);
1498 }
1499 
1500 static bool
ck_opt(axa_emsg_t * emsg,axa_p_op_t op,const axa_p_opt_t * opt,size_t opt_len)1501 ck_opt(axa_emsg_t *emsg, axa_p_op_t op, const axa_p_opt_t *opt, size_t opt_len)
1502 {
1503 	size_t val_len;
1504 	char op_buf[AXA_P_OP_STRLEN];
1505 	char opt_buf[AXA_P_OP_STRLEN];
1506 
1507 	AXA_ASSERT(opt_len >= sizeof(axa_p_opt_t) - sizeof(opt->u));
1508 
1509 	switch ((axa_p_opt_type_t)opt->type) {
1510 	case AXA_P_OPT_TRACE:
1511 		val_len = sizeof(opt->u.trace);
1512 		break;
1513 	case AXA_P_OPT_RLIMIT:
1514 		val_len = sizeof(opt->u.rlimit);
1515 		break;
1516 	case AXA_P_OPT_SAMPLE:
1517 		val_len = sizeof(opt->u.sample);
1518 		break;
1519 	case AXA_P_OPT_SNDBUF:
1520 		val_len = sizeof(opt->u.bufsize);
1521 		break;
1522 #pragma clang diagnostic push
1523 #pragma clang diagnostic ignored "-Wunreachable-code"
1524 	default:
1525 		axa_pemsg(emsg, "%s %s",
1526 			  axa_op_to_str(op_buf, sizeof(op_buf), op),
1527 			  axa_opt_to_str(opt_buf, sizeof(opt_buf), opt->type));
1528 		return (false);
1529 #pragma clang diagnostic pop
1530 	}
1531 
1532 	if (val_len != opt_len - (sizeof(axa_p_opt_t) - sizeof(opt->u))) {
1533 		axa_pemsg(emsg, "%s %s bad rate limit option length %zd",
1534 			  axa_op_to_str(op_buf, sizeof(op_buf), op),
1535 			  axa_opt_to_str(opt_buf, sizeof(opt_buf), opt->type),
1536 			  opt_len);
1537 		return (false);
1538 	}
1539 
1540 	return (true);
1541 }
1542 
1543 /*
1544  * Sanity check an AXA protocol body after the header has been checked.
1545  *	The header check has validated the body length.
1546  */
1547 bool					/* false=bad */
axa_ck_body(axa_emsg_t * emsg,axa_p_op_t op,const axa_p_body_t * body,size_t body_len)1548 axa_ck_body(axa_emsg_t *emsg, axa_p_op_t op, const axa_p_body_t *body,
1549 	    size_t body_len)
1550 {
1551 	switch (op) {
1552 	case AXA_P_OP_HELLO:
1553 		return (ck_field_null(emsg, op, body->b, body_len, "version"));
1554 	case AXA_P_OP_NOP:
1555 		break;
1556 	case AXA_P_OP_OK:
1557 	case AXA_P_OP_ERROR:
1558 		return (ck_field_null(emsg, op, body, body_len, "message"));
1559 	case AXA_P_OP_MISSED:
1560 	case AXA_P_OP_MISSED_RAD:
1561 		break;
1562 	case AXA_P_OP_WHIT:
1563 		return (ck_whit(emsg, op, &body->whit, body_len));
1564 	case AXA_P_OP_WLIST:
1565 		return (ck_watch(emsg, op, &body->wlist.w,
1566 				 body_len - (sizeof(body->wlist)
1567 					     - sizeof(body->wlist.w))));
1568 	case AXA_P_OP_AHIT:
1569 		return (ck_an(emsg, op, &body->ahit.an)
1570 			&& ck_ch(emsg, op, body->ahit.whit.hdr.ch, false));
1571 	case AXA_P_OP_ALIST:
1572 		return (ck_anom(emsg, op, &body->alist.anom,
1573 				body_len - (sizeof(body->alist)
1574 					    - sizeof(body->alist.anom))));
1575 	case AXA_P_OP_CLIST:
1576 		if (!ck_ch(emsg, op, body->clist.ch, false))
1577 			return (false);
1578 		return (ck_field_null(emsg, op, body, body_len, "channel"));
1579 
1580 
1581 	case AXA_P_OP_USER:
1582 		return (ck_field_null(emsg, op, body, body_len, "user name"));
1583 	case AXA_P_OP_JOIN:
1584 	case AXA_P_OP_PAUSE:
1585 	case AXA_P_OP_GO:
1586 		break;
1587 	case AXA_P_OP_WATCH:
1588 		return (ck_watch(emsg, op, &body->watch, body_len));
1589 	case AXA_P_OP_WGET:
1590 		break;
1591 	case AXA_P_OP_ANOM:
1592 		return (ck_anom(emsg, op, &body->anom, body_len));
1593 	case AXA_P_OP_AGET:
1594 	case AXA_P_OP_STOP:
1595 	case AXA_P_OP_ALL_STOP:
1596 		break;
1597 	case AXA_P_OP_CHANNEL:
1598 		return (ck_ch(emsg, op, body->channel.ch, true));
1599 	case AXA_P_OP_CGET:
1600 		break;
1601 	case AXA_P_OP_OPT:
1602 		return (ck_opt(emsg, op, &body->opt, body_len));
1603 	case AXA_P_OP_ACCT:
1604 		break;
1605 	case AXA_P_OP_RADU:
1606 		break;
1607 	case AXA_P_OP_MGMT_GET:
1608 		break;
1609 	case AXA_P_OP_MGMT_GETRSP:
1610 		break;
1611 	case _AXA_P_OP_KILL_REQ:
1612 		/* TODO */
1613 		break;
1614 	case _AXA_P_OP_KILL_RSP:
1615 		/* TODO */
1616 		break;
1617 	case _AXA_P_OP_STATS_REQ:
1618 		/* TODO */
1619 		break;
1620 	case _AXA_P_OP_STATS_RSP:
1621 		/* TODO */
1622 		break;
1623 	}
1624 
1625 	return (true);
1626 }
1627 
1628 axa_io_type_t
axa_io_type_parse(const char ** addrp)1629 axa_io_type_parse(const char **addrp)
1630 {
1631 	axa_io_type_t result;
1632 	const char *addr;
1633 	int i;
1634 
1635 	addr = *addrp;
1636 	addr += strspn(addr, AXA_WHITESPACE);
1637 
1638 	if (AXA_CLITCMP(addr, AXA_IO_TYPE_UNIX_STR":")) {
1639 		addr += sizeof(AXA_IO_TYPE_UNIX_STR":")-1;
1640 		result = AXA_IO_TYPE_UNIX;
1641 
1642 	} else if (AXA_CLITCMP(addr, AXA_IO_TYPE_TCP_STR":")) {
1643 		addr += sizeof(AXA_IO_TYPE_TCP_STR":")-1;
1644 		result = AXA_IO_TYPE_TCP;
1645 
1646 	} else if (AXA_CLITCMP(addr, AXA_IO_TYPE_TLS_STR":")) {
1647 		addr += sizeof(AXA_IO_TYPE_TLS_STR":")-1;
1648 		result = AXA_IO_TYPE_TLS;
1649 
1650 	} else if (AXA_CLITCMP(addr, AXA_IO_TYPE_SSH_STR":")) {
1651 		addr += sizeof(AXA_IO_TYPE_SSH_STR":")-1;
1652 		result = AXA_IO_TYPE_SSH;
1653 
1654 	} else if (AXA_CLITCMP(addr, AXA_IO_TYPE_SSH_STR)
1655 		   && 0 != (i = strspn(addr+sizeof(AXA_IO_TYPE_SSH_STR)-1,
1656 				       AXA_WHITESPACE))) {
1657 		/* allow "ssh " for upward compatibility with old sratool */
1658 		addr += sizeof(AXA_IO_TYPE_SSH_STR)-1 + i;
1659 		result = AXA_IO_TYPE_SSH;
1660 
1661 	} else if (AXA_CLITCMP(addr, AXA_IO_TYPE_APIKEY_STR":")) {
1662 		addr += sizeof(AXA_IO_TYPE_APIKEY_STR":")-1;
1663 		result = AXA_IO_TYPE_APIKEY;
1664 
1665 	} else {
1666 		return (AXA_IO_TYPE_UNKN);
1667 	}
1668 
1669 	*addrp = addr;
1670 	return (result);
1671 }
1672 
1673 /* I/O type to string */
1674 const char *
axa_io_type_to_str(axa_io_type_t type)1675 axa_io_type_to_str(axa_io_type_t type)
1676 {
1677 	switch (type) {
1678 	case AXA_IO_TYPE_UNKN:
1679 	default:
1680 		return ("?");
1681 	case AXA_IO_TYPE_UNIX:
1682 		return (AXA_IO_TYPE_UNIX_STR);
1683 	case AXA_IO_TYPE_TCP:
1684 		return (AXA_IO_TYPE_TCP_STR);
1685 	case AXA_IO_TYPE_SSH:
1686 		return (AXA_IO_TYPE_SSH_STR);
1687 	case AXA_IO_TYPE_TLS:
1688 		return (AXA_IO_TYPE_TLS_STR);
1689 	case AXA_IO_TYPE_APIKEY:
1690 		return (AXA_IO_TYPE_APIKEY_STR);
1691 	}
1692 }
1693 
1694 void
axa_io_init(axa_io_t * io)1695 axa_io_init(axa_io_t *io)
1696 {
1697 	memset(io, 0, sizeof(*io));
1698 	io->su.sa.sa_family = -1;
1699 	io->i_fd = -1;
1700 	io->o_fd = -1;
1701 	io->tun_fd = -1;
1702 	io->tun_pid = -1;
1703 	io->pvers = AXA_P_PVERS;
1704 }
1705 
1706 void
axa_io_pvers_set(axa_io_t * io,uint8_t pvers)1707 axa_io_pvers_set(axa_io_t *io, uint8_t pvers)
1708 {
1709 	AXA_ASSERT(io != NULL);
1710 	io->pvers = pvers;
1711 }
1712 
1713 void
axa_io_pvers_get(axa_io_t * io,uint8_t * pvers)1714 axa_io_pvers_get(axa_io_t *io, uint8_t *pvers)
1715 {
1716 	AXA_ASSERT(io != NULL);
1717 	*pvers = io->pvers;
1718 }
1719 
1720 void
axa_recv_flush(axa_io_t * io)1721 axa_recv_flush(axa_io_t *io)
1722 {
1723 	if (io->recv_body != NULL) {
1724 		free(io->recv_body);
1725 		io->recv_body = NULL;
1726 	}
1727 	io->recv_body_len = 0;
1728 }
1729 
1730 static void
ck_close(int fd,const char * label)1731 ck_close(int fd, const char *label)
1732 {
1733 	if (0 > close(fd)) {
1734 		/* This should not happen. */
1735 		axa_trace_msg("close(%s): %s", label, strerror(errno));
1736 	}
1737 }
1738 
1739 void
axa_io_close(axa_io_t * io)1740 axa_io_close(axa_io_t *io)
1741 {
1742 	int wstatus;
1743 
1744 	switch (io->type) {
1745 		case AXA_IO_TYPE_APIKEY:
1746 			axa_apikey_stop(io);
1747 			break;
1748 		case AXA_IO_TYPE_TLS:
1749 			axa_tls_stop(io);
1750 			break;
1751 		case AXA_IO_TYPE_UNIX:
1752 		case AXA_IO_TYPE_UNKN:
1753 		case AXA_IO_TYPE_SSH:
1754 		case AXA_IO_TYPE_TCP:
1755 		default:
1756 			break;
1757 	}
1758 
1759 	if (io->i_fd >= 0 && io->i_fd != io->o_fd)
1760 		ck_close(io->i_fd, "io->i_fd");
1761 	if (io->o_fd >= 0)
1762 		ck_close(io->o_fd, "io->o_fd");
1763 
1764 	if (io->tun_fd >= 0)
1765 		ck_close(io->tun_fd, "io->tun_fd");
1766 
1767 	if (io->tun_pid != -1) {
1768 		kill(io->tun_pid, SIGKILL);
1769 		waitpid(io->tun_pid, &wstatus, 0);
1770 	}
1771 
1772 	axa_recv_flush(io);
1773 	if (io->recv_buf != NULL)
1774 		free(io->recv_buf);
1775 
1776 	if (io->tun_buf != NULL)
1777 		free(io->tun_buf);
1778 
1779 	if (io->send_buf != NULL)
1780 		free(io->send_buf);
1781 
1782 	if (io->addr != NULL)
1783 		free(io->addr);
1784 	if (io->label != NULL)
1785 		free(io->label);
1786 
1787 	if (io->cert_file != NULL)
1788 		free(io->cert_file);
1789 	if (io->key_file != NULL)
1790 		free(io->key_file);
1791 	if (io->tls_info != NULL)
1792 		free(io->tls_info);
1793 
1794 	/* Clear the FDs, PID, buffer pointers, etc. */
1795 	axa_io_init(io);
1796 }
1797 
1798 static axa_p_direction_t
which_direction(const axa_io_t * io,bool send)1799 which_direction(const axa_io_t *io, bool send)
1800 {
1801 	bool to_srvr;
1802 	axa_p_direction_t result;
1803 
1804 	to_srvr = ((send && io->is_client)
1805 		   || (!send && !io->is_client));
1806 	if (io->is_rad) {
1807 		result = to_srvr ? AXA_P_TO_RAD : AXA_P_FROM_RAD;
1808 	} else {
1809 		result = to_srvr ? AXA_P_TO_SRA : AXA_P_FROM_SRA;
1810 	}
1811 
1812 	return (result);
1813 }
1814 
1815 /* Make an error message for a bad AXA message header that looks like
1816  * an SSH message of the day.. */
1817 static void
motd_hdr(axa_emsg_t * emsg,axa_io_t * io)1818 motd_hdr(axa_emsg_t *emsg, axa_io_t *io)
1819 {
1820 	ssize_t i;
1821 
1822 	if (io->type != AXA_IO_TYPE_SSH)
1823 		return;
1824 
1825 	/* Did we receive the header in a single read? */
1826 	if (io->recv_start != &io->recv_buf[sizeof(axa_p_hdr_t)]) {
1827 		/* No, so do not disturb the existing error message. */
1828 		return;
1829 	}
1830 
1831 	/* Yes, so find the first non-ASCII byte. */
1832 	for (i = 0; i < io->recv_bytes; ++i) {
1833 		if (io->recv_start[i] < ' '
1834 		    || io->recv_start[i] > '~')
1835 			break;
1836 	}
1837 
1838 	/* Assume it is a message of the day via SSH if
1839 	 * there is a bunch of ASCII ending with '\n' or '\r'. */
1840 	if (i == io->recv_bytes
1841 	    && (io->recv_start[i] == '\n'
1842 		|| io->recv_start[i] != '\r'))
1843 		axa_pemsg(emsg, "unexpected text \"%.*s\" from %s",
1844 			  (int)i, io->recv_buf, io->label);
1845 }
1846 
1847 axa_io_result_t
axa_recv_buf(axa_emsg_t * emsg,axa_io_t * io)1848 axa_recv_buf(axa_emsg_t *emsg, axa_io_t *io)
1849 {
1850 #define BUF_SIZE (64*1024)
1851 	ssize_t len, i;
1852 	size_t hdr_len;
1853 	uint8_t *tgt;
1854 	axa_io_result_t io_result;
1855 
1856 	/* Create unprocessed data buffer the first time. */
1857 	if (io->recv_buf == NULL) {
1858 		io->recv_buf_len = BUF_SIZE;
1859 		io->recv_buf = axa_malloc(io->recv_buf_len);
1860 		io->recv_bytes = 0;
1861 	}
1862 
1863 	if (io->recv_body_len == 0)
1864 		memset(&io->recv_hdr, 0, sizeof(io->recv_hdr));
1865 
1866 	for (;;) {
1867 		/* Decide how many more bytes we need. */
1868 		len = sizeof(io->recv_hdr) - io->recv_body_len;
1869 		if (len > 0) {
1870 			/* We do not yet have the entire header,
1871 			 * and so we must not have a place for the body. */
1872 			AXA_ASSERT(io->recv_body == NULL);
1873 
1874 			tgt = (uint8_t *)&io->recv_hdr + io->recv_body_len;
1875 
1876 		} else {
1877 			/* We have at least all of the header.
1878 			 * Check the header when we first have it. */
1879 			if (len == 0
1880 			    && !axa_ck_hdr(emsg, &io->recv_hdr, io->label,
1881 				       which_direction(io, false))) {
1882 				motd_hdr(emsg, io);
1883 				return (AXA_IO_ERR);
1884 			}
1885 
1886 			/* Stop when we have a complete message. */
1887 			hdr_len = AXA_P2H32(io->recv_hdr.len);
1888 			if (hdr_len == io->recv_body_len) {
1889 				if (!axa_ck_body(emsg, io->recv_hdr.op,
1890 						 io->recv_body,
1891 						 hdr_len-sizeof(io->recv_hdr)))
1892 					return (AXA_IO_ERR);
1893 				return (AXA_IO_OK);
1894 			}
1895 
1896 			/* Allocate the body only when needed. */
1897 			if (io->recv_body == NULL)
1898 				io->recv_body = axa_malloc(hdr_len
1899 							- sizeof(io->recv_hdr));
1900 			len = hdr_len - io->recv_body_len;
1901 			tgt = ((uint8_t *)io->recv_body
1902 			       + io->recv_body_len - sizeof(io->recv_hdr));
1903 		}
1904 
1905 		/* Read more data into the hidden buffer when we run out. */
1906 		if (io->recv_bytes == 0) {
1907 			io->recv_start = io->recv_buf;
1908 			if (io->type == AXA_IO_TYPE_TLS ||
1909 					io->type == AXA_IO_TYPE_APIKEY) {
1910 				io_result = axa_tls_read(emsg, io);
1911 				if (io_result != AXA_IO_OK)
1912 					return (io_result);
1913 			} else {
1914 				for (;;) {
1915 					i = read(io->i_fd, io->recv_buf,
1916 						 io->recv_buf_len);
1917 					if (i > 0) {
1918 					    io->recv_bytes = i;
1919 					    gettimeofday(&io->alive, NULL);
1920 					    break;
1921 					}
1922 
1923 					if (i == 0) {
1924 					    axa_pemsg(emsg, "read(%s): EOF",
1925 						      io->label);
1926 					    return (AXA_IO_ERR);
1927 					}
1928 					if (errno == EINTR)
1929 					    continue;
1930 					if (errno == EAGAIN
1931 					    || errno == EWOULDBLOCK)
1932 					    return (AXA_IO_BUSY);
1933 					axa_pemsg(emsg, "read(%s): %s",
1934 						  io->label, strerror(errno));
1935 					return (AXA_IO_ERR);
1936 				}
1937 			}
1938 		}
1939 
1940 		/* Consume data in the buffer. */
1941 		i = min(len, io->recv_bytes);
1942 		memcpy(tgt, io->recv_start, i);
1943 		io->recv_start += i;
1944 		io->recv_bytes -= i;
1945 
1946 		io->recv_body_len += i;
1947 	}
1948 }
1949 
1950 /* Wait for something to to happen */
1951 axa_io_result_t
axa_io_wait(axa_emsg_t * emsg,axa_io_t * io,time_t wait_ms,bool keepalive,bool tun)1952 axa_io_wait(axa_emsg_t *emsg, axa_io_t *io,
1953 	    time_t wait_ms, bool keepalive, bool tun)
1954 {
1955 	struct timeval now;
1956 	time_t ms;
1957 	struct pollfd pollfds[3];
1958 	int nfds, i_nfd, o_nfd, tun_nfd;
1959 	int i;
1960 
1961 	/* Stop waiting when it is time for a keepalive. */
1962 	if (keepalive) {
1963 		gettimeofday(&now, NULL);
1964 		ms = (AXA_KEEPALIVE_MS - axa_elapsed_ms(&now, &io->alive));
1965 		if (wait_ms > ms)
1966 			wait_ms = ms;
1967 	}
1968 	if (wait_ms < 0)
1969 		wait_ms = 0;
1970 
1971 	memset(pollfds, 0, sizeof(pollfds));
1972 	i_nfd = -1;
1973 	o_nfd = -1;
1974 	tun_nfd = -1;
1975 	nfds = 0;
1976 
1977 	if (io->i_fd >= 0 && io->i_events != 0) {
1978 		pollfds[nfds].fd = io->i_fd;
1979 		pollfds[nfds].events = io->i_events;
1980 		i_nfd = nfds++;
1981 	}
1982 
1983 	if (io->o_fd >= 0 && io->o_events != 0) {
1984 		pollfds[nfds].fd = io->o_fd;
1985 		pollfds[nfds].events = io->o_events;
1986 		o_nfd = nfds++;
1987 	}
1988 
1989 	/* Watch the stderr pipe from a tunnel such as ssh. */
1990 	if (tun && io->tun_fd >= 0) {
1991 		pollfds[nfds].fd = io->tun_fd;
1992 		pollfds[nfds].events = AXA_POLL_IN;
1993 		tun_nfd = nfds++;
1994 	}
1995 
1996 	i = poll(pollfds, nfds, wait_ms);
1997 	if (i < 0 && errno != EINTR) {
1998 		axa_pemsg(emsg, "poll(): %s", strerror(errno));
1999 		return (AXA_IO_ERR);
2000 	}
2001 	if (i <= 0)
2002 		return (AXA_IO_BUSY);
2003 
2004 	if (tun_nfd >= 0 && pollfds[tun_nfd].revents != 0)
2005 		return (AXA_IO_TUNERR);
2006 
2007 	if ((i_nfd >= 0 && pollfds[i_nfd].revents != 0)
2008 	    || (o_nfd >= 0 && pollfds[o_nfd].revents != 0))
2009 		return (AXA_IO_OK);
2010 
2011 	if (keepalive) {
2012 		gettimeofday(&now, NULL);
2013 		ms = (AXA_KEEPALIVE_MS - axa_elapsed_ms(&now, &io->alive));
2014 		if (ms <= 0)
2015 			return (AXA_IO_KEEPALIVE);
2016 	}
2017 	return (AXA_IO_BUSY);
2018 }
2019 
2020 /*  Wait for and read a complete AXA message from the server into
2021  * the client buffer. */
2022 axa_io_result_t
axa_input(axa_emsg_t * emsg,axa_io_t * io,time_t wait_ms)2023 axa_input(axa_emsg_t *emsg, axa_io_t *io, time_t wait_ms)
2024 {
2025 	axa_io_result_t result;
2026 
2027 	for (;;) {
2028 		if (!AXA_IO_OPENED(io)) {
2029 			axa_pemsg(emsg, "not open");
2030 			return (AXA_IO_ERR);
2031 		}
2032 		if (!AXA_IO_CONNECTED(io)) {
2033 			axa_pemsg(emsg, "not connected");
2034 			return (AXA_IO_ERR);
2035 		}
2036 
2037 		/* Read more from the peer when needed. */
2038 		if (io->recv_buf == NULL || io->recv_bytes == 0) {
2039 			result = axa_io_wait(emsg, io, wait_ms,
2040 					     AXA_IO_CONNECTED(io), true);
2041 			switch (result) {
2042 			case AXA_IO_OK:
2043 				break;
2044 			case AXA_IO_ERR:
2045 			case AXA_IO_TUNERR:
2046 			case AXA_IO_KEEPALIVE:
2047 			case AXA_IO_BUSY:
2048 				return (result);
2049 			}
2050 		}
2051 
2052 		result = axa_recv_buf(emsg, io);
2053 		switch (result) {
2054 		case AXA_IO_OK:
2055 		case AXA_IO_ERR:
2056 			return (result);
2057 		case AXA_IO_BUSY:
2058 			continue;	/* wait for the rest */
2059 		case AXA_IO_TUNERR:	/* impossible */
2060 		case AXA_IO_KEEPALIVE:	/* impossible */
2061 			AXA_FAIL("impossible axa_recv_buf() result");
2062 		}
2063 	}
2064 }
2065 
2066 size_t
axa_make_hdr(axa_emsg_t * emsg,axa_p_hdr_t * hdr,axa_p_pvers_t pvers,axa_tag_t tag,axa_p_op_t op,size_t b1_len,size_t b2_len,axa_p_direction_t dir)2067 axa_make_hdr(axa_emsg_t *emsg, axa_p_hdr_t *hdr,
2068 	     axa_p_pvers_t pvers, axa_tag_t tag, axa_p_op_t op,
2069 	     size_t b1_len, size_t b2_len, axa_p_direction_t dir)
2070 {
2071 	size_t total;
2072 
2073 	memset(hdr, 0, sizeof(axa_p_hdr_t));
2074 	hdr->tag = AXA_H2P_TAG(tag);
2075 	total = sizeof(*hdr) + b1_len + b2_len;
2076 	hdr->len = AXA_H2P32(total);
2077 	hdr->pvers = pvers;
2078 	hdr->op = op;
2079 
2080 	if (!axa_ck_hdr(emsg, hdr, "myself", dir))
2081 		return (0);
2082 
2083 	return (total);
2084 }
2085 
2086 /* Send an AXA request or response to the server or client.
2087  *	The message is in 0, 1, 2, or 3 parts.
2088  *	hdr is the optional AXA protocol header to be built
2089  *	b1 and b1_len specify an optional second part after the header
2090  *	b2 and b2_len specify the third part. */
2091 axa_io_result_t
axa_send(axa_emsg_t * emsg,axa_io_t * io,axa_tag_t tag,axa_p_op_t op,axa_p_hdr_t * hdr,const void * b1,size_t b1_len,const void * b2,size_t b2_len)2092 axa_send(axa_emsg_t *emsg, axa_io_t *io,
2093 	 axa_tag_t tag, axa_p_op_t op, axa_p_hdr_t *hdr,
2094 	 const void *b1, size_t b1_len,
2095 	 const void *b2, size_t b2_len)
2096 {
2097 	axa_p_hdr_t hdr0;
2098 	struct iovec iov[3];
2099 	int iovcnt;
2100 	ssize_t total, done;
2101 
2102 	if (hdr == NULL)
2103 		hdr = &hdr0;
2104 	total = axa_make_hdr(emsg, hdr, io->pvers, tag, op,
2105 			     b1_len, b2_len, which_direction(io, true));
2106 	if (total == 0)
2107 		return (AXA_IO_ERR);
2108 
2109 	if (io->type == AXA_IO_TYPE_TLS || io->type == AXA_IO_TYPE_APIKEY) {
2110 		/*
2111 		 * For TLS, save all 3 parts in the overflow output buffer
2112 		 * so that the AXA message can be sent as a single TLS
2113 		 * transaction.  This is expensive, but only if you ignore
2114 		 * the cost of TLS encryption.
2115 		 */
2116 		axa_send_save(io, 0, hdr, b1, b1_len, b2, b2_len);
2117 		return (axa_tls_flush(emsg, io));
2118 	}
2119 
2120 	for (;;) {
2121 		iov[0].iov_base = hdr;
2122 		iov[0].iov_len = sizeof(*hdr);
2123 		if (b1_len == 0) {
2124 			iovcnt = 1;
2125 		} else {
2126 			iov[1].iov_base = (void *)b1;
2127 			iov[1].iov_len = b1_len;
2128 			if (b2_len == 0) {
2129 				iovcnt = 2;
2130 			} else {
2131 				iov[2].iov_base = (void *)b2;
2132 				iov[2].iov_len = b2_len;
2133 				iovcnt = 3;
2134 			}
2135 		}
2136 
2137 		done = writev(io->o_fd, iov, iovcnt);
2138 		if (done > 0) {
2139 			gettimeofday(&io->alive, NULL);
2140 			break;
2141 		}
2142 
2143 		if (done < 0) {
2144 			if (errno == EINTR) {
2145 				/* ignore signals */
2146 				continue;
2147 			}
2148 			if (errno == EAGAIN || errno == EWOULDBLOCK) {
2149 				/* None sent, so give up and let
2150 				 * the caller save or discard. */
2151 				return (AXA_IO_BUSY);
2152 			}
2153 
2154 			axa_pemsg(emsg, "writev(%s): %s",
2155 				  io->label, strerror(errno));
2156 			return (AXA_IO_ERR);
2157 		}
2158 	}
2159 	if (done < total)
2160 		axa_send_save(io, done, hdr, b1, b1_len, b2, b2_len);
2161 
2162 	return (AXA_IO_OK);
2163 }
2164 
2165 /* Add to the buffer of pending output data. */
2166 void
axa_send_save(axa_io_t * io,size_t done,const axa_p_hdr_t * hdr,const void * b1,size_t b1_len,const void * b2,size_t b2_len)2167 axa_send_save(axa_io_t *io, size_t done, const axa_p_hdr_t *hdr,
2168 	      const void *b1, size_t b1_len,
2169 	      const void *b2, size_t b2_len)
2170 {
2171 	ssize_t avail_len, new_len;
2172 	ssize_t undone, chunk;
2173 	uint8_t *new_buf, *p;
2174 
2175 	undone = sizeof(*hdr)+b1_len+b2_len - done;
2176 	AXA_ASSERT(undone > 0);
2177 
2178 	/* Expand the buffer if necessary */
2179 	avail_len = io->send_buf_len - io->send_bytes;
2180 	if (avail_len < undone) {
2181 		new_len = (io->send_buf_len + undone + 1024 + 1023) & ~1024;
2182 		new_buf = axa_malloc(new_len);
2183 
2184 		/* Save previously saved data. */
2185 		if (io->send_buf != NULL) {
2186 			if (io->send_bytes != 0)
2187 				memcpy(new_buf, io->send_start, io->send_bytes);
2188 			free(io->send_buf);
2189 		}
2190 		io->send_buf = new_buf;;
2191 		io->send_start = io->send_buf;
2192 		io->send_buf_len = new_len;
2193 
2194 	} else if (avail_len - (io->send_start - io->send_buf) < undone) {
2195 		/* slide down previously pending data */
2196 		if (io->send_bytes != 0)
2197 			memmove(io->send_buf, io->send_start, io->send_bytes);
2198 		io->send_start = io->send_buf;
2199 	}
2200 
2201 	/* Copy the unsent parts of the header and two chucks of body */
2202 	p = io->send_start + io->send_bytes;
2203 	io->send_bytes += undone;
2204 
2205 	chunk = sizeof(*hdr) - done;
2206 	if (chunk > 0) {
2207 		/* Some or all of the header was not sent.
2208 		 * Save the unsent part. */
2209 		memcpy(p, (uint8_t *)hdr + done, chunk);
2210 		p += chunk;
2211 		done += chunk;
2212 	}
2213 
2214 	chunk = sizeof(*hdr)+b1_len - done;
2215 	if (chunk > 0) {
2216 		/* Some or all of the first chunk of body was not sent.
2217 		 * Save the unsent part. */
2218 		memcpy(p, ((uint8_t *)b1)+(b1_len-chunk), chunk);
2219 		p += chunk;
2220 		done += chunk;
2221 	}
2222 
2223 	chunk = sizeof(*hdr)+b1_len+b2_len - done;
2224 	if (chunk > 0) {
2225 		/* Some or all of the second chunk of body was not sent.
2226 		 * Save the unsent part. */
2227 		memcpy(p, ((uint8_t *)b2)+(b2_len-chunk), chunk);
2228 	}
2229 }
2230 
2231 /* Flush pending output */
2232 axa_io_result_t
axa_send_flush(axa_emsg_t * emsg,axa_io_t * io)2233 axa_send_flush(axa_emsg_t *emsg, axa_io_t *io)
2234 {
2235 	ssize_t done;
2236 
2237 	if (io->type == AXA_IO_TYPE_TLS || io->type == AXA_IO_TYPE_APIKEY)
2238 		return (axa_tls_flush(emsg, io));
2239 
2240 	/* Repeat other transports until nothing flows. */
2241 	for (;;) {
2242 		if (io->send_bytes == 0) {
2243 			io->o_events = 0;
2244 			return (AXA_IO_OK);
2245 		}
2246 
2247 		done = write(io->o_fd, io->send_start,
2248 			     io->send_bytes);
2249 		if (done < 0) {
2250 			if (errno != EAGAIN && errno != EWOULDBLOCK) {
2251 				io->send_bytes = 0;
2252 				axa_pemsg(emsg, "send(): %s",
2253 					  strerror(errno));
2254 				return (AXA_IO_ERR);
2255 			}
2256 
2257 			io->o_events = AXA_POLL_OUT;
2258 			return (AXA_IO_BUSY);
2259 		}
2260 		io->send_start += done;
2261 		io->send_bytes -= done;
2262 
2263 		gettimeofday(&io->alive, NULL);
2264 	}
2265 }
2266 
2267 /* Capture anything that the tunnel (eg. ssh) process says. */
2268 const char *				/* NULL or \'0' terminated string */
axa_io_tunerr(axa_io_t * io)2269 axa_io_tunerr(axa_io_t *io)
2270 {
2271 	int i;
2272 	char *p;
2273 
2274 	/* Create the buffer the first time */
2275 	if (io->tun_buf == NULL || io->tun_buf_size == 0) {
2276 		AXA_ASSERT(io->tun_buf == NULL && io->tun_buf_size == 0);
2277 		io->tun_buf_size = 120;
2278 		io->tun_buf = axa_malloc(io->tun_buf_size);
2279 	}
2280 
2281 	for (;;) {
2282 		/* Discard the previously returned line. */
2283 		if (io->tun_buf_bol != 0) {
2284 			i = io->tun_buf_len - io->tun_buf_bol;
2285 			if (i > 0)
2286 				memmove(io->tun_buf,
2287 					&io->tun_buf[io->tun_buf_bol],
2288 					i);
2289 			io->tun_buf_len -= io->tun_buf_bol;
2290 			io->tun_buf_bol = 0;
2291 		}
2292 
2293 		/* Hope to return the next line in the buffer. */
2294 		if (io->tun_buf_len > 0) {
2295 			i = min(io->tun_buf_len, io->tun_buf_size);
2296 			p = memchr(io->tun_buf, '\n', i);
2297 			if (p != NULL) {
2298 				*p = '\0';
2299 				io->tun_buf_bol = p+1 - io->tun_buf;
2300 
2301 				/* trim '\r' */
2302 				while (p > io->tun_buf && *--p == '\r')
2303 					*p = '\0';
2304 				/* Discard blank lines. */
2305 				if (p == io->tun_buf)
2306 					continue;
2307 				return (io->tun_buf);
2308 			}
2309 		}
2310 
2311 		/* Get more data, possibly completing a partial line. */
2312 		i = io->tun_buf_size-1 - io->tun_buf_len;
2313 		if (i > 0 && io->tun_fd >= 0) {
2314 			i = read(io->tun_fd,
2315 				 &io->tun_buf[io->tun_buf_len],
2316 				 i);
2317 
2318 			/* Return the 1st line in the new buffer load */
2319 			if (i > 0) {
2320 				io->tun_buf_len += i;
2321 				io->tun_buf[io->tun_buf_len] = '\0';
2322 				continue;
2323 			}
2324 
2325 			if (i < 0
2326 			    && errno != EWOULDBLOCK && errno != EAGAIN) {
2327 				/* Return error message at errors. */
2328 				snprintf(io->tun_buf, io->tun_buf_size,
2329 					 "read(tunerr): %s",
2330 					 strerror(errno));
2331 				io->tun_buf_len = strlen(io->tun_buf)+1;
2332 				close(io->tun_fd);
2333 				io->tun_fd = -1;
2334 
2335 			} else if (i == 0) {
2336 				close(io->tun_fd);
2337 				io->tun_fd = -1;
2338 			}
2339 		}
2340 
2341 		/* Return whatever we have. */
2342 		io->tun_buf_bol = io->tun_buf_len;
2343 		return ((io->tun_buf_len > 0) ? io->tun_buf : NULL);
2344 	}
2345 }
2346 
2347 /* Clean up AXA I/O functions including freeing TLS data */
2348 void
axa_io_cleanup(void)2349 axa_io_cleanup(void)
2350 {
2351 	axa_tls_cleanup();
2352 	axa_apikey_cleanup();
2353 }
2354