Delete trailing whitespace.
[freeradius.git] / src / main / radclient.c
index cb61100..fb8cd40 100644 (file)
  *   along with this program; if not, write to the Free Software
  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  *
- * Copyright 2000  The FreeRADIUS server project
+ * Copyright 2000,2006  The FreeRADIUS server project
  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
  * Copyright 2000  Alan DeKok <aland@ox.org>
  */
-static const char rcsid[] = "$Id$";
 
-#include <freeradius-devel/autoconf.h>
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
 
-#include <stdio.h>
-#include <stdlib.h>
-
-#ifdef HAVE_UNISTD_H
-#      include <unistd.h>
-#endif
+#include <freeradius-devel/libradius.h>
+#include <freeradius-devel/conf.h>
+#include <freeradius-devel/radpaths.h>
 
-#include <string.h>
 #include <ctype.h>
-#include <netdb.h>
-
-#ifdef HAVE_SYS_SELECT_H
-#      include <sys/select.h>
-#endif
 
 #ifdef HAVE_GETOPT_H
 #      include <getopt.h>
@@ -46,11 +37,6 @@ static const char rcsid[] = "$Id$";
 
 #include <assert.h>
 
-#include <freeradius-devel/conf.h>
-#include <freeradius-devel/radpaths.h>
-#include <freeradius-devel/missing.h>
-#include <freeradius-devel/libradius.h>
-
 static int retries = 10;
 static float timeout = 3;
 static const char *secret = NULL;
@@ -64,13 +50,16 @@ static int packet_code = 0;
 static lrad_ipaddr_t server_ipaddr;
 static int resend_count = 1;
 static int done = 1;
+static int print_filename = 0;
+
+static lrad_ipaddr_t client_ipaddr;
+static int client_port = 0;
 
 static int sockfd;
-static int radius_id[256];
 static int last_used_id = -1;
 
 static rbtree_t *filename_tree = NULL;
-static rbtree_t *request_tree = NULL;
+static lrad_packet_list_t *pl = NULL;
 
 static int sleep_time = -1;
 
@@ -215,14 +204,14 @@ static radclient_t *radclient_init(const char *filename)
                /*
                 *      Keep a copy of the the User-Password attribute.
                 */
