--- /dev/null
+/**
+ * rlm_jradius - The FreeRADIUS JRadius Server Module
+ * Copyright (C) 2004-2006 PicoPoint, B.V.
+ * Copyright (c) 2007 David Bird
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This module is used to connect FreeRADIUS to the JRadius server.
+ * JRadius is a Java RADIUS client and server framework, see doc/rlm_jradius
+ * and http://jradius.net/ for more information.
+ *
+ * Author(s): David Bird <dbird@acm.org>
+ *
+ * Connection pooling code based on rlm_sql, see rlm_sql/sql.c for copyright and license.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/signal.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <poll.h>
+
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
+
+#include <freeradius-devel/autoconf.h>
+#include <freeradius-devel/libradius.h>
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
+#include <freeradius-devel/conffile.h>
+#define STR_VALUE(p) ((p)->data.strvalue)
+
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#endif
+
+static const int JRADIUS_PORT = 1814;
+static const int HALF_MESSAGE_LEN = 16384;
+static const int MESSAGE_LEN = 32768;
+
+static const int JRADIUS_authenticate = 1;
+static const int JRADIUS_authorize = 2;
+static const int JRADIUS_preacct = 3;
+static const int JRADIUS_accounting = 4;
+static const int JRADIUS_checksimul = 5;
+static const int JRADIUS_pre_proxy = 6;
+static const int JRADIUS_post_proxy = 7;
+static const int JRADIUS_post_auth = 8;
+
+#define LOG_PREFIX "rlm_jradius: "
+#define MAX_HOSTS 4
+
+typedef struct jradius_socket {
+ int id;
+#ifdef HAVE_PTHREAD_H
+ pthread_mutex_t mutex;
+#endif
+ struct jradius_socket *next;
+ enum { is_connected, not_connected } state;
+
+ union {
+ int sock;
+ } con;
+} JRSOCK;
+
+typedef struct jradius_inst {
+ time_t connect_after;
+ JRSOCK * sock_pool;
+ JRSOCK * last_used;
+
+ char * name;
+ char * host [MAX_HOSTS];
+ uint32_t ipaddr [MAX_HOSTS];
+ int port [MAX_HOSTS];
+ int timeout;
+ int allow_codechange;
+ int allow_idchange;
+ int onfail;
+ char * onfail_s;
+ int keepalive;
+ int jrsock_cnt;
+} JRADIUS;
+
+typedef struct _byte_array
+{
+ unsigned int size;
+ unsigned int pos;
+ unsigned int left;
+ unsigned char * b;
+} byte_array;
+
+static CONF_PARSER module_config[] = {
+ { "name", PW_TYPE_STRING_PTR, offsetof(JRADIUS, name), NULL, "localhost"},
+ { "primary", PW_TYPE_STRING_PTR, offsetof(JRADIUS, host[0]), NULL, "localhost"},
+ { "secondary", PW_TYPE_STRING_PTR, offsetof(JRADIUS, host[1]), NULL, NULL},
+ { "tertiary", PW_TYPE_STRING_PTR, offsetof(JRADIUS, host[2]), NULL, NULL},
+ { "timeout", PW_TYPE_INTEGER, offsetof(JRADIUS, timeout), NULL, "5"},
+ { "onfail", PW_TYPE_STRING_PTR, offsetof(JRADIUS, onfail_s), NULL, NULL},
+ { "keepalive", PW_TYPE_BOOLEAN, offsetof(JRADIUS, keepalive), NULL, "yes"},
+ { "connections", PW_TYPE_INTEGER, offsetof(JRADIUS, jrsock_cnt), NULL, "8"},
+ { "allow_codechange", PW_TYPE_BOOLEAN, offsetof(JRADIUS, allow_codechange), NULL, "no"},
+ { "allow_idchange", PW_TYPE_BOOLEAN, offsetof(JRADIUS, allow_idchange), NULL, "no"},
+ { NULL, -1, 0, NULL, NULL }
+};
+
+static int connect_socket(JRSOCK *jrsock, JRADIUS *inst)
+{
+ struct sockaddr_in local_addr, serv_addr;
+ int i, connected = 0;
+ char buff[128];
+ int sock;
+
+ /*
+ * Connect to jradius servers until we succeed or die trying
+ */
+ for (i = 0; !connected && i < MAX_HOSTS && inst->ipaddr[i] > 0; i++) {
+
+ /*
+ * Allocate a TCP socket
+ */
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+ radlog(L_ERR, LOG_PREFIX "could not allocate TCP socket");
+ goto failed;
+ }
+
+ /*
+ * If we have a timeout value set, make the socket non-blocking
+ */
+ if (inst->timeout > 0 &&
+ fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK) == -1) {
+ radlog(L_ERR, LOG_PREFIX "could not set non-blocking on socket");
+ goto failed;
+ }
+
+ /*
+ * Bind to any local port
+ */
+ memset(&local_addr, 0, sizeof(local_addr));
+ local_addr.sin_family = AF_INET;
+ local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ local_addr.sin_port = htons(0);
+
+ if (bind(sock, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0) {
+ radlog(L_ERR, LOG_PREFIX "could not locally bind TCP socket");
+ goto failed;
+ }
+
+ /*
+ * Attempt connection to remote server
+ */
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ memcpy((char *) &serv_addr.sin_addr, &(inst->ipaddr[i]), 4);
+ serv_addr.sin_port = htons(inst->port[i]);
+
+ if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
+ if (inst->timeout > 0 && (errno == EINPROGRESS || errno == EWOULDBLOCK)) {
+ /*
+ * Wait to see if non-blocking socket connects or times-out
+ */
+ struct pollfd pfd;
+ memset(&pfd, 0, sizeof(pfd));
+
+ pfd.fd = sock;
+ pfd.events = POLLOUT;
+
+ if (poll(&pfd, 1, inst->timeout * 1000) == 1 && pfd.revents) {
+ /*
+ * Lets make absolutely sure we are connected
+ */
+ struct sockaddr_in sa;
+ unsigned int salen = sizeof(sa);
+ if (getpeername(sock, (struct sockaddr *) &sa, &salen) != -1) {
+ /*
+ * CONNECTED! break out of for-loop
+ */
+ connected = 1;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Timed-out
+ */
+ radlog(L_ERR, LOG_PREFIX "could not connect to %s:%d",
+ ip_ntoa(buff, inst->ipaddr[i]), inst->port[i]);
+
+ } else {
+ /*
+ * CONNECTED (instantly)! break out of for-loop
+ */
+ connected = 1;
+ break;
+ }
+
+ /*
+ * Unable to connect, cleanup and start over
+ */
+ close(sock); sock=0;
+ }
+
+ if (!connected) {
+ radlog(L_ERR, LOG_PREFIX "could not find any jradius server!");
+ goto failed;
+ }
+
+ /*
+ * If we previously set the socket to non-blocking, restore blocking
+ */
+ if (inst->timeout > 0 &&
+ fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) & ~O_NONBLOCK) == -1) {
+ radlog(L_ERR, LOG_PREFIX "could not set blocking on socket");
+ goto failed;
+ }
+
+ jrsock->state = is_connected;
+ jrsock->con.sock = sock;
+ return 1;
+
+ failed:
+ if (sock > 0) { shutdown(sock, 2); close(sock); }
+ jrsock->state = not_connected;
+ return 0;
+}
+
+static void close_socket(JRADIUS * inst, JRSOCK *jrsock) {
+ radlog(L_INFO, "rlm_jradius: Closing JRadius connection %d", jrsock->id);
+ if (jrsock->con.sock > 0) {
+ shutdown(jrsock->con.sock, 2);
+ close(jrsock->con.sock);
+ }
+ jrsock->state = not_connected;
+ jrsock->con.sock = 0;
+}
+
+static void free_socket(JRADIUS * inst, JRSOCK *jrsock) {
+ close_socket(inst, jrsock);
+ if (inst->keepalive) {
+#ifdef HAVE_PTHREAD_H
+ pthread_mutex_destroy(&jrsock->mutex);
+#endif
+ free(jrsock);
+ }
+}
+
+static int socket_send(JRSOCK *jrsock, unsigned char *b, unsigned int blen) {
+ return send(jrsock->con.sock, b, blen, 0);
+}
+
+static int init_socketpool(JRADIUS * inst)
+{
+ int i, rcode;
+ int success = 0;
+ JRSOCK *jrsock;
+
+ inst->connect_after = 0;
+ inst->sock_pool = NULL;
+
+ for (i = 0; i < inst->jrsock_cnt; i++) {
+ radlog(L_INFO, "rlm_jradius: starting JRadius connection %d", i);
+
+ if ((jrsock = rad_malloc(sizeof(*jrsock))) == 0) return -1;
+
+ memset(jrsock, 0, sizeof(*jrsock));
+ jrsock->id = i;
+ jrsock->state = not_connected;
+
+#ifdef HAVE_PTHREAD_H
+ rcode = pthread_mutex_init(&jrsock->mutex,NULL);
+ if (rcode != 0) {
+ radlog(L_ERR, "rlm_jradius: Failed to init lock: %s", strerror(errno));
+ return 0;
+ }
+#endif
+
+ if (time(NULL) > inst->connect_after)
+ if (connect_socket(jrsock, inst))
+ success = 1;
+
+ jrsock->next = inst->sock_pool;
+ inst->sock_pool = jrsock;
+ }
+ inst->last_used = NULL;
+
+ if (!success) {
+ radlog(L_DBG, "rlm_jradius: Failed to connect to JRadius server.");
+ }
+
+ return 1;
+}
+
+static void free_socketpool(JRADIUS * inst)
+{
+ JRSOCK *cur;
+ JRSOCK *next;
+
+ for (cur = inst->sock_pool; cur; cur = next) {
+ next = cur->next;
+ free_socket(inst, cur);
+ }
+
+ inst->sock_pool = NULL;
+}
+
+static JRSOCK * get_socket(JRADIUS * inst)
+{
+ JRSOCK *cur, *start;
+ int tried_to_connect = 0;
+ int unconnected = 0;
+
+ start = inst->last_used;
+ if (!start) start = inst->sock_pool;
+
+ cur = start;
+
+ while (cur) {
+#ifdef HAVE_PTHREAD_H
+ if (pthread_mutex_trylock(&cur->mutex) != 0) {
+ goto next;
+ }
+#endif
+
+ if ((cur->state == not_connected) && (time(NULL) > inst->connect_after)) {
+ radlog(L_INFO, "rlm_jradius: Trying to (re)connect unconnected handle %d", cur->id);
+ tried_to_connect++;
+ connect_socket(cur, inst);
+ }
+
+ if (cur->state == not_connected) {
+ radlog(L_DBG, "rlm_jradius: Ignoring unconnected handle %d", cur->id);
+ unconnected++;
+#ifdef HAVE_PTHREAD_H
+ pthread_mutex_unlock(&cur->mutex);
+#endif
+ goto next;
+ }
+
+ radlog(L_DBG, "rlm_jradius: Reserving JRadius socket id: %d", cur->id);
+
+ if (unconnected != 0 || tried_to_connect != 0) {
+ radlog(L_INFO, "rlm_jradius: got socket %d after skipping %d unconnected handles, tried to reconnect %d though",
+ cur->id, unconnected, tried_to_connect);
+ }
+
+ inst->last_used = cur->next;
+ return cur;
+
+ next:
+ cur = cur->next;
+ if (!cur) cur = inst->sock_pool;
+ if (cur == start) break;
+ }
+
+ radlog(L_INFO, "rlm_jradius: There are no sockets to use! skipped %d, tried to connect %d",
+ unconnected, tried_to_connect);
+ return NULL;
+}
+
+static int release_socket(JRADIUS * inst, JRSOCK * jrsock)
+{
+#ifdef HAVE_PTHREAD_H
+ pthread_mutex_unlock(&jrsock->mutex);
+#endif
+
+ radlog(L_DBG, "rlm_jradius: Released JRadius socket id: %d", jrsock->id);
+
+ return 0;
+}
+
+
+/*
+ * Initialize the jradius module
+ */
+static int jradius_instantiate(CONF_SECTION *conf, void **instance)
+{
+ JRADIUS *inst = (JRADIUS *) instance;
+ char host[128], b[128], *h;
+ int i, p, idx, port;
+
+ inst = rad_malloc(sizeof(JRADIUS));
+ memset(inst, 0, sizeof(JRADIUS));
+
+ if (cf_section_parse(conf, inst, module_config) < 0) {
+ free(inst);
+ return -1;
+ }
+
+ for (i = 0, idx = 0; i < MAX_HOSTS; i++) {
+ if (inst->host[i] && strlen(inst->host[i]) < sizeof(host)) {
+ h = inst->host[i];
+ p = JRADIUS_PORT;
+
+ strcpy(b, h);
+ if (sscanf(b, "%[^:]:%d", host, &port) == 2) { h = host; p = port; }
+
+ if (h) {
+ lrad_ipaddr_t ipaddr;
+ if (ip_hton(h, AF_INET, &ipaddr) < 0) {
+ radlog(L_ERR, "Can't find IP address for host %s", h);
+ continue;
+ }
+ if ((inst->ipaddr[idx] = ipaddr.ipaddr.ip4addr.s_addr) != htonl(INADDR_NONE)) {
+ inst->port[idx] = p;
+ radlog(L_INFO, LOG_PREFIX "configuring jradius server %s:%d", h, p);
+ idx++;
+ } else {
+ radlog(L_ERR, LOG_PREFIX "invalid jradius server %s", h);
+ }
+ }
+ }
+ }
+
+ if (inst->keepalive) init_socketpool(inst);
+
+ inst->onfail = RLM_MODULE_FAIL;
+
+ if (inst->onfail_s) {
+ if (!strcmp(inst->onfail_s, "NOOP")) inst->onfail = RLM_MODULE_NOOP;
+ else if (!strcmp(inst->onfail_s, "REJECT")) inst->onfail = RLM_MODULE_REJECT;
+ else if (!strcmp(inst->onfail_s, "OK")) inst->onfail = RLM_MODULE_OK;
+ else if (!strcmp(inst->onfail_s, "FAIL")) inst->onfail = RLM_MODULE_FAIL;
+ else radlog(L_ERR, LOG_PREFIX "invalid jradius 'onfail' state %s", inst->onfail_s);
+ }
+
+ *instance = inst;
+
+ return 0;
+}
+
+/*
+ * Initialize a byte array buffer structure
+ */
+static void init_byte_array(byte_array * ba, unsigned char *b, int blen)
+{
+ ba->b = b;
+ ba->size = ba->left = blen;
+ ba->pos = 0;
+}
+
+/*
+ * Pack a single byte into a byte array buffer
+ */
+static int pack_byte(byte_array * ba, unsigned char c)
+{
+ if (ba->left < 1) return -1;
+
+ ba->b[ba->pos] = c;
+ ba->pos++;
+ ba->left--;
+
+ return 0;
+}
+
+/*
+ * Pack an array of bytes into a byte array buffer
+ */
+static int pack_bytes(byte_array * ba, unsigned char *d, unsigned int dlen)
+{
+ if (ba->left < dlen) return -1;
+
+ memcpy((void *)(ba->b + ba->pos), d, dlen);
+ ba->pos += dlen;
+ ba->left -= dlen;
+
+ return 0;
+}
+
+/*
+ * Pack an integer into a byte array buffer (adjusting for byte-order)
+ */
+static int pack_int(byte_array * ba, unsigned int i)
+{
+ if (ba->left < 4) return -1;
+
+ i = htonl(i);
+
+ memcpy((void *)(ba->b + ba->pos), (void *)&i, 4);
+ ba->pos += 4;
+ ba->left -= 4;
+
+ return 0;
+}
+
+/*
+ * Pack one byte array buffer into another byte array buffer
+ */
+static int pack_array(byte_array * ba, byte_array * a)
+{
+ if (ba->left < a->pos) return -1;
+
+ memcpy((void *)(ba->b + ba->pos), (void *)a->b, a->pos);
+ ba->pos += a->pos;
+ ba->left -= a->pos;
+
+ return 0;
+}
+
+/*
+ * Pack radius attributes into a byte array buffer
+ */
+static int pack_vps(byte_array * ba, VALUE_PAIR * vps)
+{
+ VALUE_PAIR * vp;
+
+ for (vp = vps; vp != NULL; vp = vp->next) {
+
+ radlog(L_DBG, LOG_PREFIX "packing attribute %s (type: %d; len: %d)",
+ vp->name, vp->attribute, vp->length);
+
+ if (pack_int(ba, vp->attribute) == -1) return -1;
+ if (pack_int(ba, vp->length) == -1) return -1;
+ if (pack_int(ba, vp->operator) == -1) return -1;
+
+ switch (vp->type) {
+ case PW_TYPE_INTEGER:
+ case PW_TYPE_DATE:
+ if (pack_int(ba, vp->lvalue) == -1) return -1;
+ break;
+ case PW_TYPE_IPADDR:
+ if (pack_bytes(ba, (void *)&vp->lvalue, vp->length) == -1) return -1;
+ break;
+ default:
+ if (pack_bytes(ba, (void *)STR_VALUE(vp), vp->length) == -1) return -1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Pack a radius packet into a byte array buffer
+ */
+static int pack_packet(byte_array * ba, RADIUS_PACKET * p)
+{
+ /*unsigned char code = p->code;*/
+ unsigned char buff[HALF_MESSAGE_LEN];
+ byte_array pba;
+
+ init_byte_array(&pba, buff, sizeof(buff));
+
+ if (pack_vps (&pba, p->vps) == -1) return -1;
+
+ radlog(L_DBG, LOG_PREFIX "packing packet with code: %d (attr length: %d)", p->code, pba.pos);
+
+ if (pack_byte (ba, p->code) == -1) return -1;
+ if (pack_byte (ba, p->id) == -1) return -1;
+ if (pack_int (ba, pba.pos) == -1) return -1;
+ if (pba.pos == 0) return 0;
+ if (pack_array (ba, &pba) == -1) return -1;
+
+ return 0;
+}
+
+static int pack_request(byte_array * ba, REQUEST *r)
+{
+ unsigned char buff[HALF_MESSAGE_LEN];
+ byte_array pba;
+
+ init_byte_array(&pba, buff, sizeof(buff));
+
+ if (pack_vps (&pba, r->config_items) == -1) return -1;
+ if (pack_int (ba, pba.pos) == -1) return -1;
+ if (pba.pos == 0) return 0;
+ if (pack_array (ba, &pba) == -1) return -1;
+
+ return 0;
+}
+
+/*
+ * Read a single byte from socket
+ */
+static int read_byte(JRSOCK *jrsock, unsigned char *b)
+{
+ return (read(jrsock->con.sock, b, 1) == 1) ? 0 : -1;
+}
+
+static void unpack_int(unsigned char *c, unsigned int *i)
+{
+ unsigned int ii;
+ memcpy((void *)&ii, c, 4);
+ *i = ntohl(ii);
+}
+
+/*
+ * Read an integer from the socket (adjusting for byte-order)
+ */
+static int read_int(JRSOCK *jrsock, unsigned int *i)
+{
+ unsigned char c[4];
+
+ if (read(jrsock->con.sock, c, 4) != 4) return -1;
+ unpack_int(c, i);
+
+ return 0;
+}
+
+/*
+ * Read a value-pair list from the socket
+ */
+static int read_vps(JRSOCK *jrsock, VALUE_PAIR **pl, int plen)
+{
+ VALUE_PAIR *vp;
+ unsigned char buff[MESSAGE_LEN];
+ unsigned int alen, atype, aop;
+ int rlen = 0;
+
+ while (rlen < plen) {
+ if (read_int (jrsock, &atype) == -1) return -1; rlen += 4;
+ if (read_int (jrsock, &alen) == -1) return -1; rlen += 4;
+ if (read_int (jrsock, &aop) == -1) return -1; rlen += 4;
+
+ radlog(L_DBG, LOG_PREFIX "reading attribute: type=%d; len=%d", atype, alen);
+
+ if (alen >= sizeof(buff)) {
+ radlog(L_ERR, LOG_PREFIX "packet value too large (len: %d)", alen);
+ return -1;
+ }
+
+ if (read(jrsock->con.sock, buff, alen) != (int)alen) return -1; rlen += alen;
+ buff[alen]=0;
+
+ /*
+ * Create new attribute
+ */
+ vp = paircreate(atype, -1);
+ vp->operator = aop;
+
+ if (vp->type == -1) {
+ /*
+ * FreeRADIUS should know about the same attributes that JRadius knows
+ */
+ radlog(L_ERR, LOG_PREFIX "received attribute we do not recognize (type: %d)", atype);
+ pairbasicfree(vp);
+ continue;
+ }
+
+ /*
+ * Fill in the attribute value based on type
+ */
+ switch (vp->type) {
+ case PW_TYPE_INTEGER:
+ case PW_TYPE_DATE:
+ {
+ unpack_int(buff, &vp->lvalue);
+ vp->length = 4;
+ }
+ break;
+
+ case PW_TYPE_IPADDR:
+ memcpy((void *)&vp->lvalue, buff, 4);
+ vp->length = 4;
+ break;
+
+ default:
+ if (alen < sizeof(STR_VALUE(vp))) {
+ memcpy((void *)STR_VALUE(vp), buff, alen);
+ vp->length = alen;
+ }
+ break;
+ }
+
+ /*
+ * Add the attribute to the packet
+ */
+ pairadd(pl, vp);
+ }
+
+ return rlen;
+}
+
+/*
+ * Read a radius packet from the socket
+ */
+static int read_packet(JRADIUS * inst, JRSOCK *jrsock, RADIUS_PACKET *p)
+{
+ unsigned char code;
+ unsigned char id;
+ unsigned int plen;
+
+ if (read_byte (jrsock, &code) == -1) return -1;
+ if (read_byte (jrsock, &id) == -1) return -1;
+ if (read_int (jrsock, &plen) == -1) return -1;
+
+ radlog(L_DBG, LOG_PREFIX "reading packet: code=%d len=%d", (int)code, plen);
+
+ if (inst->allow_codechange)
+ if (code != p->code) {
+ radlog(L_INFO, LOG_PREFIX "changing packet code from %d to %d", p->code, code);
+ p->code = code;
+ }
+
+ if (inst->allow_idchange)
+ if (id != p->id) {
+ radlog(L_INFO, LOG_PREFIX "changing packet id from %d to %d", p->id, id);
+ p->id = id;
+ }
+
+ /*
+ * Delete previous attribute list
+ */
+ pairfree(&p->vps);
+
+ if (plen == 0) return 0;
+
+ if (read_vps (jrsock, &p->vps, plen) == -1) return -1;
+
+ return 0;
+}
+
+static int read_request(JRSOCK *jrsock, REQUEST *p)
+{
+ unsigned int plen;
+
+ if (read_int(jrsock, &plen) == -1) return -1;
+
+ radlog(L_DBG, LOG_PREFIX "reading request: config_item: len=%d", plen);
+
+ /*
+ * Delete previous attribute list
+ */
+ pairfree(&p->config_items);
+
+ if (plen == 0) return 0;
+
+ if (read_vps(jrsock, &p->config_items, plen) == -1) return -1;
+
+ return 0;
+}
+
+static int rlm_jradius_call(char func, void *instance, REQUEST *req, int isproxy)
+{
+ JRADIUS * inst = instance;
+ RADIUS_PACKET * request = isproxy ? req->proxy : req->packet;
+ RADIUS_PACKET * reply = isproxy ? req->proxy_reply : req->reply;
+ JRSOCK * jrsock = 0;
+ JRSOCK sjrsock;
+
+ int exitstatus = inst->onfail;
+ unsigned char rcode, pcount;
+
+ unsigned char buff[MESSAGE_LEN];
+ byte_array ba;
+
+ char * n = inst->name;
+ unsigned int nlen = strlen(n);
+ const char * err = 0;
+ int rc, attempt2=0;
+
+#define W_ERR(s) { err=s; goto packerror; }
+#define R_ERR(s) { err=s; goto parseerror; }
+
+ if (inst->keepalive) {
+ jrsock = get_socket(inst);
+ if (!jrsock) return exitstatus;
+ } else {
+ jrsock = &sjrsock;
+ memset(jrsock, 0, sizeof(*jrsock));
+ jrsock->state = not_connected;
+ }
+
+ init_byte_array(&ba, buff, sizeof(buff));
+
+ pcount = 0;
+ if (request) pcount++;
+ if (reply) pcount++;
+
+ /*
+ * Create byte array to send to jradius
+ */
+ if ((rc = pack_int (&ba, nlen)) == -1) W_ERR("pack_int(nlen)");
+ if ((rc = pack_bytes (&ba, (void *)n, nlen)) == -1) W_ERR("pack_bytes(name)");
+ if ((rc = pack_byte (&ba, func)) == -1) W_ERR("pack_byte(fun)");
+ if ((rc = pack_byte (&ba, pcount)) == -1) W_ERR("pack_byte(pcnt)");
+ if (pcount > 0 && (rc = pack_packet (&ba, request)) == -1) W_ERR("pack_packet(req)");
+ if (pcount > 1 && (rc = pack_packet (&ba, reply)) == -1) W_ERR("pack_packet(rep)");
+ if ((rc = pack_request(&ba, req)) == -1) W_ERR("pack_request()");
+
+ /*
+ * Send data
+ */
+ start_over:
+ if (jrsock->state == not_connected) {
+ if (attempt2) radlog(L_ERR, LOG_PREFIX "reconnecting socket id %d", jrsock->id);
+ if (!connect_socket(jrsock, inst)) {
+ if (attempt2) radlog(L_ERR, LOG_PREFIX "could not reconnect socket %d, giving up", jrsock->id);
+ goto cleanup;
+ }
+ }
+ radlog(L_DBG, LOG_PREFIX "sending %d bytes to socket %d", ba.pos, jrsock->id);
+ if (socket_send(jrsock, ba.b, ba.pos) != (int)ba.pos ||
+ (rc = read_byte (jrsock, &rcode)) == -1) {
+ /*
+ * With an error on the write or the first read, try closing the socket
+ * and reconnecting to see if that improves matters any (tries this only once)
+ */
+ radlog(L_ERR, LOG_PREFIX "error sending request with socket %d", jrsock->id);
+ if (!inst->keepalive || attempt2) W_ERR("socket_send/first_read");
+ close_socket(inst, jrsock);
+ attempt2 = 1;
+ goto start_over;
+ }
+
+ /*
+ * Read result
+ */
+ if ((rc = read_byte (jrsock, &pcount)) == -1) R_ERR("read_byte(pcnt)");
+
+ radlog(L_DBG, LOG_PREFIX "return code %d; receiving %d packets", (int)rcode, (int)pcount);
+
+ if (pcount > 0 && request) if ((rc = read_packet (inst, jrsock, request)) == -1) R_ERR("read_packet(req)");
+ if (pcount > 1 && reply) if ((rc = read_packet (inst, jrsock, reply)) == -1) R_ERR("read_packet(rep)");
+
+ if ((rc = read_request (jrsock, req)) == -1) R_ERR("read_request()");
+
+ /*
+ * Since we deleted all the attribute lists in the request,
+ * we need to reconfigure a few pointers in the REQUEST object
+ */
+ if (req->username) {
+ req->username = pairfind(request->vps, PW_USER_NAME);
+ }
+ if (req->password) {
+ req->password = pairfind(request->vps, PW_PASSWORD);
+ if (!req->password) req->password = pairfind(request->vps, PW_CHAP_PASSWORD);
+ }
+
+ /*
+ * All done, set return code and cleanup
+ */
+ exitstatus = (int)rcode;
+ goto cleanup;
+
+ parseerror:
+ radlog(L_ERR, LOG_PREFIX "problem parsing the data [%s]",err);
+ if (inst->keepalive) close_socket(inst, jrsock);
+ goto cleanup;
+
+ packerror:
+ radlog(L_ERR, LOG_PREFIX "problem packing the data[%s]",err);
+ if (inst->keepalive) close_socket(inst, jrsock);
+
+ cleanup:
+ if (inst->keepalive)
+ release_socket(inst, jrsock);
+ else
+ close_socket(inst, jrsock);
+
+ return exitstatus;
+}
+
+static int jradius_authenticate(void *instance, REQUEST *request)
+{
+ return rlm_jradius_call(JRADIUS_authenticate, instance, request, 0);
+}
+
+static int jradius_authorize(void *instance, REQUEST *request)
+{
+ return rlm_jradius_call(JRADIUS_authorize, instance, request, 0);
+}
+
+static int jradius_preacct(void *instance, REQUEST *request)
+{
+ return rlm_jradius_call(JRADIUS_preacct, instance, request, 0);
+}
+
+static int jradius_accounting(void *instance, REQUEST *request)
+{
+ return rlm_jradius_call(JRADIUS_accounting, instance, request, 0);
+}
+
+static int jradius_checksimul(void *instance, REQUEST *request)
+{
+ return rlm_jradius_call(JRADIUS_checksimul, instance, request, 0);
+}
+
+static int jradius_pre_proxy(void *instance, REQUEST *request)
+{
+ return rlm_jradius_call(JRADIUS_pre_proxy, instance, request, 1);
+}
+
+static int jradius_post_proxy(void *instance, REQUEST *request)
+{
+ return rlm_jradius_call(JRADIUS_post_proxy, instance, request, 1);
+}
+
+static int jradius_post_auth(void *instance, REQUEST *request)
+{
+ return rlm_jradius_call(JRADIUS_post_auth, instance, request, 0);
+}
+
+static int jradius_detach(void *instance)
+{
+ JRADIUS *inst = (JRADIUS *) instance;
+ free_socketpool(inst);
+ free(inst);
+ return 0;
+}
+
+module_t rlm_jradius = {
+ RLM_MODULE_INIT,
+ "jradius",
+ RLM_TYPE_THREAD_SAFE,
+ jradius_instantiate,
+ jradius_detach,
+ {
+ jradius_authenticate,
+ jradius_authorize,
+ jradius_preacct,
+ jradius_accounting,
+ jradius_checksimul,
+ jradius_pre_proxy,
+ jradius_post_proxy,
+ jradius_post_auth
+ },
+};
+