From 48caee2acde01f188590b7955963f294a746f2d6 Mon Sep 17 00:00:00 2001 From: Brian Somers Date: Fri, 5 Feb 1999 11:23:44 +0000 Subject: Allow an alternate to rad_send_request() for programs that don't wish to wait for the RADIUS server to respond. Reviewed by: jdp --- lib/libradius/libradius.3 | 58 +++++++- lib/libradius/radlib.c | 293 +++++++++++++++++++++++------------------ lib/libradius/radlib.h | 5 + lib/libradius/radlib_private.h | 5 +- 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) { @@ -452,6 +522,69 @@ rad_get_attr(struct rad_handle *h, const void **value, size_t *len) return type; } +/* + * 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. @@ -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 -- cgit v1.2.3