aboutsummaryrefslogtreecommitdiff
path: root/lib/libradius
diff options
context:
space:
mode:
authorBrian Somers <brian@FreeBSD.org>1999-02-05 11:23:44 +0000
committerBrian Somers <brian@FreeBSD.org>1999-02-05 11:23:44 +0000
commit48caee2acde01f188590b7955963f294a746f2d6 (patch)
treed66dd8d2f2a88a5b466a667973d40b96beb28e2f /lib/libradius
parentd7f9b906ed6e6902b9affd5e7029d3f4ce8b8d7d (diff)
downloadsrc-48caee2acde01f188590b7955963f294a746f2d6.tar.gz
src-48caee2acde01f188590b7955963f294a746f2d6.zip
Allow an alternate to rad_send_request() for programs that
don't wish to wait for the RADIUS server to respond. Reviewed by: jdp
Notes
Notes: svn path=/head/; revision=43662
Diffstat (limited to 'lib/libradius')
-rw-r--r--lib/libradius/libradius.358
-rw-r--r--lib/libradius/radlib.c293
-rw-r--r--lib/libradius/radlib.h5
-rw-r--r--lib/libradius/radlib_private.h5
4 files changed, 224 insertions, 137 deletions
diff --git a/lib/libradius/libradius.3 b/lib/libradius/libradius.3
index 21e65b8c086c..d62922e81868 100644
--- a/lib/libradius/libradius.3
+++ b/lib/libradius/libradius.3
@@ -39,6 +39,8 @@
.Ft int
.Fn rad_config "struct rad_handle *h" "const char *file"
.Ft int
+.Fn rad_continue_send_request "struct rad_handle *h" "int selected" "int *fd" "struct timeval *tv"
+.Ft int
.Fn rad_create_request "struct rad_handle *h" "int code"
.Ft struct in_addr
.Fn rad_cvt_addr "const void *data"
@@ -48,6 +50,8 @@
.Fn rad_cvt_string "const void *data" "size_t len"
.Ft int
.Fn rad_get_attr "struct rad_handle *h" "const void **data" "size_t *len"
+.Ft int
+.Fn rad_init_send_request "struct rad_handle *h" "int *fd" "struct timeval *tv"
.Ft struct rad_handle *
.Fn rad_open "void"
.Ft int
@@ -179,10 +183,16 @@ The
.Fn rad_put_X
functions return 0 on success, or -1 if an error occurs.
.Sh SENDING THE REQUEST AND RECEIVING THE RESPONSE
-After the RADIUS request has been constructed, it is sent by means
-of
-.Fn rad_send_request .
-This function sends the request and waits for a valid reply,
+After the RADIUS request has been constructed, it is sent either by means of
+.Fn rad_send_request
+or by a combination of calls to
+.Fn rad_init_send_request
+and
+.Fn rad_continue_send_request .
+.Pp
+The
+.Fn rad_send_request
+function sends the request and waits for a valid reply,
retrying the defined servers in round-robin fashion as necessary.
If a valid response is received,
.Fn rad_send_request
@@ -196,9 +206,43 @@ If no valid response is received,
.Fn rad_send_request
returns -1.
.Pp
+As an alternative, if you do not wish to block waiting for a response,
+.Fn rad_init_send_request
+and
+.Fn rad_continue_send_request
+may be used instead. If a reply is received from the RADIUS server or a
+timeout occurs, these functions return a value as described for
+.Fn rad_send_request .
+Otherwise, a value of zero is returned and the values pointed to by
+.Ar fd
+and
+.Ar tv
+are set to the descriptor and timeout that should be passed to
+.Xr select 2 .
+.Pp
+.Fn rad_init_send_request
+must be called first, followed by repeated calls to
+.Fn rad_continue_send_request
+as long as a return value of zero is given.
+Between each call, the application should call
+.Xr select 2 ,
+passing
+.Ar *fd
+as a read descriptor and timing out after the interval specified by
+.Ar tv .
+When select returns,
+.Fn rad_continue_send_request
+should be called with
+.Ar selected
+set to a non-zero value if
+.Xr select 2
+indicated that the descriptor is readable.
+.Pp
Like RADIUS requests, each response may contain zero or more
attributes. After a response has been received successfully by
-.Fn rad_send_request ,
+.Fn rad_send_request
+or
+.Fn rad_continue_send_request ,
its attributes can be extracted one by one using
.Fn rad_get_attr .
Each time
@@ -286,6 +330,10 @@ which can be retrieved using
.It
.Fn rad_put_string
.It
+.Fn rad_init_send_request
+.It
+.Fn rad_continue_send_request
+.It
.Fn rad_send_request
.El
.Pp
diff --git a/lib/libradius/radlib.c b/lib/libradius/radlib.c
index 4537373e1108..a58aa6376866 100644
--- a/lib/libradius/radlib.c
+++ b/lib/libradius/radlib.c
@@ -362,8 +362,6 @@ rad_config(struct rad_handle *h, const char *path)
if (rad_add_server(h, host, port, secret, timeout, maxtries) ==
-1) {
- char msg[ERRSIZE];
-
strcpy(msg, h->errmsg);
generr(h, "%s:%d: %s", path, linenum, msg);
retval = -1;
@@ -376,6 +374,78 @@ rad_config(struct rad_handle *h, const char *path)
return retval;
}
+/*
+ * rad_init_send_request() must have previously been called.
+ * Returns:
+ * 0 The application should select on *fd with a timeout of tv before
+ * calling rad_continue_send_request again.
+ * < 0 Failure
+ * > 0 Success
+ */
+int
+rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
+ struct timeval *tv)
+{
+ int n;
+
+ if (selected) {
+ struct sockaddr_in from;
+ int fromlen;
+
+ fromlen = sizeof from;
+ h->resp_len = recvfrom(h->fd, h->response,
+ MSGSIZE, MSG_WAITALL, (struct sockaddr *)&from, &fromlen);
+ if (h->resp_len == -1) {
+ generr(h, "recvfrom: %s", strerror(errno));
+ return -1;
+ }
+ if (is_valid_response(h, h->srv, &from)) {
+ h->resp_len = h->response[POS_LENGTH] << 8 |
+ h->response[POS_LENGTH+1];
+ h->resp_pos = POS_ATTRS;
+ return h->response[POS_CODE];
+ }
+ }
+
+ if (h->try == h->total_tries) {
+ generr(h, "No valid RADIUS responses received");
+ return -1;
+ }
+
+ /*
+ * Scan round-robin to the next server that has some
+ * tries left. There is guaranteed to be one, or we
+ * would have exited this loop by now.
+ */
+ while (h->servers[h->srv].num_tries >= h->servers[h->srv].max_tries)
+ if (++h->srv >= h->num_servers)
+ h->srv = 0;
+
+ /* Insert the scrambled password into the request */
+ if (h->pass_pos != 0)
+ insert_scrambled_password(h, h->srv);
+
+ /* Send the request */
+ n = sendto(h->fd, h->request, h->req_len, 0,
+ (const struct sockaddr *)&h->servers[h->srv].addr,
+ sizeof h->servers[h->srv].addr);
+ if (n != h->req_len) {
+ if (n == -1)
+ generr(h, "sendto: %s", strerror(errno));
+ else
+ generr(h, "sendto: short write");
+ return -1;
+ }
+
+ h->try++;
+ h->servers[h->srv].num_tries++;
+ tv->tv_sec = h->servers[h->srv].timeout;
+ tv->tv_usec = 0;
+ *fd = h->fd;
+
+ return 0;
+}
+
int
rad_create_request(struct rad_handle *h, int code)
{
@@ -453,6 +523,69 @@ rad_get_attr(struct rad_handle *h, const void **value, size_t *len)
}
/*
+ * Returns -1 on error, 0 to indicate no event and >0 for success
+ */
+int
+rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
+{
+ int srv;
+
+ /* Make sure we have a socket to use */
+ if (h->fd == -1) {
+ struct sockaddr_in sin;
+
+ if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
+ generr(h, "Cannot create socket: %s", strerror(errno));
+ return -1;
+ }
+ memset(&sin, 0, sizeof sin);
+ sin.sin_len = sizeof sin;
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ sin.sin_port = htons(0);
+ if (bind(h->fd, (const struct sockaddr *)&sin,
+ sizeof sin) == -1) {
+ generr(h, "bind: %s", strerror(errno));
+ close(h->fd);
+ h->fd = -1;
+ return -1;
+ }
+ }
+
+ /* Make sure the user gave us a password */
+ if (h->pass_pos == 0 && !h->chap_pass) {
+ generr(h, "No User or Chap Password attributes given");
+ return -1;
+ }
+ if (h->pass_pos != 0 && h->chap_pass) {
+ generr(h, "Both User and Chap Password attributes given");
+ return -1;
+ }
+
+ /* Fill in the length field in the message */
+ h->request[POS_LENGTH] = h->req_len >> 8;
+ h->request[POS_LENGTH+1] = h->req_len;
+
+ /*
+ * Count the total number of tries we will make, and zero the
+ * counter for each server.
+ */
+ h->total_tries = 0;
+ for (srv = 0; srv < h->num_servers; srv++) {
+ h->total_tries += h->servers[srv].max_tries;
+ h->servers[srv].num_tries = 0;
+ }
+ if (h->total_tries == 0) {
+ generr(h, "No RADIUS servers specified");
+ return -1;
+ }
+
+ h->try = h->srv = 0;
+
+ return rad_continue_send_request(h, 0, fd, tv);
+}
+
+/*
* Create and initialize a rad_handle structure, and return it to the
* caller. Can fail only if the necessary memory cannot be allocated.
* In that case, it returns NULL.
@@ -520,151 +653,49 @@ rad_put_string(struct rad_handle *h, int type, const char *str)
int
rad_send_request(struct rad_handle *h)
{
- int total_tries;
- int try;
- int srv;
+ struct timeval timelimit;
+ struct timeval tv;
+ int fd;
int n;
- int got_valid_response;
-
- /* Make sure we have a socket to use */
- if (h->fd == -1) {
- struct sockaddr_in sin;
- if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
- generr(h, "Cannot create socket: %s", strerror(errno));
- return -1;
- }
- memset(&sin, 0, sizeof sin);
- sin.sin_len = sizeof sin;
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = INADDR_ANY;
- sin.sin_port = htons(0);
- if (bind(h->fd, (const struct sockaddr *)&sin,
- sizeof sin) == -1) {
- generr(h, "bind: %s", strerror(errno));
- close(h->fd);
- h->fd = -1;
- return -1;
- }
- }
+ n = rad_init_send_request(h, &fd, &tv);
- /* Make sure the user gave us a password */
- if (h->pass_pos == 0 && !h->chap_pass) {
- generr(h, "No User or Chap Password attributes given");
- return -1;
- }
+ if (n != 0)
+ return n;
- if (h->pass_pos != 0 && h->chap_pass) {
- generr(h, "Both User and Chap Password attributes given");
- return -1;
- }
+ gettimeofday(&timelimit, NULL);
+ timeradd(&tv, &timelimit, &timelimit);
- /* Fill in the length field in the message */
- h->request[POS_LENGTH] = h->req_len >> 8;
- h->request[POS_LENGTH+1] = h->req_len;
+ for ( ; ; ) {
+ fd_set readfds;
- /*
- * Count the total number of tries we will make, and zero the
- * counter for each server.
- */
- total_tries = 0;
- for (srv = 0; srv < h->num_servers; srv++) {
- total_tries += h->servers[srv].max_tries;
- h->servers[srv].num_tries = 0;
- }
- if (total_tries == 0) {
- generr(h, "No RADIUS servers specified");
- return -1;
- }
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
- srv = 0;
- got_valid_response = 0;
- for (try = 0; try < total_tries; try++) {
- struct timeval timelimit;
- struct timeval tv;
+ n = select(fd + 1, &readfds, NULL, NULL, &tv);
- /*
- * Scan round-robin to the next server that has some
- * tries left. There is guaranteed to be one, or we
- * would have exited this loop by now.
- */
- while (h->servers[srv].num_tries >=
- h->servers[srv].max_tries)
- if (++srv >= h->num_servers)
- srv = 0;
-
- /* Insert the scrambled password into the request */
- if (h->pass_pos != 0)
- insert_scrambled_password(h, srv);
-
- /* Send the request */
- n = sendto(h->fd, h->request, h->req_len, 0,
- (const struct sockaddr *)&h->servers[srv].addr,
- sizeof h->servers[srv].addr);
- if (n != h->req_len) {
- if (n == -1)
- generr(h, "sendto: %s", strerror(errno));
- else
- generr(h, "sendto: short write");
+ if (n == -1) {
+ generr(h, "select: %s", strerror(errno));
return -1;
}
- h->servers[srv].num_tries++;
- /* Wait for a valid response */
- gettimeofday(&timelimit, NULL);
- timelimit.tv_sec += h->servers[srv].timeout;
-
- tv.tv_sec = h->servers[srv].timeout;
- tv.tv_usec = 0;
- for ( ; ; ) {
- fd_set readfds;
-
- FD_ZERO(&readfds);
- FD_SET(h->fd, &readfds);
- n = select(h->fd + 1, &readfds, NULL, NULL, &tv);
- if (n == -1) {
- generr(h, "select: %s", strerror(errno));
- return -1;
- }
- if (n == 0) /* Timed out */
- break;
- if (FD_ISSET(h->fd, &readfds)) {
- struct sockaddr_in from;
- int fromlen;
-
- fromlen = sizeof from;
- h->resp_len = recvfrom(h->fd, h->response,
- MSGSIZE, MSG_WAITALL,
- (struct sockaddr *)&from, &fromlen);
- if (h->resp_len == -1) {
- generr(h, "recvfrom: %s",
- strerror(errno));
- return -1;
- }
- if (is_valid_response(h, srv, &from)) {
- got_valid_response = 1;
- break;
- }
- }
+ if (!FD_ISSET(fd, &readfds)) {
/* Compute a new timeout */
gettimeofday(&tv, NULL);
timersub(&timelimit, &tv, &tv);
- if (tv.tv_sec < 0) /* Still poll once more */
- timerclear(&tv);
+ if (tv.tv_sec > 0 || (tv.tv_sec == 0 && tv.tv_usec > 0))
+ /* Continue the select */
+ continue;
}
- if (got_valid_response)
- break;
- /* Advance to the next server */
- if (++srv >= h->num_servers)
- srv = 0;
- }
- if (!got_valid_response) {
- generr(h, "No valid RADIUS responses received");
- return -1;
+
+ n = rad_continue_send_request(h, n, &fd, &tv);
+
+ if (n != 0)
+ return n;
+
+ gettimeofday(&timelimit, NULL);
+ timeradd(&tv, &timelimit, &timelimit);
}
- h->resp_len = h->response[POS_LENGTH] << 8 | h->response[POS_LENGTH+1];
- h->resp_pos = POS_ATTRS;
- return h->response[POS_CODE];
}
const char *
diff --git a/lib/libradius/radlib.h b/lib/libradius/radlib.h
index 146bcd51b29a..5e0ce9f01ac1 100644
--- a/lib/libradius/radlib.h
+++ b/lib/libradius/radlib.h
@@ -99,18 +99,23 @@
#define RAD_LOGIN_LAT_PORT 63 /* Integer */
struct rad_handle;
+struct timeval;
__BEGIN_DECLS
int rad_add_server(struct rad_handle *,
const char *, int, const char *, int, int);
void rad_close(struct rad_handle *);
int rad_config(struct rad_handle *, const char *);
+int rad_continue_send_request(struct rad_handle *, int,
+ int *, struct timeval *);
int rad_create_request(struct rad_handle *, int);
struct in_addr rad_cvt_addr(const void *);
u_int32_t rad_cvt_int(const void *);
char *rad_cvt_string(const void *, size_t);
int rad_get_attr(struct rad_handle *, const void **,
size_t *);
+int rad_init_send_request(struct rad_handle *, int *,
+ struct timeval *);
struct rad_handle *rad_open(void);
int rad_put_addr(struct rad_handle *, int, struct in_addr);
int rad_put_attr(struct rad_handle *, int,
diff --git a/lib/libradius/radlib_private.h b/lib/libradius/radlib_private.h
index ec181f6edc5c..21ef9cb2da35 100644
--- a/lib/libradius/radlib_private.h
+++ b/lib/libradius/radlib_private.h
@@ -74,10 +74,13 @@ struct rad_handle {
char pass[PASSSIZE]; /* Cleartext password */
int pass_len; /* Length of cleartext password */
int pass_pos; /* Position of scrambled password */
- unsigned chap_pass : 1; /* Have we got a CHAP_PASSWORD ? */
+ char chap_pass; /* Have we got a CHAP_PASSWORD ? */
unsigned char response[MSGSIZE]; /* Response received */
int resp_len; /* Length of response */
int resp_pos; /* Current position scanning attrs */
+ int total_tries; /* How many requests we'll send */
+ int try; /* How many requests we've sent */
+ int srv; /* Server number we did last */
};
#endif