Hi all,

I make a patch to support IPv6 for libradius.

This is a quick hack.
Unfortunatly, I can't test it over IPv6 environment, only IPv4 environment.
Because, I don't have a RADIUS which supports IPv6 access.

This patch extents radius.conf(5) format.
 - specify 10 or more RADISU (original: up to 10)
 - specify IPv6 address (original: IPv4 only)
   ex.)
    auth [2003:3:14::1]:1645 "I can't see you" 5 4
    auth [2003:3:14::2]      $X*#..38947ax-+=
    auth localhost           "test teset"

Please test it if you take interest in this patch.

Thanks,
-------
YAMAMOTO Shigeru        <[EMAIL PROTECTED]>
Index: radlib.c
===================================================================
RCS file: /share/cvsup/FreeBSD/current/usr/src/lib/libradius/radlib.c,v
retrieving revision 1.10
diff -u -r1.10 radlib.c
--- radlib.c    12 Jun 2002 00:21:07 -0000      1.10
+++ radlib.c    10 Mar 2003 03:14:40 -0000
@@ -48,10 +48,10 @@
 static void     clear_password(struct rad_handle *);
 static void     generr(struct rad_handle *, const char *, ...)
                    __printflike(2, 3);
-static void     insert_scrambled_password(struct rad_handle *, int);
-static void     insert_request_authenticator(struct rad_handle *, int);
-static int      is_valid_response(struct rad_handle *, int,
-                   const struct sockaddr_in *);
+static void     insert_scrambled_password(struct rad_handle *, struct rad_server*);
+static void     insert_request_authenticator(struct rad_handle *, struct rad_server*);
+static int      is_valid_response(struct rad_handle *, struct rad_server*,
+                       const struct sockaddr *);
 static int      put_password_attr(struct rad_handle *, int,
                    const void *, size_t);
 static int      put_raw_attr(struct rad_handle *, int,
@@ -79,15 +79,13 @@
 }
 
 static void
-insert_scrambled_password(struct rad_handle *h, int srv)
+insert_scrambled_password(struct rad_handle* h, struct rad_server* server)
 {
        MD5_CTX ctx;
        unsigned char md5[16];
-       const struct rad_server *srvp;
        int padded_len;
        int pos;
 
-       srvp = &h->servers[srv];
        padded_len = h->pass_len == 0 ? 16 : (h->pass_len+15) & ~0xf;
 
        memcpy(md5, &h->request[POS_AUTH], LEN_AUTH);
@@ -96,7 +94,7 @@
 
                /* Calculate the new scrambler */
                MD5Init(&ctx);
-               MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
+               MD5Update(&ctx, server->secret, strlen(server->secret));
                MD5Update(&ctx, md5, 16);
                MD5Final(md5, &ctx);
 
@@ -113,19 +111,16 @@
 }
 
 static void
-insert_request_authenticator(struct rad_handle *h, int srv)
+insert_request_authenticator(struct rad_handle* h, struct rad_server* server)
 {
        MD5_CTX ctx;
-       const struct rad_server *srvp;
-
-       srvp = &h->servers[srv];
 
        /* Create the request authenticator */
        MD5Init(&ctx);
        MD5Update(&ctx, &h->request[POS_CODE], POS_AUTH - POS_CODE);
        MD5Update(&ctx, memset(&h->request[POS_AUTH], 0, LEN_AUTH), LEN_AUTH);
        MD5Update(&ctx, &h->request[POS_ATTRS], h->req_len - POS_ATTRS);
-       MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
+       MD5Update(&ctx, server->secret, strlen(server->secret));
        MD5Final(&h->request[POS_AUTH], &ctx);
 }
 
@@ -134,40 +129,48 @@
  * specified server.
  */
 static int
