Delete trailing whitespace.
[freeradius.git] / src / main / radclient.c
index a09793f..fb8cd40 100644 (file)
  *
  *   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
+ *   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>
-#include <sys/socket.h>
-
-#ifdef HAVE_NETINET_IN_H
-#      include <netinet/in.h>
-#endif
-
-#ifdef HAVE_SYS_SELECT_H
-#      include <sys/select.h>
-#endif
 
 #ifdef HAVE_GETOPT_H
 #      include <getopt.h>
@@ -51,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;
@@ -69,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;
 
@@ -192,14 +176,14 @@ static radclient_t *radclient_init(const char *filename)
                 */
                radclient = malloc(sizeof(*radclient));
                if (!radclient) {
-                       perror("radclient: ");
+                       perror("radclient: X");
                        return NULL; /* memory leak "start" */
                }
                memset(radclient, 0, sizeof(*radclient));
 
                radclient->request = rad_alloc(1);
                if (!radclient->request) {
-                       librad_perror("radclient: ");
+                       librad_perror("radclient: Y");
                        radclient_free(radclient);
                        return NULL; /* memory leak "start" */
                }
@@ -220,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';
@@ -246,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:
@@ -264,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:
@@ -342,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;
 }
@@ -393,58 +377,10 @@ static int filename_walk(void *context, void *data)
 
 
 /*
- *     Compare two RADIUS_PACKET data structures, based on a number
- *     of criteria.
+ *     Deallocate packet ID, etc.
  */
-static int request_cmp(const void *one, const void *two)
+static void deallocate_id(radclient_t *radclient)
 {
-       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.
- */
-static void request_free(void *data)
-{
-       radclient_t *radclient = (radclient_t *) data;
-
        if (!radclient || !radclient->request ||
            (radclient->request->id < 0)) {
                return;
@@ -453,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;
 
        /*
@@ -485,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;
@@ -536,8 +472,6 @@ static void print_hex(RADIUS_PACKET *packet)
  */
 static int send_one_packet(radclient_t *radclient)
 {
-       int i;
-
        assert(radclient->done == 0);
 
        /*
@@ -553,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;
@@ -584,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
@@ -594,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);
 
@@ -605,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);
 
@@ -623,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);
 
@@ -657,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.
@@ -709,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;
@@ -728,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);
@@ -744,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.
@@ -775,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.
         */
@@ -797,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;
 }
@@ -836,9 +770,6 @@ int main(int argc, char **argv)
        int parallel = 1;
        radclient_t     *this;
        int force_af = AF_UNSPEC;
-       int len = 0;
-        struct sockaddr_storage ss;
-        struct sockaddr_in *s4;
 
        librad_debug = 0;
 
@@ -848,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;
@@ -872,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);
@@ -886,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();
@@ -975,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';
 
@@ -992,7 +927,7 @@ int main(int argc, char **argv)
                }
 
                if (ip_hton(hostname, force_af, &server_ipaddr) < 0) {
-                       fprintf(stderr, "radclient: Failed to find IP address for host %s: %s\n", argv[1], strerror(errno));
+                       fprintf(stderr, "radclient: Failed to find IP address for host %s: %s\n", hostname, strerror(errno));
                        exit(1);
                }
 
@@ -1002,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.
         */
@@ -1075,54 +1008,32 @@ int main(int argc, char **argv)
        }
 
        /*
-        * Bind only if Packet-Src-IP(v6)Address Attribute is found
+        *      Bind to the first specified IP address and port.
+        *      This means we ignore later ones.
         */
-       switch (radclient_head->request->src_ipaddr.af) {
-       case AF_UNSPEC:
-       default:
-               /*
-                *      Grab the socket.
-                */
-               if ((sockfd = socket(server_ipaddr.af, SOCK_DGRAM, 0)) < 0) {
-                       perror("radclient: socket: ");
-                       exit(1);
-               }
-               break;
-
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
-       case AF_INET6:
-               {
-                       struct sockaddr_in6 *s6;
-                       s6 = (struct sockaddr_in6 *)&ss;
-                       len = sizeof(struct sockaddr_in6);
-                       s6->sin6_family = AF_INET6;
-                       s6->sin6_flowinfo = 0;
-                       s6->sin6_port = htons(radclient_head->request->src_port);
-                       memcpy(&s6->sin6_addr, &radclient_head->request->src_ipaddr.ipaddr, 16);
-               }
-               goto sock_bind;
-#endif
-
-       case AF_INET:
-               s4 = (struct sockaddr_in *)&ss;
-               len = sizeof(struct sockaddr_in);
-               s4->sin_family = AF_INET;
-               s4->sin_port = htons(radclient_head->request->src_port);
-               memcpy(&s4->sin_addr, &radclient_head->request->src_ipaddr.ipaddr, 4);
-               goto sock_bind;
+       if (radclient_head->request->src_ipaddr.af == AF_UNSPEC) {
+               memset(&client_ipaddr, 0, sizeof(client_ipaddr));
+               client_ipaddr.af = server_ipaddr.af;
+               client_port = 0;
+       } else {
+               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);
+       }
 
-       sock_bind:
-               if ((sockfd = socket(radclient_head->request->src_ipaddr.af,
-                                SOCK_DGRAM, 0)) < 0) {
+       pl = lrad_packet_list_create(1);
+       if (!pl) {
+               fprintf(stderr, "radclient: Out of memory\n");
+               exit(1);
+       }
 
-                       perror("radclient: socket: ");
-                       exit(1);
-               }
-               if (bind(sockfd, (struct sockaddr *)&ss, len) < 0) {
-                       perror("radclient: bind: ");
-                       exit(1);
-               }
-               break;
+       if (!lrad_packet_list_socket_add(pl, sockfd)) {
+               fprintf(stderr, "radclient: Out of memory\n");
+               exit(1);
        }
 
        /*
@@ -1130,13 +1041,13 @@ int main(int argc, char **argv)
         *      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.
@@ -1217,7 +1128,7 @@ int main(int argc, char **argv)
                                                tv.tv_sec = 0;
                                                tv.tv_usec = 1000000/persec;
                                        }
-                                       
+
                                        /*
                                         *      Sleep for milliseconds,
                                         *      portably.
@@ -1248,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;
@@ -1267,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);