* 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,2006 The FreeRADIUS server project
+ * Copyright 2000,2006,2014 The FreeRADIUS server project
* Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
* Copyright 2000 Alan DeKok <aland@ox.org>
*/
RCSID("$Id$")
-#include <freeradius-devel/libradius.h>
+#include <freeradius-devel/radclient.h>
#include <freeradius-devel/radpaths.h>
#include <freeradius-devel/conf.h>
-
#include <ctype.h>
#ifdef HAVE_GETOPT_H
-# include <getopt.h>
+# include <getopt.h>
#endif
#include <assert.h>
#include "smbdes.h"
#include "mschap.h"
-static bool success = false;
static int retries = 3;
static float timeout = 5;
static char const *secret = NULL;
static bool do_output = true;
-typedef struct rc_stats {
- uint64_t accepted; //!< Requests to which we received a accept
- uint64_t rejected; //!< Requests to which we received a reject
- uint64_t lost; //!< Requests to which we received no response
- uint64_t passed; //!< Requests which passed a filter
- uint64_t failed; //!< Requests which failed a fitler
-} rc_stats_t;
-
static rc_stats_t stats;
-static int server_port = 0;
-static int packet_code = 0;
+static uint16_t server_port = 0;
+static int packet_code = PW_CODE_UNDEFINED;
static fr_ipaddr_t server_ipaddr;
static int resend_count = 1;
static bool done = true;
static bool print_filename = false;
static fr_ipaddr_t client_ipaddr;
-static int client_port = 0;
+static uint16_t client_port = 0;
static int sockfd;
static int last_used_id = -1;
static int sleep_time = -1;
-typedef struct rc_request rc_request_t;
-
-typedef struct rc_file_pair {
- char const *packets; //!< The file containing the request packet
- char const *filters; //!< The file containing the definition of the
- //!< packet we want to match.
-} rc_file_pair_t;
-
-struct rc_request {
- rc_request_t *prev;
- rc_request_t *next;
-
- rc_file_pair_t *files; //!< Request and response file names.
-
- int request_number; //!< The number (within the file) of the request were reading.
-
- char password[256];
- time_t timestamp;
-
- RADIUS_PACKET *packet; //!< The outgoing request.
- RADIUS_PACKET *reply; //!< The incoming response.
- VALUE_PAIR *filter; //!< If the reply passes the filter, then the request passes.
-
- int resend;
- int tries;
- bool done; //!< Whether the request is complete.
-};
-
static rc_request_t *request_head = NULL;
static rc_request_t *rc_request_tail = NULL;
+static int rc_debug_flag;
+
char const *radclient_version = "radclient version " RADIUSD_VERSION_STRING
#ifdef RADIUSD_VERSION_COMMIT
" (git #" STRINGIFY(RADIUSD_VERSION_COMMIT) ")"
{
fprintf(stderr, "Usage: radclient [options] server[:port] <command> [<secret>]\n");
- fprintf(stderr, " <command> One of auth, acct, status, coa, or disconnect.\n");
+ fprintf(stderr, " <command> One of auth, acct, status, coa, disconnect or auto.\n");
fprintf(stderr, " -4 Use IPv4 address of server\n");
fprintf(stderr, " -6 Use IPv6 address of server.\n");
fprintf(stderr, " -c <count> Send each packet 'count' times.\n");
exit(1);
}
+static const FR_NAME_NUMBER request_types[] = {
+ { "auth", PW_CODE_ACCESS_REQUEST },
+ { "challenge", PW_CODE_ACCESS_CHALLENGE },
+ { "acct", PW_CODE_ACCOUNTING_REQUEST },
+ { "status", PW_CODE_STATUS_SERVER },
+ { "disconnect", PW_CODE_DISCONNECT_REQUEST },
+ { "coa", PW_CODE_COA_REQUEST },
+ { "auto", PW_CODE_UNDEFINED },
+
+ { NULL, 0}
+};
+
/*
* Free a radclient struct, which may (or may not)
* already be in the list.
}
+static int getport(char const *name)
+{
+ struct servent *svp;
+
+ svp = getservbyname(name, "udp");
+ if (!svp) {
+ return 0;
+ }
+
+ return ntohs(svp->s_port);
+}
+
+/*
+ * Set a port from the request type if we don't already have one
+ */
+static void radclient_get_port(PW_CODE type, uint16_t *port)
+{
+ switch (type) {
+ default:
+ case PW_CODE_ACCESS_REQUEST:
+ case PW_CODE_ACCESS_CHALLENGE:
+ case PW_CODE_STATUS_SERVER:
+ if (*port == 0) *port = getport("radius");
+ if (*port == 0) *port = PW_AUTH_UDP_PORT;
+ return;
+
+ case PW_CODE_ACCOUNTING_REQUEST:
+ if (*port == 0) *port = getport("radacct");
+ if (*port == 0) *port = PW_ACCT_UDP_PORT;
+ return;
+
+ case PW_CODE_DISCONNECT_REQUEST:
+ if (*port == 0) *port = PW_POD_UDP_PORT;
+ return;
+
+ case PW_CODE_COA_REQUEST:
+ if (*port == 0) *port = PW_COA_UDP_PORT;
+ return;
+
+ case PW_CODE_UNDEFINED:
+ if (*port == 0) *port = 0;
+ return;
+ }
+}
+
+/*
+ * Resolve a port to a request type
+ */
+static PW_CODE radclient_get_code(uint16_t port)
+{
+ if ((port == getport("radius")) || (port == PW_AUTH_UDP_PORT) || (port == PW_AUTH_UDP_PORT_ALT)) {
+ return PW_CODE_ACCESS_REQUEST;
+ }
+ if ((port == getport("radacct")) || (port == PW_ACCT_UDP_PORT) || (port == PW_ACCT_UDP_PORT_ALT)) {
+ return PW_CODE_ACCOUNTING_REQUEST;
+ }
+ if (port == PW_COA_UDP_PORT) return PW_CODE_COA_REQUEST;
+ if (port == PW_POD_UDP_PORT) return PW_CODE_DISCONNECT_REQUEST;
+
+ return PW_CODE_UNDEFINED;
+}
+
/*
* Initialize a radclient data structure and add it to
* the global linked list.
VALUE_PAIR *vp;
rc_request_t *request;
bool packets_done = false;
- int request_number = 1;
+ uint64_t num = 0;
assert(files->packets != NULL);
/*
* Determine where to read the VP's from.
*/
- if (strcmp(files->packets, "-") != 0) {
+ if (strcmp(files->packets, "stdin") != 0) {
packets = fopen(files->packets, "r");
if (!packets) {
- fprintf(stderr, "radclient: Error opening %s: %s\n",
- files->packets, strerror(errno));
+ ERROR("Error opening %s: %s", files->packets, strerror(errno));
return 0;
}
if (files->filters) {
filters = fopen(files->filters, "r");
if (!filters) {
- fprintf(stderr, "radclient: Error opening %s: %s\n",
- files->filters, strerror(errno));
+ ERROR("Error opening %s: %s", files->filters, strerror(errno));
fclose(packets);
return 0;
}
*/
request = talloc_zero(ctx, rc_request_t);
if (!request) {
- fprintf(stderr, "radclient: Out of memory\n");
+ ERROR("Out of memory");
goto error;
}
- talloc_set_destructor(request, _rc_request_free);
- request->packet = rad_alloc(request, 1);
+ request->packet = rad_alloc(request, true);
if (!request->packet) {
- fprintf(stderr, "radclient: Out of memory\n");
+ ERROR("Out of memory");
goto error;
}
request->files = files;
request->packet->id = -1; /* allocate when sending */
- request->request_number = request_number++;
+ request->num = num++;
+
+ /*
+ * Read the request VP's.
+ */
+ if (readvp2(&request->packet->vps, request->packet, packets, &packets_done) < 0) {
+ REDEBUG("Error parsing \"%s\"", files->packets);
+ goto error;
+ }
/*
- * Read the VP's.
+ * Skip empty entries
*/
- request->packet->vps = readvp2(request, packets, &packets_done, "radclient:");
- if (!request->packet->vps) goto error;
+ if (!request->packet->vps) {
+ talloc_free(request);
+ continue;
+ }
/*
* Read in filter VP's.
if (filters) {
bool filters_done;
- request->filter = readvp2(request, filters, &filters_done, "radclient:");
- if (!request->filter) {
+ if (readvp2(&request->filter, request, filters, &filters_done) < 0) {
+ REDEBUG("Error parsing \"%s\"", files->filters);
goto error;
}
if (filters_done && !packets_done) {
- fprintf(stderr, "radclient: Differing number of packets/filters in %s:%s "
- "(too many requests))", files->packets, files->filters);
+ REDEBUG("Differing number of packets/filters in %s:%s "
+ "(too many requests))", files->packets, files->filters);
goto error;
}
if (!filters_done && packets_done) {
- fprintf(stderr, "radclient: Differing number of packets/filters in %s:%s "
- "(too many filters))", files->packets, files->filters);
+ REDEBUG("Differing number of packets/filters in %s:%s "
+ "(too many filters))", files->packets, files->filters);
goto error;
}
- }
- /*
- * Keep a copy of the the User-Password attribute.
- */
- if ((vp = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
- strlcpy(request->password, vp->vp_strvalue,
- sizeof(request->password));
- /*
- * Otherwise keep a copy of the CHAP-Password attribute.
- */
- } else if ((vp = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
- strlcpy(request->password, vp->vp_strvalue,
- sizeof(request->password));
+ /*
+ * xlat expansions aren't supported here
+ */
+ for (vp = fr_cursor_init(&cursor, &request->filter);
+ vp;
+ vp = fr_cursor_next(&cursor)) {
+ if (vp->type == VT_XLAT) {
+ vp->type = VT_DATA;
+ vp->vp_strvalue = vp->value.xlat;
+ vp->length = talloc_array_length(vp->vp_strvalue) - 1;
+ }
- } else if ((vp = pairfind(request->packet->vps, PW_MSCHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
- strlcpy(request->password, vp->vp_strvalue,
- sizeof(request->password));
- } else {
- request->password[0] = '\0';
+ if (vp->da->vendor == 0 ) switch (vp->da->attr) {
+ case PW_RESPONSE_PACKET_TYPE:
+ case PW_PACKET_TYPE:
+ fr_cursor_remove(&cursor); /* so we don't break the filter */
+ request->filter_code = vp->vp_integer;
+ talloc_free(vp);
+
+ default:
+ break;
+ }
+ }
+
+ /*
+ * This allows efficient list comparisons later
+ */
+ pairsort(&request->filter, attrtagcmp);
}
+ request->password[0] = '\0';
/*
- * Fix up Digest-Attributes issues
+ * Process special attributes
*/
for (vp = fr_cursor_init(&cursor, &request->packet->vps);
vp;
vp = fr_cursor_next(&cursor)) {
- /*
- * Double quoted strings get marked up as xlat expansions,
- * but we don't support that in request.
- */
+ /*
+ * Double quoted strings get marked up as xlat expansions,
+ * but we don't support that in request.
+ */
if (vp->type == VT_XLAT) {
- vp->vp_strvalue = vp->value.xlat;
- vp->value.xlat = NULL;
vp->type = VT_DATA;
+ vp->vp_strvalue = vp->value.xlat;
+ vp->length = talloc_array_length(vp->vp_strvalue) - 1;
}
if (!vp->da->vendor) switch (vp->da->attr) {
default:
break;
- /*
- * Allow it to set the packet type in
- * the attributes read from the file.
- */
+ /*
+ * Allow it to set the packet type in
+ * the attributes read from the file.
+ */
case PW_PACKET_TYPE:
request->packet->code = vp->vp_integer;
break;
+ case PW_RESPONSE_PACKET_TYPE:
+ request->filter_code = vp->vp_integer;
+ break;
+
case PW_PACKET_DST_PORT:
request->packet->dst_port = (vp->vp_integer & 0xffff);
break;
case PW_DIGEST_CNONCE:
case PW_DIGEST_NONCE_COUNT:
case PW_DIGEST_USER_NAME:
- /* overlapping! */
- {
- DICT_ATTR const *da;
- uint8_t *p;
+ /* overlapping! */
+ {
+ DICT_ATTR const *da;
+ uint8_t *p, *q;
+
+ p = talloc_array(vp, uint8_t, vp->length + 2);
+
+ memcpy(p + 2, vp->vp_octets, vp->length);
+ p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
+ vp->length += 2;
+ p[1] = vp->length;
+
+ da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
+ if (!da) {
+ ERROR("Out of memory");
+ goto error;
+ }
+ vp->da = da;
- p = talloc_array(vp, uint8_t, vp->length + 2);
+ /*
+ * Re-do pairmemsteal ourselves,
+ * because we play games with
+ * vp->da, and pairmemsteal goes
+ * to GREAT lengths to sanitize
+ * and fix and change and
+ * double-check the various
+ * fields.
+ */
+ memcpy(&q, &vp->vp_octets, sizeof(q));
+ talloc_free(q);
- memcpy(p + 2, vp->vp_octets, vp->length);
- p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
- vp->length += 2;
- p[1] = vp->length;
+ vp->vp_octets = talloc_steal(vp, p);
+ vp->type = VT_DATA;
- pairmemsteal(vp, p);
+ VERIFY_VP(vp);
+ }
+ break;
- da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
- if (!da) {
- fprintf(stderr, "radclient: Out of memory\n");
- goto error;
- }
+ /*
+ * Keep a copy of the the password attribute.
+ */
+ case PW_USER_PASSWORD:
+ case PW_CHAP_PASSWORD:
+ case PW_MS_CHAP_PASSWORD:
+ strlcpy(request->password, vp->vp_strvalue, sizeof(request->password));
+ break;
+
+ case PW_RADCLIENT_TEST_NAME:
+ request->name = vp->vp_strvalue;
+ break;
+ }
+ } /* loop over the VP's we read in */
- vp->da = da;
+ /*
+ * Use the default set on the command line
+ */
+ if (request->packet->code == PW_CODE_UNDEFINED) request->packet->code = packet_code;
+
+ /*
+ * Default to the filename
+ */
+ if (!request->name) request->name = request->files->packets;
+
+ /*
+ * Automatically set the response code from the request code
+ * (if one wasn't already set).
+ */
+ if (request->filter_code == PW_CODE_UNDEFINED) {
+ switch (request->packet->code) {
+ case PW_CODE_ACCESS_REQUEST:
+ request->filter_code = PW_CODE_ACCESS_ACCEPT;
+ break;
+
+ case PW_CODE_ACCOUNTING_REQUEST:
+ request->filter_code = PW_CODE_ACCOUNTING_RESPONSE;
+ break;
+
+ case PW_CODE_COA_REQUEST:
+ request->filter_code = PW_CODE_COA_ACK;
+ break;
+
+ case PW_CODE_DISCONNECT_REQUEST:
+ request->filter_code = PW_CODE_DISCONNECT_ACK;
+ break;
+
+ case PW_CODE_STATUS_SERVER:
+ switch (radclient_get_code(request->packet->dst_port)) {
+ case PW_CODE_ACCESS_REQUEST:
+ request->filter_code = PW_CODE_ACCESS_ACCEPT;
+ break;
+
+ case PW_CODE_ACCOUNTING_REQUEST:
+ request->filter_code = PW_CODE_ACCOUNTING_RESPONSE;
+ break;
+
+ default:
+ REDEBUG("Can't determine expected response to Status-Server request, specify "
+ "a well known RADIUS port, or add a Response-Packet-Type attribute "
+ "to the request of filter");
+ goto error;
}
+ break;
+ case PW_CODE_UNDEFINED:
+ REDEBUG("Both Packet-Type and Response-Packet-Type undefined, specify at least one, "
+ "or a well known RADIUS port");
+ goto error;
+
+ default:
+ REDEBUG("Can't determine expected Response-Packet-Type for Packet-Type %i",
+ request->packet->code);
+ goto error;
+ }
+ /*
+ * Automatically set the request code from the response code
+ * (if one wasn't already set).
+ */
+ } else if (request->packet->code == PW_CODE_UNDEFINED) {
+ switch (request->filter_code) {
+ case PW_CODE_ACCESS_ACCEPT:
+ case PW_CODE_ACCESS_REJECT:
+ request->packet->code = PW_CODE_ACCESS_REQUEST;
break;
+
+ case PW_CODE_ACCOUNTING_RESPONSE:
+ request->packet->code = PW_CODE_ACCOUNTING_REQUEST;
+ break;
+
+ case PW_CODE_DISCONNECT_ACK:
+ case PW_CODE_DISCONNECT_NAK:
+ request->packet->code = PW_CODE_DISCONNECT_REQUEST;
+ break;
+
+ case PW_CODE_COA_ACK:
+ case PW_CODE_COA_NAK:
+ request->packet->code = PW_CODE_COA_REQUEST;
+ break;
+
+ default:
+ REDEBUG("Can't determine expected Packet-Type for Response-Packet-Type %i",
+ request->filter_code);
+ goto error;
}
- } /* loop over the VP's we read in */
+ }
+
+ /*
+ * Automatically set the dst port (if one wasn't already set).
+ */
+ if (request->packet->dst_port == 0) {
+ radclient_get_port(request->packet->code, &request->packet->dst_port);
+ if (request->packet->dst_port == 0) {
+ REDEBUG("Can't determine destination port");
+ goto error;
+ }
+ }
/*
* Add it to the tail of the list.
rc_request_tail = request;
request->next = NULL;
+ /*
+ * Set the destructor so it removes itself from the
+ * request list when freed. We don't set this until
+ * the packet is actually in the list, else we trigger
+ * the asserts in the free callback.
+ */
+ talloc_set_destructor(request, _rc_request_free);
} while (!packets_done); /* loop until the file is done. */
if (packets != stdin) fclose(packets);
}
if (request->packet->dst_ipaddr.af == AF_UNSPEC) {
if (server_ipaddr.af == AF_UNSPEC) {
- fprintf(stderr, "radclient: No server was given, but request %d in file %s "
- "did not contain Packet-Dst-IP-Address\n",
- request->request_number, request->files->packets);
+ ERROR("No server was given, and request %" PRIu64 " in file %s did not contain "
+ "Packet-Dst-IP-Address", request->num, request->files->packets);
return -1;
}
request->packet->dst_ipaddr = server_ipaddr;
}
if (request->packet->code == 0) {
if (packet_code == -1) {
- fprintf(stderr, "radclient: Request was \"auto\", but request %d in file %s "
- "did not contain Packet-Type\n",
- request->request_number, request->files->packets);
+ ERROR("Request was \"auto\", and request %" PRIu64 " in file %s did not contain Packet-Type",
+ request->num, request->files->packets);
return -1;
}
request->packet->code = packet_code;
/*
* Read request(s) from the file.
*/
- if (!radclient_init(files, files)) {
- return -1; /* stop walking */
- }
+ if (!radclient_init(files, files)) return -1; /* stop walking */
return 0;
}
* and ensure that the next packet has a unique
* authentication vector.
*/
- if (request->packet->data) {
- talloc_free(request->packet->data);
- request->packet->data = NULL;
- }
-
+ if (request->packet->data) TALLOC_FREE(request->packet->data);
if (request->reply) rad_free(&request->reply);
}
-
-static void print_hex(RADIUS_PACKET *packet)
-{
- int i;
-
- if (!packet->data) return;
-
- printf(" Code:\t\t%u\n", packet->data[0]);
- printf(" Id:\t\t%u\n", packet->data[1]);
- printf(" Length:\t%u\n", ((packet->data[2] << 8) |
- (packet->data[3])));
- printf(" Vector:\t");
- for (i = 4; i < 20; i++) {
- printf("%02x", packet->data[i]);
- }
- printf("\n");
-
- if (packet->data_len > 20) {
- int total;
- uint8_t const *ptr;
- printf(" Data:");
-
- total = packet->data_len - 20;
- ptr = packet->data + 20;
-
- while (total > 0) {
- int attrlen;
-
- printf("\t\t");
- if (total < 2) { /* too short */
- printf("%02x\n", *ptr);
- break;
- }
-
- if (ptr[1] > total) { /* too long */
- for (i = 0; i < total; i++) {
- printf("%02x ", ptr[i]);
- }
- break;
- }
-
- printf("%02x %02x ", ptr[0], ptr[1]);
- attrlen = ptr[1] - 2;
- ptr += 2;
- total -= 2;
-
- for (i = 0; i < attrlen; i++) {
- if ((i > 0) && ((i & 0x0f) == 0x00))
- printf("\t\t\t");
- printf("%02x ", ptr[i]);
- if ((i & 0x0f) == 0x0f) printf("\n");
- }
-
- if ((attrlen & 0x0f) != 0x00) printf("\n");
-
- ptr += attrlen;
- total -= attrlen;
- }
- }
- fflush(stdout);
-}
-
/*
* Send one packet.
*/
* Remember when we have to wake up, to re-send the
* request, of we didn't receive a reply.
*/
- if ((sleep_time == -1) || (sleep_time > (int) timeout)) {
- sleep_time = (int) timeout;
- }
+ if ((sleep_time == -1) || (sleep_time > (int) timeout)) sleep_time = (int) timeout;
/*
* Haven't sent the packet yet. Initialize it.
*/
retry:
request->packet->src_ipaddr.af = server_ipaddr.af;
- rcode = fr_packet_list_id_alloc(pl, ipproto,
- &request->packet, NULL);
+ rcode = fr_packet_list_id_alloc(pl, ipproto, &request->packet, NULL);
if (!rcode) {
int mysockfd;
#ifdef WITH_TCP
if (proto) {
mysockfd = fr_tcp_client_socket(NULL,
- &server_ipaddr,
- server_port);
+ &request->packet->dst_ipaddr,
+ request->packet->dst_port);
} else
#endif
mysockfd = fr_socket(&client_ipaddr, 0);
if (mysockfd < 0) {
- fprintf(stderr, "radclient: Can't open new socket: %s\n",
- strerror(errno));
+ ERROR("Failed opening socket");
exit(1);
}
if (!fr_packet_list_socket_add(pl, mysockfd, ipproto,
- &server_ipaddr,
- server_port, NULL)) {
- fprintf(stderr, "radclient: Can't add new socket\n");
+ &request->packet->dst_ipaddr,
+ request->packet->dst_port, NULL)) {
+ ERROR("Can't add new socket");
exit(1);
}
goto retry;
if ((vp = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
pairstrcpy(vp, request->password);
-
} else if ((vp = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
bool already_hex = false;
vp->vp_octets = p;
vp->length = 17;
}
- } else if (pairfind(request->packet->vps, PW_MSCHAP_PASSWORD, 0, TAG_ANY) != NULL) {
- mschapv1_encode(request->packet,
- &request->packet->vps,
- request->password);
- } else if (fr_debug_flag) {
- printf("WARNING: No password in the request\n");
+ } else if (pairfind(request->packet->vps, PW_MS_CHAP_PASSWORD, 0, TAG_ANY) != NULL) {
+ mschapv1_encode(request->packet, &request->packet->vps, request->password);
+ } else {
+ DEBUG("WARNING: No password in the request");
}
}
*/
fr_packet_list_yank(pl, request->packet);
- fprintf(stderr, "radclient: no reply from server for ID %d socket %d\n",
+ REDEBUG("No reply from server for ID %d socket %d",
request->packet->id, request->packet->sockfd);
deallocate_id(request);
request->tries++;
}
-
/*
* Send the packet.
*/
if (rad_send(request->packet, NULL, secret) < 0) {
- fprintf(stderr, "radclient: Failed to send packet for ID %d: %s\n",
- request->packet->id, fr_strerror());
+ REDEBUG("Failed to send packet for ID %d", request->packet->id);
}
- if (fr_debug_flag > 2) print_hex(request->packet);
-
return 0;
}
max_fd = fr_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;
- } else {
- tv.tv_sec = wait_time;
- }
+ tv.tv_sec = (wait_time <= 0) ? 0 : wait_time;
tv.tv_usec = 0;
/*
* No packet was received.
*/
- if (select(max_fd, &set, NULL, NULL, &tv) <= 0) {
- return 0;
- }
+ if (select(max_fd, &set, NULL, NULL, &tv) <= 0) return 0;
/*
* Look for the packet.
*/
-
reply = fr_packet_list_recv(pl, &set);
if (!reply) {
- fprintf(stderr, "radclient: received bad packet: %s\n",
- fr_strerror());
+ ERROR("Received bad packet");
#ifdef WITH_TCP
/*
* If the packet is bad, we close the socket.
* udpfromto issues. We may have bound to "*",
* and we want to find the replies that are sent to
* (say) 127.0.0.1.
+ *
+ * This only works if were not using any of the
+ * Packet-* attributes, or running with 'auto'.
*/
reply->dst_ipaddr = client_ipaddr;
reply->dst_port = client_port;
#ifdef WITH_TCP
- reply->src_ipaddr = server_ipaddr;
- reply->src_port = server_port;
+ if (server_port > 0) {
+ reply->src_ipaddr = server_ipaddr;
+ reply->src_port = server_port;
+ }
#endif
- if (fr_debug_flag > 2) print_hex(reply);
-
packet_p = fr_packet_list_find_byreply(pl, reply);
if (!packet_p) {
- fprintf(stderr, "radclient: received reply to request we did not send. (id=%d socket %d)\n",
- reply->id, reply->sockfd);
+ ERROR("Received reply to request we did not send. (id=%d socket %d)",
+ reply->id, reply->sockfd);
rad_free(&reply);
return -1; /* got reply to packet we didn't send */
}
* FIXME: Silently drop it and listen for another packet.
*/
if (rad_verify(reply, request->packet, secret) < 0) {
- fr_perror("rad_verify");
+ REDEBUG("Reply verification failed");
stats.lost++;
goto packet_done; /* shared secret is incorrect */
}
if (print_filename) {
- printf("%s:%d %d\n", request->files->packets, request->request_number, reply->code);
+ RDEBUG("%s response code %d", request->files->packets, reply->code);
}
deallocate_id(request);
* If this fails, we're out of memory.
*/
if (rad_decode(request->reply, request->packet, secret) != 0) {
- fr_perror("rad_decode");
+ REDEBUG("Reply decode failed");
stats.lost++;
goto packet_done;
}
- /* libradius debug already prints out the value pairs for us */
- if (!fr_debug_flag && do_output) {
- printf("Received reply ID %d, code %d, length = %zd\n",
- request->reply->id, request->reply->code,
- request->reply->data_len);
- vp_printlist(stdout, request->reply->vps);
- }
-
- if ((request->reply->code == PW_AUTHENTICATION_ACK) ||
- (request->reply->code == PW_ACCOUNTING_RESPONSE) ||
- (request->reply->code == PW_COA_ACK) ||
- (request->reply->code == PW_DISCONNECT_ACK)) {
- success = true; /* have a good reply */
+ /*
+ * Increment counters...
+ */
+ switch (request->reply->code) {
+ case PW_CODE_ACCESS_ACCEPT:
+ case PW_CODE_ACCOUNTING_RESPONSE:
+ case PW_CODE_COA_ACK:
+ case PW_CODE_DISCONNECT_ACK:
stats.accepted++;
- } else {
+ break;
+
+ case PW_CODE_ACCESS_CHALLENGE:
+ break;
+
+ default:
stats.rejected++;
}
- if (request->resend == resend_count) {
- request->done = true;
- }
+ /*
+ * If we had an expected response code, check to see if the
+ * packet matched that.
+ */
+ if (request->reply->code != request->filter_code) {
+ if (is_radius_code(request->reply->code)) {
+ REDEBUG("%s: Expected %s got %s", request->name, fr_packet_codes[request->filter_code],
+ fr_packet_codes[request->reply->code]);
+ } else {
+ REDEBUG("%s: Expected %u got %i", request->name, request->filter_code,
+ request->reply->code);
+ }
+ stats.failed++;
+ /*
+ * Check if the contents of the packet matched the filter
+ */
+ } else if (!request->filter) {
+ stats.passed++;
+ } else {
+ VALUE_PAIR const *failed[2];
- if (request->filter) {
- if (pairvalidate(request->filter, request->reply->vps)) {
- printf("Packet passed filter!\n");
+ pairsort(&request->reply->vps, attrtagcmp);
+ if (pairvalidate(failed, request->filter, request->reply->vps)) {
+ RDEBUG("%s: Response passed filter", request->name);
stats.passed++;
} else {
- printf("Packet failed filter!\n");
+ pairvalidate_debug(request, failed);
+ REDEBUG("%s: Response for failed filter", request->name);
stats.failed++;
}
}
- packet_done:
+ if (request->resend == resend_count) {
+ request->done = true;
+ }
+
+packet_done:
rad_free(&request->reply);
rad_free(&reply); /* may be NULL */
return 0;
}
-
-static int getport(char const *name)
-{
- struct servent *svp;
-
- svp = getservbyname (name, "udp");
- if (!svp) {
- return 0;
- }
-
- return ntohs(svp->s_port);
-}
-
int main(int argc, char **argv)
{
int c;
rc_request_t *this;
int force_af = AF_UNSPEC;
+ /*
+ * It's easier having two sets of flags to set the
+ * verbosity of library calls and the verbosity of
+ * radclient.
+ */
+ rc_debug_flag = 1;
fr_debug_flag = 0;
+ fr_log_fp = stdout;
#ifndef NDEBUG
- fr_fault_setup(getenv("PANIC_ACTION"), argv[0]);
+ if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
+ fr_perror("radclient");
+ exit(EXIT_FAILURE);
+ }
#endif
talloc_set_log_stderr();
- filename_tree = rbtree_create(filename_cmp, NULL, 0);
+ filename_tree = rbtree_create(NULL, filename_cmp, NULL, 0);
if (!filename_tree) {
oom:
- fprintf(stderr, "radclient: Out of memory\n");
+ ERROR("Out of memory");
exit(1);
}
case '4':
force_af = AF_INET;
break;
+
case '6':
force_af = AF_INET6;
break;
+
case 'c':
if (!isdigit((int) *optarg))
usage();
resend_count = atoi(optarg);
break;
+
case 'D':
dict_dir = optarg;
break;
+
case 'd':
radius_dir = optarg;
break;
+
case 'f':
{
char const *p;
files->filters = p + 1;
} else {
files->packets = optarg;
+ files->filters = NULL;
}
rbtree_insert(filename_tree, (void *) files);
}
break;
+
case 'F':
print_filename = true;
break;
+
case 'i': /* currently broken */
if (!isdigit((int) *optarg))
usage();
do_output = false;
fr_log_fp = NULL; /* no output from you, either! */
break;
+
case 'r':
- if (!isdigit((int) *optarg))
- usage();
+ if (!isdigit((int) *optarg)) usage();
retries = atoi(optarg);
if ((retries == 0) || (retries > 1000)) usage();
break;
+
case 's':
do_summary = true;
break;
+
case 'S':
{
char *p;
fp = fopen(optarg, "r");
if (!fp) {
- fprintf(stderr, "radclient: Error opening %s: %s\n",
- optarg, strerror(errno));
+ ERROR("Error opening %s: %s", optarg, fr_syserror(errno));
exit(1);
}
if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
- fprintf(stderr, "radclient: Error reading %s: %s\n",
- optarg, strerror(errno));
+ ERROR("Error reading %s: %s", optarg, fr_syserror(errno));
exit(1);
}
fclose(fp);
}
if (strlen(filesecret) < 2) {
- fprintf(stderr, "radclient: Secret in %s is too short\n", optarg);
+ ERROR("Secret in %s is too short", optarg);
exit(1);
}
secret = filesecret;
}
break;
+
case 't':
if (!isdigit((int) *optarg))
usage();
timeout = atof(optarg);
break;
+
case 'v':
- printf("%s\n", radclient_version);
+ DEBUG("%s", radclient_version);
exit(0);
break;
+
case 'x':
fr_debug_flag++;
- fr_log_fp = stdout;
+ rc_debug_flag++;
break;
+
case 'h':
default:
usage();
argc -= (optind - 1);
argv += (optind - 1);
- if ((argc < 3) ||
- ((secret == NULL) && (argc < 4))) {
+ if ((argc < 3) || ((secret == NULL) && (argc < 4))) {
+ ERROR("Insufficient arguments");
usage();
}
-
/*
* Mismatch between the binary and the libraries it depends on
*/
fr_perror("radclient");
return 1;
}
+ fr_strerror(); /* Clear the error buffer */
+
+ /*
+ * Get the request type
+ */
+ if (!isdigit((int) argv[2][0])) {
+ packet_code = fr_str2int(request_types, argv[2], -2);
+ if (packet_code == -2) {
+ ERROR("Unrecognised request type \"%s\"", argv[2]);
+ usage();
+ }
+ } else {
+ packet_code = atoi(argv[2]);
+ }
/*
* Resolve hostname.
portname = NULL;
}
- if (ip_hton(hostname, force_af, &server_ipaddr) < 0) {
- fprintf(stderr, "radclient: Failed to find IP address for host %s: %s\n", hostname, strerror(errno));
+ if (ip_hton(&server_ipaddr, force_af, hostname, false) < 0) {
+ ERROR("Failed to find IP address for host %s: %s", hostname, strerror(errno));
exit(1);
}
* Strip port from hostname if needed.
*/
if (portname) server_port = atoi(portname);
- }
- /*
- * See what kind of request we want to send.
- */
- if (strcmp(argv[2], "auth") == 0) {
- if (server_port == 0) server_port = getport("radius");
- 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;
- packet_code = PW_ACCOUNTING_REQUEST;
- do_summary = false;
-
- } else if (strcmp(argv[2], "status") == 0) {
- if (server_port == 0) server_port = getport("radius");
- if (server_port == 0) server_port = PW_AUTH_UDP_PORT;
- packet_code = PW_STATUS_SERVER;
-
- } else if (strcmp(argv[2], "disconnect") == 0) {
- if (server_port == 0) server_port = PW_COA_UDP_PORT;
- packet_code = PW_DISCONNECT_REQUEST;
-
- } else if (strcmp(argv[2], "coa") == 0) {
- if (server_port == 0) server_port = PW_COA_UDP_PORT;
- packet_code = PW_COA_REQUEST;
-
- } else if (strcmp(argv[2], "auto") == 0) {
- packet_code = -1;
-
- } else if (isdigit((int) argv[2][0])) {
- if (server_port == 0) server_port = getport("radius");
- if (server_port == 0) server_port = PW_AUTH_UDP_PORT;
- packet_code = atoi(argv[2]);
- } else {
- usage();
+ /*
+ * Work backwards from the port to determine the packet type
+ */
+ if (packet_code == PW_CODE_UNDEFINED) packet_code = radclient_get_code(server_port);
}
+ radclient_get_port(packet_code, &server_port);
+
/*
* Add the secret.
*/
rc_file_pair_t *files;
files = talloc_zero(talloc_autofree_context(), rc_file_pair_t);
- files->packets = "-";
+ files->packets = "stdin";
if (!radclient_init(files, files)) {
exit(1);
}
* Walk over the list of filenames, creating the requests.
*/
if (rbtree_walk(filename_tree, RBTREE_IN_ORDER, filename_walk, NULL) != 0) {
- fprintf(stderr, "Failed walking over filenames\n");
+ ERROR("Failed parsing input files");
exit(1);
}
* No packets read. Die.
*/
if (!request_head) {
- fprintf(stderr, "radclient: Nothing to send.\n");
+ ERROR("Nothing to send");
exit(1);
}
#endif
sockfd = fr_socket(&client_ipaddr, client_port);
if (sockfd < 0) {
- fprintf(stderr, "radclient: socket: %s\n", fr_strerror());
+ ERROR("Error opening socket");
exit(1);
}
pl = fr_packet_list_create(1);
if (!pl) {
- fprintf(stderr, "radclient: Out of memory\n");
+ ERROR("Out of memory");
exit(1);
}
if (!fr_packet_list_socket_add(pl, sockfd, ipproto, &server_ipaddr,
server_port, NULL)) {
- fprintf(stderr, "radclient: Out of memory\n");
+ ERROR("Out of memory");
exit(1);
}
rbtree_free(filename_tree);
fr_packet_list_free(pl);
- while (request_head) talloc_free(request_head);
+ while (request_head) TALLOC_FREE(request_head);
dict_free();
if (do_summary) {
- printf("\n"
- "\tAccess-Accept's : %" PRIu64 "\n"
- "\tAccess-Reject's : %" PRIu64 "\n"
- "\tLost : %" PRIu64 "\n"
- "\tPassed filter : %" PRIu64 "\n"
- "\tFailed filter : %" PRIu64 "\n",
- stats.accepted,
- stats.rejected,
- stats.lost,
- stats.passed,
- stats.failed
+ DEBUG("Packet summary:\n"
+ "\tAccepted : %" PRIu64 "\n"
+ "\tRejected : %" PRIu64 "\n"
+ "\tLost : %" PRIu64 "\n"
+ "\tPassed filter : %" PRIu64 "\n"
+ "\tFailed filter : %" PRIu64,
+ stats.accepted,
+ stats.rejected,
+ stats.lost,
+ stats.passed,
+ stats.failed
);
}
- if (success) return 0;
-
- return 1;
+ if ((stats.lost > 0) || (stats.failed > 0)) {
+ exit(1);
+ }
+ exit(0);
}