summaryrefslogtreecommitdiffstats
path: root/usr.sbin/radiusctl/radiusctl.c
diff options
context:
space:
mode:
authordlg <dlg@openbsd.org>2020-02-24 07:07:11 +0000
committerdlg <dlg@openbsd.org>2020-02-24 07:07:11 +0000
commit5d013a5e71db5e71f0fe09a81819b10cff6acbb0 (patch)
treea426b33027d12556464882be04a7589e5e978312 /usr.sbin/radiusctl/radiusctl.c
parentFix typo. Patch from itoama at live.jp via github PR#173. (diff)
downloadwireguard-openbsd-5d013a5e71db5e71f0fe09a81819b10cff6acbb0.tar.xz
wireguard-openbsd-5d013a5e71db5e71f0fe09a81819b10cff6acbb0.zip
add retries and timeouts for test packets.
the most important bit of this is that the command will exit after a timeout period. this is currently handy if you're running radiusctl as a check from relayd, because at the moment it to get confused about who it's children are if you run a lot of checks too rapidly. before timeouts were added, radiusctl would wait forever for a reply, but a reply may never arrive because networks are unreliable, and worse, computers are unreliable and may be down for extended periods of time. the number of retries, the interval between retries, and the overall wait time can be tweaked via command line arguments. the defaults are set to something that seems reasonable if you're running a test. ok yasuoka@
Diffstat (limited to 'usr.sbin/radiusctl/radiusctl.c')
-rw-r--r--usr.sbin/radiusctl/radiusctl.c141
1 files changed, 121 insertions, 20 deletions
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