diff options
Diffstat (limited to 'lib/libradius/radlib.c')
-rw-r--r-- | lib/libradius/radlib.c | 293 |
1 files changed, 162 insertions, 131 deletions
diff --git a/lib/libradius/radlib.c b/lib/libradius/radlib.c index 4537373..a58aa63 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 * |