-is_valid_response(struct rad_handle *h, int srv,
-    const struct sockaddr_in *from)
+is_valid_response(struct rad_handle* h, struct rad_server* server, const struct 
sockaddr* from)
 {
-       MD5_CTX ctx;
-       unsigned char md5[16];
-       const struct rad_server *srvp;
-       int len;
-
-       srvp = &h->servers[srv];
+       int     valid = 0;
 
        /* Check the source address */
-       if (from->sin_family != srvp->addr.sin_family ||
-           from->sin_addr.s_addr != srvp->addr.sin_addr.s_addr ||
-           from->sin_port != srvp->addr.sin_port)
-               return 0;
-
-       /* Check the message length */
-       if (h->resp_len < POS_ATTRS)
-               return 0;
-       len = h->response[POS_LENGTH] << 8 | h->response[POS_LENGTH+1];
-       if (len > h->resp_len)
-               return 0;
-
-       /* Check the response authenticator */
-       MD5Init(&ctx);
-       MD5Update(&ctx, &h->response[POS_CODE], POS_AUTH - POS_CODE);
-       MD5Update(&ctx, &h->request[POS_AUTH], LEN_AUTH);
-       MD5Update(&ctx, &h->response[POS_ATTRS], len - POS_ATTRS);
-       MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
-       MD5Final(md5, &ctx);
-       if (memcmp(&h->response[POS_AUTH], md5, sizeof md5) != 0)
-               return 0;
+       if (from->sa_family == server->addr.ss_family && memcmp(from, &(server->addr), 
from->sa_len) == 0) {
+               /* Check the message length */
+               if (h->resp_len < POS_ATTRS) {
+                       valid = 0;
+               }
+               else {
+                       int len;
+
+                       len = h->response[POS_LENGTH] << 8 | h->response[POS_LENGTH+1];
+                       if (len > h->resp_len) {
+                               valid = 0;
+                       }
+                       else {
+                               MD5_CTX ctx;
+                               unsigned char md5[16];
+
+                               /* Check the response authenticator */
+                               MD5Init(&ctx);
+                               MD5Update(&ctx, &h->response[POS_CODE], POS_AUTH - 
POS_CODE);
+                               MD5Update(&ctx, &h->request[POS_AUTH], LEN_AUTH);
+                               MD5Update(&ctx, &h->response[POS_ATTRS], len - 
POS_ATTRS);
+                               MD5Update(&ctx, server->secret, 
strlen(server->secret));
+                               MD5Final(md5, &ctx);
+                               if (memcmp(&h->response[POS_AUTH], md5, sizeof md5) != 
0) {
+                                       valid = 0;
+                               }
+                               else {
+                                       valid = 1;
+                               }
+                       }
+               }
+       }
+       else {
+               valid = 0;
+       }
 
-       return 1;
+       return(valid);
 }
 
 static int
