static fr_bio_packet_t *client_bio = NULL;
+static fr_radius_client_bio_info_t const *client_info = NULL;
+
static int ipproto = IPPROTO_UDP;
static fr_dlist_head_t rc_request_list;
static fr_dict_t const *dict_freeradius;
static fr_dict_t const *dict_radius;
+static TALLOC_CTX *autofree = NULL;
+
extern fr_dict_autoload_t radclient_dict[];
fr_dict_autoload_t radclient_dict[] = {
{ .out = &dict_freeradius, .proto = "freeradius" },
*/
static int radclient_sane(rc_request_t *request)
{
+ request->packet->socket.inet.src_ipaddr = fd_config.src_ipaddr;
+ request->packet->socket.inet.src_port = fd_config.src_port;
+
if (request->packet->socket.inet.dst_port == 0) {
request->packet->socket.inet.dst_port = fd_config.dst_port;
}
+
if (request->packet->socket.inet.dst_ipaddr.af == AF_UNSPEC) {
if (fd_config.dst_ipaddr.af == AF_UNSPEC) {
ERROR("No server was given, and request %" PRIu64 " in file %s did not contain "
}
request->packet->socket.inet.dst_ipaddr = fd_config.dst_ipaddr;
}
+
if (request->packet->code == 0) {
if (packet_code == -1) {
ERROR("Request was \"auto\", and request %" PRIu64 " in file %s did not contain Packet-Type",
}
request->packet->code = packet_code;
}
+
request->packet->socket.fd = -1;
return 0;
* Send the current packet.
*/
if (fr_bio_packet_write(client, request, request->packet, &request->request_pairs) < 0) {
- REDEBUG("Failed writing packet");
+ REDEBUG("Failed writing packet - %s", fr_strerror());
return -1;
}
fr_assert(0);
}
-static void client_read(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
+static void client_read(fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
{
fr_bio_packet_t *client = uctx;
rc_request_t *request;
* @todo list_ctx is ignored
*/
rcode = fr_bio_packet_read(client, (void **) &request, &reply, client, &list);
- if (rcode < 0) fr_assert(0);
+ if (rcode < 0) {
+ ERROR("Failed reading packet - %s", fr_bio_strerror(rcode));
+ fr_exit_now(1);
+ }
- if (!rcode) {
- ERROR("Failed reading packet - %s", fr_strerror());
- fr_assert(0);
+ /*
+ * Not a RADIUS packet, or not a reply to a packet we sent.
+ */
+ if (!rcode) return;
+
+ fr_pair_list_append(&request->reply_pairs, &list);
+
+ fr_packet_log(&default_log, reply, &request->reply_pairs, true);
+
+ /*
+ * Don't leave a dangling pointer around.
+ */
+ if (current == request) {
+ current = fr_dlist_prev(&rc_request_list, current);
}
- fr_packet_log(&default_log, reply, &list, true);
+ talloc_free(request);
- fr_assert(0);
+ if (!fr_radius_client_bio_outstanding(client)) {
+ fr_event_loop_exit(el, 1);
+ }
}
static fr_event_update_t const pause_write[] = {
if (send_one_packet(client, request) < 0) fr_assert(0);
}
+static void client_connect(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
+{
+ fr_bio_packet_t *client = uctx;
+
+ if (fr_radius_client_bio_connect(client) < 0) {
+ ERROR("Failed connecting socket: %s", fr_strerror());
+ fr_exit_now(1);
+ }
+
+ if (fr_event_fd_insert(autofree, NULL, el, fd, client_read, client_write, client_error, client) < 0) {
+ fr_perror("radclient");
+ fr_exit_now(1);
+ }
+}
+
/**
*
char filesecret[256];
FILE *fp;
int do_summary = false;
- TALLOC_CTX *autofree;
fr_dlist_head_t filenames;
/*
default_log.fd = STDOUT_FILENO;
default_log.print_level = false;
+ /*
+ * Initialize the kind of socket we want.
+ */
fd_config = (fr_bio_fd_config_t) {
.type = FR_BIO_FD_CONNECTED,
.socket_type = SOCK_DGRAM,
.path = NULL,
.filename = NULL,
- .async = false,
+ .async = true,
};
/*
- * Initialize our client configuration.
+ * Initialize the client configuration.
*/
client_config = (fr_radius_client_config_t) {
.log = &default_log,
},
};
+ /***********************************************************************
+ *
+ * Parse command-line options.
+ *
+ ***********************************************************************/
+
while ((c = getopt(argc, argv, "46c:C:d:D:f:FhP:r:sS:t:vx")) != -1) switch (c) {
case '4':
fd_config.dst_ipaddr.af = AF_INET;
ERROR("Secret in %s is too short", optarg);
fr_exit_now(1);
}
- secret = talloc_strdup(NULL, filesecret);
+ secret = talloc_strdup(autofree, filesecret);
client_config.verify.secret = (uint8_t *) secret;
client_config.verify.secret_len = talloc_array_length(secret) - 1;
}
ERROR("Insufficient arguments");
usage();
}
- /*
- * Mismatch between the binary and the libraries it depends on
- */
- if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
- fr_perror("radclient");
- fr_exit_now(EXIT_FAILURE);
- }
-
- if (!fr_dict_global_ctx_init(NULL, true, dict_dir)) {
- fr_perror("radclient");
- fr_exit_now(EXIT_FAILURE);
- }
-
- if (fr_radius_global_init() < 0) {
- fr_perror("radclient");
- fr_exit_now(EXIT_FAILURE);
- }
-
- if (fr_dict_autoload(radclient_dict) < 0) {
- fr_perror("radclient");
- exit(EXIT_FAILURE);
- }
-
- if (fr_dict_attr_autoload(radclient_dict_attr) < 0) {
- fr_perror("radclient");
- exit(EXIT_FAILURE);
- }
-
- if (fr_dict_read(fr_dict_unconst(dict_freeradius), raddb_dir, FR_DICTIONARY_FILE) == -1) {
- fr_log_perror(&default_log, L_ERR, __FILE__, __LINE__, NULL,
- "Failed to initialize the dictionaries");
- exit(EXIT_FAILURE);
- }
-
- packet_global_init();
-
- fr_strerror_clear(); /* Clear the error buffer */
/*
* Get the request type
fr_assert(packet_code != 0);
fr_assert(packet_code < FR_RADIUS_CODE_MAX);
+
+ /*
+ * Initialize the retry configuration for this type of packet.
+ */
client_config.allowed[packet_code] = true;
client_config.retry[packet_code] = (fr_retry_config_t) {
.irt = fr_time_delta_from_sec(2),
* Add the secret.
*/
if (argv[3]) {
- secret = talloc_strdup(NULL, argv[3]);
+ secret = talloc_strdup(autofree, argv[3]);
client_config.verify.secret = (uint8_t *) secret;
client_config.verify.secret_len = talloc_array_length(secret) - 1;
}
+ /***********************************************************************
+ *
+ * We're done parsing command-line options, bootstrap the various libraries, etc.
+ *
+ ***********************************************************************/
+
+ if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
+ fr_perror("radclient");
+ fr_exit_now(EXIT_FAILURE);
+ }
+
+ if (!fr_dict_global_ctx_init(NULL, true, dict_dir)) {
+ fr_perror("radclient");
+ fr_exit_now(EXIT_FAILURE);
+ }
+
+ if (fr_radius_global_init() < 0) {
+ fr_perror("radclient");
+ fr_exit_now(EXIT_FAILURE);
+ }
+
+ if (fr_dict_autoload(radclient_dict) < 0) {
+ fr_perror("radclient");
+ exit(EXIT_FAILURE);
+ }
+
+ if (fr_dict_attr_autoload(radclient_dict_attr) < 0) {
+ fr_perror("radclient");
+ exit(EXIT_FAILURE);
+ }
+
+ if (fr_dict_read(fr_dict_unconst(dict_freeradius), raddb_dir, FR_DICTIONARY_FILE) == -1) {
+ fr_log_perror(&default_log, L_ERR, __FILE__, __LINE__, NULL,
+ "Failed to initialize the dictionaries");
+ exit(EXIT_FAILURE);
+ }
+
+ packet_global_init();
+
+ openssl3_init();
+
+ fr_strerror_clear();
+
+ /***********************************************************************
+ *
+ * We're done bootstrapping the libraries and dictionaries, read the input files.
+ *
+ ***********************************************************************/
+
/*
- * If no '-f' is specified, we're reading from stdin.
+ * If no '-f' is specified, then we are reading from stdin.
*/
if (fr_dlist_num_elements(&filenames) == 0) {
rc_file_pair_t *files;
}
/*
- * No packets read. Die.
+ * No packets were read. Die.
*/
if (!fr_dlist_num_elements(&rc_request_list)) {
ERROR("Nothing to send");
fr_exit_now(1);
}
- openssl3_init();
+ /***********************************************************************
+ *
+ * We're done reading files, open the socket, event loop, and start sending packets.
+ *
+ ***********************************************************************/
client_config.retry_cfg.el = fr_event_list_alloc(autofree, _loop_status, NULL);
if (!client_config.retry_cfg.el) {
fr_exit_now(1);
}
+ /*
+ * Open the RADIUS client bio, and then get the information associated with it.
+ */
client_bio = fr_radius_client_bio_alloc(autofree, &client_config, &fd_config);
if (!client_bio) {
ERROR("Failed opening socket: %s", fr_strerror());
fr_exit_now(1);
}
- /*
- * @todo - keep calling connect() when socket is readable?
- */
- if (fr_radius_client_bio_connect(client_bio) < 0) {
- ERROR("Failed connecting socket: %s", fr_strerror());
- fr_exit_now(1);
- }
+ client_info = fr_radius_client_bio_info(client_bio);
+ fr_assert(client_info != NULL);
bio = fr_radius_client_bio_get_fd(client_bio);
fr_assert(bio != NULL);
fr_assert(fd_info != NULL);
/*
- * Walk over the list of packets, sanity checking
- * everything.
+ * Walk over the list of packets, updating to use the correct addresses, and sanity checking them.
*/
fr_dlist_foreach(&rc_request_list, rc_request_t, this) {
- this->packet->socket.inet.src_ipaddr = fd_config.src_ipaddr;
- this->packet->socket.inet.src_port = fd_config.src_port;
if (radclient_sane(this) != 0) {
fr_exit_now(1);
}
}
- if (fr_event_fd_insert(NULL, client_config.retry_cfg.el, fd_info->socket.fd, client_read, client_write, client_error, client_bio) < 0) {
+ /*
+ * Always bounce through a connect(), even if we don't need it.
+ *
+ * Once the connect() passes, we start reading from the request list, and processing packets.
+ */
+ if (fr_event_fd_insert(autofree, NULL, client_config.retry_cfg.el, fd_info->socket.fd, NULL,
+ client_connect, client_error, client_bio) < 0) {
fr_perror("radclient");
fr_exit_now(1);
}
+ /***********************************************************************
+ *
+ * Run the main event loop until we either see an error, or we have sent (and received) all of the packets.
+ *
+ ***********************************************************************/
(void) fr_event_loop(client_config.retry_cfg.el);
+ /***********************************************************************
+ *
+ * We are done the event loop. Start cleaning things up.
+ *
+ ***********************************************************************/
fr_dlist_talloc_free(&rc_request_list);
- talloc_free(secret);
+ (void) fr_event_fd_delete(client_config.retry_cfg.el, fd_info->socket.fd, FR_EVENT_FILTER_IO);
fr_radius_global_free();
*/
fr_atexit_global_trigger_all();
- if ((stats.lost > 0) || (stats.failed > 0)) return EXIT_FAILURE;
-
openssl3_free();
+ if ((stats.lost > 0) || (stats.failed > 0)) return EXIT_FAILURE;
+
return ret;
}