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 const char *secret = NULL;
static int do_output = 1;
-static int filedone = 0;
static int totalapp = 0;
static int totaldeny = 0;
static int totallost = 0;
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;
struct radclient_t *next;
const char *filename;
+ int packet_number; /* in the file */
char password[256];
time_t timestamp;
RADIUS_PACKET *request;
static radclient_t *radclient_tail = NULL;
-/*
- * Read valuepairs from the fp up to End-Of-File.
- */
-static VALUE_PAIR *readvp(FILE *fp)
-{
- return readvp2(fp, &filedone, "radclient:");
-}
-
-static void usage(void)
+static void NEVER_RETURNS usage(void)
{
fprintf(stderr, "Usage: radclient [options] server[:port] <command> [<secret>]\n");
-
+
fprintf(stderr, " <command> One of auth, acct, status, or disconnect.\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(void *data)
+static void radclient_free(radclient_t *radclient)
{
- radclient_t *radclient = (radclient_t *) data;
-
- if (!radclient) return;
+ radclient_t *prev, *next;
if (radclient->request) rad_free(&radclient->request);
if (radclient->reply) rad_free(&radclient->reply);
- if (!radclient->prev) {
- assert(radclient_head = radclient);
- radclient_head = radclient->next;
- } else {
+ prev = radclient->prev;
+ next = radclient->next;
+
+ if (prev) {
assert(radclient_head != radclient);
- radclient->prev->next = radclient->next;
+ prev->next = next;
+ } else if (radclient_head) {
+ assert(radclient_head == radclient);
+ radclient_head = next;
}
- if (!radclient->next) {
- assert(radclient_tail = radclient);
- radclient_tail = radclient->prev;
- } else {
+ if (next) {
assert(radclient_tail != radclient);
- radclient->next->prev = radclient->prev;
+ next->prev = prev;
+ } else if (radclient_tail) {
+ assert(radclient_tail == radclient);
+ radclient_tail = prev;
}
free(radclient);
{
FILE *fp;
VALUE_PAIR *vp;
- radclient_t *radclient;
-
- /*
- * Allocate it.
- */
- radclient = malloc(sizeof(*radclient));
- if (!radclient) {
- perror("radclient: ");
- return NULL;
- }
- memset(radclient, 0, sizeof(*radclient));
-
- radclient->request = rad_alloc(1);
- if (!radclient->request) {
- librad_perror("radclient: ");
- radclient_free(radclient);
- return NULL;
- }
+ radclient_t *start, *radclient, *prev = NULL;
+ int filedone = 0;
+ int packet_number = 1;
- radclient->filename = filename;
- radclient->request->id = -1; /* allocate when sending */
+ start = NULL;
+ assert(filename != NULL);
/*
- * Read valuepairs.
- * Maybe read them, from stdin, if there's no
- * filename, or if the filename is '-'.
+ * Determine where to read the VP's from.
*/
- if (filename && (strcmp(filename, "-") != 0)) {
+ if (strcmp(filename, "-") != 0) {
fp = fopen(filename, "r");
if (!fp) {
fprintf(stderr, "radclient: Error opening %s: %s\n",
filename, strerror(errno));
- radclient_free(radclient);
return NULL;
}
} else {
fp = stdin;
}
-
- /*
- * Read the VP's.
- */
- radclient->request->vps = readvp(fp);
- if (fp != stdin) fclose(fp);
- if (!radclient->request->vps) {
- librad_perror("radclient: ");
- radclient_free(radclient);
- return NULL;
- }
/*
- * Keep a copy of the the User-Password attribute.
+ * Loop until the file is done.
*/
- if ((vp = pairfind(radclient->request->vps, PW_PASSWORD)) != NULL) {
- strNcpy(radclient->password, (char *)vp->strvalue, sizeof(vp->strvalue));
+ do {
/*
- * Otherwise keep a copy of the CHAP-Password attribute.
+ * Allocate it.
*/
- } else if ((vp = pairfind(radclient->request->vps, PW_CHAP_PASSWORD)) != NULL) {
- strNcpy(radclient->password, (char *)vp->strvalue, sizeof(vp->strvalue));
- } else {
- radclient->password[0] = '\0';
- }
-
- /*
- * Fix up Digest-Attributes issues
- */
- for (vp = radclient->request->vps; vp != NULL; vp = vp->next) {
- switch (vp->attribute) {
- default:
- break;
+ radclient = malloc(sizeof(*radclient));
+ if (!radclient) {
+ perror("radclient: ");
+ return NULL; /* memory leak "start" */
+ }
+ memset(radclient, 0, sizeof(*radclient));
+
+ radclient->request = rad_alloc(1);
+ if (!radclient->request) {
+ librad_perror("radclient: ");
+ radclient_free(radclient);
+ return NULL; /* memory leak "start" */
+ }
+
+ radclient->filename = filename;
+ radclient->request->id = -1; /* allocate when sending */
+ radclient->packet_number = packet_number++;
+
+ /*
+ * Read the VP's.
+ */
+ radclient->request->vps = readvp2(fp, &filedone, "radclient:");
+ if (!radclient->request->vps) {
+ radclient_free(radclient);
+ 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(radclient->password));
/*
- * Allow it to set the packet type in
- * the attributes read from the file.
+ * Otherwise keep a copy of the CHAP-Password attribute.
*/
- case PW_PACKET_TYPE:
- radclient->request->code = vp->lvalue;
- break;
-
- case PW_PACKET_DST_PORT:
- radclient->request->dst_port = (vp->lvalue & 0xffff);
- break;
-
- case PW_DIGEST_REALM:
- case PW_DIGEST_NONCE:
- case PW_DIGEST_METHOD:
- case PW_DIGEST_URI:
- case PW_DIGEST_QOP:
- case PW_DIGEST_ALGORITHM:
- case PW_DIGEST_BODY_DIGEST:
- case PW_DIGEST_CNONCE:
- case PW_DIGEST_NONCE_COUNT:
- case PW_DIGEST_USER_NAME:
- /* overlapping! */
- memmove(&vp->strvalue[2], &vp->strvalue[0], vp->length);
- vp->strvalue[0] = vp->attribute - PW_DIGEST_REALM + 1;
- vp->length += 2;
- vp->strvalue[1] = vp->length;
- vp->attribute = PW_DIGEST_ATTRIBUTES;
- break;
+ } else if ((vp = pairfind(radclient->request->vps, PW_CHAP_PASSWORD)) != NULL) {
+ strNcpy(radclient->password, (char *)vp->strvalue, sizeof(radclient->password));
+ } else {
+ radclient->password[0] = '\0';
}
- } /* loop over the VP's we read in */
+
+ /*
+ * Fix up Digest-Attributes issues
+ */
+ for (vp = radclient->request->vps; vp != NULL; vp = vp->next) {
+ switch (vp->attribute) {
+ default:
+ break;
+
+ /*
+ * Allow it to set the packet type in
+ * the attributes read from the file.
+ */
+ case PW_PACKET_TYPE:
+ radclient->request->code = vp->lvalue;
+ break;
+
+ case PW_PACKET_DST_PORT:
+ radclient->request->dst_port = (vp->lvalue & 0xffff);
+ break;
+
+ case PW_DIGEST_REALM:
+ case PW_DIGEST_NONCE:
+ case PW_DIGEST_METHOD:
+ case PW_DIGEST_URI:
+ case PW_DIGEST_QOP:
+ case PW_DIGEST_ALGORITHM:
+ case PW_DIGEST_BODY_DIGEST:
+ case PW_DIGEST_CNONCE:
+ case PW_DIGEST_NONCE_COUNT:
+ case PW_DIGEST_USER_NAME:
+ /* overlapping! */
+ memmove(&vp->strvalue[2], &vp->strvalue[0], vp->length);
+ vp->strvalue[0] = vp->attribute - PW_DIGEST_REALM + 1;
+ vp->length += 2;
+ vp->strvalue[1] = vp->length;
+ vp->attribute = PW_DIGEST_ATTRIBUTES;
+ break;
+ }
+ } /* loop over the VP's we read in */
+
+ if (!start) {
+ start = radclient;
+ prev = start;
+ } else {
+ prev->next = radclient;
+ radclient->prev = prev;
+ prev = radclient;
+ }
+ } while (!filedone); /* loop until the file is done. */
+
+ if (fp != stdin) fclose(fp);
/*
* And we're done.
*/
- return radclient;
+ return start;
}
if (radclient->request->code == 0) {
if (packet_code == -1) {
- fprintf(stderr, "radclient: Request was \"auto\", but file %s did not contain Packet-Type\n", radclient->filename);
+ fprintf(stderr, "radclient: Request was \"auto\", but request %d in file %s did not contain Packet-Type\n",
+ radclient->packet_number, radclient->filename);
return -1;
}
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.
if (!radclient) {
exit(1);
}
-
+
if (!radclient_head) {
assert(radclient_tail == NULL);
radclient_head = radclient;
- radclient_tail = radclient;
} else {
assert(radclient_tail->next == NULL);
radclient_tail->next = radclient;
radclient->prev = radclient_tail;
- radclient_tail = radclient;
}
+ /*
+ * We may have had a list of "radclient" structures
+ * returned to us.
+ */
+ while (radclient->next) radclient = radclient->next;
+ radclient_tail = radclient;
+
return 0;
}
{
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
assert(radclient->request->id != -1);
assert(radclient->request->data == NULL);
-
+
librad_md5_calc(radclient->request->vector, radclient->request->vector,
sizeof(radclient->request->vector));
-
+
/*
* Update the password, so it can be encrypted with the
* new authentication vector.
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;
}
RADIUS_PACKET myrequest, *reply;
rbnode_t *node;
-
+
/* And wait for reply, timing out as necessary */
FD_ZERO(&set);
FD_SET(sockfd, &set);
-
+
if (wait_time <= 0) {
tv.tv_sec = 0;
} else {
tv.tv_sec = wait_time;
}
tv.tv_usec = 0;
-
+
/*
* No packet was received.
*/
if (select(sockfd + 1, &set, NULL, NULL, &tv) != 1) {
return 0;
}
-
+
/*
* Look for the packet.
*/
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))
+ if (!isdigit((int) *optarg))
usage();
resend_count = atoi(optarg);
break;
case 'f':
rbtree_insert(filename_tree, optarg);
break;
+ case 'i':
+ if (!isdigit((int) *optarg))
+ usage();
+ last_used_id = atoi(optarg);
+ if ((last_used_id < 0) || (last_used_id > 255)) {
+ usage();
+ }
+ break;
+
+ case 'n':
+ persec = atoi(optarg);
+ if (persec <= 0) usage();
+ break;
+
+ case 'p':
+ parallel = atoi(optarg);
+ if (parallel <= 0) usage();
+ break;
+
case 'q':
do_output = 0;
break;
- case 'x':
- librad_debug++;
- break;
case 'r':
- if (!isdigit((int) *optarg))
+ if (!isdigit((int) *optarg))
usage();
retries = atoi(optarg);
- break;
- case 'i':
- if (!isdigit((int) *optarg))
- usage();
- id = atoi(optarg);
- if ((id < 0) || (id > 255)) {
- usage();
- }
+ if ((retries == 0) || (retries > 1000)) usage();
break;
case 's':
do_summary = 1;
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 'S':
fp = fopen(optarg, "r");
if (!fp) {
}
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;
if (argv[3]) secret = argv[3];
/*
+ * If no '-f' is specified, we're reading from stdin.
+ */
+ if (rbtree_num_elements(filename_tree) == 0) {
+ rbtree_insert(filename_tree, "-");
+ }
+
+ /*
* 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;
done = 1;
sleep_time = -1;
/*
* Walk over the packets, sending them.
*/
+
for (this = radclient_head; this != NULL; this = next) {
next = this->next;
- radclient_send(this);
+ /*
+ * 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, unless '-p' is specified, in
+ * which case N packets from each file
+ * are sent in parallel.
+ */
+ if (this->filename != filename) {
+ filename = this->filename;
+ 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 { /* haven't sent this packet, we're not done */
+ assert(this->done == 0);
+ assert(this->reply == NULL);
+ done = 0;
}
}
*/
if (rbtree_num_elements(request_tree) > 0) {
done = 0;
+ } else {
+ sleep_time = 0;
}
/*