@@ -222,65 +225,83 @@
 rad_add_server(struct rad_handle *h, const char *host, int port,
     const char *secret, int timeout, int tries)
 {
-       struct rad_server *srvp;
+       int                                     error = 0;
+       struct addrinfo         hints;
+       struct addrinfo*        res0;
+       char                            sbuf[NI_MAXSERV];
 
-       if (h->num_servers >= MAXSERVERS) {
-               generr(h, "Too many RADIUS servers specified");
-               return -1;
+       if (port != 0) {
+               snprintf(sbuf, sizeof(sbuf), "%d", port);
+       }
+       else if (h->type == RADIUS_AUTH) {
+               snprintf(sbuf, sizeof(sbuf), "radius");
+       }
+       else {
+               snprintf(sbuf, sizeof(sbuf), "radacct");
        }
-       srvp = &h->servers[h->num_servers];
-
-       memset(&srvp->addr, 0, sizeof srvp->addr);
-       srvp->addr.sin_len = sizeof srvp->addr;
-       srvp->addr.sin_family = AF_INET;
-       if (!inet_aton(host, &srvp->addr.sin_addr)) {
-               struct hostent *hent;
 
-               if ((hent = gethostbyname(host)) == NULL) {
-                       generr(h, "%s: host not found", host);
-                       return -1;
-               }
-               memcpy(&srvp->addr.sin_addr, hent->h_addr,
-                   sizeof srvp->addr.sin_addr);
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = PF_UNSPEC;
+       hints.ai_socktype = SOCK_DGRAM;
+       error = getaddrinfo(host, sbuf, &hints, &res0);
+       if (error) {
+               generr(h, "%s:%d: %s", host, port, gai_strerror(error));
        }
-       if (port != 0)
-               srvp->addr.sin_port = htons(port);
        else {
-               struct servent *sent;
+               struct addrinfo*        res;
 
-               if (h->type == RADIUS_AUTH)
-                       srvp->addr.sin_port =
-                           (sent = getservbyname("radius", "udp")) != NULL ?
-                               sent->s_port : htons(RADIUS_PORT);
-               else
-                       srvp->addr.sin_port =
-                           (sent = getservbyname("radacct", "udp")) != NULL ?
-                               sent->s_port : htons(RADACCT_PORT);
-       }
-       if ((srvp->secret = strdup(secret)) == NULL) {
-               generr(h, "Out of memory");
-               return -1;
+               for (res = res0; res != (struct addrinfo*)0; res = res->ai_next) {
+                       struct rad_server*      server;
+
+                       server = (struct rad_server*)calloc(1, sizeof(*server));
+                       if (!server) {
+                               error = errno;
+                               generr(h, "%s", strerror(error));
+                       }
+                       else {
+                               server->secret = strdup(secret);
+                               error = errno;
+                               if (server->secret == NULL) {
+                                       generr(h, "%s", strerror(error));
+                               }
+                               else {
+                                       memcpy(&(server->addr), res->ai_addr, 
res->ai_addrlen);
+                                       server->timeout = timeout;
+                                       server->max_tries = tries;
+                                       server->num_tries = 0;
+
+                                       TAILQ_INSERT_TAIL(&(h->servers), server, link);
+                                       error = 0;
+                               }
+                       }
+               }
+
+               freeaddrinfo(res0);
        }
-       srvp->timeout = timeout;
-       srvp->max_tries = tries;
-       srvp->num_tries = 0;
-       h->num_servers++;
-       return 0;
+
+       return(error);
 }
 
 void
-rad_close(struct rad_handle *h)
+rad_close(struct rad_handle* h)
 {
-       int srv;
-
-       if (h->fd != -1)
+       if (h->fd >= 0) {
                close(h->fd);
-       for (srv = 0;  srv < h->num_servers;  srv++) {
-               memset(h->servers[srv].secret, 0,
-                   strlen(h->servers[srv].secret));
-               free(h->servers[srv].secret);
+               h->fd = -1;
+       }
+
+       while (!TAILQ_EMPTY(&(h->servers))) {
+               struct rad_server*      server;
+
+               server = TAILQ_FIRST(&(h->servers));
+               TAILQ_REMOVE(&(h->servers), server, link);
+               free(server->secret);
+               free(server);
        }
+       h->server = (struct rad_server*)0;
+
        clear_password(h);
+
        free(h);
 }
 
@@ -379,14 +400,27 @@
                        continue;
 
                /* Parse and validate the fields. */
-               res = host;
-               host = strsep(&res, ":");
-               port_str = strsep(&res, ":");
+               res = strchr(fields[1], '[');
+               if (res != NULL) {
+               /* IPv6:
+                *              [aaaa:bbbb:cccc:dddd:eeee:ffff:gggg:hhhh]
+                *              [aaaa:bbbb:cccc:dddd:eeee:ffff:gggg:hhhh]:port
+                */
+                       res ++;
+                       host = strsep(&res, "]");
+                       strsep(&res, ":");      /* dummy */
+                       port_str = strsep(&res, ":");
+               }
+               else {
+                       res = host;
+                       host = strsep(&res, ":");
+                       port_str = strsep(&res, ":");
+               }
                if (port_str != NULL) {
                        port = strtoul(port_str, &end, 10);
                        if (*end != '\0') {
                                generr(h, "%s:%d: invalid port", path,
-                                   linenum);
+                                       linenum);
                                retval = -1;
                                break;
                        }
@@ -427,6 +461,43 @@
        return retval;
 }
 
+static
+int
+rad_open_socket(struct rad_handle* h) {
+       int     error = 0;
+
+       if (h->server) {
+               /* Make sure we have a socket to use */
+               if (h->fd >= 0) {
+                       close(h->fd);
+                       h->fd = -1;
+               }
+
+               if ((h->fd = socket(h->server->addr.ss_family, SOCK_DGRAM, 
IPPROTO_UDP)) == -1) {
+                       error = errno;
+                       generr(h, "Cannot create socket: %s", strerror(errno));
+               }
+               else {
+                       struct sockaddr_storage ss;
+
+                       memset(&ss, 0, sizeof ss);
+                       ss.ss_len = h->server->addr.ss_len;
+                       ss.ss_family = h->server->addr.ss_family;
+                       if (bind(h->fd, (const struct sockaddr*)&ss, ss.ss_len) == -1) 
{
+                               error = errno;
+                               generr(h, "bind: %s", strerror(errno));
+                               close(h->fd);
+                               h->fd = -1;
+                       }
+               }
+       }
+       else {
+               error = EADDRNOTAVAIL;
+       }
+
+       return(error);
+}
+
 /*
  * rad_init_send_request() must have previously been called.
  * Returns:
@@ -442,7 +513,7 @@
        int n;
 
        if (selected) {
-               struct sockaddr_in from;
+               struct sockaddr_storage from;
                int fromlen;
 
                fromlen = sizeof from;
@@ -452,7 +523,7 @@
                        generr(h, "recvfrom: %s", strerror(errno));
                        return -1;
                }
-               if (is_valid_response(h, h->srv, &from)) {
+               if (is_valid_response(h, h->server, (const struct sockaddr*)&from)) {
                        h->resp_len = h->response[POS_LENGTH] << 8 |
                            h->response[POS_LENGTH+1];
                        h->resp_pos = POS_ATTRS;
@@ -470,22 +541,54 @@
          * 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;
+       if (!TAILQ_EMPTY(&(h->servers))) {
+               struct rad_server*      server;
+               struct rad_server*      candidate = (struct rad_server*)0;
+
+               server = h->server;
+               do {
+                       if (server->num_tries < server->max_tries) {
+                       /* found a candidate */
+                               candidate = server;
+                               break;
+                       }
+
+                       if (server == TAILQ_LAST(&(h->servers), list_head_rad_server)) 
{
+                               server = TAILQ_FIRST(&(h->servers));
+                       }
+                       else {
+                               server = TAILQ_NEXT(server, link);
+                       }
+               } while (server != h->server);
+
+               if (candidate) {
+                       h->server = candidate;
+                       if (rad_open_socket(h) != 0) {
+                               return(-1);
+                       }
+               }
+               else {
+                       generr(h, "no server selected");
+                       return -1;
+               }
+       }
+       else {
+               generr(h, "no servers");
+               return -1;
+       }
 
        if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST)
                /* Insert the request authenticator into the request */
