summaryrefslogtreecommitdiff
path: root/usr.sbin/radiusctl
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/radiusctl')
-rw-r--r--usr.sbin/radiusctl/Makefile6
-rw-r--r--usr.sbin/radiusctl/parser.c98
-rw-r--r--usr.sbin/radiusctl/parser.h21
-rw-r--r--usr.sbin/radiusctl/radiusctl.817
-rw-r--r--usr.sbin/radiusctl/radiusctl.c141
5 files changed, 253 insertions, 30 deletions
diff --git a/usr.sbin/radiusctl/Makefile b/usr.sbin/radiusctl/Makefile
index 73704e55083..48cec71affc 100644
--- a/usr.sbin/radiusctl/Makefile
+++ b/usr.sbin/radiusctl/Makefile
@@ -1,9 +1,9 @@
-# $OpenBSD: Makefile,v 1.2 2015/08/03 04:10:21 yasuoka Exp $
+# $OpenBSD: Makefile,v 1.3 2020/02/24 07:07:11 dlg Exp $
PROG= radiusctl
SRCS= radiusctl.c parser.c chap_ms.c
MAN= radiusctl.8
CFLAGS+= -Wall -Wextra -Wno-unused-parameter
-LDADD+= -lradius -lcrypto
-DPADD+= ${LIBRADIUS} ${LIBCRYPTO}
+LDADD+= -lradius -lcrypto -levent
+DPADD+= ${LIBRADIUS} ${LIBCRYPTO} ${LIBEVENT}
.include <bsd.prog.mk>
diff --git a/usr.sbin/radiusctl/parser.c b/usr.sbin/radiusctl/parser.c
index b666206f616..3b97790c530 100644
--- a/usr.sbin/radiusctl/parser.c
+++ b/usr.sbin/radiusctl/parser.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: parser.c,v 1.1 2015/07/21 04:06:04 yasuoka Exp $ */
+/* $OpenBSD: parser.c,v 1.2 2020/02/24 07:07:11 dlg Exp $ */
/*
* Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
@@ -18,6 +18,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <sys/time.h>
+
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -35,6 +37,9 @@ enum token_type {
PORT,
METHOD,
NAS_PORT,
+ TRIES,
+ INTERVAL,
+ MAXWAIT,
ENDTOKEN
};
@@ -45,7 +50,11 @@ struct token {
const struct token *next;
};
-static struct parse_result res;
+static struct parse_result res = {
+ .tries = TEST_TRIES_DEFAULT,
+ .interval = { TEST_INTERVAL_DEFAULT, 0 },
+ .maxwait = { TEST_MAXWAIT_DEFAULT, 0 },
+};
static const struct token t_test[];
static const struct token t_secret[];
@@ -55,6 +64,9 @@ static const struct token t_password[];
static const struct token t_port[];
static const struct token t_method[];
static const struct token t_nas_port[];
+static const struct token t_tries[];
+static const struct token t_interval[];
+static const struct token t_maxwait[];
static const struct token t_main[] = {
{ KEYWORD, "test", TEST, t_test },
@@ -82,6 +94,9 @@ static const struct token t_test_opts[] = {
{ KEYWORD, "port", NONE, t_port },
{ KEYWORD, "method", NONE, t_method },
{ KEYWORD, "nas-port", NONE, t_nas_port },
+ { KEYWORD, "interval", NONE, t_interval },
+ { KEYWORD, "tries", NONE, t_tries },
+ { KEYWORD, "maxwait", NONE, t_maxwait },
{ ENDTOKEN, "", NONE, NULL }
};
@@ -105,6 +120,21 @@ static const struct token t_nas_port[] = {
{ ENDTOKEN, "", NONE, NULL }
};
+static const struct token t_tries[] = {
+ { TRIES, "", NONE, t_test_opts },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
+static const struct token t_interval[] = {
+ { INTERVAL, "", NONE, t_test_opts },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
+static const struct token t_maxwait[] = {
+ { MAXWAIT, "", NONE, t_test_opts },
+ { ENDTOKEN, "", NONE, NULL }
+};
+
static const struct token *match_token(char *, const struct token []);
static void show_valid_args(const struct token []);
@@ -115,8 +145,6 @@ parse(int argc, char *argv[])
const struct token *table = t_main;
const struct token *match;
- bzero(&res, sizeof(res));
-
while (argc >= 0) {
if ((match = match_token(argv[0], table)) == NULL) {
fprintf(stderr, "valid commands/args:\n");
@@ -138,6 +166,12 @@ parse(int argc, char *argv[])
return (NULL);
}
+ if (res.tries * res.interval.tv_sec > res.maxwait.tv_sec) {
+ fprintf(stderr, "tries %u by interval %lld > maxwait %lld",
+ res.tries, res.interval.tv_sec, res.maxwait.tv_sec);
+ return (NULL);
+ }
+
return (&res);
}
@@ -240,6 +274,50 @@ match_token(char *word, const struct token table[])
res.nas_port = num;
t = &table[i];
break;
+
+ case TRIES:
+ if (word == NULL)
+ break;
+ num = strtonum(word,
+ TEST_TRIES_MIN, TEST_TRIES_MAX, &errstr);
+ if (errstr != NULL) {
+ printf("invalid argument: %s is %s"
+ " for \"tries\"\n", word, errstr);
+ return (NULL);
+ }
+ match++;
+ res.tries = num;
+ t = &table[i];
+ break;
+ case INTERVAL:
+ if (word == NULL)
+ break;
+ num = strtonum(word,
+ TEST_INTERVAL_MIN, TEST_INTERVAL_MAX, &errstr);
+ if (errstr != NULL) {
+ printf("invalid argument: %s is %s"
+ " for \"interval\"\n", word, errstr);
+ return (NULL);
+ }
+ match++;
+ res.interval.tv_sec = num;
+ t = &table[i];
+ break;
+ case MAXWAIT:
+ if (word == NULL)
+ break;
+ num = strtonum(word,
+ TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX, &errstr);
+ if (errstr != NULL) {
+ printf("invalid argument: %s is %s"
+ " for \"maxwait\"\n", word, errstr);
+ return (NULL);
+ }
+ match++;
+ res.maxwait.tv_sec = num;
+ t = &table[i];
+ break;
+
case ENDTOKEN:
break;
}
@@ -293,6 +371,18 @@ show_valid_args(const struct token table[])
case NAS_PORT:
fprintf(stderr, " <nas-port (0-65535)>\n");
break;
+ case TRIES:
+ fprintf(stderr, " <tries (%u-%u)>\n",
+ TEST_TRIES_MIN, TEST_TRIES_MAX);
+ break;
+ case INTERVAL:
+ fprintf(stderr, " <interval (%u-%u)>\n",
+ TEST_INTERVAL_MIN, TEST_INTERVAL_MAX);
+ break;
+ case MAXWAIT:
+ fprintf(stderr, " <maxwait (%u-%u)>\n",
+ TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX);
+ break;
case ENDTOKEN:
break;
}
diff --git a/usr.sbin/radiusctl/parser.h b/usr.sbin/radiusctl/parser.h
index b84b4b4d29d..6fd0a6c30f7 100644
--- a/usr.sbin/radiusctl/parser.h
+++ b/usr.sbin/radiusctl/parser.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: parser.h,v 1.1 2015/07/21 04:06:04 yasuoka Exp $ */
+/* $OpenBSD: parser.h,v 1.2 2020/02/24 07:07:11 dlg Exp $ */
/* This file is derived from OpenBSD:src/usr.sbin/ikectl/parser.h 1.9 */
/*
@@ -31,6 +31,18 @@ enum auth_method {
MSCHAPV2
};
+#define TEST_TRIES_MIN 1
+#define TEST_TRIES_MAX 32
+#define TEST_TRIES_DEFAULT 3
+
+#define TEST_INTERVAL_MIN 1
+#define TEST_INTERVAL_MAX 10
+#define TEST_INTERVAL_DEFAULT 2
+
+#define TEST_MAXWAIT_MIN 3
+#define TEST_MAXWAIT_MAX 60
+#define TEST_MAXWAIT_DEFAULT 8
+
struct parse_result {
enum actions action;
const char *hostname;
@@ -40,6 +52,13 @@ struct parse_result {
u_short port;
int nas_port;
enum auth_method auth_method;
+
+ /* number of packets to try sending */
+ unsigned int tries;
+ /* how long between packet sends */
+ struct timeval interval;
+ /* overall process wait time for a reply */
+ struct timeval maxwait;
};
struct parse_result *parse(int, char *[]);
diff --git a/usr.sbin/radiusctl/radiusctl.8 b/usr.sbin/radiusctl/radiusctl.8
index 02595580692..6fb1dc8b7d6 100644
--- a/usr.sbin/radiusctl/radiusctl.8
+++ b/usr.sbin/radiusctl/radiusctl.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: radiusctl.8,v 1.3 2015/08/25 01:21:57 yasuoka Exp $
+.\" $OpenBSD: radiusctl.8,v 1.4 2020/02/24 07:07:11 dlg Exp $
.\"
.\" Copyright (c) YASUOKA Masahiko <yasuoka@yasuoka.net>
.\"
@@ -15,7 +15,7 @@
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.\"
-.Dd $Mdocdate: August 25 2015 $
+.Dd $Mdocdate: February 24 2020 $
.Dt RADIUSCTL 8
.Os
.Sh NAME
@@ -77,6 +77,19 @@ when sending a packet to
.Ar hostname .
If the port is omitted,
the default port number 1812 is used.
+.It Cm tries Ar number
+Specifies the number of packets to try sending.
+By default
+.Nm
+will send 3 packets before giving up.
+.It Cm interval Ar seconds
+Specifies how many seconds to wait before resending a packet.
+The default interval between packet retries is 2 seconds.
+.It Cm maxwait Ar seconds
+Specifies the maximum amount of time to wait for a valid reply packet.
+By default
+.Nm
+will wait 8 seconds for a valid reply.
.El
.El
.Sh SEE ALSO
diff --git a/usr.sbin/radiusctl/radiusctl.c b/usr.sbin/radiusctl/radiusctl.c
index 6b1d03e1637..c374884eb01 100644
--- a/usr.sbin/radiusctl/radiusctl.c
+++ b/usr.sbin/radiusctl/radiusctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: radiusctl.c,v 1.7 2019/04/01 09:51:56 yasuoka Exp $ */
+/* $OpenBSD: radiusctl.c,v 1.8 2020/02/24 07:07:11 dlg Exp $ */
/*
* Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
*
@@ -19,6 +19,7 @@
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <errno.h>
#include <err.h>
#include <md5.h>
#include <netdb.h>
@@ -30,11 +31,13 @@
#include <radius.h>
+#include <event.h>
+
#include "parser.h"
#include "chap_ms.h"
-static void radius_test (struct parse_result *);
+static int radius_test (struct parse_result *);
static void radius_dump (FILE *, RADIUS_PACKET *, bool,
const char *);
static const char *radius_code_str (int code);
@@ -53,6 +56,7 @@ main(int argc, char *argv[])
{
int ch;
struct parse_result *result;
+ int ecode = EXIT_SUCCESS;
while ((ch = getopt(argc, argv, "")) != -1)
switch (ch) {
@@ -72,21 +76,38 @@ main(int argc, char *argv[])
case TEST:
if (pledge("stdio dns inet", NULL) == -1)
err(EXIT_FAILURE, "pledge");
- radius_test(result);
+ ecode = radius_test(result);
break;
}
- return (EXIT_SUCCESS);
+ return (ecode);
}
-static void
+struct radius_test {
+ const struct parse_result *res;
+ int ecode;
+
+ RADIUS_PACKET *reqpkt;
+ int sock;
+ unsigned int tries;
+ struct event ev_send;
+ struct event ev_recv;
+ struct event ev_timedout;
+};
+
+static void radius_test_send(int, short, void *);
+static void radius_test_recv(int, short, void *);
+static void radius_test_timedout(int, short, void *);
+
+static int
radius_test(struct parse_result *res)
{
+ struct radius_test test = { .res = res };
+ RADIUS_PACKET *reqpkt;
struct addrinfo hints, *ai;
int sock, retval;
struct sockaddr_storage sockaddr;
socklen_t sockaddrlen;
- RADIUS_PACKET *reqpkt, *respkt;
struct sockaddr_in *sin4;
struct sockaddr_in6 *sin6;
uint32_t u32val;
@@ -110,7 +131,8 @@ radius_test(struct parse_result *res)
((struct sockaddr_in *)ai->ai_addr)->sin_port =
htons(res->port);
- sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ sock = socket(ai->ai_family, ai->ai_socktype | SOCK_NONBLOCK,
+ ai->ai_protocol);
if (sock == -1)
err(1, "socket");
@@ -211,24 +233,31 @@ radius_test(struct parse_result *res)
radius_put_message_authenticator(reqpkt, res->secret);
+ event_init();
+
+ test.ecode = EXIT_FAILURE;
+ test.res = res;
+ test.sock = sock;
+ test.reqpkt = reqpkt;
+
+ event_set(&test.ev_recv, sock, EV_READ|EV_PERSIST,
+ radius_test_recv, &test);
+
+ evtimer_set(&test.ev_send, radius_test_send, &test);
+ evtimer_set(&test.ev_timedout, radius_test_timedout, &test);
+
+ event_add(&test.ev_recv, NULL);
+ evtimer_add(&test.ev_timedout, &res->maxwait);
+
/* Send! */
fprintf(stderr, "Sending:\n");
radius_dump(stdout, reqpkt, false, res->secret);
- if (send(sock, radius_get_data(reqpkt), radius_get_length(reqpkt), 0)
- == -1)
- warn("send");
- if ((respkt = radius_recv(sock, 0)) == NULL)
- warn("recv");
- else {
- radius_set_request_packet(respkt, reqpkt);
- fprintf(stderr, "\nReceived:\n");
- radius_dump(stdout, respkt, true, res->secret);
- }
+ radius_test_send(0, EV_TIMEOUT, &test);
+
+ event_dispatch();
/* Release the resources */
radius_delete_packet(reqpkt);
- if (respkt)
- radius_delete_packet(respkt);
close(sock);
freeaddrinfo(ai);
@@ -236,7 +265,79 @@ radius_test(struct parse_result *res)
if (res->password)
explicit_bzero((char *)res->password, strlen(res->password));
- return;
+ return (test.ecode);
+}
+
+static void
+radius_test_send(int thing, short revents, void *arg)
+{
+ struct radius_test *test = arg;
+ RADIUS_PACKET *reqpkt = test->reqpkt;
+ ssize_t rv;
+
+retry:
+ rv = send(test->sock,
+ radius_get_data(reqpkt), radius_get_length(reqpkt), 0);
+ if (rv == -1) {
+ switch (errno) {
+ case EINTR:
+ case EAGAIN:
+ goto retry;
+ default:
+ break;
+ }
+
+ warn("send");
+ }
+
+ if (++test->tries >= test->res->tries)
+ return;
+
+ evtimer_add(&test->ev_send, &test->res->interval);
+}
+
+static void
+radius_test_recv(int sock, short revents, void *arg)
+{
+ struct radius_test *test = arg;
+ RADIUS_PACKET *respkt;
+ RADIUS_PACKET *reqpkt = test->reqpkt;
+
+retry:
+ respkt = radius_recv(sock, 0);
+ if (respkt == NULL) {
+ switch (errno) {
+ case EINTR:
+ case EAGAIN:
+ goto retry;
+ default:
+ break;
+ }
+
+ warn("recv");
+ return;
+ }
+
+ radius_set_request_packet(respkt, reqpkt);
+ if (radius_get_id(respkt) == radius_get_id(reqpkt)) {
+ fprintf(stderr, "\nReceived:\n");
+ radius_dump(stdout, respkt, true, test->res->secret);
+
+ event_del(&test->ev_recv);
+ evtimer_del(&test->ev_send);
+ evtimer_del(&test->ev_timedout);
+ test->ecode = EXIT_SUCCESS;
+ }
+
+ radius_delete_packet(respkt);
+}
+
+static void
+radius_test_timedout(int thing, short revents, void *arg)
+{
+ struct radius_test *test = arg;
+
+ event_del(&test->ev_recv);
}
static void