static const char rcsid[] = "$Id$";
#include "autoconf.h"
-#include "libradius.h"
#include <stdio.h>
#include <stdlib.h>
#include "conf.h"
#include "radpaths.h"
#include "missing.h"
+#include "libradius.h"
static int retries = 10;
static float timeout = 3;
static int sockfd;
static int radius_id[256];
-static int last_used_id = 0;
+static int last_used_id = -1;
static rbtree_t *filename_tree = NULL;
static rbtree_t *request_tree = NULL;
static radclient_t *radclient_tail = NULL;
-static void usage(void)
+static void NEVER_RETURNS usage(void)
{
fprintf(stderr, "Usage: radclient [options] server[:port] <command> [<secret>]\n");
fprintf(stderr, " -c count Send each packet 'count' times.\n");
fprintf(stderr, " -d raddb Set dictionary directory.\n");
fprintf(stderr, " -f file Read packets from file, not stdin.\n");
- fprintf(stderr, " -r retries If timeout, retry sending the packet 'retries' times.\n");
- fprintf(stderr, " -t timeout Wait 'timeout' seconds before retrying (may be a floating point number).\n");
fprintf(stderr, " -i id Set request id to 'id'. Values may be 0..255\n");
- fprintf(stderr, " -S file read secret from file, not command line.\n");
+ fprintf(stderr, " -n num Send N requests/s\n");
+ fprintf(stderr, " -p num Send 'num' packets from a file in parallel.\n");
fprintf(stderr, " -q Do not print anything out.\n");
+ fprintf(stderr, " -r retries If timeout, retry sending the packet 'retries' times.\n");
fprintf(stderr, " -s Print out summary information of auth results.\n");
+ fprintf(stderr, " -S file read secret from file, not command line.\n");
+ fprintf(stderr, " -t timeout Wait 'timeout' seconds before retrying (may be a floating point number).\n");
fprintf(stderr, " -v Show program version information.\n");
fprintf(stderr, " -x Debugging mode.\n");
}
/*
- * Free a radclient struct
+ * Free a radclient struct, which may (or may not)
+ * already be in the list.
*/
static void radclient_free(radclient_t *radclient)
{
if (prev) {
assert(radclient_head != radclient);
prev->next = next;
- } else {
+ } else if (radclient_head) {
assert(radclient_head == radclient);
radclient_head = next;
}
if (next) {
assert(radclient_tail != radclient);
next->prev = prev;
- } else {
+ } else if (radclient_tail) {
assert(radclient_tail == radclient);
radclient_tail = prev;
}
radclient->request->vps = readvp2(fp, &filedone, "radclient:");
if (!radclient->request->vps) {
radclient_free(radclient);
- return NULL; /* memory leak "start" */
+ return start; /* done: return the list */
}
/*
* Keep a copy of the the User-Password attribute.
*/
if ((vp = pairfind(radclient->request->vps, PW_PASSWORD)) != NULL) {
- strNcpy(radclient->password, (char *)vp->strvalue, sizeof(vp->strvalue));
+ strNcpy(radclient->password, (char *)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, (char *)vp->strvalue, sizeof(vp->strvalue));
+ strNcpy(radclient->password, (char *)vp->strvalue, sizeof(radclient->password));
} else {
radclient->password[0] = '\0';
}
return strcmp((const char *) one, (const char *) two);
}
-static int filename_walk(void *data)
+static int filename_walk(void *context, void *data)
{
const char *filename = data;
radclient_t *radclient;
+ context = context; /* -Wunused */
+
/*
* Initialize the request we're about
* to send.
{
int i;
- /*
- * Sent this packet as many times as requested.
- * ignore it.
- */
- if (radclient->resend >= resend_count) {
- radclient->done = 1;
- return 0;
- }
+ assert(radclient->done == 0);
/*
* Remember when we have to wake up, to re-send the
VALUE_PAIR *vp;
if ((vp = pairfind(radclient->request->vps, PW_PASSWORD)) != NULL) {
- strNcpy((char *)vp->strvalue, radclient->password, strlen(radclient->password) + 1);
- vp->length = strlen(radclient->password);
+ strNcpy((char *)vp->strvalue, radclient->password, sizeof(vp->strvalue));
+ vp->length = strlen(vp->strvalue);
} else if ((vp = pairfind(radclient->request->vps, PW_CHAP_PASSWORD)) != NULL) {
- strNcpy((char *)vp->strvalue, radclient->password, strlen(radclient->password) + 1);
- vp->length = strlen(radclient->password);
+ strNcpy((char *)vp->strvalue, radclient->password, sizeof(vp->strvalue));
+ vp->length = strlen(vp->strvalue);
rad_chap_encode(radclient->request, (char *) vp->strvalue, radclient->request->id, vp);
vp->length = 17;
assert(0 == 1);
}
- } else 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);
- totallost++;
- return -1;
-
- /*
- * FIXME: Do stuff for packet loss.
- */
-
} else { /* radclient->request->id >= 0 */
time_t now = time(NULL);
return 0;
}
+ /*
+ * 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);
+
+ /*
+ * Normally we mark it "done" when we've received
+ * the response, but this is a special case.
+ */
+ if (radclient->resend == resend_count) {
+ radclient->done = 1;
+ }
+ totallost++;
+ return -1;
+ }
+
+ /*
+ * We are trying later.
+ */
radclient->timestamp = now;
radclient->tries++;
}
/*
* Send the packet.
*/
- rad_send(radclient->request, NULL, secret);
+ if (rad_send(radclient->request, NULL, secret) < 0) {
+ fprintf(stderr, "radclient: Failed to send packet for ID %d: %s\n",
+ radclient->request->id, librad_errstr);
+ }
return 0;
}
node = rbtree_find(request_tree, &myclient);
if (!node) {
fprintf(stderr, "radclient: received response to request we did not send.\n");
+ rad_free(&reply);
return -1; /* got reply to packet we didn't send */
}
if (rad_decode(reply, radclient->request, secret) != 0) {
librad_perror("rad_decode");
totallost++;
- return -1;
+ goto packet_done; /* shared secret is incorrect */
}
/* libradius debug already prints out the value pairs for us */
totaldeny++;
}
- if (radclient->reply) rad_free(&radclient->reply);
-
- return 0;
-}
-
-/*
- * Walk over the tree, sending packets.
- */
-static int radclient_send(radclient_t *radclient)
-{
- /*
- * Send the current packet.
- */
- send_one_packet(radclient);
-
- /*
- * Do rad_recv(), and look for the response in the tree,
- * but don't wait for a response.
- */
- recv_one_packet(0);
+packet_done:
+ rad_free(&radclient->reply);
/*
- * Still elements to wa
+ * Once we've sent the packet as many times as requested,
+ * mark it done.
*/
- if (radclient->resend < resend_count) {
- done = 0;
- sleep_time = 0;
+ if (radclient->resend == resend_count) {
+ assert((node = rbtree_find(request_tree, radclient)) == NULL);
+ radclient->done = 1;
}
return 0;
char filesecret[256];
FILE *fp;
int do_summary = 0;
- int id;
+ int persec = 0;
+ int parallel = 1;
radclient_t *this;
- id = ((int)getpid() & 0xff);
librad_debug = 0;
filename_tree = rbtree_create(filename_cmp, NULL, 0);
exit(1);
}
- while ((c = getopt(argc, argv, "c:d:f:hi:qst:r:S:xv")) != EOF) switch(c) {
+ while ((c = getopt(argc, argv, "c:d:f:hi:n:p:qr:sS:t:vx")) != EOF) switch(c) {
case 'c':
if (!isdigit((int) *optarg))
usage();
case 'f':
rbtree_insert(filename_tree, optarg);
break;
- case 'q':
- do_output = 0;
- break;
- case 'x':
- librad_debug++;
- break;
- case 'r':
- if (!isdigit((int) *optarg))
- usage();
- retries = atoi(optarg);
- break;
case 'i':
if (!isdigit((int) *optarg))
usage();
- id = atoi(optarg);
- if ((id < 0) || (id > 255)) {
+ last_used_id = atoi(optarg);
+ if ((last_used_id < 0) || (last_used_id > 255)) {
usage();
}
break;
- case 's':
- do_summary = 1;
+
+ case 'n':
+ persec = atoi(optarg);
+ if (persec <= 0) usage();
break;
- case 't':
+
+ case 'p':
+ parallel = atoi(optarg);
+ if (parallel <= 0) usage();
+ break;
+
+ case 'q':
+ do_output = 0;
+ break;
+ case 'r':
if (!isdigit((int) *optarg))
usage();
- timeout = atof(optarg);
+ retries = atoi(optarg);
+ if ((retries == 0) || (retries > 1000)) usage();
break;
- case 'v':
- printf("radclient: $Id$ built on " __DATE__ " at " __TIME__ "\n");
- exit(0);
+ case 's':
+ do_summary = 1;
break;
case 'S':
fp = fopen(optarg, "r");
}
secret = filesecret;
break;
+ case 't':
+ if (!isdigit((int) *optarg))
+ usage();
+ timeout = atof(optarg);
+ break;
+ case 'v':
+ printf("radclient: $Id$ built on " __DATE__ " at " __TIME__ "\n");
+ exit(0);
+ break;
+ case 'x':
+ librad_debug++;
+ break;
case 'h':
default:
usage();
if (server_port == 0) server_port = PW_AUTH_UDP_PORT;
packet_code = PW_AUTHENTICATION_REQUEST;
+ } else if (strcmp(argv[2], "challenge") == 0) {
+ if (server_port == 0) server_port = getport("radius");
+ if (server_port == 0) server_port = PW_AUTH_UDP_PORT;
+ packet_code = PW_ACCESS_CHALLENGE;
+
} else if (strcmp(argv[2], "acct") == 0) {
if (server_port == 0) server_port = getport("radacct");
if (server_port == 0) server_port = PW_ACCT_UDP_PORT;
/*
* Walk over the list of filenames, creating the requests.
*/
- if (rbtree_walk(filename_tree, filename_walk, InOrder) != 0) {
+ if (rbtree_walk(filename_tree, InOrder, filename_walk, NULL) != 0) {
exit(1);
}
}
}
- last_used_id = getpid() & 0xff;
+ if (last_used_id < 0) last_used_id = getpid() & 0xff;
/*
* Walk over the packets to send, until
* loop.
*/
do {
+ int n = parallel;
radclient_t *next;
const char *filename = NULL;
next = this->next;
/*
+ * If there's a packet to receive,
+ * receive it, but don't wait for a
+ * packet.
+ */
+ recv_one_packet(0);
+
+ /*
+ * This packet is done. Delete it.
+ */
+ if (this->done) {
+ radclient_free(this);
+ continue;
+ }
+
+ /*
* Packets from multiple '-f' are sent
- * in parallel. Packets from one file
- * are sent in series.
+ * in parallel.
+ *
+ * Packets from one file are sent in
+ * series, unless '-p' is specified, in
+ * which case N packets from each file
+ * are sent in parallel.
*/
if (this->filename != filename) {
filename = this->filename;
- radclient_send(this);
- if (this->done) {
- radclient_free(this);
+ n = parallel;
+ }
+
+ if (n > 0) {
+ n--;
+
+ /*
+ * Send the current packet.
+ */
+ send_one_packet(this);
+
+ /*
+ * Wait a little before sending
+ * the next packet, if told to.
+ */
+ if (persec) {
+ struct timeval tv;
+
+ /*
+ * Don't sleep elsewhere.
+ */
+ sleep_time = 0;
+
+ if (persec == 1) {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ } else {
+ tv.tv_sec = 0;
+ tv.tv_usec = 1000000/persec;
+ }
+
+ /*
+ * Sleep for milliseconds,
+ * portably.
+ *
+ * If we get an error or
+ * a signal, treat it like
+ * a normal timeout.
+ */
+ select(0, NULL, NULL, NULL, &tv);
+ }
+
+ /*
+ * If we haven't sent this packet
+ * often enough, we're not done,
+ * and we shouldn't sleep.
+ */
+ if (this->resend < resend_count) {
+ done = 0;
+ sleep_time = 0;
}
- } else {
+ } else { /* haven't sent this packet, we're not done */
assert(this->done == 0);
assert(this->reply == NULL);
done = 0;