-               insert_request_authenticator(h, h->srv);
+               insert_request_authenticator(h, h->server);
        else
                /* Insert the scrambled password into the request */
                if (h->pass_pos != 0)
-                       insert_scrambled_password(h, h->srv);
+                       insert_scrambled_password(h, h->server);
 
        /* 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);
+           (const struct sockaddr *)&h->server->addr,
+           h->server->addr.ss_len);
        if (n != h->req_len) {
                if (n == -1)
                        generr(h, "sendto: %s", strerror(errno));
@@ -495,8 +598,8 @@
        }
 
        h->try++;
-       h->servers[h->srv].num_tries++;
-       tv->tv_sec = h->servers[h->srv].timeout;
+       h->server->num_tries++;
+       tv->tv_sec = h->server->timeout;
        tv->tv_usec = 0;
        *fd = h->fd;
 
@@ -585,30 +688,6 @@
 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;
-               }
-       }
-
        if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
                /* Make sure no password given */
                if (h->pass_pos || h->chap_pass) {
@@ -636,16 +715,26 @@
         * 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;
+       h->server = (struct rad_server*)0;
+       if (!TAILQ_EMPTY(&(h->servers))) {
+               struct rad_server*      server;
+
+               TAILQ_FOREACH(server, &(h->servers), link) {
+                       h->total_tries += server->max_tries;
+                       server->num_tries = 0;
+               }
+
+               h->server = TAILQ_FIRST(&(h->servers));
+               if (rad_open_socket(h) != 0) {
+                       return -1;
+               }
        }
        if (h->total_tries == 0) {
                generr(h, "No RADIUS servers specified");
                return -1;
        }
 
-       h->try = h->srv = 0;
+       h->try = 0;
 
        return rad_continue_send_request(h, 0, fd, tv);
 }
