Patch pecl-radius-1.2.7-ipv6.diff for radius Bug #59619
Patch version 2013-07-17 07:57 UTC
Return to Bug #59619 |
Download this patch
Patch Revisions:
Developer: bruno.premont@restena.lu
--- radius-1.2.7/radlib.c 2013-06-27 23:14:24.000000000 +0200
+++ radius-1.2.7-ipv6/radlib.c 2013-07-04 09:50:37.649509454 +0200
@@ -47,6 +47,7 @@
#include <string.h>
#ifndef PHP_WIN32
#include <unistd.h>
+#include <fcntl.h>
#endif
#include "radlib_compat.h"
@@ -58,13 +58,15 @@ static void generr(struct rad_handle *,
__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 int is_valid_response(struct rad_handle *, 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,
const void *, size_t);
static int split(char *, char *[], int, char *, size_t);
+static int rad_init_socket(struct rad_handle *h);
+static int rad_receive(struct rad_handle *h, int fd);
+static int rad_send_request_next(struct rad_handle *h, struct timeval *tv);
static void
clear_password(struct rad_handle *h)
@@ -142,20 +144,38 @@ insert_request_authenticator(struct rad_
* 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, const struct sockaddr *from)
{
MD5_CTX ctx;
unsigned char md5[16];
- const struct rad_server *srvp;
- int len;
+ const struct rad_server *srvp = NULL;
+ int len, srv;
- srvp = &h->servers[srv];
+ for (srv = 0; srv < h->num_servers; srv++) {
+ /* Check the source address */
+ if (from->sa_family != h->servers[srv].addr.addr.sa_family)
+ continue;
+ if (from->sa_family == AF_INET) {
+ const struct sockaddr_in *from4 = (const struct sockaddr_in *)from;
+ const struct sockaddr_in *srva4 = &h->servers[srv].addr.addr4;
+ if (from4->sin_port != srva4->sin_port ||
+ memcmp(&from4->sin_addr, &srva4->sin_addr, sizeof(from4->sin_addr)) != 0)
+ continue;
+ } else if (from->sa_family == AF_INET6) {
+ const struct sockaddr_in6 *from6 = (const struct sockaddr_in6 *)from;
+ const struct sockaddr_in6 *srva6 = &h->servers[srv].addr.addr6;
+ if (from6->sin6_port != srva6->sin6_port ||
+ memcmp(&from6->sin6_addr, &srva6->sin6_addr, sizeof(from6->sin6_addr)) != 0)
+ continue;
+ } else {
+ return 0;
+ }
+ srvp = &h->servers[srv];
+ break;
+ }
- /* 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)
+ /* Does not correspond to any of our records */
+ if (srvp == NULL)
return 0;
/* Check the message length */
@@ -231,49 +251,75 @@ int
rad_add_server(struct rad_handle *h, const char *host, int port,
const char *secret, int timeout, int tries)
{
- struct rad_server *srvp;
+ struct addrinfo hints;
+ struct addrinfo *result, *rp;
+ int r, n = h->num_servers;
+ short p;
if (h->num_servers >= MAXSERVERS) {
generr(h, "Too many RADIUS servers specified");
return -1;
}
- srvp = &h->servers[h->num_servers];
- memset(&srvp->addr, 0, sizeof srvp->addr);
- srvp->addr.sin_family = AF_INET;
- if (!inet_aton(host, &srvp->addr.sin_addr)) {
- struct hostent *hent;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = 0;
+ hints.ai_protocol = 0;
- 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);
+ if ((r = getaddrinfo(host, NULL, &hints, &result)) != 0) {
+ generr(h, "%s: %s", host, gai_strerror(r));
+ return -1;
}
- if (port != 0)
- srvp->addr.sin_port = htons((short) port);
- else {
+
+ if (port == 0) {
struct servent *sent;
if (h->type == RADIUS_AUTH)
- srvp->addr.sin_port =
- (sent = getservbyname("radius", "udp")) != NULL ?
+ p = (sent = getservbyname("radius", "udp")) != NULL ?
sent->s_port : htons(RADIUS_PORT);
else
- srvp->addr.sin_port =
- (sent = getservbyname("radacct", "udp")) != NULL ?
+ p = (sent = getservbyname("radacct", "udp")) != NULL ?
sent->s_port : htons(RADACCT_PORT);
+ } else
+ p = htons((short)port);
+
+ for (rp = result; rp != NULL; rp = rp->ai_next) {
+ struct rad_server *srvp;
+ if (n >= MAXSERVERS) {
+ generr(h, "Too many RADIUS servers specified");
+ goto err;
+ }
+ srvp = &h->servers[n];
+ memset(&srvp->addr, 0, sizeof srvp->addr);
+ if (rp->ai_family == AF_INET) {
+ memcpy(&srvp->addr, rp->ai_addr, sizeof(struct sockaddr_in));
+ srvp->addr.addr4.sin_port = p;
+ } else if (rp->ai_family == AF_INET6) {
+ memcpy(&srvp->addr, rp->ai_addr, sizeof(struct sockaddr_in6));
+ srvp->addr.addr6.sin6_port = p;
+ } else
+ continue;
+ n++;
+ srvp->timeout = timeout;
+ srvp->max_tries = tries;
+ srvp->num_tries = 0;
+ if ((srvp->secret = strdup(secret)) == NULL) {
+ generr(h, "Out of memory");
+ goto err;
+ }
}
- if ((srvp->secret = strdup(secret)) == NULL) {
- generr(h, "Out of memory");
- return -1;
- }
- srvp->timeout = timeout;
- srvp->max_tries = tries;
- srvp->num_tries = 0;
- h->num_servers++;
+ h->num_servers = n;
+ freeaddrinfo(result);
return 0;
+
+err:
+ freeaddrinfo(result);
+ while (n > h->num_servers) {
+ n--;
+ free(h->servers[n].secret);
+ }
+ return -1;
}
void
@@ -281,8 +327,10 @@ rad_close(struct rad_handle *h)
{
int srv;
- if (h->fd != -1)
- close(h->fd);
+ if (h->fd4 != -1)
+ close(h->fd4);
+ if (h->fd6 != -1)
+ close(h->fd6);
for (srv = 0; srv < h->num_servers; srv++) {
memset(h->servers[srv].secret, 0,
strlen(h->servers[srv].secret));
@@ -436,42 +484,18 @@ rad_config(struct rad_handle *h, const c
}
/*
- * rad_init_send_request() must have previously been called.
+ * rad_init_socket() must have previously been called.
* Returns:
- * 0 The application should select on *fd with a timeout of tv before
+ * > 0 The application should select on *fd with a timeout of tv before
* calling rad_continue_send_request again.
* < 0 Failure
- * > 0 Success
+ * 0 The application should call us again for next server
*/
int
-rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
- struct timeval *tv)
+static rad_send_request_next(struct rad_handle *h, 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) {
-#ifdef PHP_WIN32
- generr(h, "recfrom: %d", WSAGetLastError());
-#else
- generr(h, "recvfrom: %s", strerror(errno));
-#endif
- 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;
@@ -495,28 +519,24 @@ rad_continue_send_request(struct rad_han
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)
-#ifdef PHP_WIN32
- generr(h, "sendto: %d", WSAGetLastError());
-#else
- generr(h, "sendto: %s", strerror(errno));
-#endif
- else
- generr(h, "sendto: short write");
- return -1;
+ if (h->servers[h->srv].addr.addr.sa_family == AF_INET6 && h->fd6 != -1) {
+ n = sendto(h->fd6, h->request, h->req_len, 0,
+ &h->servers[h->srv].addr.addr,
+ sizeof h->servers[h->srv].addr.addr6);
+ } else if (h->servers[h->srv].addr.addr.sa_family == AF_INET && h->fd4 != -1) {
+ n = sendto(h->fd4, h->request, h->req_len, 0,
+ &h->servers[h->srv].addr.addr,
+ sizeof h->servers[h->srv].addr.addr4);
+ } else {
+ h->servers[h->srv].num_tries++;
+ return 0;
}
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;
+ return n != h->req_len ? 0 : 1;
}
int
@@ -602,41 +622,91 @@ rad_get_attr(struct rad_handle *h, const
}
/*
- * Returns -1 on error, 0 to indicate no event and >0 for success
+ * Returns -1 on error, 0 on success
*/
-int
-rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
+static int
+rad_init_socket(struct rad_handle *h)
{
- int srv;
+ int srv, tries4 = 0, tries6 = 0;
- /* Make sure we have a socket to use */
- if (h->fd == -1) {
- struct sockaddr_in sin;
+ h->total_tries = 0;
- if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
+ for (srv = 0; srv < h->num_servers; srv++)
+ if (h->servers[srv].addr.addr.sa_family == AF_INET)
+ tries4 += h->servers[srv].max_tries;
+ else if (h->servers[srv].addr.addr.sa_family == AF_INET6)
+ tries6 += h->servers[srv].max_tries;
+
+ /* Make sure we have a IPv4 socket to use */
+ if (h->fd4 == -1 && tries4 > 0) {
+ struct sockaddr_in sin4;
+ int so;
+
+ h->fd4 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (h->fd4 == -1) {
#ifdef PHP_WIN32
- generr(h, "Cannot create socket: %d", WSAGetLastError());
+ generr(h, "Cannot create ipv4 socket: %d", WSAGetLastError());
#else
- generr(h, "Cannot create socket: %s", strerror(errno));
+ generr(h, "Cannot create ipv4 socket: %s", strerror(errno));
#endif
- return -1;
+ goto done4;
}
- memset(&sin, 0, 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) {
+
+ memset(&sin4, 0, sizeof sin4);
+ sin4.sin_family = AF_INET;
+ sin4.sin_addr.s_addr = INADDR_ANY;
+ sin4.sin_port = htons(0);
+ if (bind(h->fd4, (const struct sockaddr *)&sin4, sizeof sin4) == -1) {
#ifdef PHP_WIN32
- generr(h, "bind: %d", WSAGetLastError());
+ generr(h, "bind ipv4: %d", WSAGetLastError());
#else
- generr(h, "bind: %s", strerror(errno));
+ generr(h, "bind ipv4: %s", strerror(errno));
#endif
- close(h->fd);
- h->fd = -1;
- return -1;
+ close(h->fd4);
+ h->fd4 = -1;
+ goto done4;
+ }
+ so = fcntl(h->fd4, F_GETFL);
+ if (so != -1)
+ fcntl(h->fd4, F_SETFL, O_NONBLOCK | so);
+ }
+done4:
+ /* Make sure we have a IPv6 socket to use */
+ if (h->fd6 == -1 && tries6 > 0) {
+ struct sockaddr_in6 sin6;
+ int so = 1;
+
+ h->fd6 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (h->fd6 == -1 && errno != EAFNOSUPPORT) {
+#ifdef PHP_WIN32
+ generr(h, "Cannot create ipv6 socket: %d", WSAGetLastError());
+#else
+ generr(h, "Cannot create ipv6 socket: %s", strerror(errno));
+#endif
+ goto done6;
}
+
+ memset(&sin6, 0, sizeof sin6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(0);
+ setsockopt(h->fd6, IPPROTO_IPV6, IPV6_V6ONLY, &so, sizeof so);
+ if (bind(h->fd6, (const struct sockaddr *)&sin6, sizeof sin6) == -1) {
+#ifdef PHP_WIN32
+ generr(h, "bind ipv6: %d", WSAGetLastError());
+#else
+ generr(h, "bind ipv6: %s", strerror(errno));
+#endif
+ close(h->fd6);
+ h->fd6 = -1;
+ goto done6;
+ }
+ so = fcntl(h->fd6, F_GETFL);
+ if (so != -1)
+ fcntl(h->fd6, F_SETFL, O_NONBLOCK | so);
}
+done6:
+ if (h->fd4 == -1 && h->fd6 == -1)
+ return -1;
if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
/* Make sure no password given */
@@ -665,18 +735,17 @@ rad_init_send_request(struct rad_handle
* 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->fd4 != -1)
+ h->total_tries += tries4;
+ if (h->fd6 != -1)
+ h->total_tries += tries6;
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);
+ return 0;
}
/*
@@ -693,7 +762,8 @@ rad_auth_open(void)
if (h != NULL) {
TSRMLS_FETCH();
php_srand(time(NULL) * getpid() * (unsigned long) (php_combined_lcg(TSRMLS_C) * 10000.0) TSRMLS_CC);
- h->fd = -1;
+ h->fd4 = -1;
+ h->fd6 = -1;
h->num_servers = 0;
h->ident = php_rand(TSRMLS_C);
h->errmsg[0] = '\0';
@@ -770,6 +840,40 @@ rad_put_string(struct rad_handle *h, int
return rad_put_attr(h, type, str, strlen(str));
}
+static int
+rad_receive(struct rad_handle *h, int fd)
+{
+ union {
+ struct sockaddr_in from4;
+ struct sockaddr_in6 from6;
+ } from;
+ socklen_t fromlen;
+
+retry:
+ fromlen = sizeof from;
+ h->resp_len = recvfrom(fd, h->response,
+ MSGSIZE, MSG_WAITALL, (struct sockaddr *)&from, &fromlen);
+ if (h->resp_len == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ return 0;
+ if (errno == EINTR)
+ goto retry;
+#ifdef PHP_WIN32
+ generr(h, "recfrom: %d", WSAGetLastError());
+#else
+ generr(h, "recvfrom: %s", strerror(errno));
+#endif
+ return -1;
+ }
+
+ if (is_valid_response(h, (struct sockaddr *)&from)) {
+ h->resp_len = h->response[POS_LENGTH] << 8 |
+ h->response[POS_LENGTH+1];
+ h->resp_pos = POS_ATTRS;
+ return h->response[POS_CODE];
+ } else
+ goto retry;
+}
/*
* Returns the response type code on success, or -1 on failure.
*/
@@ -778,47 +882,56 @@ rad_send_request(struct rad_handle *h)
{
struct timeval timelimit;
struct timeval tv;
- int fd;
int n;
- n = rad_init_send_request(h, &fd, &tv);
+ n = rad_init_socket(h);
if (n != 0)
return n;
- gettimeofday(&timelimit, NULL);
- timeradd(&tv, &timelimit, &timelimit);
-
- for ( ; ; ) {
+ while (h->try < h->total_tries) {
fd_set readfds;
- FD_ZERO(&readfds);
- FD_SET(fd, &readfds);
+ n = rad_send_request_next(h, &tv);
+ if (++h->srv >= h->num_servers)
+ h->srv = 0;
+ if (n == -1)
+ return n;
+ else if (n == 0)
+ continue;
- n = select(fd + 1, &readfds, NULL, NULL, &tv);
+ gettimeofday(&timelimit, NULL);
+ timeradd(&tv, &timelimit, &timelimit);
- if (n == -1) {
+wait:
+ FD_ZERO(&readfds);
+ if (h->fd4 != -1)
+ FD_SET(h->fd4, &readfds);
+ if (h->fd6 != -1)
+ FD_SET(h->fd6, &readfds);
+
+ n = select((h->fd4 > h->fd6 ? h->fd4 : h->fd6) + 1, &readfds, NULL, NULL, &tv);
+
+ if (n > 0) {
+ if (h->fd4 != -1 && FD_ISSET(h->fd4, &readfds))
+ if ((n = rad_receive(h, h->fd4)))
+ return n;
+ if (h->fd6 != -1 && FD_ISSET(h->fd6, &readfds))
+ if ((n = rad_receive(h, h->fd6)))
+ return n;
+ } else if (n == -1 && errno != EINTR) {
generr(h, "select: %s", strerror(errno));
return -1;
}
-
- if (!FD_ISSET(fd, &readfds)) {
- /* Compute a new timeout */
- gettimeofday(&tv, NULL);
- timersub(&timelimit, &tv, &tv);
- if (tv.tv_sec > 0 || (tv.tv_sec == 0 && tv.tv_usec > 0))
- /* Continue the select */
- continue;
- }
-
- n = rad_continue_send_request(h, n, &fd, &tv);
-
- if (n != 0)
- return n;
-
- gettimeofday(&timelimit, NULL);
- timeradd(&tv, &timelimit, &timelimit);
+ /* Compute a new timeout */
+ gettimeofday(&tv, NULL);
+ timersub(&timelimit, &tv, &tv);
+ if (tv.tv_sec > 0 || (tv.tv_sec == 0 && tv.tv_usec > 0))
+ /* Continue the select */
+ goto wait;
}
+ generr(h, "%s", strerror(ETIMEDOUT));
+ return -1;
}
const char *
--- radius-1.2.7/radlib.h 2013-06-27 23:14:24.000000000 +0200
+++ radius-1.2.7-ipv6/radlib.h 2013-07-04 09:45:05.816058221 +0200
@@ -183,14 +183,11 @@ int rad_add_server(struct rad_handle
struct rad_handle *rad_auth_open(void);
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); /* Deprecated, == rad_auth_open */
int rad_put_addr(struct rad_handle *, int, struct in_addr);
int rad_put_attr(struct rad_handle *, int, const void *, size_t);
--- radius-1.2.7/radlib_private.h 2013-06-27 23:14:24.000000000 +0200
+++ radius-1.2.7-ipv6/radlib_private.h 2013-07-01 16:08:27.000000000 +0200
@@ -66,7 +66,11 @@
#define POS_ATTRS 20 /* Start of attributes */
struct rad_server {
- struct sockaddr_in addr; /* Address of server */
+ union {
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 addr6;
+ struct sockaddr addr;
+ } addr; /* Address of server */
char *secret; /* Shared secret */
int timeout; /* Timeout in seconds */
int max_tries; /* Number of tries before giving up */
@@ -74,7 +78,8 @@ struct rad_server {
};
struct rad_handle {
- int fd; /* Socket file descriptor */
+ int fd4; /* Socket file descriptor */
+ int fd6; /* Socket file descriptor */
struct rad_server servers[MAXSERVERS]; /* Servers to contact */
int num_servers; /* Number of valid server entries */
int ident; /* Current identifier value */
|