- initial version of rlm_jradius with directions and dictionary
[freeradius.git] / src / modules / rlm_jradius / rlm_jradius.c
1 /**
2  * rlm_jradius - The FreeRADIUS JRadius Server Module
3  * Copyright (C) 2004-2006 PicoPoint, B.V.
4  * Copyright (c) 2007 David Bird
5  * 
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; either version 2 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
14  * for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  *  This module is used to connect FreeRADIUS to the JRadius server. 
21  *  JRadius is a Java RADIUS client and server framework, see doc/rlm_jradius
22  *  and http://jradius.net/ for more information. 
23  *
24  *  Author(s): David Bird <dbird@acm.org>
25  *
26  *  Connection pooling code based on rlm_sql, see rlm_sql/sql.c for copyright and license.
27  */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/signal.h>
33 #include <sys/types.h>
34 #include <fcntl.h>
35 #include <poll.h>
36
37 #include <freeradius-devel/ident.h>
38 RCSID("$Id$")
39
40 #include <freeradius-devel/autoconf.h>
41 #include <freeradius-devel/libradius.h>
42 #include <freeradius-devel/radiusd.h>
43 #include <freeradius-devel/modules.h>
44 #include <freeradius-devel/conffile.h>
45 #define STR_VALUE(p) ((p)->data.strvalue)
46
47 #ifdef HAVE_PTHREAD_H
48 #include <pthread.h>
49 #endif
50
51 #ifdef HAVE_SYS_SOCKET_H
52 #include <sys/socket.h>
53 #endif
54
55 #ifndef O_NONBLOCK
56 #define O_NONBLOCK O_NDELAY
57 #endif
58
59 static const int JRADIUS_PORT         = 1814;
60 static const int HALF_MESSAGE_LEN     = 16384;
61 static const int MESSAGE_LEN          = 32768;
62
63 static const int JRADIUS_authenticate = 1;
64 static const int JRADIUS_authorize    = 2;
65 static const int JRADIUS_preacct      = 3;
66 static const int JRADIUS_accounting   = 4;
67 static const int JRADIUS_checksimul   = 5;
68 static const int JRADIUS_pre_proxy    = 6;
69 static const int JRADIUS_post_proxy   = 7;
70 static const int JRADIUS_post_auth    = 8;
71
72 #define LOG_PREFIX  "rlm_jradius: "
73 #define MAX_HOSTS   4
74
75 typedef struct jradius_socket {
76   int  id;
77 #ifdef HAVE_PTHREAD_H
78   pthread_mutex_t mutex;
79 #endif
80   struct jradius_socket *next;
81   enum { is_connected, not_connected } state;
82   
83   union {
84     int sock;
85   } con;
86 } JRSOCK;
87
88 typedef struct jradius_inst {
89   time_t      connect_after;
90   JRSOCK    * sock_pool;
91   JRSOCK    * last_used;
92
93   char     * name;
94   char     * host   [MAX_HOSTS];
95   uint32_t   ipaddr [MAX_HOSTS];
96   int        port   [MAX_HOSTS];
97   int        timeout;
98   int        allow_codechange;
99   int        allow_idchange;
100   int        onfail;
101   char     * onfail_s;
102   int        keepalive;
103   int        jrsock_cnt;
104 } JRADIUS;
105
106 typedef struct _byte_array
107 {
108   unsigned int size;
109   unsigned int pos;
110   unsigned int left;
111   unsigned char * b;
112 } byte_array;
113
114 static CONF_PARSER module_config[] = {
115   { "name",         PW_TYPE_STRING_PTR,  offsetof(JRADIUS, name),       NULL,  "localhost"},
116   { "primary",      PW_TYPE_STRING_PTR,  offsetof(JRADIUS, host[0]),    NULL,  "localhost"},
117   { "secondary",    PW_TYPE_STRING_PTR,  offsetof(JRADIUS, host[1]),    NULL,  NULL},
118   { "tertiary",     PW_TYPE_STRING_PTR,  offsetof(JRADIUS, host[2]),    NULL,  NULL},
119   { "timeout",      PW_TYPE_INTEGER,     offsetof(JRADIUS, timeout),    NULL,  "5"},
120   { "onfail",       PW_TYPE_STRING_PTR,  offsetof(JRADIUS, onfail_s),   NULL,  NULL},
121   { "keepalive",    PW_TYPE_BOOLEAN,     offsetof(JRADIUS, keepalive),  NULL,  "yes"},
122   { "connections",  PW_TYPE_INTEGER,     offsetof(JRADIUS, jrsock_cnt), NULL,  "8"},
123   { "allow_codechange", PW_TYPE_BOOLEAN, offsetof(JRADIUS, allow_codechange),  NULL,  "no"},
124   { "allow_idchange",   PW_TYPE_BOOLEAN, offsetof(JRADIUS, allow_idchange),    NULL,  "no"},
125   { NULL, -1, 0, NULL, NULL }
126 };
127
128 static int connect_socket(JRSOCK *jrsock, JRADIUS *inst)
129 {
130   struct sockaddr_in local_addr, serv_addr;
131   int i, connected = 0;
132   char buff[128];
133   int sock;
134
135   /*
136    *     Connect to jradius servers until we succeed or die trying
137    */
138   for (i = 0; !connected && i < MAX_HOSTS && inst->ipaddr[i] > 0; i++) {
139
140     /*
141      *     Allocate a TCP socket
142      */
143     if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
144       radlog(L_ERR, LOG_PREFIX "could not allocate TCP socket");
145       goto failed;
146     }
147     
148     /*
149      *     If we have a timeout value set, make the socket non-blocking
150      */
151     if (inst->timeout > 0 &&
152         fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK) == -1) {
153       radlog(L_ERR, LOG_PREFIX "could not set non-blocking on socket");
154       goto failed;
155     }
156     
157     /*
158      *     Bind to any local port
159      */
160     memset(&local_addr, 0, sizeof(local_addr));
161     local_addr.sin_family = AF_INET;
162     local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
163     local_addr.sin_port = htons(0);
164     
165     if (bind(sock, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0) {
166       radlog(L_ERR, LOG_PREFIX "could not locally bind TCP socket");
167       goto failed;
168     }
169     
170     /*
171      *     Attempt connection to remote server
172      */
173     memset(&serv_addr, 0, sizeof(serv_addr));
174     serv_addr.sin_family = AF_INET;
175     memcpy((char *) &serv_addr.sin_addr, &(inst->ipaddr[i]), 4);
176     serv_addr.sin_port = htons(inst->port[i]);
177     
178     if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
179       if (inst->timeout > 0 && (errno == EINPROGRESS || errno == EWOULDBLOCK)) {
180         /*
181          *     Wait to see if non-blocking socket connects or times-out
182          */
183         struct pollfd pfd;
184         memset(&pfd, 0, sizeof(pfd));
185
186         pfd.fd = sock;
187         pfd.events = POLLOUT;
188
189         if (poll(&pfd, 1, inst->timeout * 1000) == 1 && pfd.revents) {
190           /*
191            *     Lets make absolutely sure we are connected
192            */
193           struct sockaddr_in sa;
194           unsigned int salen = sizeof(sa);
195           if (getpeername(sock, (struct sockaddr *) &sa, &salen) != -1) {
196             /*
197              *     CONNECTED! break out of for-loop
198              */
199             connected = 1;
200             break;
201           }
202         }
203       }
204
205       /*
206        *     Timed-out
207        */
208       radlog(L_ERR, LOG_PREFIX "could not connect to %s:%d", 
209              ip_ntoa(buff, inst->ipaddr[i]), inst->port[i]);
210
211     } else {
212       /*
213        *     CONNECTED (instantly)! break out of for-loop
214        */
215       connected = 1;
216       break;
217     }
218
219     /*
220      *     Unable to connect, cleanup and start over
221      */
222     close(sock); sock=0;
223   }
224
225   if (!connected) {
226     radlog(L_ERR, LOG_PREFIX "could not find any jradius server!");
227     goto failed;
228   }
229
230   /*
231    *     If we previously set the socket to non-blocking, restore blocking 
232    */
233   if (inst->timeout > 0 &&
234       fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) & ~O_NONBLOCK) == -1) {
235     radlog(L_ERR, LOG_PREFIX "could not set blocking on socket");
236     goto failed;
237   }
238
239   jrsock->state = is_connected;
240   jrsock->con.sock = sock;
241   return 1;
242
243  failed:
244   if (sock > 0) { shutdown(sock, 2); close(sock); }
245   jrsock->state = not_connected;
246   return 0;
247 }
248
249 static void close_socket(JRADIUS * inst, JRSOCK *jrsock) {
250   radlog(L_INFO, "rlm_jradius: Closing JRadius connection %d", jrsock->id);
251   if (jrsock->con.sock > 0) { 
252     shutdown(jrsock->con.sock, 2); 
253     close(jrsock->con.sock); 
254   }
255   jrsock->state = not_connected;
256   jrsock->con.sock = 0;
257 }
258
259 static void free_socket(JRADIUS * inst, JRSOCK *jrsock) {
260   close_socket(inst, jrsock);
261   if (inst->keepalive) {
262 #ifdef HAVE_PTHREAD_H
263     pthread_mutex_destroy(&jrsock->mutex);
264 #endif
265     free(jrsock);
266   }
267 }
268
269 static int socket_send(JRSOCK *jrsock, unsigned char *b, unsigned int blen) {
270   return send(jrsock->con.sock, b, blen, 0);
271 }
272
273 static int init_socketpool(JRADIUS * inst)
274 {
275   int i, rcode;
276   int success = 0;
277   JRSOCK *jrsock;
278   
279   inst->connect_after = 0;
280   inst->sock_pool = NULL;
281   
282   for (i = 0; i < inst->jrsock_cnt; i++) {
283     radlog(L_INFO, "rlm_jradius: starting JRadius connection %d", i);
284     
285     if ((jrsock = rad_malloc(sizeof(*jrsock))) == 0) return -1;
286     
287     memset(jrsock, 0, sizeof(*jrsock));
288     jrsock->id = i;
289     jrsock->state = not_connected;
290
291 #ifdef HAVE_PTHREAD_H
292     rcode = pthread_mutex_init(&jrsock->mutex,NULL);
293     if (rcode != 0) {
294       radlog(L_ERR, "rlm_jradius: Failed to init lock: %s", strerror(errno));
295       return 0;
296     }
297 #endif
298
299     if (time(NULL) > inst->connect_after)
300       if (connect_socket(jrsock, inst))
301         success = 1;
302     
303     jrsock->next = inst->sock_pool;
304     inst->sock_pool = jrsock;
305   }
306   inst->last_used = NULL;
307   
308   if (!success) {
309     radlog(L_DBG, "rlm_jradius: Failed to connect to JRadius server.");
310   }
311   
312   return 1;
313 }
314
315 static void free_socketpool(JRADIUS * inst)
316 {
317   JRSOCK *cur;
318   JRSOCK *next;
319
320   for (cur = inst->sock_pool; cur; cur = next) {
321     next = cur->next;
322     free_socket(inst, cur);
323   }
324   
325   inst->sock_pool = NULL;
326 }
327
328 static JRSOCK * get_socket(JRADIUS * inst)
329 {
330   JRSOCK *cur, *start;
331   int tried_to_connect = 0;
332   int unconnected = 0;
333
334   start = inst->last_used;
335   if (!start) start = inst->sock_pool;
336   
337   cur = start;
338   
339   while (cur) {
340 #ifdef HAVE_PTHREAD_H
341     if (pthread_mutex_trylock(&cur->mutex) != 0) {
342       goto next;
343     } 
344 #endif
345     
346     if ((cur->state == not_connected) && (time(NULL) > inst->connect_after)) {
347       radlog(L_INFO, "rlm_jradius: Trying to (re)connect unconnected handle %d", cur->id);
348       tried_to_connect++;
349       connect_socket(cur, inst);
350     }
351     
352     if (cur->state == not_connected) {
353       radlog(L_DBG, "rlm_jradius: Ignoring unconnected handle %d", cur->id);
354       unconnected++;
355 #ifdef HAVE_PTHREAD_H
356       pthread_mutex_unlock(&cur->mutex);
357 #endif
358       goto next;
359     }
360     
361     radlog(L_DBG, "rlm_jradius: Reserving JRadius socket id: %d", cur->id);
362     
363     if (unconnected != 0 || tried_to_connect != 0) {
364       radlog(L_INFO, "rlm_jradius: got socket %d after skipping %d unconnected handles, tried to reconnect %d though", 
365              cur->id, unconnected, tried_to_connect);
366     }
367
368     inst->last_used = cur->next;
369     return cur;
370     
371   next:
372     cur = cur->next;
373     if (!cur) cur = inst->sock_pool;
374     if (cur == start) break;
375   }
376   
377   radlog(L_INFO, "rlm_jradius: There are no sockets to use! skipped %d, tried to connect %d", 
378          unconnected, tried_to_connect);
379   return NULL;
380 }
381
382 static int release_socket(JRADIUS * inst, JRSOCK * jrsock)
383 {
384 #ifdef HAVE_PTHREAD_H
385   pthread_mutex_unlock(&jrsock->mutex);
386 #endif
387   
388   radlog(L_DBG, "rlm_jradius: Released JRadius socket id: %d", jrsock->id);
389   
390   return 0;
391 }
392
393
394 /*
395  *     Initialize the jradius module
396  */
397 static int jradius_instantiate(CONF_SECTION *conf, void **instance)
398 {
399   JRADIUS *inst = (JRADIUS *) instance;
400   char host[128], b[128], *h;
401   int i, p, idx, port;
402
403   inst = rad_malloc(sizeof(JRADIUS));
404   memset(inst, 0, sizeof(JRADIUS));
405
406   if (cf_section_parse(conf, inst, module_config) < 0) {
407     free(inst);
408     return -1;
409   }
410
411   for (i = 0, idx = 0; i < MAX_HOSTS; i++) {
412     if (inst->host[i] && strlen(inst->host[i]) < sizeof(host)) {
413       h = inst->host[i];
414       p = JRADIUS_PORT;
415       
416       strcpy(b, h);
417       if (sscanf(b, "%[^:]:%d", host, &port) == 2) { h = host; p = port; }
418
419       if (h) {
420         lrad_ipaddr_t ipaddr;
421         if (ip_hton(h, AF_INET, &ipaddr) < 0) {
422           radlog(L_ERR, "Can't find IP address for host %s", h);
423           continue;
424         }
425         if ((inst->ipaddr[idx] = ipaddr.ipaddr.ip4addr.s_addr) != htonl(INADDR_NONE)) {
426           inst->port[idx] = p;
427           radlog(L_INFO, LOG_PREFIX "configuring jradius server %s:%d", h, p);
428           idx++;
429         } else {
430           radlog(L_ERR, LOG_PREFIX "invalid jradius server %s", h);
431         }
432       }
433     }
434   }
435
436   if (inst->keepalive) init_socketpool(inst);
437
438   inst->onfail = RLM_MODULE_FAIL;
439
440   if (inst->onfail_s) {
441     if      (!strcmp(inst->onfail_s, "NOOP"))    inst->onfail = RLM_MODULE_NOOP;
442     else if (!strcmp(inst->onfail_s, "REJECT"))  inst->onfail = RLM_MODULE_REJECT;
443     else if (!strcmp(inst->onfail_s, "OK"))      inst->onfail = RLM_MODULE_OK;
444     else if (!strcmp(inst->onfail_s, "FAIL"))    inst->onfail = RLM_MODULE_FAIL;
445     else radlog(L_ERR, LOG_PREFIX "invalid jradius 'onfail' state %s", inst->onfail_s);
446   }
447
448   *instance = inst;
449
450   return 0;
451 }
452
453 /*
454  *     Initialize a byte array buffer structure
455  */
456 static void init_byte_array(byte_array * ba, unsigned char *b, int blen)
457 {
458   ba->b = b;
459   ba->size = ba->left = blen;
460   ba->pos = 0;
461 }
462
463 /*
464  *     Pack a single byte into a byte array buffer
465  */
466 static int pack_byte(byte_array * ba, unsigned char c)
467 {
468   if (ba->left < 1) return -1;
469
470   ba->b[ba->pos] = c;
471   ba->pos++;
472   ba->left--;
473
474   return 0;
475 }
476
477 /*
478  *     Pack an array of bytes into a byte array buffer
479  */
480 static int pack_bytes(byte_array * ba, unsigned char *d, unsigned int dlen)
481 {
482   if (ba->left < dlen) return -1;
483
484   memcpy((void *)(ba->b + ba->pos), d, dlen);
485   ba->pos  += dlen;
486   ba->left -= dlen;
487
488   return 0;
489 }
490
491 /*
492  *     Pack an integer into a byte array buffer (adjusting for byte-order)
493  */
494 static int pack_int(byte_array * ba, unsigned int i)
495 {
496   if (ba->left < 4) return -1;
497
498   i = htonl(i);
499
500   memcpy((void *)(ba->b + ba->pos), (void *)&i, 4);
501   ba->pos  += 4;
502   ba->left -= 4;
503
504   return 0;
505 }
506
507 /*
508  *     Pack one byte array buffer into another byte array buffer
509  */
510 static int pack_array(byte_array * ba, byte_array * a)
511 {
512   if (ba->left < a->pos) return -1;
513
514   memcpy((void *)(ba->b + ba->pos), (void *)a->b, a->pos);
515   ba->pos  += a->pos;
516   ba->left -= a->pos;
517
518   return 0;
519 }
520
521 /*
522  *     Pack radius attributes into a byte array buffer
523  */
524 static int pack_vps(byte_array * ba, VALUE_PAIR * vps)
525 {
526   VALUE_PAIR * vp;
527
528   for (vp = vps; vp != NULL; vp = vp->next) {
529
530     radlog(L_DBG, LOG_PREFIX "packing attribute %s (type: %d; len: %d)", 
531            vp->name, vp->attribute, vp->length);
532
533     if (pack_int(ba, vp->attribute) == -1) return -1;
534     if (pack_int(ba, vp->length)    == -1) return -1;
535     if (pack_int(ba, vp->operator)  == -1) return -1;
536
537     switch (vp->type) {
538       case PW_TYPE_INTEGER:
539       case PW_TYPE_DATE:
540         if (pack_int(ba, vp->lvalue) == -1) return -1;
541         break;
542       case PW_TYPE_IPADDR:
543         if (pack_bytes(ba, (void *)&vp->lvalue, vp->length) == -1) return -1;
544         break;
545       default:
546         if (pack_bytes(ba, (void *)STR_VALUE(vp), vp->length) == -1) return -1;
547         break;
548     }
549   }
550
551   return 0;
552 }
553
554 /*
555  *     Pack a radius packet into a byte array buffer
556  */
557 static int pack_packet(byte_array * ba, RADIUS_PACKET * p)
558 {
559   /*unsigned char code = p->code;*/
560   unsigned char buff[HALF_MESSAGE_LEN];
561   byte_array pba;
562
563   init_byte_array(&pba, buff, sizeof(buff));
564
565   if (pack_vps   (&pba, p->vps) == -1) return -1;
566
567   radlog(L_DBG, LOG_PREFIX "packing packet with code: %d (attr length: %d)", p->code, pba.pos);
568
569   if (pack_byte  (ba, p->code)  == -1) return -1;
570   if (pack_byte  (ba, p->id)    == -1) return -1;
571   if (pack_int   (ba, pba.pos)  == -1) return -1;
572   if (pba.pos == 0) return 0;
573   if (pack_array (ba, &pba)     == -1) return -1;
574
575   return 0;
576 }
577
578 static int pack_request(byte_array * ba, REQUEST *r)
579 {
580   unsigned char buff[HALF_MESSAGE_LEN];
581   byte_array pba;
582
583   init_byte_array(&pba, buff, sizeof(buff));
584
585   if (pack_vps   (&pba, r->config_items) == -1) return -1;
586   if (pack_int   (ba, pba.pos)  == -1) return -1;
587   if (pba.pos == 0) return 0;
588   if (pack_array (ba, &pba)     == -1) return -1;
589       
590   return 0;
591 }
592
593 /*
594  *     Read a single byte from socket
595  */
596 static int read_byte(JRSOCK *jrsock, unsigned char *b)
597 {
598   return (read(jrsock->con.sock, b, 1) == 1) ? 0 : -1;
599 }
600
601 static void unpack_int(unsigned char *c, unsigned int *i)
602 {
603   unsigned int ii;
604   memcpy((void *)&ii, c, 4);
605   *i = ntohl(ii);
606 }
607
608 /*
609  *     Read an integer from the socket (adjusting for byte-order)
610  */
611 static int read_int(JRSOCK *jrsock, unsigned int *i)
612 {
613   unsigned char c[4];
614
615   if (read(jrsock->con.sock, c, 4) != 4) return -1;
616   unpack_int(c, i);
617
618   return 0;
619 }
620
621 /*
622  *     Read a value-pair list from the socket
623  */
624 static int read_vps(JRSOCK *jrsock, VALUE_PAIR **pl, int plen)
625 {
626   VALUE_PAIR *vp;
627   unsigned char buff[MESSAGE_LEN];
628   unsigned int alen, atype, aop;
629   int rlen = 0;
630   
631   while (rlen < plen) {
632     if (read_int (jrsock, &atype) == -1) return -1; rlen += 4;
633     if (read_int (jrsock, &alen)  == -1) return -1; rlen += 4;
634     if (read_int (jrsock, &aop)   == -1) return -1; rlen += 4; 
635
636     radlog(L_DBG, LOG_PREFIX "reading attribute: type=%d; len=%d", atype, alen);
637
638     if (alen >= sizeof(buff)) {
639       radlog(L_ERR, LOG_PREFIX "packet value too large (len: %d)", alen);
640       return -1;
641     }
642
643     if (read(jrsock->con.sock, buff, alen) != (int)alen) return -1; rlen += alen;
644     buff[alen]=0;
645
646     /*
647      *     Create new attribute
648      */
649     vp = paircreate(atype, -1);
650     vp->operator = aop;
651
652     if (vp->type == -1) {
653       /*
654        *     FreeRADIUS should know about the same attributes that JRadius knows
655        */
656       radlog(L_ERR, LOG_PREFIX "received attribute we do not recognize (type: %d)", atype);
657       pairbasicfree(vp);
658       continue;
659     }
660
661     /*
662      *     Fill in the attribute value based on type
663      */
664     switch (vp->type) {
665       case PW_TYPE_INTEGER:
666       case PW_TYPE_DATE:
667         {
668           unpack_int(buff, &vp->lvalue);
669           vp->length = 4;
670         }
671         break;
672
673       case PW_TYPE_IPADDR:
674         memcpy((void *)&vp->lvalue, buff, 4);
675         vp->length = 4;
676         break;
677
678       default:
679         if (alen < sizeof(STR_VALUE(vp))) {
680           memcpy((void *)STR_VALUE(vp), buff, alen);
681             vp->length = alen;
682         }
683         break;
684     }
685
686     /*
687      *     Add the attribute to the packet
688      */
689     pairadd(pl, vp);
690   } 
691
692   return rlen;
693 }
694
695 /*
696  *     Read a radius packet from the socket
697  */
698 static int read_packet(JRADIUS * inst, JRSOCK *jrsock, RADIUS_PACKET *p)
699 {
700   unsigned char code;
701   unsigned char id;
702   unsigned int plen;
703
704   if (read_byte (jrsock, &code) == -1) return -1;
705   if (read_byte (jrsock, &id)   == -1) return -1;
706   if (read_int  (jrsock, &plen) == -1) return -1;
707
708   radlog(L_DBG, LOG_PREFIX "reading packet: code=%d len=%d", (int)code, plen);
709
710   if (inst->allow_codechange)
711     if (code != p->code) {
712       radlog(L_INFO, LOG_PREFIX "changing packet code from %d to %d", p->code, code);
713       p->code = code;
714     }
715
716   if (inst->allow_idchange)
717     if (id != p->id) {
718       radlog(L_INFO, LOG_PREFIX "changing packet id from %d to %d", p->id, id);
719       p->id = id;
720     }
721   
722   /*
723    *     Delete previous attribute list
724    */
725   pairfree(&p->vps);
726
727   if (plen == 0) return 0;
728
729   if (read_vps (jrsock, &p->vps, plen) == -1) return -1;
730
731   return 0;
732 }
733
734 static int read_request(JRSOCK *jrsock, REQUEST *p)
735 {
736   unsigned int plen;
737
738   if (read_int(jrsock, &plen) == -1) return -1;
739
740   radlog(L_DBG, LOG_PREFIX "reading request: config_item: len=%d", plen);
741
742   /*
743    *     Delete previous attribute list
744    */
745   pairfree(&p->config_items);
746
747   if (plen == 0) return 0;
748
749   if (read_vps(jrsock, &p->config_items, plen) == -1) return -1;
750
751   return 0;
752 }
753
754 static int rlm_jradius_call(char func, void *instance, REQUEST *req, int isproxy)
755 {
756   JRADIUS        * inst    = instance;
757   RADIUS_PACKET  * request = isproxy ? req->proxy : req->packet;
758   RADIUS_PACKET  * reply   = isproxy ? req->proxy_reply : req->reply;
759   JRSOCK         * jrsock  = 0;
760   JRSOCK           sjrsock;
761
762   int exitstatus = inst->onfail;
763   unsigned char rcode, pcount;
764
765   unsigned char buff[MESSAGE_LEN];
766   byte_array ba;
767
768   char * n = inst->name;
769   unsigned int nlen = strlen(n);
770   const char * err = 0;
771   int rc, attempt2=0;
772
773 #define W_ERR(s) { err=s; goto packerror;  }
774 #define R_ERR(s) { err=s; goto parseerror; }
775
776   if (inst->keepalive) {
777     jrsock = get_socket(inst);
778     if (!jrsock) return exitstatus;
779   } else {
780     jrsock = &sjrsock;
781     memset(jrsock, 0, sizeof(*jrsock));
782     jrsock->state = not_connected;
783   }
784
785   init_byte_array(&ba, buff, sizeof(buff));
786
787   pcount = 0;
788   if (request) pcount++;
789   if (reply) pcount++;
790
791   /*
792    *     Create byte array to send to jradius
793    */
794   if ((rc = pack_int    (&ba, nlen))                  == -1)  W_ERR("pack_int(nlen)");
795   if ((rc = pack_bytes  (&ba, (void *)n, nlen))       == -1)  W_ERR("pack_bytes(name)");
796   if ((rc = pack_byte   (&ba, func))                  == -1)  W_ERR("pack_byte(fun)");
797   if ((rc = pack_byte   (&ba, pcount))                == -1)  W_ERR("pack_byte(pcnt)");
798   if (pcount > 0 && (rc = pack_packet (&ba, request)) == -1)  W_ERR("pack_packet(req)");
799   if (pcount > 1 && (rc = pack_packet (&ba, reply))   == -1)  W_ERR("pack_packet(rep)");
800   if ((rc = pack_request(&ba, req))                   == -1)  W_ERR("pack_request()");
801
802   /*
803    *     Send data
804    */
805  start_over:
806   if (jrsock->state == not_connected) {
807     if (attempt2) radlog(L_ERR, LOG_PREFIX "reconnecting socket id %d", jrsock->id);
808     if (!connect_socket(jrsock, inst)) {
809       if (attempt2) radlog(L_ERR, LOG_PREFIX "could not reconnect socket %d, giving up", jrsock->id);
810       goto cleanup;
811     }
812   }
813   radlog(L_DBG, LOG_PREFIX "sending %d bytes to socket %d", ba.pos, jrsock->id);
814   if (socket_send(jrsock, ba.b, ba.pos) != (int)ba.pos ||
815       (rc = read_byte (jrsock, &rcode)) == -1) {
816     /*
817      *   With an error on the write or the first read, try closing the socket
818      *   and reconnecting to see if that improves matters any (tries this only once)
819      */
820     radlog(L_ERR, LOG_PREFIX "error sending request with socket %d", jrsock->id);
821     if (!inst->keepalive || attempt2) W_ERR("socket_send/first_read");
822     close_socket(inst, jrsock);
823     attempt2 = 1;
824     goto start_over;
825   }
826
827   /*
828    *     Read result
829    */
830   if ((rc = read_byte (jrsock, &pcount)) == -1)  R_ERR("read_byte(pcnt)");
831
832   radlog(L_DBG, LOG_PREFIX "return code %d; receiving %d packets", (int)rcode, (int)pcount);
833
834   if (pcount > 0 && request) if ((rc = read_packet (inst, jrsock, request)) == -1)  R_ERR("read_packet(req)");
835   if (pcount > 1 && reply)   if ((rc = read_packet (inst, jrsock, reply))   == -1)  R_ERR("read_packet(rep)");
836
837   if ((rc = read_request (jrsock, req)) == -1) R_ERR("read_request()");
838
839   /*
840    *    Since we deleted all the attribute lists in the request,
841    *    we need to reconfigure a few pointers in the REQUEST object
842    */
843   if (req->username) {
844     req->username = pairfind(request->vps, PW_USER_NAME);
845   }
846   if (req->password) {
847     req->password = pairfind(request->vps, PW_PASSWORD);
848     if (!req->password) req->password = pairfind(request->vps, PW_CHAP_PASSWORD);
849   }
850
851   /*
852    *    All done, set return code and cleanup
853    */
854   exitstatus = (int)rcode;
855   goto cleanup;
856
857  parseerror:
858   radlog(L_ERR, LOG_PREFIX "problem parsing the data [%s]",err);
859   if (inst->keepalive) close_socket(inst, jrsock);
860   goto cleanup;
861
862  packerror:
863   radlog(L_ERR, LOG_PREFIX "problem packing the data[%s]",err);
864   if (inst->keepalive) close_socket(inst, jrsock);
865
866  cleanup:
867   if (inst->keepalive) 
868     release_socket(inst, jrsock);
869   else  
870     close_socket(inst, jrsock);
871
872   return exitstatus;
873 }
874
875 static int jradius_authenticate(void *instance, REQUEST *request)
876 {
877   return rlm_jradius_call(JRADIUS_authenticate, instance, request, 0);
878 }
879
880 static int jradius_authorize(void *instance, REQUEST *request)
881 {
882   return rlm_jradius_call(JRADIUS_authorize, instance, request, 0);
883 }
884
885 static int jradius_preacct(void *instance, REQUEST *request)
886 {
887   return rlm_jradius_call(JRADIUS_preacct, instance, request, 0);
888 }
889
890 static int jradius_accounting(void *instance, REQUEST *request)
891 {
892   return rlm_jradius_call(JRADIUS_accounting, instance, request, 0);
893 }
894
895 static int jradius_checksimul(void *instance, REQUEST *request)
896 {
897   return rlm_jradius_call(JRADIUS_checksimul, instance, request, 0);
898 }
899
900 static int jradius_pre_proxy(void *instance, REQUEST *request)
901 {
902   return rlm_jradius_call(JRADIUS_pre_proxy, instance, request, 1);
903 }
904
905 static int jradius_post_proxy(void *instance, REQUEST *request)
906 {
907   return rlm_jradius_call(JRADIUS_post_proxy, instance, request, 1);
908 }
909
910 static int jradius_post_auth(void *instance, REQUEST *request)
911 {
912   return rlm_jradius_call(JRADIUS_post_auth, instance, request, 0);
913 }
914
915 static int jradius_detach(void *instance)
916 {
917   JRADIUS *inst = (JRADIUS *) instance;
918   free_socketpool(inst);
919   free(inst);
920   return 0;
921 }
922
923 module_t rlm_jradius = {
924   RLM_MODULE_INIT,
925   "jradius",
926   RLM_TYPE_THREAD_SAFE,
927   jradius_instantiate,
928   jradius_detach,
929   {
930     jradius_authenticate,
931     jradius_authorize,
932     jradius_preacct,
933     jradius_accounting,
934     jradius_checksimul,
935     jradius_pre_proxy,
936     jradius_post_proxy,
937     jradius_post_auth
938   },
939 };
940