@@ -660,11 +749,12 @@
 {
        struct rad_handle *h;
 
-       h = (struct rad_handle *)malloc(sizeof(struct rad_handle));
+       h = (struct rad_handle *)calloc(1, sizeof(struct rad_handle));
        if (h != NULL) {
                srandomdev();
                h->fd = -1;
-               h->num_servers = 0;
+               TAILQ_INIT(&(h->servers));
+               h->server = (struct rad_server*)0;
                h->ident = random();
                h->errmsg[0] = '\0';
                memset(h->pass, 0, sizeof h->pass);
@@ -943,5 +1033,5 @@
 const char *
 rad_server_secret(struct rad_handle *h)
 {
-       return (h->servers[h->srv].secret);
+       return (h->server->secret);
 }
Index: radlib_private.h
===================================================================
RCS file: /share/cvsup/FreeBSD/current/usr/src/lib/libradius/radlib_private.h,v
retrieving revision 1.5
diff -u -r1.5 radlib_private.h
--- radlib_private.h    7 May 2002 10:47:18 -0000       1.5
+++ radlib_private.h    10 Mar 2003 03:14:40 -0000
@@ -31,6 +31,7 @@
 
 #include <sys/types.h>
 #include <netinet/in.h>
+#include <sys/queue.h>
 
 #include "radlib.h"
 #include "radlib_vs.h"
@@ -62,17 +63,20 @@
 #define POS_ATTRS      20              /* Start of attributes */
 
 struct rad_server {
-       struct sockaddr_in addr;        /* Address of server */
+       struct sockaddr_storage addr;   /* Address of server */
        char            *secret;        /* Shared secret */
        int              timeout;       /* Timeout in seconds */
        int              max_tries;     /* Number of tries before giving up */
        int              num_tries;     /* Number of tries so far */
+
+       TAILQ_ENTRY(rad_server) link;
 };
+TAILQ_HEAD(list_head_rad_server, rad_server);
 
 struct rad_handle {
-       int              fd;            /* Socket file descriptor */
-       struct rad_server servers[MAXSERVERS];  /* Servers to contact */
-       int              num_servers;   /* Number of valid server entries */
+       int             fd;             /* Socket file descriptor */
+       struct list_head_rad_server     servers;        /* Servers to contact */
+       struct rad_server*      server; /* Server we did last */
        int              ident;         /* Current identifier value */
        char             errmsg[ERRSIZE];       /* Most recent error message */
        unsigned char    request[MSGSIZE];      /* Request to send */
@@ -86,7 +90,6 @@
        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 */
        int              type;          /* Handle type */
 };
 

Reply via email to