-               if ((vp = pairfind(radclient->request->vps, PW_PASSWORD)) != NULL) {
-                       strNcpy(radclient->password, vp->vp_strvalue,
+               if ((vp = pairfind(radclient->request->vps, PW_USER_PASSWORD)) != NULL) {
+                       strlcpy(radclient->password, vp->vp_strvalue,
                                sizeof(radclient->password));
                        /*
                         *      Otherwise keep a copy of the CHAP-Password attribute.
                         */
                } else if ((vp = pairfind(radclient->request->vps, PW_CHAP_PASSWORD)) != NULL) {
-                       strNcpy(radclient->password, vp->vp_strvalue,
+                       strlcpy(radclient->password, vp->vp_strvalue,
                                sizeof(radclient->password));
                } else {
                        radclient->password[0] = '\0';
@@ -241,16 +230,16 @@ static radclient_t *radclient_init(const char *filename)
                                 *      the attributes read from the file.
                                 */
                        case PW_PACKET_TYPE:
-                               radclient->request->code = vp->lvalue;
+                               radclient->request->code = vp->vp_integer;
                                break;
 
                        case PW_PACKET_DST_PORT:
-                               radclient->request->dst_port = (vp->lvalue & 0xffff);
+                               radclient->request->dst_port = (vp->vp_integer & 0xffff);
                                break;
 
                        case PW_PACKET_DST_IP_ADDRESS:
                                radclient->request->dst_ipaddr.af = AF_INET;
-                               radclient->request->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->lvalue;
+                               radclient->request->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
                                break;
 
                        case PW_PACKET_DST_IPV6_ADDRESS:
@@ -259,12 +248,12 @@ static radclient_t *radclient_init(const char *filename)
                                break;
 
                        case PW_PACKET_SRC_PORT:
-                               radclient->request->src_port = (vp->lvalue & 0xffff);
+                               radclient->request->src_port = (vp->vp_integer & 0xffff);
                                break;
 
                        case PW_PACKET_SRC_IP_ADDRESS:
                                radclient->request->src_ipaddr.af = AF_INET;
-                               radclient->request->src_ipaddr.ipaddr.ip4addr.s_addr = vp->lvalue;
+                               radclient->request->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
                                break;
 
                        case PW_PACKET_SRC_IPV6_ADDRESS:
@@ -337,7 +326,7 @@ static int radclient_sane(radclient_t *radclient)
 
                radclient->request->code = packet_code;
        }
-       radclient->request->sockfd = sockfd;
+       radclient->request->sockfd = -1;
 
        return 0;
 }
@@ -388,58 +377,10 @@ static int filename_walk(void *context, void *data)
 
 
 /*
- *     Compare two RADIUS_PACKET data structures, based on a number
- *     of criteria.
- */
-static int request_cmp(const void *one, const void *two)
-{
-       int rcode;
-       const radclient_t *a = one;
-       const radclient_t *b = two;
-
-       /*
-        *      The following code looks unreasonable, but it's
-        *      the only way to make the comparisons work.
-        */
-       if (a->request->id < b->request->id) return -1;
-       if (a->request->id > b->request->id) return +1;
-
-       if (a->request->dst_ipaddr.af < b->request->dst_ipaddr.af) return -1;
-       if (a->request->dst_ipaddr.af > b->request->dst_ipaddr.af) return +1;
-
-       switch (a->request->dst_ipaddr.af) {
-       case AF_INET:
-               rcode = memcmp(&a->request->dst_ipaddr.ipaddr.ip4addr,
-                              &b->request->dst_ipaddr.ipaddr.ip4addr,
-                              sizeof(a->request->dst_ipaddr.ipaddr.ip4addr));
-               break;
-       case AF_INET6:
-               rcode = memcmp(&a->request->dst_ipaddr.ipaddr.ip6addr,
-                              &b->request->dst_ipaddr.ipaddr.ip6addr,
-                              sizeof(a->request->dst_ipaddr.ipaddr.ip6addr));
-               break;
-       default:                /* FIXME: die! */
-               return -1;
-               break;
-       }
-       if (rcode != 0) return rcode;
-
-       if (a->request->dst_port < b->request->dst_port) return -1;
-       if (a->request->dst_port > b->request->dst_port) return +1;
-
-       /*
-        *      Everything's equal.  Say so.
-        */
-       return 0;
-}
-
-/*
- *     "Free" a request.
+ *     Deallocate packet ID, etc.
  */
-static void request_free(void *data)
+static void deallocate_id(radclient_t *radclient)
 {
-       radclient_t *radclient = (radclient_t *) data;
-
        if (!radclient || !radclient->request ||
            (radclient->request->id < 0)) {
                return;
@@ -448,7 +389,7 @@ static void request_free(void *data)
        /*
         *      One more unused RADIUS ID.
         */
-       radius_id[radclient->request->id] = 0;
+       lrad_packet_list_id_free(pl, radclient->request);
        radclient->request->id = -1;
 
        /*
@@ -480,7 +421,7 @@ static void print_hex(RADIUS_PACKET *packet)
                printf("%02x", packet->data[i]);
        }
        printf("\n");
-       
+
        if (packet->data_len > 20) {
                int total;
                const uint8_t *ptr;
@@ -531,8 +472,6 @@ static void print_hex(RADIUS_PACKET *packet)
  */
 static int send_one_packet(radclient_t *radclient)
 {
-       int i;
-
        assert(radclient->done == 0);
 
        /*
@@ -548,29 +487,33 @@ static int send_one_packet(radclient_t *radclient)
         *      Haven't sent the packet yet.  Initialize it.
         */
        if (radclient->request->id == -1) {
-               int found = 0;
+               int i, rcode;
 
                assert(radclient->reply == NULL);
 
                /*
-                *      Find a free packet Id
-                */
-               for (i = 0; i < 256; i++) {
-                       if (radius_id[(last_used_id + i) & 0xff] == 0) {
-                               last_used_id = (last_used_id + i) & 0xff;
-                               radius_id[last_used_id] = 1;
-                               radclient->request->id = last_used_id++;
-                               found = 1;
-                               break;
-                       }
-               }
-
-               /*
                 *      Didn't find a free packet ID, we're not done,
                 *      we don't sleep, and we stop trying to process
                 *      this packet.
                 */
-               if (!found) {
+       retry:
+               rcode = lrad_packet_list_id_alloc(pl, radclient->request);
+               if (rcode < 0) {
+                       int mysockfd;
+
+                       mysockfd = lrad_socket(&client_ipaddr, 0);
+                       if (!mysockfd) {
+                               fprintf(stderr, "radclient: Can't open new socket\n");
+                               exit(1);
+                       }
+                       if (!lrad_packet_list_socket_add(pl, mysockfd)) {
+                               fprintf(stderr, "radclient: Can't add new socket\n");
+                               exit(1);
+                       }
+                       goto retry;
+               }
+
+               if (rcode == 0) {
                        done = 0;
                        sleep_time = 0;
                        return 0;
@@ -579,8 +522,9 @@ static int send_one_packet(radclient_t *radclient)
                assert(radclient->request->id != -1);
                assert(radclient->request->data == NULL);
 
-               librad_md5_calc(radclient->request->vector, radclient->request->vector,
-                               sizeof(radclient->request->vector));
+               for (i = 0; i < 4; i++) {
+                       *((uint32_t *) radclient->request->vector) = lrad_rand();
+               }
 
                /*
                 *      Update the password, so it can be encrypted with the
@@ -589,8 +533,8 @@ static int send_one_packet(radclient_t *radclient)
                if (radclient->password[0] != '\0') {
                        VALUE_PAIR *vp;
 
-                       if ((vp = pairfind(radclient->request->vps, PW_PASSWORD)) != NULL) {
-                               strNcpy(vp->vp_strvalue, radclient->password,
+                       if ((vp = pairfind(radclient->request->vps, PW_USER_PASSWORD)) != NULL) {
+                               strlcpy(vp->vp_strvalue, radclient->password,
                                        sizeof(vp->vp_strvalue));
                                vp->length = strlen(vp->vp_strvalue);
 
@@ -600,7 +544,7 @@ static int send_one_packet(radclient_t *radclient)
                           *           AND vp->length != 17
                           *           AND rad_chap_encode() != vp->vp_octets
                           */
-                               strNcpy(vp->vp_strvalue, radclient->password,
+                               strlcpy(vp->vp_strvalue, radclient->password,
                                        sizeof(vp->vp_strvalue));
                                vp->length = strlen(vp->vp_strvalue);
 
@@ -618,10 +562,11 @@ static int send_one_packet(radclient_t *radclient)
                /*
                 *      Duplicate found.  Serious error!
                 */
-               if (rbtree_insert(request_tree, radclient) == 0) {
+               if (!lrad_packet_list_insert(pl, &radclient->request)) {
                        assert(0 == 1);
                }
 
+
        } else {                /* radclient->request->id >= 0 */
                time_t now = time(NULL);
 
@@ -652,19 +597,17 @@ static int send_one_packet(radclient_t *radclient)
                 *      We're not trying later, maybe the packet is done.
                 */
                if (radclient->tries == retries) {
-                       rbnode_t *node;
                        assert(radclient->request->id >= 0);
-                       
+
                        /*
                         *      Delete the request from the tree of
                         *      outstanding requests.
                         */
-                       node = rbtree_find(request_tree, radclient);
-                       assert(node != NULL);
-                       
-                       fprintf(stderr, "radclient: no response from server for ID %d\n", radclient->request->id);
-                       rbtree_delete(request_tree, node);
-                       
+                       lrad_packet_list_yank(pl, radclient->request);
+
+                       fprintf(stderr, "radclient: no response from server for ID %d socket %d\n", radclient->request->id, radclient->request->sockfd);
+                       deallocate_id(radclient);
+
                        /*
                         *      Normally we mark it "done" when we've received
                         *      the response, but this is a special case.
@@ -704,14 +647,15 @@ static int recv_one_packet(int wait_time)
 {
        fd_set          set;
        struct timeval  tv;
-       radclient_t     myclient, *radclient;
-       RADIUS_PACKET   myrequest, *reply;
-       rbnode_t        *node;
-
+       radclient_t     *radclient;
+       RADIUS_PACKET   *reply, **request_p;
+       volatile int max_fd;
 
        /* And wait for reply, timing out as necessary */
        FD_ZERO(&set);
-       FD_SET(sockfd, &set);
+
+       max_fd = lrad_packet_list_fd_set(pl, &set);
+       if (max_fd < 0) exit(1); /* no sockets to listen on! */
 
        if (wait_time <= 0) {
                tv.tv_sec = 0;
@@ -723,14 +667,14 @@ static int recv_one_packet(int wait_time)
        /*
         *      No packet was received.
         */
-       if (select(sockfd + 1, &set, NULL, NULL, &tv) != 1) {
+       if (select(max_fd, &set, NULL, NULL, &tv) <= 0) {
                return 0;
        }
 
        /*
         *      Look for the packet.
         */
-       reply = rad_recv(sockfd);
+       reply = lrad_packet_list_recv(pl, &set);
        if (!reply) {
                fprintf(stderr, "radclient: received bad packet: %s\n",
                        librad_errstr);
@@ -739,26 +683,13 @@ static int recv_one_packet(int wait_time)
 
        if (librad_debug > 2) print_hex(reply);
 
-       myclient.request = &myrequest;
-       myrequest.id = reply->id;
-       myrequest.dst_ipaddr = reply->src_ipaddr;
-       myrequest.dst_port = reply->src_port;
-
-       node = rbtree_find(request_tree, &myclient);
-       if (!node) {
-               fprintf(stderr, "radclient: received response to request we did not send. (%d)\n", myrequest.id);
+       request_p = lrad_packet_list_find_byreply(pl, reply);
+       if (!request_p) {
+               fprintf(stderr, "radclient: received response to request we did not send. (id=%d socket %d)\n", reply->id, reply->sockfd);
                rad_free(&reply);
                return -1;      /* got reply to packet we didn't send */
        }
-
-       radclient = rbtree_node2data(request_tree, node);
-       assert(radclient != NULL);
-       rbtree_delete(request_tree, node);
-       assert(radclient->request->id == -1);
-       assert(radclient->request->data == NULL);
-
-       assert(radclient->reply == NULL);
-       radclient->reply = reply;
+       radclient = lrad_packet2myptr(radclient_t, request, request_p);
 
        /*
         *      Fails the signature validation: not a real reply.
@@ -770,6 +701,14 @@ static int recv_one_packet(int wait_time)
                goto packet_done; /* shared secret is incorrect */
        }
 
+       lrad_packet_list_yank(pl, radclient->request);
+       if (print_filename) printf("%s:%d %d\n",
+                                  radclient->filename,
+                                  radclient->packet_number,
+                                  reply->code);
+       deallocate_id(radclient);
+       radclient->reply = reply;
+
        /*
         *      If this fails, we're out of memory.
         */
@@ -792,16 +731,16 @@ static int recv_one_packet(int wait_time)
        }
 
 packet_done:
-       rad_free(&radclient->reply);
-
        /*
         *      Once we've sent the packet as many times as requested,
         *      mark it done.
         */
-       if (radclient->resend == resend_count) {
-               assert((node = rbtree_find(request_tree, radclient)) == NULL);
+       if ((radclient->done) ||
+           (radclient->resend == resend_count)) {
+               assert(lrad_packet_list_find(pl, radclient->request) == NULL);
                radclient->done = 1;
        }
+       rad_free(&radclient->reply);
 
        return 0;
 }
@@ -840,13 +779,7 @@ int main(int argc, char **argv)
                exit(1);
        }
 
-       request_tree = rbtree_create(request_cmp, request_free, 0);
-       if (!request_tree) {
-               fprintf(stderr, "radclient: Out of memory\n");
-               exit(1);
-       }
-
-       while ((c = getopt(argc, argv, "46c:d:f:hi:n:p:qr:sS:t:vx")) != EOF) switch(c) {
+       while ((c = getopt(argc, argv, "46c:d:f:Fhi:n:p:qr:sS:t:vx")) != EOF) switch(c) {
                case '4':
                        force_af = AF_INET;
                        break;
@@ -864,7 +797,10 @@ int main(int argc, char **argv)
                case 'f':
                        rbtree_insert(filename_tree, optarg);
                        break;
-               case 'i':
+               case 'F':
+                       print_filename = 1;
+                       break;
+               case 'i':       /* currently broken */
                        if (!isdigit((int) *optarg))
                                usage();
                        last_used_id = atoi(optarg);
@@ -878,6 +814,13 @@ int main(int argc, char **argv)
                        if (persec <= 0) usage();
                        break;
 
+                       /*
+                        *      Note that sending MANY requests in
+                        *      parallel can over-run the kernel
+                        *      queues, and Linux will happily discard
+                        *      packets.  So even if the server responds,
+                        *      the client may not see the response.
+                        */
                case 'p':
                        parallel = atoi(optarg);
                        if (parallel <= 0) usage();
@@ -967,7 +910,7 @@ int main(int argc, char **argv)
                        if ((p - argv[1]) >= sizeof(buffer)) {
                                usage();
                        }
-                       
+
                        memcpy(buffer, argv[1] + 1, p - argv[1] - 1);
                        buffer[p - argv[1] - 1] = '\0';
 
@@ -994,8 +937,6 @@ int main(int argc, char **argv)
                if (portname) server_port = atoi(portname);
        }
 
-       memset(radius_id, 0, sizeof(radius_id));
-
        /*
         *      See what kind of request we want to send.
         */
@@ -1071,28 +1012,42 @@ int main(int argc, char **argv)
         *      This means we ignore later ones.
         */
        if (radclient_head->request->src_ipaddr.af == AF_UNSPEC) {
-               sockfd = lrad_socket(&server_ipaddr, 0);
+               memset(&client_ipaddr, 0, sizeof(client_ipaddr));
+               client_ipaddr.af = server_ipaddr.af;
+               client_port = 0;
        } else {
-               sockfd = lrad_socket(&radclient_head->request->src_ipaddr,
-                                    radclient_head->request->src_port);
+               client_ipaddr = radclient_head->request->src_ipaddr;
+               client_port = radclient_head->request->src_port;
        }
+       sockfd = lrad_socket(&client_ipaddr, client_port);
        if (sockfd < 0) {
                fprintf(stderr, "radclient: socket: %s\n", librad_errstr);
                exit(1);
        }
 
+       pl = lrad_packet_list_create(1);
+       if (!pl) {
+               fprintf(stderr, "radclient: Out of memory\n");
+               exit(1);
+       }
+
+       if (!lrad_packet_list_socket_add(pl, sockfd)) {
+               fprintf(stderr, "radclient: Out of memory\n");
+               exit(1);
+       }
+
        /*
         *      Walk over the list of packets, sanity checking
         *      everything.
         */
        for (this = radclient_head; this != NULL; this = this->next) {
+               this->request->src_ipaddr = client_ipaddr;
+               this->request->src_port = client_port;
                if (radclient_sane(this) != 0) {
                        exit(1);
                }
        }
 
-       if (last_used_id < 0) last_used_id = getpid() & 0xff;
-
        /*
         *      Walk over the packets to send, until
         *      we're all done.
@@ -1173,7 +1128,7 @@ int main(int argc, char **argv)
                                                tv.tv_sec = 0;
                                                tv.tv_usec = 1000000/persec;
                                        }
-                                       
+
                                        /*
                                         *      Sleep for milliseconds,
                                         *      portably.
@@ -1204,7 +1159,7 @@ int main(int argc, char **argv)
                /*
                 *      Still have outstanding requests.
                 */
-               if (rbtree_num_elements(request_tree) > 0) {
+               if (lrad_packet_list_num_elements(pl) > 0) {
                        done = 0;
                } else {
                        sleep_time = 0;
@@ -1223,7 +1178,8 @@ int main(int argc, char **argv)
        } while (!done);
 
        rbtree_free(filename_tree);
-       rbtree_free(request_tree);
+       lrad_packet_list_free(pl);
+       dict_free();
 
        if (do_summary) {
                printf("\n\t   Total approved auths:  %d\n", totalapp);