xref: /openbsd-src/usr.sbin/radiusctl/radiusctl.c (revision 882428cdbdd2944d8f59bc8621c131fd814fb6ee)
1*882428cdSclaudio /*	$OpenBSD: radiusctl.c,v 1.17 2024/11/21 13:43:10 claudio Exp $	*/
2a7ca44b8Syasuoka /*
3a7ca44b8Syasuoka  * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
4a7ca44b8Syasuoka  *
5a7ca44b8Syasuoka  * Permission to use, copy, modify, and distribute this software for any
6a7ca44b8Syasuoka  * purpose with or without fee is hereby granted, provided that the above
7a7ca44b8Syasuoka  * copyright notice and this permission notice appear in all copies.
8a7ca44b8Syasuoka  *
9a7ca44b8Syasuoka  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10a7ca44b8Syasuoka  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11a7ca44b8Syasuoka  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12a7ca44b8Syasuoka  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13a7ca44b8Syasuoka  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14a7ca44b8Syasuoka  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15a7ca44b8Syasuoka  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16a7ca44b8Syasuoka  */
17a7ca44b8Syasuoka #include <sys/types.h>
18842565f2Syasuoka #include <sys/cdefs.h>
19a7ca44b8Syasuoka #include <sys/socket.h>
20842565f2Syasuoka #include <sys/time.h>
21842565f2Syasuoka #include <sys/uio.h>
22842565f2Syasuoka #include <sys/un.h>
23a7ca44b8Syasuoka #include <netinet/in.h>
24a7ca44b8Syasuoka #include <arpa/inet.h>
25842565f2Syasuoka 
26a7ca44b8Syasuoka #include <err.h>
27842565f2Syasuoka #include <errno.h>
28842565f2Syasuoka #include <event.h>
29842565f2Syasuoka #include <imsg.h>
30842565f2Syasuoka #include <inttypes.h>
31a7ca44b8Syasuoka #include <md5.h>
32a7ca44b8Syasuoka #include <netdb.h>
33842565f2Syasuoka #include <radius.h>
34a7ca44b8Syasuoka #include <stdbool.h>
35842565f2Syasuoka #include <stddef.h>
36842565f2Syasuoka #include <stdint.h>
37a7ca44b8Syasuoka #include <stdio.h>
38a7ca44b8Syasuoka #include <stdlib.h>
39a7ca44b8Syasuoka #include <string.h>
40842565f2Syasuoka #include <sysexits.h>
41842565f2Syasuoka #include <time.h>
42a7ca44b8Syasuoka #include <unistd.h>
43a7ca44b8Syasuoka 
44a7ca44b8Syasuoka #include "parser.h"
45842565f2Syasuoka #include "radiusd.h"
46842565f2Syasuoka #include "radiusd_ipcp.h"
47a7ca44b8Syasuoka #include "chap_ms.h"
48842565f2Syasuoka #include "json.h"
49a7ca44b8Syasuoka 
50842565f2Syasuoka #ifndef MAXIMUM
51842565f2Syasuoka #define MAXIMUM(_a, _b)	(((_a) > (_b))? (_a) : (_b))
52842565f2Syasuoka #endif
53a7ca44b8Syasuoka 
545d013a5eSdlg static int		 radius_test(struct parse_result *);
55a7ca44b8Syasuoka static void		 radius_dump(FILE *, RADIUS_PACKET *, bool,
56a7ca44b8Syasuoka 			    const char *);
57842565f2Syasuoka 
58842565f2Syasuoka static int		 ipcp_handle_imsg(struct parse_result *, struct imsg *,
59842565f2Syasuoka 			    int);
60842565f2Syasuoka static void		 ipcp_handle_show(struct radiusd_ipcp_db_dump *,
61842565f2Syasuoka 			    size_t, int);
62842565f2Syasuoka static void		 ipcp_handle_dumps(struct radiusd_ipcp_db_dump *,
63842565f2Syasuoka 			    size_t, int);
64842565f2Syasuoka static void		 ipcp_handle_dump(struct radiusd_ipcp_db_dump *,
65842565f2Syasuoka 			    size_t, int);
66842565f2Syasuoka static void		 ipcp_handle_dump0(struct radiusd_ipcp_db_dump *,
67842565f2Syasuoka 			    size_t, struct timespec *, struct timespec *,
68842565f2Syasuoka 			    struct timespec *, int);
69842565f2Syasuoka static void		 ipcp_handle_stat(struct radiusd_ipcp_statistics *);
70842565f2Syasuoka static void		 ipcp_handle_jsons(struct radiusd_ipcp_db_dump *,
71842565f2Syasuoka 			    size_t, int);
72842565f2Syasuoka static void		 ipcp_handle_json(struct radiusd_ipcp_db_dump *,
73842565f2Syasuoka 			    size_t, struct radiusd_ipcp_statistics *, int);
74842565f2Syasuoka static void		 ipcp_handle_json0(struct radiusd_ipcp_db_dump *,
75842565f2Syasuoka 			    size_t, struct timespec *, struct timespec *,
76842565f2Syasuoka 			    struct timespec *, int);
77842565f2Syasuoka 
78a7ca44b8Syasuoka static const char	*radius_code_str(int code);
79a7ca44b8Syasuoka static const char	*hexstr(const u_char *, int, char *, int);
80842565f2Syasuoka static const char	*sockaddr_str(struct sockaddr *, char *, size_t);
81842565f2Syasuoka static const char	*time_long_str(struct timespec *, char *, size_t);
82842565f2Syasuoka static const char	*time_short_str(struct timespec *, struct timespec *,
83842565f2Syasuoka 			    char *, size_t);
84842565f2Syasuoka static const char	*humanize_seconds(long, char *, size_t);
85a7ca44b8Syasuoka 
86a7ca44b8Syasuoka static void
87a7ca44b8Syasuoka usage(void)
88a7ca44b8Syasuoka {
89a7ca44b8Syasuoka 	extern char *__progname;
90a7ca44b8Syasuoka 
91de90af90Syasuoka 	fprintf(stderr, "usage: %s command [argument ...]\n", __progname);
92a7ca44b8Syasuoka }
93a7ca44b8Syasuoka 
94a7ca44b8Syasuoka int
95a7ca44b8Syasuoka main(int argc, char *argv[])
96a7ca44b8Syasuoka {
97842565f2Syasuoka 	int			 ch, sock, done = 0;
98842565f2Syasuoka 	ssize_t			 n;
99842565f2Syasuoka 	struct parse_result	*res;
100842565f2Syasuoka 	struct sockaddr_un	 sun;
101842565f2Syasuoka 	struct imsgbuf		 ibuf;
102842565f2Syasuoka 	struct imsg		 imsg;
103842565f2Syasuoka 	struct iovec		 iov[5];
104842565f2Syasuoka 	int			 niov = 0, cnt = 0;
105842565f2Syasuoka 	char			 module_name[RADIUSD_MODULE_NAME_LEN + 1];
106a7ca44b8Syasuoka 
10783aeedf6Syasuoka 	while ((ch = getopt(argc, argv, "")) != -1)
108a7ca44b8Syasuoka 		switch (ch) {
10983aeedf6Syasuoka 		default:
110a7ca44b8Syasuoka 			usage();
111ef9ad095Smillert 			return (EXIT_FAILURE);
112a7ca44b8Syasuoka 		}
113a7ca44b8Syasuoka 	argc -= optind;
114a7ca44b8Syasuoka 	argv += optind;
115a7ca44b8Syasuoka 
116842565f2Syasuoka 	if (unveil(RADIUSD_SOCK, "rw") == -1)
117842565f2Syasuoka 		err(EX_OSERR, "unveil");
118842565f2Syasuoka 	if (pledge("stdio unix rpath dns inet", NULL) == -1)
119842565f2Syasuoka 		err(EX_OSERR, "pledge");
120a7ca44b8Syasuoka 
121842565f2Syasuoka 	res = parse(argc, argv);
122842565f2Syasuoka 	if (res == NULL)
123842565f2Syasuoka 		exit(EX_USAGE);
124842565f2Syasuoka 
125842565f2Syasuoka 	switch (res->action) {
126842565f2Syasuoka 	default:
127842565f2Syasuoka 		break;
128a7ca44b8Syasuoka 	case NONE:
129842565f2Syasuoka 		exit(EXIT_SUCCESS);
130a7ca44b8Syasuoka 		break;
131a7ca44b8Syasuoka 	case TEST:
13204581dc7Syasuoka 		if (pledge("stdio dns inet", NULL) == -1)
13304581dc7Syasuoka 			err(EXIT_FAILURE, "pledge");
134842565f2Syasuoka 		exit(radius_test(res));
135a7ca44b8Syasuoka 		break;
136a7ca44b8Syasuoka 	}
137a7ca44b8Syasuoka 
138842565f2Syasuoka 	if (pledge("stdio unix rpath", NULL) == -1)
139842565f2Syasuoka 		err(EX_OSERR, "pledge");
140842565f2Syasuoka 
141842565f2Syasuoka 	memset(&sun, 0, sizeof(sun));
142842565f2Syasuoka 	sun.sun_family = AF_UNIX;
143842565f2Syasuoka 	sun.sun_len = sizeof(sun);
144842565f2Syasuoka 	strlcpy(sun.sun_path, RADIUSD_SOCK, sizeof(sun.sun_path));
145842565f2Syasuoka 
146842565f2Syasuoka 	if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
147842565f2Syasuoka 		err(EX_OSERR, "socket");
148842565f2Syasuoka 	if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
149842565f2Syasuoka 		err(EX_OSERR, "connect");
150*882428cdSclaudio 	if (imsgbuf_init(&ibuf, sock) == -1)
151*882428cdSclaudio 		err(EX_OSERR, "imsgbuf_init");
152842565f2Syasuoka 
153842565f2Syasuoka 	res = parse(argc, argv);
154842565f2Syasuoka 	if (res == NULL)
155842565f2Syasuoka 		exit(EX_USAGE);
156842565f2Syasuoka 
157842565f2Syasuoka 	switch (res->action) {
158842565f2Syasuoka 	case TEST:
159842565f2Syasuoka 	case NONE:
160842565f2Syasuoka 		abort();
161842565f2Syasuoka 		break;
162842565f2Syasuoka 	case IPCP_SHOW:
163842565f2Syasuoka 	case IPCP_DUMP:
164842565f2Syasuoka 	case IPCP_MONITOR:
165842565f2Syasuoka 		memset(module_name, 0, sizeof(module_name));
166842565f2Syasuoka 		strlcpy(module_name, "ipcp",
167842565f2Syasuoka 		    sizeof(module_name));
168842565f2Syasuoka 		iov[niov].iov_base = module_name;
169842565f2Syasuoka 		iov[niov++].iov_len = RADIUSD_MODULE_NAME_LEN;
170842565f2Syasuoka 		imsg_composev(&ibuf, (res->action == IPCP_MONITOR)?
171842565f2Syasuoka 		    IMSG_RADIUSD_MODULE_IPCP_MONITOR :
172842565f2Syasuoka 		    IMSG_RADIUSD_MODULE_IPCP_DUMP, 0, 0, -1, iov, niov);
173842565f2Syasuoka 		break;
174eff8f878Syasuoka 	case IPCP_DELETE:
175842565f2Syasuoka 	case IPCP_DISCONNECT:
176842565f2Syasuoka 		memset(module_name, 0, sizeof(module_name));
177842565f2Syasuoka 		strlcpy(module_name, "ipcp",
178842565f2Syasuoka 		    sizeof(module_name));
179842565f2Syasuoka 		iov[niov].iov_base = module_name;
180842565f2Syasuoka 		iov[niov++].iov_len = RADIUSD_MODULE_NAME_LEN;
181842565f2Syasuoka 		iov[niov].iov_base = &res->session_seq;
182842565f2Syasuoka 		iov[niov++].iov_len = sizeof(res->session_seq);
183eff8f878Syasuoka 		imsg_composev(&ibuf,
184eff8f878Syasuoka 		    (res->action == IPCP_DELETE)
185eff8f878Syasuoka 		    ? IMSG_RADIUSD_MODULE_IPCP_DELETE
186eff8f878Syasuoka 		    : IMSG_RADIUSD_MODULE_IPCP_DISCONNECT, 0, 0, -1, iov, niov);
187842565f2Syasuoka 		break;
188842565f2Syasuoka 	}
189dd7efffeSclaudio 	if (imsgbuf_flush(&ibuf) == -1)
190dd7efffeSclaudio 		err(1, "ibuf_ctl: imsgbuf_flush error");
191842565f2Syasuoka 	while (!done) {
1924f3fb1ffSclaudio 		if (imsgbuf_read(&ibuf) != 1)
193842565f2Syasuoka 			break;
194842565f2Syasuoka 		for (;;) {
195842565f2Syasuoka 			if ((n = imsg_get(&ibuf, &imsg)) <= 0) {
196842565f2Syasuoka 				if (n != 0)
197842565f2Syasuoka 					done = 1;
198842565f2Syasuoka 				break;
199842565f2Syasuoka 			}
200842565f2Syasuoka 			switch (res->action) {
201842565f2Syasuoka 			case IPCP_SHOW:
202842565f2Syasuoka 			case IPCP_DUMP:
203842565f2Syasuoka 			case IPCP_MONITOR:
204eff8f878Syasuoka 			case IPCP_DELETE:
205c0127aecSyasuoka 			case IPCP_DISCONNECT:
206842565f2Syasuoka 				done = ipcp_handle_imsg(res, &imsg, cnt++);
207842565f2Syasuoka 				break;
208842565f2Syasuoka 			default:
209842565f2Syasuoka 				break;
210842565f2Syasuoka 			}
211842565f2Syasuoka 			imsg_free(&imsg);
212842565f2Syasuoka 			if (done)
213842565f2Syasuoka 				break;
214842565f2Syasuoka 
215842565f2Syasuoka 		}
216842565f2Syasuoka 	}
217842565f2Syasuoka 	close(sock);
218842565f2Syasuoka 
219842565f2Syasuoka 	exit(EXIT_SUCCESS);
220a7ca44b8Syasuoka }
221a7ca44b8Syasuoka 
222842565f2Syasuoka /***********************************************************************
223842565f2Syasuoka  * "test"
224842565f2Syasuoka  ***********************************************************************/
2255d013a5eSdlg struct radius_test {
2265d013a5eSdlg 	const struct parse_result	*res;
2275d013a5eSdlg 	int				 ecode;
2285d013a5eSdlg 
2295d013a5eSdlg 	RADIUS_PACKET			*reqpkt;
2305d013a5eSdlg 	int				 sock;
2315d013a5eSdlg 	unsigned int			 tries;
2325d013a5eSdlg 	struct event			 ev_send;
2335d013a5eSdlg 	struct event			 ev_recv;
2345d013a5eSdlg 	struct event			 ev_timedout;
2355d013a5eSdlg };
2365d013a5eSdlg 
2375d013a5eSdlg static void	radius_test_send(int, short, void *);
2385d013a5eSdlg static void	radius_test_recv(int, short, void *);
2395d013a5eSdlg static void	radius_test_timedout(int, short, void *);
2405d013a5eSdlg 
2415d013a5eSdlg static int
242a7ca44b8Syasuoka radius_test(struct parse_result *res)
243a7ca44b8Syasuoka {
2445d013a5eSdlg 	struct radius_test	 test = { .res = res };
2455d013a5eSdlg 	RADIUS_PACKET		*reqpkt;
246a7ca44b8Syasuoka 	struct addrinfo		 hints, *ai;
247a7ca44b8Syasuoka 	int			 sock, retval;
248a7ca44b8Syasuoka 	struct sockaddr_storage	 sockaddr;
249a7ca44b8Syasuoka 	socklen_t		 sockaddrlen;
250a7ca44b8Syasuoka 	struct sockaddr_in	*sin4;
251a7ca44b8Syasuoka 	struct sockaddr_in6	*sin6;
252a7ca44b8Syasuoka 	uint32_t		 u32val;
253a7ca44b8Syasuoka 	uint8_t			 id;
254a7ca44b8Syasuoka 
255a7ca44b8Syasuoka 	reqpkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST);
256a7ca44b8Syasuoka 	if (reqpkt == NULL)
257a7ca44b8Syasuoka 		err(1, "radius_new_request_packet");
258a7ca44b8Syasuoka 	id = arc4random();
259a7ca44b8Syasuoka 	radius_set_id(reqpkt, id);
260a7ca44b8Syasuoka 
261a7ca44b8Syasuoka 	memset(&hints, 0, sizeof(hints));
262a7ca44b8Syasuoka 	hints.ai_family = PF_UNSPEC;
263a7ca44b8Syasuoka 	hints.ai_socktype = SOCK_DGRAM;
264a7ca44b8Syasuoka 
265a7ca44b8Syasuoka 	retval = getaddrinfo(res->hostname, "radius", &hints, &ai);
266a7ca44b8Syasuoka 	if (retval)
267a7ca44b8Syasuoka 		errx(1, "%s %s", res->hostname, gai_strerror(retval));
268a7ca44b8Syasuoka 
269a7ca44b8Syasuoka 	if (res->port != 0)
270a7ca44b8Syasuoka 		((struct sockaddr_in *)ai->ai_addr)->sin_port =
271a7ca44b8Syasuoka 		    htons(res->port);
272a7ca44b8Syasuoka 
2735d013a5eSdlg 	sock = socket(ai->ai_family, ai->ai_socktype | SOCK_NONBLOCK,
2745d013a5eSdlg 	    ai->ai_protocol);
275a7ca44b8Syasuoka 	if (sock == -1)
276a7ca44b8Syasuoka 		err(1, "socket");
277a7ca44b8Syasuoka 
278a7ca44b8Syasuoka 	/* Prepare NAS-IP{,V6}-ADDRESS attribute */
279a7ca44b8Syasuoka 	if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1)
280a7ca44b8Syasuoka 		err(1, "connect");
281a7ca44b8Syasuoka 	sockaddrlen = sizeof(sockaddr);
282a7ca44b8Syasuoka 	if (getsockname(sock, (struct sockaddr *)&sockaddr, &sockaddrlen) == -1)
283a7ca44b8Syasuoka 		err(1, "getsockname");
284a7ca44b8Syasuoka 	sin4 = (struct sockaddr_in *)&sockaddr;
285a7ca44b8Syasuoka 	sin6 = (struct sockaddr_in6 *)&sockaddr;
286a7ca44b8Syasuoka 	switch (sockaddr.ss_family) {
287a7ca44b8Syasuoka 	case AF_INET:
288a7ca44b8Syasuoka 		radius_put_ipv4_attr(reqpkt, RADIUS_TYPE_NAS_IP_ADDRESS,
289a7ca44b8Syasuoka 		    sin4->sin_addr);
290a7ca44b8Syasuoka 		break;
291a7ca44b8Syasuoka 	case AF_INET6:
292a7ca44b8Syasuoka 		radius_put_raw_attr(reqpkt, RADIUS_TYPE_NAS_IPV6_ADDRESS,
293a7ca44b8Syasuoka 		    sin6->sin6_addr.s6_addr, sizeof(sin6->sin6_addr.s6_addr));
294a7ca44b8Syasuoka 		break;
295a7ca44b8Syasuoka 	}
296a7ca44b8Syasuoka 
297a7ca44b8Syasuoka 	/* User-Name and User-Password */
298a7ca44b8Syasuoka 	radius_put_string_attr(reqpkt, RADIUS_TYPE_USER_NAME,
299a7ca44b8Syasuoka 	    res->username);
300a7ca44b8Syasuoka 
301a7ca44b8Syasuoka 	switch (res->auth_method) {
302a7ca44b8Syasuoka 	case PAP:
303a7ca44b8Syasuoka 		if (res->password != NULL)
304a7ca44b8Syasuoka 			radius_put_user_password_attr(reqpkt, res->password,
305a7ca44b8Syasuoka 			    res->secret);
306a7ca44b8Syasuoka 		break;
307a7ca44b8Syasuoka 	case CHAP:
308a7ca44b8Syasuoka 	    {
309a7ca44b8Syasuoka 		u_char	 chal[16];
310a7ca44b8Syasuoka 		u_char	 resp[1 + MD5_DIGEST_LENGTH]; /* "1 + " for CHAP Id */
311a7ca44b8Syasuoka 		MD5_CTX	 md5ctx;
312a7ca44b8Syasuoka 
3137aed478bSyasuoka 		arc4random_buf(chal, sizeof(chal));
314a7ca44b8Syasuoka 		arc4random_buf(resp, 1);	/* CHAP Id is random */
315a7ca44b8Syasuoka 		MD5Init(&md5ctx);
316a7ca44b8Syasuoka 		MD5Update(&md5ctx, resp, 1);
317a7ca44b8Syasuoka 		if (res->password != NULL)
318a7ca44b8Syasuoka 			MD5Update(&md5ctx, res->password,
319a7ca44b8Syasuoka 			    strlen(res->password));
320a7ca44b8Syasuoka 		MD5Update(&md5ctx, chal, sizeof(chal));
321a7ca44b8Syasuoka 		MD5Final(resp + 1, &md5ctx);
322a7ca44b8Syasuoka 		radius_put_raw_attr(reqpkt, RADIUS_TYPE_CHAP_CHALLENGE,
323a7ca44b8Syasuoka 		    chal, sizeof(chal));
324a7ca44b8Syasuoka 		radius_put_raw_attr(reqpkt, RADIUS_TYPE_CHAP_PASSWORD,
325a7ca44b8Syasuoka 		    resp, sizeof(resp));
326a7ca44b8Syasuoka 	    }
327a7ca44b8Syasuoka 		break;
328a7ca44b8Syasuoka 	case MSCHAPV2:
329a7ca44b8Syasuoka 	    {
330a7ca44b8Syasuoka 		u_char	pass[256], chal[16];
331a7ca44b8Syasuoka 		u_int	i, lpass;
332a7ca44b8Syasuoka 		struct _resp {
333a7ca44b8Syasuoka 			u_int8_t ident;
334a7ca44b8Syasuoka 			u_int8_t flags;
335a7ca44b8Syasuoka 			char peer_challenge[16];
336a7ca44b8Syasuoka 			char reserved[8];
337a7ca44b8Syasuoka 			char response[24];
338a7ca44b8Syasuoka 		} __packed resp;
339a7ca44b8Syasuoka 
340a7ca44b8Syasuoka 		if (res->password == NULL) {
341a7ca44b8Syasuoka 			lpass = 0;
342a7ca44b8Syasuoka 		} else {
343a7ca44b8Syasuoka 			lpass = strlen(res->password);
344a7ca44b8Syasuoka 			if (lpass * 2 >= sizeof(pass))
345a7ca44b8Syasuoka 				err(1, "password too long");
346a7ca44b8Syasuoka 			for (i = 0; i < lpass; i++) {
347a7ca44b8Syasuoka 				pass[i * 2] = res->password[i];
348a7ca44b8Syasuoka 				pass[i * 2 + 1] = 0;
349a7ca44b8Syasuoka 			}
350a7ca44b8Syasuoka 		}
351a7ca44b8Syasuoka 
352a7ca44b8Syasuoka 		memset(&resp, 0, sizeof(resp));
353a7ca44b8Syasuoka 		resp.ident = arc4random();
354a7ca44b8Syasuoka 		arc4random_buf(chal, sizeof(chal));
355a7ca44b8Syasuoka 		arc4random_buf(resp.peer_challenge,
356a7ca44b8Syasuoka 		    sizeof(resp.peer_challenge));
357a7ca44b8Syasuoka 
358a7ca44b8Syasuoka 		mschap_nt_response(chal, resp.peer_challenge,
359a7ca44b8Syasuoka 		    (char *)res->username, strlen(res->username), pass,
360a7ca44b8Syasuoka 		    lpass * 2, resp.response);
361a7ca44b8Syasuoka 
362a7ca44b8Syasuoka 		radius_put_vs_raw_attr(reqpkt, RADIUS_VENDOR_MICROSOFT,
363a7ca44b8Syasuoka 		    RADIUS_VTYPE_MS_CHAP_CHALLENGE, chal, sizeof(chal));
364a7ca44b8Syasuoka 		radius_put_vs_raw_attr(reqpkt, RADIUS_VENDOR_MICROSOFT,
365a7ca44b8Syasuoka 		    RADIUS_VTYPE_MS_CHAP2_RESPONSE, &resp, sizeof(resp));
366a7ca44b8Syasuoka 		explicit_bzero(pass, sizeof(pass));
367a7ca44b8Syasuoka 	    }
368a7ca44b8Syasuoka 		break;
369a7ca44b8Syasuoka 
370a7ca44b8Syasuoka 	}
371a7ca44b8Syasuoka 	u32val = htonl(res->nas_port);
372a7ca44b8Syasuoka 	radius_put_raw_attr(reqpkt, RADIUS_TYPE_NAS_PORT, &u32val, 4);
373a7ca44b8Syasuoka 
374a852e27aSyasuoka 	if (res->msgauth)
375a7ca44b8Syasuoka 		radius_put_message_authenticator(reqpkt, res->secret);
376a7ca44b8Syasuoka 
3775d013a5eSdlg 	event_init();
3785d013a5eSdlg 
3795d013a5eSdlg 	test.ecode = EXIT_FAILURE;
3805d013a5eSdlg 	test.res = res;
3815d013a5eSdlg 	test.sock = sock;
3825d013a5eSdlg 	test.reqpkt = reqpkt;
3835d013a5eSdlg 
3845d013a5eSdlg 	event_set(&test.ev_recv, sock, EV_READ|EV_PERSIST,
3855d013a5eSdlg 	    radius_test_recv, &test);
3865d013a5eSdlg 
3875d013a5eSdlg 	evtimer_set(&test.ev_send, radius_test_send, &test);
3885d013a5eSdlg 	evtimer_set(&test.ev_timedout, radius_test_timedout, &test);
3895d013a5eSdlg 
3905d013a5eSdlg 	event_add(&test.ev_recv, NULL);
3915d013a5eSdlg 	evtimer_add(&test.ev_timedout, &res->maxwait);
3925d013a5eSdlg 
393a7ca44b8Syasuoka 	/* Send! */
394a7ca44b8Syasuoka 	fprintf(stderr, "Sending:\n");
395a7ca44b8Syasuoka 	radius_dump(stdout, reqpkt, false, res->secret);
3965d013a5eSdlg 	radius_test_send(0, EV_TIMEOUT, &test);
3975d013a5eSdlg 
3985d013a5eSdlg 	event_dispatch();
399a7ca44b8Syasuoka 
400a7ca44b8Syasuoka 	/* Release the resources */
401a7ca44b8Syasuoka 	radius_delete_packet(reqpkt);
402a7ca44b8Syasuoka 	close(sock);
403a7ca44b8Syasuoka 	freeaddrinfo(ai);
404a7ca44b8Syasuoka 
405a7ca44b8Syasuoka 	explicit_bzero((char *)res->secret, strlen(res->secret));
406a7ca44b8Syasuoka 	if (res->password)
407a7ca44b8Syasuoka 		explicit_bzero((char *)res->password, strlen(res->password));
408a7ca44b8Syasuoka 
4095d013a5eSdlg 	return (test.ecode);
4105d013a5eSdlg }
4115d013a5eSdlg 
4125d013a5eSdlg static void
4135d013a5eSdlg radius_test_send(int thing, short revents, void *arg)
4145d013a5eSdlg {
4155d013a5eSdlg 	struct radius_test	*test = arg;
4165d013a5eSdlg 	RADIUS_PACKET		*reqpkt = test->reqpkt;
4175d013a5eSdlg 	ssize_t			 rv;
4185d013a5eSdlg 
4195d013a5eSdlg retry:
4205d013a5eSdlg 	rv = send(test->sock,
4215d013a5eSdlg 	    radius_get_data(reqpkt), radius_get_length(reqpkt), 0);
4225d013a5eSdlg 	if (rv == -1) {
4235d013a5eSdlg 		switch (errno) {
4245d013a5eSdlg 		case EINTR:
4255d013a5eSdlg 		case EAGAIN:
4265d013a5eSdlg 			goto retry;
4275d013a5eSdlg 		default:
4285d013a5eSdlg 			break;
4295d013a5eSdlg 		}
4305d013a5eSdlg 
4315d013a5eSdlg 		warn("send");
4325d013a5eSdlg 	}
4335d013a5eSdlg 
4345d013a5eSdlg 	if (++test->tries >= test->res->tries)
435a7ca44b8Syasuoka 		return;
4365d013a5eSdlg 
4375d013a5eSdlg 	evtimer_add(&test->ev_send, &test->res->interval);
4385d013a5eSdlg }
4395d013a5eSdlg 
4405d013a5eSdlg static void
4415d013a5eSdlg radius_test_recv(int sock, short revents, void *arg)
4425d013a5eSdlg {
4435d013a5eSdlg 	struct radius_test	*test = arg;
4445d013a5eSdlg 	RADIUS_PACKET		*respkt;
4455d013a5eSdlg 	RADIUS_PACKET		*reqpkt = test->reqpkt;
4465d013a5eSdlg 
4475d013a5eSdlg retry:
4485d013a5eSdlg 	respkt = radius_recv(sock, 0);
4495d013a5eSdlg 	if (respkt == NULL) {
4505d013a5eSdlg 		switch (errno) {
4515d013a5eSdlg 		case EINTR:
4525d013a5eSdlg 		case EAGAIN:
4535d013a5eSdlg 			goto retry;
4545d013a5eSdlg 		default:
4555d013a5eSdlg 			break;
4565d013a5eSdlg 		}
4575d013a5eSdlg 
4585d013a5eSdlg 		warn("recv");
4595d013a5eSdlg 		return;
4605d013a5eSdlg 	}
4615d013a5eSdlg 
4625d013a5eSdlg 	radius_set_request_packet(respkt, reqpkt);
4635d013a5eSdlg 	if (radius_get_id(respkt) == radius_get_id(reqpkt)) {
4645d013a5eSdlg 		fprintf(stderr, "\nReceived:\n");
4655d013a5eSdlg 		radius_dump(stdout, respkt, true, test->res->secret);
4665d013a5eSdlg 
4675d013a5eSdlg 		event_del(&test->ev_recv);
4685d013a5eSdlg 		evtimer_del(&test->ev_send);
4695d013a5eSdlg 		evtimer_del(&test->ev_timedout);
4705d013a5eSdlg 		test->ecode = EXIT_SUCCESS;
4715d013a5eSdlg 	}
4725d013a5eSdlg 
4735d013a5eSdlg 	radius_delete_packet(respkt);
4745d013a5eSdlg }
4755d013a5eSdlg 
4765d013a5eSdlg static void
4775d013a5eSdlg radius_test_timedout(int thing, short revents, void *arg)
4785d013a5eSdlg {
4795d013a5eSdlg 	struct radius_test	*test = arg;
4805d013a5eSdlg 
4815d013a5eSdlg 	event_del(&test->ev_recv);
482a7ca44b8Syasuoka }
483a7ca44b8Syasuoka 
484a7ca44b8Syasuoka static void
485a7ca44b8Syasuoka radius_dump(FILE *out, RADIUS_PACKET *pkt, bool resp, const char *secret)
486a7ca44b8Syasuoka {
487a7ca44b8Syasuoka 	size_t		 len;
488a7ca44b8Syasuoka 	char		 buf[256], buf1[256];
489a7ca44b8Syasuoka 	uint32_t	 u32val;
490a7ca44b8Syasuoka 	struct in_addr	 ipv4;
491a7ca44b8Syasuoka 
492a7ca44b8Syasuoka 	fprintf(out,
493a7ca44b8Syasuoka 	    "    Id                        = %d\n"
494a7ca44b8Syasuoka 	    "    Code                      = %s(%d)\n",
495a7ca44b8Syasuoka 	    (int)radius_get_id(pkt), radius_code_str((int)radius_get_code(pkt)),
496a7ca44b8Syasuoka 	    (int)radius_get_code(pkt));
497b9225786Syasuoka 	if (resp && secret) {
498b9225786Syasuoka 		fprintf(out, "    Authenticator             = %s\n",
499a7ca44b8Syasuoka 		    (radius_check_response_authenticator(pkt, secret) == 0)
500a7ca44b8Syasuoka 		    ? "Verified" : "NG");
501b9225786Syasuoka 		fprintf(out, "    Message-Authenticator     = %s\n",
502b9225786Syasuoka 		    (!radius_has_attr(pkt, RADIUS_TYPE_MESSAGE_AUTHENTICATOR))
503b9225786Syasuoka 		    ? "(Not present)"
504b9225786Syasuoka 		    : (radius_check_message_authenticator(pkt, secret) == 0)
505b9225786Syasuoka 		    ? "Verified" : "NG");
506b9225786Syasuoka 	}
507a852e27aSyasuoka 	if (!resp)
508a852e27aSyasuoka 		fprintf(out, "    Message-Authenticator     = %s\n",
509a852e27aSyasuoka 		    (radius_has_attr(pkt, RADIUS_TYPE_MESSAGE_AUTHENTICATOR))
510a852e27aSyasuoka 		    ? "(Present)" : "(Not present)");
511a7ca44b8Syasuoka 
512a7ca44b8Syasuoka 	if (radius_get_string_attr(pkt, RADIUS_TYPE_USER_NAME, buf,
513a7ca44b8Syasuoka 	    sizeof(buf)) == 0)
514a7ca44b8Syasuoka 		fprintf(out, "    User-Name                 = \"%s\"\n", buf);
515a7ca44b8Syasuoka 
516a7ca44b8Syasuoka 	if (secret &&
517a7ca44b8Syasuoka 	    radius_get_user_password_attr(pkt, buf, sizeof(buf), secret) == 0)
518a7ca44b8Syasuoka 		fprintf(out, "    User-Password             = \"%s\"\n", buf);
519a7ca44b8Syasuoka 
520a7ca44b8Syasuoka 	memset(buf, 0, sizeof(buf));
521a7ca44b8Syasuoka 	len = sizeof(buf);
522a7ca44b8Syasuoka 	if (radius_get_raw_attr(pkt, RADIUS_TYPE_CHAP_PASSWORD, buf, &len)
523a7ca44b8Syasuoka 	    == 0)
524a7ca44b8Syasuoka 		fprintf(out, "    CHAP-Password             = %s\n",
52583aeedf6Syasuoka 		    (hexstr(buf, len, buf1, sizeof(buf1)))
52683aeedf6Syasuoka 			    ? buf1 : "(too long)");
527a7ca44b8Syasuoka 
528a7ca44b8Syasuoka 	memset(buf, 0, sizeof(buf));
529a7ca44b8Syasuoka 	len = sizeof(buf);
530a7ca44b8Syasuoka 	if (radius_get_raw_attr(pkt, RADIUS_TYPE_CHAP_CHALLENGE, buf, &len)
531a7ca44b8Syasuoka 	    == 0)
532a7ca44b8Syasuoka 		fprintf(out, "    CHAP-Challenge            = %s\n",
53383aeedf6Syasuoka 		    (hexstr(buf, len, buf1, sizeof(buf1)))
53483aeedf6Syasuoka 			? buf1 : "(too long)");
535a7ca44b8Syasuoka 
536a7ca44b8Syasuoka 	memset(buf, 0, sizeof(buf));
537a7ca44b8Syasuoka 	len = sizeof(buf);
538a7ca44b8Syasuoka 	if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT,
539a7ca44b8Syasuoka 	    RADIUS_VTYPE_MS_CHAP_CHALLENGE, buf, &len) == 0)
540a7ca44b8Syasuoka 		fprintf(out, "    MS-CHAP-Challenge         = %s\n",
54183aeedf6Syasuoka 		    (hexstr(buf, len, buf1, sizeof(buf1)))
54283aeedf6Syasuoka 			? buf1 : "(too long)");
543a7ca44b8Syasuoka 
544a7ca44b8Syasuoka 	memset(buf, 0, sizeof(buf));
545a7ca44b8Syasuoka 	len = sizeof(buf);
546a7ca44b8Syasuoka 	if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT,
547a7ca44b8Syasuoka 	    RADIUS_VTYPE_MS_CHAP2_RESPONSE, buf, &len) == 0)
548a7ca44b8Syasuoka 		fprintf(out, "    MS-CHAP2-Response         = %s\n",
549a7ca44b8Syasuoka 		    (hexstr(buf, len, buf1, sizeof(buf1)))
550a7ca44b8Syasuoka 		    ? buf1 : "(too long)");
551a7ca44b8Syasuoka 
552a7ca44b8Syasuoka 	memset(buf, 0, sizeof(buf));
553a7ca44b8Syasuoka 	len = sizeof(buf) - 1;
554a7ca44b8Syasuoka 	if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT,
555a7ca44b8Syasuoka 	    RADIUS_VTYPE_MS_CHAP2_SUCCESS, buf, &len) == 0) {
556a7ca44b8Syasuoka 		fprintf(out, "    MS-CHAP-Success           = Id=%u \"%s\"\n",
557a7ca44b8Syasuoka 		    (u_int)(u_char)buf[0], buf + 1);
558a7ca44b8Syasuoka 	}
559a7ca44b8Syasuoka 
560a7ca44b8Syasuoka 	memset(buf, 0, sizeof(buf));
561a7ca44b8Syasuoka 	len = sizeof(buf) - 1;
562a7ca44b8Syasuoka 	if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT,
563a7ca44b8Syasuoka 	    RADIUS_VTYPE_MS_CHAP_ERROR, buf, &len) == 0) {
564a7ca44b8Syasuoka 		fprintf(out, "    MS-CHAP-Error             = Id=%u \"%s\"\n",
565a7ca44b8Syasuoka 		    (u_int)(u_char)buf[0], buf + 1);
566a7ca44b8Syasuoka 	}
567a7ca44b8Syasuoka 
568a7ca44b8Syasuoka 	memset(buf, 0, sizeof(buf));
569a7ca44b8Syasuoka 	len = sizeof(buf);
570a7ca44b8Syasuoka 	if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT,
571a7ca44b8Syasuoka 	    RADIUS_VTYPE_MPPE_SEND_KEY, buf, &len) == 0)
572a7ca44b8Syasuoka 		fprintf(out, "    MS-MPPE-Send-Key          = %s\n",
573a7ca44b8Syasuoka 		    (hexstr(buf, len, buf1, sizeof(buf1)))
574a7ca44b8Syasuoka 		    ? buf1 : "(too long)");
575a7ca44b8Syasuoka 
576a7ca44b8Syasuoka 	memset(buf, 0, sizeof(buf));
577a7ca44b8Syasuoka 	len = sizeof(buf);
578a7ca44b8Syasuoka 	if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT,
579a7ca44b8Syasuoka 	    RADIUS_VTYPE_MPPE_RECV_KEY, buf, &len) == 0)
580a7ca44b8Syasuoka 		fprintf(out, "    MS-MPPE-Recv-Key          = %s\n",
581a7ca44b8Syasuoka 		    (hexstr(buf, len, buf1, sizeof(buf1)))
582a7ca44b8Syasuoka 		    ? buf1 : "(too long)");
583a7ca44b8Syasuoka 
584a7ca44b8Syasuoka 	memset(buf, 0, sizeof(buf));
585a7ca44b8Syasuoka 	len = sizeof(buf);
586a7ca44b8Syasuoka 	if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT,
587a7ca44b8Syasuoka 	    RADIUS_VTYPE_MPPE_ENCRYPTION_POLICY, buf, &len) == 0)
588a7ca44b8Syasuoka 		fprintf(out, "    MS-MPPE-Encryption-Policy = 0x%08x\n",
589a7ca44b8Syasuoka 		    ntohl(*(u_long *)buf));
590a7ca44b8Syasuoka 
591a7ca44b8Syasuoka 	memset(buf, 0, sizeof(buf));
592a7ca44b8Syasuoka 	len = sizeof(buf);
593a7ca44b8Syasuoka 	if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT,
594a7ca44b8Syasuoka 	    RADIUS_VTYPE_MPPE_ENCRYPTION_TYPES, buf, &len) == 0)
595a7ca44b8Syasuoka 		fprintf(out, "    MS-MPPE-Encryption-Types  = 0x%08x\n",
596a7ca44b8Syasuoka 		    ntohl(*(u_long *)buf));
597a7ca44b8Syasuoka 
598a7ca44b8Syasuoka 	if (radius_get_string_attr(pkt, RADIUS_TYPE_REPLY_MESSAGE, buf,
599a7ca44b8Syasuoka 	    sizeof(buf)) == 0)
600a7ca44b8Syasuoka 		fprintf(out, "    Reply-Message             = \"%s\"\n", buf);
601a7ca44b8Syasuoka 
602a7ca44b8Syasuoka 	memset(buf, 0, sizeof(buf));
603a7ca44b8Syasuoka 	len = sizeof(buf);
604a7ca44b8Syasuoka 	if (radius_get_uint32_attr(pkt, RADIUS_TYPE_NAS_PORT, &u32val) == 0)
605a7ca44b8Syasuoka 		fprintf(out, "    NAS-Port                  = %lu\n",
606a7ca44b8Syasuoka 		    (u_long)u32val);
607a7ca44b8Syasuoka 
608a7ca44b8Syasuoka 	memset(buf, 0, sizeof(buf));
609a7ca44b8Syasuoka 	len = sizeof(buf);
610a7ca44b8Syasuoka 	if (radius_get_ipv4_attr(pkt, RADIUS_TYPE_NAS_IP_ADDRESS, &ipv4) == 0)
611a7ca44b8Syasuoka 		fprintf(out, "    NAS-IP-Address            = %s\n",
612a7ca44b8Syasuoka 		    inet_ntoa(ipv4));
613a7ca44b8Syasuoka 
614a7ca44b8Syasuoka 	memset(buf, 0, sizeof(buf));
615a7ca44b8Syasuoka 	len = sizeof(buf);
616a7ca44b8Syasuoka 	if (radius_get_raw_attr(pkt, RADIUS_TYPE_NAS_IPV6_ADDRESS, buf, &len)
617a7ca44b8Syasuoka 	    == 0)
618a7ca44b8Syasuoka 		fprintf(out, "    NAS-IPv6-Address          = %s\n",
619a7ca44b8Syasuoka 		    inet_ntop(AF_INET6, buf, buf1, len));
620a7ca44b8Syasuoka 
621a7ca44b8Syasuoka }
622a7ca44b8Syasuoka 
623842565f2Syasuoka /***********************************************************************
624842565f2Syasuoka  * ipcp
625842565f2Syasuoka  ***********************************************************************/
626842565f2Syasuoka int
627842565f2Syasuoka ipcp_handle_imsg(struct parse_result *res, struct imsg *imsg, int cnt)
628842565f2Syasuoka {
629842565f2Syasuoka 	ssize_t				 datalen;
630842565f2Syasuoka 	struct radiusd_ipcp_db_dump	*dump;
631842565f2Syasuoka 	struct radiusd_ipcp_statistics	*stat;
632842565f2Syasuoka 	int				 done = 0;
633842565f2Syasuoka 
634842565f2Syasuoka 	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
635842565f2Syasuoka 	switch (imsg->hdr.type) {
636c0127aecSyasuoka 	case IMSG_OK:
637c0127aecSyasuoka 		if (datalen > 0 && *((char *)imsg->data + datalen - 1) == '\0')
638c0127aecSyasuoka 			fprintf(stderr, "OK: %s\n", (char *)imsg->data);
639c0127aecSyasuoka 		else
640c0127aecSyasuoka 			fprintf(stderr, "OK\n");
641c0127aecSyasuoka 		done = 1;
642c0127aecSyasuoka 		break;
643842565f2Syasuoka 	case IMSG_NG:
644842565f2Syasuoka 		if (datalen > 0 && *((char *)imsg->data + datalen - 1) == '\0')
645842565f2Syasuoka 			fprintf(stderr, "error: %s\n", (char *)imsg->data);
646842565f2Syasuoka 		else
647842565f2Syasuoka 			fprintf(stderr, "error\n");
648842565f2Syasuoka 		exit(EXIT_FAILURE);
649842565f2Syasuoka 	case IMSG_RADIUSD_MODULE_IPCP_DUMP:
650842565f2Syasuoka 		if ((size_t)datalen < sizeof(struct
651842565f2Syasuoka 		    radiusd_ipcp_db_dump))
652842565f2Syasuoka 			errx(1, "received a message which size is invalid");
653842565f2Syasuoka 		dump = imsg->data;
654842565f2Syasuoka 		if (res->action == IPCP_SHOW)
655842565f2Syasuoka 			ipcp_handle_show(dump, datalen, (cnt++ == 0)? 1 : 0);
656842565f2Syasuoka 		else {
657842565f2Syasuoka 			if (res->flags & FLAGS_JSON)
658842565f2Syasuoka 				ipcp_handle_jsons(dump, datalen,
659842565f2Syasuoka 				    (cnt++ == 0)? 1 : 0);
660842565f2Syasuoka 			else
661842565f2Syasuoka 				ipcp_handle_dumps(dump, datalen,
662842565f2Syasuoka 				    (cnt++ == 0)? 1 : 0);
663842565f2Syasuoka 		}
664842565f2Syasuoka 		if (dump->islast &&
665842565f2Syasuoka 		    (res->action == IPCP_SHOW || res->action == IPCP_DUMP))
666842565f2Syasuoka 			done = 1;
667842565f2Syasuoka 		break;
668842565f2Syasuoka 	case IMSG_RADIUSD_MODULE_IPCP_START:
669842565f2Syasuoka 		if ((size_t)datalen < offsetof(struct
670842565f2Syasuoka 		    radiusd_ipcp_db_dump, records[1]))
671842565f2Syasuoka 			errx(1, "received a message which size is invalid");
672842565f2Syasuoka 		dump = imsg->data;
673842565f2Syasuoka 		if (res->flags & FLAGS_JSON)
674842565f2Syasuoka 			ipcp_handle_json(dump, datalen, NULL, 0);
675842565f2Syasuoka 		else {
676842565f2Syasuoka 			printf("Start\n");
677842565f2Syasuoka 			ipcp_handle_dump(dump, datalen, 0);
678842565f2Syasuoka 		}
679842565f2Syasuoka 		break;
680842565f2Syasuoka 	case IMSG_RADIUSD_MODULE_IPCP_STOP:
681842565f2Syasuoka 		if ((size_t)datalen < offsetof(
682842565f2Syasuoka 		    struct radiusd_ipcp_db_dump,
683842565f2Syasuoka 		    records[1]) +
684842565f2Syasuoka 		    sizeof(struct
685842565f2Syasuoka 		    radiusd_ipcp_statistics))
686842565f2Syasuoka 			errx(1, "received a message which size is invalid");
687842565f2Syasuoka 		dump = imsg->data;
688842565f2Syasuoka 		stat = (struct radiusd_ipcp_statistics *)
689842565f2Syasuoka 		    ((char *)imsg->data + offsetof(
690842565f2Syasuoka 			struct radiusd_ipcp_db_dump, records[1]));
691842565f2Syasuoka 		if (res->flags & FLAGS_JSON)
692842565f2Syasuoka 			ipcp_handle_json(dump, datalen, stat, 0);
693842565f2Syasuoka 		else {
694842565f2Syasuoka 			printf("Stop\n");
695842565f2Syasuoka 			ipcp_handle_dump(dump, datalen, 0);
696842565f2Syasuoka 			ipcp_handle_stat(stat);
697842565f2Syasuoka 		}
698842565f2Syasuoka 		break;
699842565f2Syasuoka 	}
700842565f2Syasuoka 
701842565f2Syasuoka 	return (done);
702842565f2Syasuoka }
703842565f2Syasuoka 
704842565f2Syasuoka static void
705842565f2Syasuoka ipcp_handle_show(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, int first)
706842565f2Syasuoka {
707842565f2Syasuoka 	int		 i, width;
708842565f2Syasuoka 	uint32_t	 maxseq = 999;
709842565f2Syasuoka 	char		 buf0[128], buf1[NI_MAXHOST + NI_MAXSERV + 4], buf2[80];
710842565f2Syasuoka 	struct timespec	 upt, now, dif, start;
711842565f2Syasuoka 
712842565f2Syasuoka 	clock_gettime(CLOCK_BOOTTIME, &upt);
713842565f2Syasuoka 	clock_gettime(CLOCK_REALTIME, &now);
714842565f2Syasuoka 	timespecsub(&now, &upt, &upt);
715842565f2Syasuoka 
716842565f2Syasuoka 	for (i = 0; ; i++) {
717842565f2Syasuoka 		if (offsetof(struct radiusd_ipcp_db_dump, records[i])
718842565f2Syasuoka 		    >= dumpsiz)
719842565f2Syasuoka 			break;
720842565f2Syasuoka 		maxseq = MAXIMUM(maxseq, dump->records[i].rec.seq);
721842565f2Syasuoka 	}
722842565f2Syasuoka 	for (width = 0; maxseq != 0; maxseq /= 10, width++)
723842565f2Syasuoka 		;
724842565f2Syasuoka 
725842565f2Syasuoka 	for (i = 0; ; i++) {
726842565f2Syasuoka 		if (offsetof(struct radiusd_ipcp_db_dump, records[i])
727842565f2Syasuoka 		    >= dumpsiz)
728842565f2Syasuoka 			break;
729842565f2Syasuoka 		if (i == 0 && first)
730842565f2Syasuoka 			printf("%-*s Assigned        Username               "
731842565f2Syasuoka 			    "Start    Tunnel From\n"
732842565f2Syasuoka 			    "%.*s --------------- ---------------------- "
733842565f2Syasuoka 			    "-------- %.*s\n", width, "Seq", width,
734842565f2Syasuoka 			    "----------", 28 - width,
735842565f2Syasuoka 			    "-------------------------");
736842565f2Syasuoka 		timespecadd(&upt, &dump->records[i].rec.start, &start);
737842565f2Syasuoka 		timespecsub(&now, &start, &dif);
738842565f2Syasuoka 		printf("%*d %-15s %-22s %-8s %s\n",
739842565f2Syasuoka 		    width, dump->records[i].rec.seq,
740842565f2Syasuoka 		    inet_ntop(dump->records[i].af, &dump->records[i].addr,
741842565f2Syasuoka 		    buf0, sizeof(buf0)), dump->records[i].rec.username,
742842565f2Syasuoka 		    time_short_str(&start, &dif, buf2, sizeof(buf2)),
743842565f2Syasuoka 		    sockaddr_str(
744842565f2Syasuoka 		    (struct sockaddr *)&dump->records[i].rec.tun_client, buf1,
745842565f2Syasuoka 		    sizeof(buf1)));
746842565f2Syasuoka 	}
747842565f2Syasuoka }
748842565f2Syasuoka static void
749842565f2Syasuoka ipcp_handle_dump(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, int idx)
750842565f2Syasuoka {
751842565f2Syasuoka 	struct timespec	 upt, now, dif, start, timeout;
752842565f2Syasuoka 
753842565f2Syasuoka 	clock_gettime(CLOCK_BOOTTIME, &upt);
754842565f2Syasuoka 	clock_gettime(CLOCK_REALTIME, &now);
755842565f2Syasuoka 	timespecsub(&now, &upt, &upt);
756842565f2Syasuoka 
757842565f2Syasuoka 	timespecadd(&upt, &dump->records[idx].rec.start, &start);
758842565f2Syasuoka 	timespecsub(&now, &start, &dif);
759842565f2Syasuoka 
760842565f2Syasuoka 	if (dump->records[idx].rec.start.tv_sec == 0)
761842565f2Syasuoka 		ipcp_handle_dump0(dump, dumpsiz, &dif, &start, NULL, idx);
762842565f2Syasuoka 	else {
763842565f2Syasuoka 		timespecadd(&upt, &dump->records[idx].rec.timeout, &timeout);
764842565f2Syasuoka 		ipcp_handle_dump0(dump, dumpsiz, &dif, &start, &timeout, idx);
765842565f2Syasuoka 	}
766842565f2Syasuoka }
767842565f2Syasuoka 
768842565f2Syasuoka static void
769842565f2Syasuoka ipcp_handle_dump0(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz,
770842565f2Syasuoka     struct timespec *dif, struct timespec *start, struct timespec *timeout,
771842565f2Syasuoka     int idx)
772842565f2Syasuoka {
773842565f2Syasuoka 	char		 buf0[128], buf1[NI_MAXHOST + NI_MAXSERV + 4], buf2[80];
774842565f2Syasuoka 
775842565f2Syasuoka 	printf(
776842565f2Syasuoka 	    "    Sequence Number     : %u\n"
777842565f2Syasuoka 	    "    Session Id          : %s\n"
778842565f2Syasuoka 	    "    Username            : %s\n"
779842565f2Syasuoka 	    "    Auth Method         : %s\n"
780842565f2Syasuoka 	    "    Assigned IP Address : %s\n"
781842565f2Syasuoka 	    "    Start Time          : %s\n"
782842565f2Syasuoka 	    "    Elapsed Time        : %lld second%s%s\n",
783842565f2Syasuoka 	    dump->records[idx].rec.seq, dump->records[idx].rec.session_id,
784842565f2Syasuoka 	    dump->records[idx].rec.username, dump->records[idx].rec.auth_method,
785842565f2Syasuoka 	    inet_ntop(dump->records[idx].af, &dump->records[idx].addr, buf0,
786842565f2Syasuoka 	    sizeof(buf0)), time_long_str(start, buf1, sizeof(buf1)),
787842565f2Syasuoka 	    (long long)dif->tv_sec, (dif->tv_sec == 0)? "" : "s",
788842565f2Syasuoka 	    humanize_seconds(dif->tv_sec, buf2, sizeof(buf2)));
789842565f2Syasuoka 	if (timeout != NULL)
790842565f2Syasuoka 		printf("    Timeout             : %s\n",
791842565f2Syasuoka 		    time_long_str(timeout, buf0, sizeof(buf0)));
792842565f2Syasuoka 	printf(
793842565f2Syasuoka 	    "    NAS Identifier      : %s\n"
794842565f2Syasuoka 	    "    Tunnel Type         : %s\n"
795842565f2Syasuoka 	    "    Tunnel From         : %s\n",
796842565f2Syasuoka 	    dump->records[idx].rec.nas_id, dump->records[idx].rec.tun_type,
797842565f2Syasuoka 	    sockaddr_str((struct sockaddr *)
798842565f2Syasuoka 		&dump->records[idx].rec.tun_client, buf1, sizeof(buf1)));
799842565f2Syasuoka }
800842565f2Syasuoka 
801842565f2Syasuoka void
802842565f2Syasuoka ipcp_handle_stat(struct radiusd_ipcp_statistics *stat)
803842565f2Syasuoka {
804842565f2Syasuoka 	printf(
805842565f2Syasuoka 	    "    Terminate Cause     : %s\n"
806842565f2Syasuoka 	    "    Input Packets       : %"PRIu32"\n"
807842565f2Syasuoka 	    "    Output Packets      : %"PRIu32"\n"
808842565f2Syasuoka 	    "    Input Bytes         : %"PRIu64"\n"
809842565f2Syasuoka 	    "    Output Bytes        : %"PRIu64"\n",
810842565f2Syasuoka 	    stat->cause, stat->ipackets, stat->opackets, stat->ibytes,
811842565f2Syasuoka 	    stat->obytes);
812842565f2Syasuoka }
813842565f2Syasuoka 
814842565f2Syasuoka static void
815842565f2Syasuoka ipcp_handle_jsons(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, int first)
816842565f2Syasuoka {
817842565f2Syasuoka 	int		 i;
818842565f2Syasuoka 	struct timespec	 upt, now, dif, start, timeout;
819842565f2Syasuoka 
820842565f2Syasuoka 	clock_gettime(CLOCK_BOOTTIME, &upt);
821842565f2Syasuoka 	clock_gettime(CLOCK_REALTIME, &now);
822842565f2Syasuoka 	timespecsub(&now, &upt, &upt);
823842565f2Syasuoka 
824842565f2Syasuoka 	for (i = 0; ; i++) {
825842565f2Syasuoka 		if (offsetof(struct radiusd_ipcp_db_dump, records[i])
826842565f2Syasuoka 		    >= dumpsiz)
827842565f2Syasuoka 			break;
828842565f2Syasuoka 		timespecadd(&upt, &dump->records[i].rec.start, &start);
829842565f2Syasuoka 		timespecsub(&now, &start, &dif);
830842565f2Syasuoka 		json_do_start(stdout);
831842565f2Syasuoka 		json_do_string("action", "start");
832842565f2Syasuoka 		if (dump->records[i].rec.timeout.tv_sec == 0)
833842565f2Syasuoka 			ipcp_handle_json0(dump, dumpsiz, &dif, &start, NULL, i);
834842565f2Syasuoka 		else {
835842565f2Syasuoka 			timespecadd(&upt, &dump->records[i].rec.timeout,
836842565f2Syasuoka 			    &timeout);
837842565f2Syasuoka 			ipcp_handle_json0(dump, dumpsiz, &dif, &start, &timeout,
838842565f2Syasuoka 			    i);
839842565f2Syasuoka 		}
840842565f2Syasuoka 		json_do_finish();
841842565f2Syasuoka 	}
842842565f2Syasuoka 	fflush(stdout);
843842565f2Syasuoka }
844842565f2Syasuoka 
845842565f2Syasuoka static void
846842565f2Syasuoka ipcp_handle_json(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz,
847842565f2Syasuoka     struct radiusd_ipcp_statistics *stat, int idx)
848842565f2Syasuoka {
849842565f2Syasuoka 	struct timespec	 upt, now, dif, start, timeout;
850842565f2Syasuoka 
851842565f2Syasuoka 	json_do_start(stdout);
852842565f2Syasuoka 	clock_gettime(CLOCK_BOOTTIME, &upt);
853842565f2Syasuoka 	clock_gettime(CLOCK_REALTIME, &now);
854842565f2Syasuoka 	timespecsub(&now, &upt, &upt);
855842565f2Syasuoka 	timespecadd(&upt, &dump->records[idx].rec.start, &start);
856842565f2Syasuoka 	timespecsub(&now, &start, &dif);
857842565f2Syasuoka 
858842565f2Syasuoka 	if (stat == NULL)
859842565f2Syasuoka 		json_do_string("action", "start");
860842565f2Syasuoka 	else
861842565f2Syasuoka 		json_do_string("action", "stop");
862842565f2Syasuoka 	if (dump->records[idx].rec.timeout.tv_sec == 0)
863842565f2Syasuoka 		ipcp_handle_json0(dump, dumpsiz, &dif, &start, NULL, idx);
864842565f2Syasuoka 	else {
865842565f2Syasuoka 		timespecadd(&upt, &dump->records[idx].rec.timeout, &timeout);
866842565f2Syasuoka 		ipcp_handle_json0(dump, dumpsiz, &dif, &start, &timeout, idx);
867842565f2Syasuoka 	}
868842565f2Syasuoka 	if (stat != NULL) {
869842565f2Syasuoka 		json_do_string("terminate-cause", stat->cause);
870842565f2Syasuoka 		json_do_uint("input-packets", stat->ipackets);
871842565f2Syasuoka 		json_do_uint("output-packets", stat->opackets);
872842565f2Syasuoka 		json_do_uint("input-bytes", stat->ibytes);
873842565f2Syasuoka 		json_do_uint("output-bytes", stat->obytes);
874842565f2Syasuoka 	}
875842565f2Syasuoka 	json_do_finish();
876842565f2Syasuoka 	fflush(stdout);
877842565f2Syasuoka }
878842565f2Syasuoka 
879842565f2Syasuoka static void
880842565f2Syasuoka ipcp_handle_json0(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz,
881842565f2Syasuoka     struct timespec *dif, struct timespec *start, struct timespec *timeout,
882842565f2Syasuoka     int idx)
883842565f2Syasuoka {
884842565f2Syasuoka 	char		 buf[128];
885842565f2Syasuoka 
886842565f2Syasuoka 	json_do_uint("sequence-number", dump->records[idx].rec.seq);
887842565f2Syasuoka 	json_do_string("session-id", dump->records[idx].rec.session_id);
888842565f2Syasuoka 	json_do_string("username", dump->records[idx].rec.username);
889842565f2Syasuoka 	json_do_string("auth-method", dump->records[idx].rec.auth_method);
890842565f2Syasuoka 	json_do_string("assigned-ip-address", inet_ntop(dump->records[idx].af,
891842565f2Syasuoka 	    &dump->records[idx].addr, buf, sizeof(buf)));
892842565f2Syasuoka 	json_do_uint("start", start->tv_sec);
893842565f2Syasuoka 	json_do_uint("elapsed", dif->tv_sec);
894842565f2Syasuoka 	if (timeout != NULL)
895842565f2Syasuoka 		json_do_uint("timeout", timeout->tv_sec);
896842565f2Syasuoka 	json_do_string("nas-identifier", dump->records[idx].rec.nas_id);
897842565f2Syasuoka 	json_do_string("tunnel-type", dump->records[idx].rec.tun_type);
898842565f2Syasuoka 	json_do_string("tunnel-from",
899842565f2Syasuoka 	    sockaddr_str((struct sockaddr *)&dump->records[idx].rec.tun_client,
900842565f2Syasuoka 	    buf, sizeof(buf)));
901842565f2Syasuoka }
902842565f2Syasuoka 
903842565f2Syasuoka static void
904842565f2Syasuoka ipcp_handle_dumps(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, int first)
905842565f2Syasuoka {
906842565f2Syasuoka 	static int	 cnt = 0;
907842565f2Syasuoka 	int		 i;
908842565f2Syasuoka 	struct timespec	 upt, now, dif, start, timeout;
909842565f2Syasuoka 
910842565f2Syasuoka 	clock_gettime(CLOCK_BOOTTIME, &upt);
911842565f2Syasuoka 	clock_gettime(CLOCK_REALTIME, &now);
912842565f2Syasuoka 	timespecsub(&now, &upt, &upt);
913842565f2Syasuoka 
914842565f2Syasuoka 	if (first)
915842565f2Syasuoka 		cnt = 0;
916842565f2Syasuoka 	for (i = 0; ; i++, cnt++) {
917842565f2Syasuoka 		if (offsetof(struct radiusd_ipcp_db_dump, records[i])
918842565f2Syasuoka 		    >= dumpsiz)
919842565f2Syasuoka 			break;
920842565f2Syasuoka 		timespecadd(&upt, &dump->records[i].rec.start, &start);
921842565f2Syasuoka 		timespecsub(&now, &start, &dif);
922842565f2Syasuoka 		printf("#%d\n", cnt + 1);
923842565f2Syasuoka 		if (dump->records[i].rec.timeout.tv_sec == 0)
924842565f2Syasuoka 			ipcp_handle_dump0(dump, dumpsiz, &dif, &start, NULL, i);
925842565f2Syasuoka 		else {
926842565f2Syasuoka 			timespecadd(&upt, &dump->records[i].rec.timeout,
927842565f2Syasuoka 			    &timeout);
928842565f2Syasuoka 			ipcp_handle_dump0(dump, dumpsiz, &dif, &start,
929842565f2Syasuoka 			    &timeout, i);
930842565f2Syasuoka 		}
931842565f2Syasuoka 	}
932842565f2Syasuoka }
933842565f2Syasuoka 
934842565f2Syasuoka 
935842565f2Syasuoka /***********************************************************************
936842565f2Syasuoka  * Miscellaneous functions
937842565f2Syasuoka  ***********************************************************************/
938842565f2Syasuoka const char *
939a7ca44b8Syasuoka radius_code_str(int code)
940a7ca44b8Syasuoka {
941a7ca44b8Syasuoka 	int i;
942a7ca44b8Syasuoka 	static struct _codestr {
943a7ca44b8Syasuoka 		int		 code;
944a7ca44b8Syasuoka 		const char	*str;
945a7ca44b8Syasuoka 	} codestr[] = {
946a7ca44b8Syasuoka 	    { RADIUS_CODE_ACCESS_REQUEST,	"Access-Request" },
947a7ca44b8Syasuoka 	    { RADIUS_CODE_ACCESS_ACCEPT,	"Access-Accept" },
948a7ca44b8Syasuoka 	    { RADIUS_CODE_ACCESS_REJECT,	"Access-Reject" },
949a7ca44b8Syasuoka 	    { RADIUS_CODE_ACCOUNTING_REQUEST,	"Accounting-Request" },
950a7ca44b8Syasuoka 	    { RADIUS_CODE_ACCOUNTING_RESPONSE,	"Accounting-Response" },
951a7ca44b8Syasuoka 	    { RADIUS_CODE_ACCESS_CHALLENGE,	"Access-Challenge" },
952a7ca44b8Syasuoka 	    { RADIUS_CODE_STATUS_SERVER,	"Status-Server" },
953a7ca44b8Syasuoka 	    { RADIUS_CODE_STATUS_CLIENT,	"Status-Client" },
954a7ca44b8Syasuoka 	    { -1, NULL }
955a7ca44b8Syasuoka 	};
956a7ca44b8Syasuoka 
957a7ca44b8Syasuoka 	for (i = 0; codestr[i].code != -1; i++) {
958a7ca44b8Syasuoka 		if (codestr[i].code == code)
959a7ca44b8Syasuoka 			return (codestr[i].str);
960a7ca44b8Syasuoka 	}
961a7ca44b8Syasuoka 
962a7ca44b8Syasuoka 	return ("Unknown");
963a7ca44b8Syasuoka }
964a7ca44b8Syasuoka 
965a7ca44b8Syasuoka static const char *
966a7ca44b8Syasuoka hexstr(const u_char *data, int len, char *str, int strsiz)
967a7ca44b8Syasuoka {
968a7ca44b8Syasuoka 	int			 i, off = 0;
969a7ca44b8Syasuoka 	static const char	 hex[] = "0123456789abcdef";
970a7ca44b8Syasuoka 
971a7ca44b8Syasuoka 	for (i = 0; i < len; i++) {
972a7ca44b8Syasuoka 		if (strsiz - off < 3)
973a7ca44b8Syasuoka 			return (NULL);
974a7ca44b8Syasuoka 		str[off++] = hex[(data[i] & 0xf0) >> 4];
975a7ca44b8Syasuoka 		str[off++] = hex[(data[i] & 0x0f)];
976a7ca44b8Syasuoka 		str[off++] = ' ';
977a7ca44b8Syasuoka 	}
978a7ca44b8Syasuoka 	if (strsiz - off < 1)
979a7ca44b8Syasuoka 		return (NULL);
980a7ca44b8Syasuoka 
981a7ca44b8Syasuoka 	str[off++] = '\0';
982a7ca44b8Syasuoka 
983a7ca44b8Syasuoka 	return (str);
984a7ca44b8Syasuoka }
985842565f2Syasuoka 
986842565f2Syasuoka const char *
987842565f2Syasuoka sockaddr_str(struct sockaddr *sa, char *buf, size_t bufsiz)
988842565f2Syasuoka {
989842565f2Syasuoka 	int	noport, ret;
990842565f2Syasuoka 	char	hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
991842565f2Syasuoka 
992842565f2Syasuoka 	if (ntohs(((struct sockaddr_in *)sa)->sin_port) == 0) {
993842565f2Syasuoka 		noport = 1;
994842565f2Syasuoka 		ret = getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), NULL, 0,
995842565f2Syasuoka 		    NI_NUMERICHOST);
996842565f2Syasuoka 	} else {
997842565f2Syasuoka 		noport = 0;
998842565f2Syasuoka 		ret = getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), sbuf,
999842565f2Syasuoka 		    sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
1000842565f2Syasuoka 	}
1001842565f2Syasuoka 	if (ret != 0)
1002842565f2Syasuoka 		return "";
1003842565f2Syasuoka 	if (noport)
1004842565f2Syasuoka 		strlcpy(buf, hbuf, bufsiz);
1005842565f2Syasuoka 	else if (sa->sa_family == AF_INET6)
1006842565f2Syasuoka 		snprintf(buf, bufsiz, "[%s]:%s", hbuf, sbuf);
1007842565f2Syasuoka 	else
1008842565f2Syasuoka 		snprintf(buf, bufsiz, "%s:%s", hbuf, sbuf);
1009842565f2Syasuoka 
1010842565f2Syasuoka 	return (buf);
1011842565f2Syasuoka }
1012842565f2Syasuoka 
1013842565f2Syasuoka const char *
1014842565f2Syasuoka time_long_str(struct timespec *tim, char *buf, size_t bufsiz)
1015842565f2Syasuoka {
1016842565f2Syasuoka 	struct tm	 tm;
1017842565f2Syasuoka 
1018842565f2Syasuoka 	localtime_r(&tim->tv_sec, &tm);
1019842565f2Syasuoka 	strftime(buf, bufsiz, "%F %T", &tm);
1020842565f2Syasuoka 
1021842565f2Syasuoka 	return (buf);
1022842565f2Syasuoka }
1023842565f2Syasuoka 
1024842565f2Syasuoka const char *
1025842565f2Syasuoka time_short_str(struct timespec *tim, struct timespec *dif, char *buf,
1026842565f2Syasuoka     size_t bufsiz)
1027842565f2Syasuoka {
1028842565f2Syasuoka 	struct tm	 tm;
1029842565f2Syasuoka 
1030842565f2Syasuoka 	localtime_r(&tim->tv_sec, &tm);
1031842565f2Syasuoka 	if (dif->tv_sec < 12 * 60 * 60)
1032842565f2Syasuoka 		strftime(buf, bufsiz, "%l:%M%p", &tm);
1033842565f2Syasuoka 	else if (dif->tv_sec < 7 * 24 * 60 * 60)
1034842565f2Syasuoka 		strftime(buf, bufsiz, "%e%b%y", &tm);
1035842565f2Syasuoka 	else
1036842565f2Syasuoka 		strftime(buf, bufsiz, "%m/%d", &tm);
1037842565f2Syasuoka 
1038842565f2Syasuoka 	return (buf);
1039842565f2Syasuoka }
1040842565f2Syasuoka 
1041842565f2Syasuoka const char *
1042842565f2Syasuoka humanize_seconds(long seconds, char *buf, size_t bufsiz)
1043842565f2Syasuoka {
1044842565f2Syasuoka 	char	 fbuf[80];
1045842565f2Syasuoka 	int	 hour, min;
1046842565f2Syasuoka 
1047842565f2Syasuoka 	hour = seconds / 3600;
1048842565f2Syasuoka 	min = (seconds % 3600) / 60;
1049842565f2Syasuoka 
1050842565f2Syasuoka 	if (bufsiz == 0)
1051842565f2Syasuoka 		return NULL;
1052842565f2Syasuoka 	buf[0] = '\0';
1053842565f2Syasuoka 	if (hour != 0 || min != 0) {
1054842565f2Syasuoka 		strlcat(buf, " (", bufsiz);
1055842565f2Syasuoka 		if (hour != 0) {
1056842565f2Syasuoka 			snprintf(fbuf, sizeof(fbuf), "%d hour%s", hour,
1057842565f2Syasuoka 			    (hour == 1)? "" : "s");
1058842565f2Syasuoka 			strlcat(buf, fbuf, bufsiz);
1059842565f2Syasuoka 		}
1060842565f2Syasuoka 		if (hour != 0 && min != 0)
1061842565f2Syasuoka 			strlcat(buf, " and ", bufsiz);
1062842565f2Syasuoka 		if (min != 0) {
1063842565f2Syasuoka 			snprintf(fbuf, sizeof(fbuf), "%d minute%s", min,
1064842565f2Syasuoka 			    (min == 1)? "" : "s");
1065842565f2Syasuoka 			strlcat(buf, fbuf, bufsiz);
1066842565f2Syasuoka 		}
1067842565f2Syasuoka 		strlcat(buf, ")", bufsiz);
1068842565f2Syasuoka 	}
1069842565f2Syasuoka 
1070842565f2Syasuoka 	return (buf);
1071842565f2Syasuoka }
1072