2 * command.c Command socket processing.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2008 The FreeRADIUS server project
21 * Copyright 2008 Alan DeKok <aland@deployingradius.com>
24 #ifdef WITH_COMMAND_SOCKET
26 #include <freeradius-devel/parser.h>
27 #include <freeradius-devel/modcall.h>
28 #include <freeradius-devel/md5.h>
29 #include <freeradius-devel/channel.h>
32 #ifdef HAVE_INTTYPES_H
39 #define SUN_LEN(su) (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
43 #ifdef HAVE_SYS_STAT_H
50 typedef struct fr_command_table_t fr_command_table_t;
52 typedef int (*fr_command_func_t)(rad_listen_t *, int, char *argv[]);
57 #define CMD_FAIL FR_CHANNEL_FAIL
58 #define CMD_OK FR_CHANNEL_SUCCESS
60 struct fr_command_table_t {
62 int mode; /* read/write */
64 fr_command_func_t func;
65 fr_command_table_t *table;
68 #define COMMAND_BUFFER_SIZE (1024)
70 typedef struct fr_cs_buffer_t {
75 char buffer[COMMAND_BUFFER_SIZE];
78 #define COMMAND_SOCKET_MAGIC (0xffdeadee)
79 typedef struct fr_command_socket_t {
82 char *copy; /* <sigh> */
87 char const *mode_name;
92 * The next few entries handle fake packets injected by
95 fr_ipaddr_t src_ipaddr; /* src_port is always 0 */
96 fr_ipaddr_t dst_ipaddr;
98 rad_listen_t *inject_listener;
99 RADCLIENT *inject_client;
102 } fr_command_socket_t;
104 static const CONF_PARSER command_config[] = {
105 { "socket", FR_CONF_OFFSET(PW_TYPE_STRING, fr_command_socket_t, path), "${run_dir}/radiusd.sock" },
106 { "uid", FR_CONF_OFFSET(PW_TYPE_STRING, fr_command_socket_t, uid_name), NULL },
107 { "gid", FR_CONF_OFFSET(PW_TYPE_STRING, fr_command_socket_t, gid_name), NULL },
108 { "mode", FR_CONF_OFFSET(PW_TYPE_STRING, fr_command_socket_t, mode_name), NULL },
109 { "peercred", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_command_socket_t, peercred), "yes" },
110 CONF_PARSER_TERMINATOR
113 static FR_NAME_NUMBER mode_names[] = {
115 { "read-only", FR_READ },
116 { "read-write", FR_READ | FR_WRITE },
117 { "rw", FR_READ | FR_WRITE },
121 #if !defined(HAVE_GETPEEREID) && defined(SO_PEERCRED)
122 static int getpeereid(int s, uid_t *euid, gid_t *egid)
125 socklen_t cl = sizeof(cr);
127 if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cl) < 0) {
136 /* we now have getpeereid() in this file */
137 #define HAVE_GETPEEREID (1)
139 #endif /* HAVE_GETPEEREID */
141 /** Initialise a socket for use with peercred authentication
143 * This function initialises a socket and path in a way suitable for use with
146 * @param path to socket.
147 * @param uid that should own the socket (linux only).
148 * @param gid that should own the socket (linux only).
149 * @return 0 on success -1 on failure.
152 static int fr_server_domain_socket_peercred(char const *path, uid_t uid, gid_t gid)
154 static int fr_server_domain_socket_peercred(char const *path, uid_t UNUSED uid, UNUSED gid_t gid)
160 struct sockaddr_un salocal;
164 fr_strerror_printf("No path provided, was NULL");
169 if (len >= sizeof(salocal.sun_path)) {
170 fr_strerror_printf("Path too long in socket filename");
174 if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
175 fr_strerror_printf("Failed creating socket: %s", fr_syserror(errno));
179 memset(&salocal, 0, sizeof(salocal));
180 salocal.sun_family = AF_UNIX;
181 memcpy(salocal.sun_path, path, len + 1); /* SUN_LEN does strlen */
183 socklen = SUN_LEN(&salocal);
188 if (stat(path, &buf) < 0) {
189 if (errno != ENOENT) {
190 fr_strerror_printf("Failed to stat %s: %s", path, fr_syserror(errno));
196 * FIXME: Check the enclosing directory?
198 } else { /* it exists */
201 if (!S_ISREG(buf.st_mode)
203 && !S_ISSOCK(buf.st_mode)
206 fr_strerror_printf("Cannot turn %s into socket", path);
212 * Refuse to open sockets not owned by us.
214 if (buf.st_uid != geteuid()) {
215 fr_strerror_printf("We do not own %s", path);
221 * Check if a server is already listening on the
224 client_fd = fr_socket_client_unix(path, false);
225 if (client_fd >= 0) {
226 fr_strerror_printf("Control socket '%s' is already in use", path);
232 if (unlink(path) < 0) {
233 fr_strerror_printf("Failed to delete %s: %s", path, fr_syserror(errno));
239 if (bind(sockfd, (struct sockaddr *)&salocal, socklen) < 0) {
240 fr_strerror_printf("Failed binding to %s: %s", path, fr_syserror(errno));
246 * FIXME: There's a race condition here. But Linux
247 * doesn't seem to permit fchmod on domain sockets.
249 if (chmod(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) < 0) {
250 fr_strerror_printf("Failed setting permissions on %s: %s", path, fr_syserror(errno));
255 if (listen(sockfd, 8) < 0) {
256 fr_strerror_printf("Failed listening to %s: %s", path, fr_syserror(errno));
265 if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0) {
266 fr_strerror_printf("Failure getting socket flags: %s", fr_syserror(errno));
272 if( fcntl(sockfd, F_SETFL, flags) < 0) {
273 fr_strerror_printf("Failure setting socket flags: %s", fr_syserror(errno));
281 * Changing socket permissions only works on linux.
282 * BSDs ignore socket permissions.
286 * Don't chown it from (possibly) non-root to root.
287 * Do chown it from (possibly) root to non-root.
289 if ((uid != (uid_t) -1) || (gid != (gid_t) -1)) {
291 * Don't do chown if it's already owned by us.
293 if (fstat(sockfd, &buf) < 0) {
294 fr_strerror_printf("Failed reading %s: %s", path, fr_syserror(errno));
299 if ((buf.st_uid != uid) || (buf.st_gid != gid)) {
301 if (fchown(sockfd, uid, gid) < 0) {
302 fr_strerror_printf("Failed setting ownership of %s to (%d, %d): %s",
303 path, uid, gid, fr_syserror(errno));
316 #if !defined(HAVE_OPENAT) || !defined(HAVE_MKDIRAT) || !defined(HAVE_UNLINKAT)
317 static int fr_server_domain_socket_perm(UNUSED char const *path, UNUSED uid_t uid, UNUSED gid_t gid)
319 fr_strerror_printf("Unable to initialise control socket. Set peercred = yes or update to "
320 "POSIX-2008 compliant libc");
324 /** Alternative function for creating Unix domain sockets and enforcing permissions
326 * Unlike fr_server_unix_socket which is intended to be used with peercred auth
327 * this function relies on the file system to enforce access.
329 * The way it does this depends on the operating system. On Linux systems permissions
330 * can be set on the socket directly and the system will enforce them.
332 * On most other systems fchown and fchmod fail when called with socket descriptors,
333 * and although permissions can be changed in other ways, they're not enforced.
335 * For these systems we use the permissions on the parent directory to enforce
336 * permissions on the socket. It's not safe to modify these permissions ourselves
337 * due to TOCTOU attacks, so if they don't match what we require, we error out and
338 * get the user to change them (which arguably isn't any safer, but releases us of
339 * the responsibility).
341 * @note must be called without effective root permissions (fr_suid_down).
343 * @param path where domain socket should be created.
344 * @return a file descriptor for the bound socket on success, -1 on failure.
346 static int fr_server_domain_socket_perm(char const *path, uid_t uid, gid_t gid)
348 int dir_fd = -1, sock_fd = -1, parent_fd = -1;
350 char *buff = NULL, *dir = NULL, *p;
361 struct sockaddr_un salocal;
369 * Determine the correct permissions for the socket, or its
370 * containing directory.
372 perm |= S_IREAD | S_IWRITE | S_IEXEC;
373 if (gid != (gid_t) -1) perm |= S_IRGRP | S_IWGRP | S_IXGRP;
375 buff = talloc_strdup(NULL, path);
376 if (!buff) return -1;
379 * Some implementations modify it in place others use internal
380 * storage *sigh*. dirname also formats the path else we wouldn't
385 dir = talloc_strdup(NULL, dir);
390 p = strrchr(dir, FR_DIR_SEP);
392 fr_strerror_printf("Failed determining parent directory");
395 if (sock_fd >= 0) close(sock_fd);
396 if (dir_fd >= 0) close(dir_fd);
397 if (parent_fd >= 0) close(parent_fd);
404 * Ensure the parent of the control socket directory exists,
405 * and the euid we're running under has access to it.
407 parent_fd = open(dir, O_DIRECTORY);
412 if (rad_getpwuid(NULL, &user, euid) < 0) goto error;
413 if (rad_getgrgid(NULL, &group, egid) < 0) {
417 fr_strerror_printf("Can't open directory \"%s\": %s. Must be created manually, or modified, "
418 "with permissions that allow writing by user %s or group %s", dir,
419 user->pw_name, group->gr_name, fr_syserror(errno));
427 dir_fd = openat(parent_fd, p + 1, O_NOFOLLOW | O_DIRECTORY);
431 if (errno != ENOENT) {
432 fr_strerror_printf("Failed opening control socket directory: %s", fr_syserror(errno));
437 * This fails if the radius user can't write
438 * to the parent directory.
440 if (mkdirat(parent_fd, p + 1, 0700) < 0) {
441 fr_strerror_printf("Failed creating control socket directory: %s", fr_syserror(errno));
445 dir_fd = openat(parent_fd, p + 1, O_NOFOLLOW | O_DIRECTORY);
447 fr_strerror_printf("Failed opening the control socket directory we created: %s",
451 if (fchmod(dir_fd, perm) < 0) {
452 fr_strerror_printf("Failed setting permissions on control socket directory: %s",
458 if ((uid != (uid_t)-1) || (gid != (gid_t)-1)) ret = fchown(dir_fd, uid, gid);
461 fr_strerror_printf("Failed changing ownership of control socket directory: %s",
466 * Control socket dir already exists, but we still need to
467 * check the permissions are what we expect.
473 ret = fstat(dir_fd, &st);
475 fr_strerror_printf("Failed checking permissions of control socket directory: %s",
480 if ((uid != (uid_t)-1) && (st.st_uid != uid)) {
481 struct passwd *need_user, *have_user;
483 if (rad_getpwuid(NULL, &need_user, uid) < 0) goto error;
484 if (rad_getpwuid(NULL, &have_user, st.st_uid) < 0) {
485 talloc_free(need_user);
488 fr_strerror_printf("Control socket directory must be owned by user %s, "
489 "currently owned by %s", need_user->pw_name, have_user->pw_name);
490 talloc_free(need_user);
491 talloc_free(have_user);
495 if ((gid != (gid_t)-1) && (st.st_gid != gid)) {
496 struct group *need_group, *have_group;
498 if (rad_getgrgid(NULL, &need_group, gid) < 0) goto error;
499 if (rad_getgrgid(NULL, &have_group, st.st_gid) < 0) {
500 talloc_free(need_group);
503 fr_strerror_printf("Control socket directory \"%s\" must be owned by group %s, "
504 "currently owned by %s", dir, need_group->gr_name, have_group->gr_name);
505 talloc_free(need_group);
506 talloc_free(have_group);
510 if ((perm & 0x0c) != (st.st_mode & 0x0c)) {
511 char str_need[10], oct_need[5];
512 char str_have[10], oct_have[5];
514 rad_mode_to_str(str_need, perm);
515 rad_mode_to_oct(oct_need, perm);
516 rad_mode_to_str(str_have, st.st_mode);
517 rad_mode_to_oct(oct_have, st.st_mode);
518 fr_strerror_printf("Control socket directory must have permissions %s (%s), current "
519 "permissions are %s (%s)", str_need, oct_need, str_have, oct_have);
524 * Check if a server is already listening on the
527 client_fd = fr_socket_client_unix(path, false);
528 if (client_fd >= 0) {
529 fr_strerror_printf("Control socket '%s' is already in use", path);
535 name = strrchr(path, FR_DIR_SEP);
537 fr_strerror_printf("Can't determine socket name");
543 * We've checked the containing directory has the permissions
544 * we expect, and as we have the FD, and aren't following
545 * symlinks no one can trick us into changing or creating a
548 * It's possible an attacker may still be able to create hard
549 * links, for the socket file. But they would need write
550 * access to the directory we just created or verified, so
551 * this attack vector is unlikely.
553 if ((uid != (uid_t)-1) && (rad_seuid(uid) < 0)) goto error;
554 if ((gid != (gid_t)-1) && (rad_segid(gid) < 0)) {
560 * The original code, did openat, used fstat to figure out
561 * what type the file was and then used unlinkat to unlink
562 * it. Except on OSX (at least) openat refuses to open
563 * socket files. So we now rely on the fact that unlinkat
564 * has sane and consistent behaviour, and will not unlink
565 * directories. unlinkat should also fail if the socket user
566 * hasn't got permission to modify the socket.
568 if ((unlinkat(dir_fd, name, 0) < 0) && (errno != ENOENT)) {
569 fr_strerror_printf("Failed removing stale socket: %s", fr_syserror(errno));
571 if (uid != (uid_t)-1) rad_seuid(euid);
572 if (gid != (gid_t)-1) rad_segid(egid);
579 * At this point we should have established a secure directory
580 * to house our socket, and cleared out any stale sockets.
582 sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
584 fr_strerror_printf("Failed creating socket: %s", fr_syserror(errno));
593 if (len >= sizeof(salocal.sun_path)) {
594 fr_strerror_printf("Path too long in socket filename");
598 memset(&salocal, 0, sizeof(salocal));
599 salocal.sun_family = AF_UNIX;
602 memcpy(salocal.sun_path, name, len + 1); /* SUN_LEN does strlen */
604 memcpy(salocal.sun_path, path, len + 1); /* SUN_LEN does strlen */
606 socklen = SUN_LEN(&salocal);
609 * Direct socket permissions are only useful on Linux which
610 * actually enforces them. BSDs don't. They also need to be
611 * set before binding the socket to a file.
614 if (fchmod(sock_fd, perm) < 0) {
615 char str_need[10], oct_need[5];
617 rad_mode_to_str(str_need, perm);
618 rad_mode_to_oct(oct_need, perm);
619 fr_strerror_printf("Failed changing socket permissions to %s (%s)", str_need, oct_need);
624 if (fchown(sock_fd, uid, gid) < 0) {
628 if (rad_getpwuid(NULL, &user, uid) < 0) goto sock_error;
629 if (rad_getgrgid(NULL, &group, gid) < 0) {
634 fr_strerror_printf("Failed changing ownership of socket to %s:%s", user->pw_name, group->gr_name);
641 * The correct function to use here is bindat(), but only
642 * quite recent versions of FreeBSD actually have it, and
643 * it's definitely not POSIX.
646 if (bindat(dir_fd, sock_fd, (struct sockaddr *)&salocal, socklen) < 0) {
648 if (bind(sock_fd, (struct sockaddr *)&salocal, socklen) < 0) {
650 fr_strerror_printf("Failed binding socket: %s", fr_syserror(errno));
654 if (listen(sock_fd, 8) < 0) {
655 fr_strerror_printf("Failed listening on socket: %s", fr_syserror(errno));
663 flags = fcntl(sock_fd, F_GETFL, NULL);
665 fr_strerror_printf("Failed getting socket flags: %s", fr_syserror(errno));
670 if (fcntl(sock_fd, F_SETFL, flags) < 0) {
671 fr_strerror_printf("Failed setting nonblocking socket flag: %s", fr_syserror(errno));
677 if (uid != (uid_t)-1) rad_seuid(euid);
678 if (gid != (gid_t)-1) rad_segid(egid);
680 if (dir_fd >= 0) close(dir_fd);
681 if (parent_fd >= 0) close(parent_fd);
687 static void command_close_socket(rad_listen_t *this)
689 this->status = RAD_LISTEN_STATUS_EOL;
692 * This removes the socket from the event fd, so no one
693 * will be calling us any more.
695 radius_update_listener(this);
698 static ssize_t CC_HINT(format (printf, 2, 3)) cprintf(rad_listen_t *listener, char const *fmt, ...)
705 len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
708 if (listener->status == RAD_LISTEN_STATUS_EOL) return 0;
710 r = fr_channel_write(listener->fd, FR_CHANNEL_STDOUT, buffer, len);
711 if (r <= 0) command_close_socket(listener);
714 * FIXME: Keep writing until done?
719 static ssize_t CC_HINT(format (printf, 2, 3)) cprintf_error(rad_listen_t *listener, char const *fmt, ...)
726 len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
729 if (listener->status == RAD_LISTEN_STATUS_EOL) return 0;
731 r = fr_channel_write(listener->fd, FR_CHANNEL_STDERR, buffer, len);
732 if (r <= 0) command_close_socket(listener);
735 * FIXME: Keep writing until done?
740 static int command_hup(rad_listen_t *listener, int argc, char *argv[])
743 module_instance_t *mi;
747 radius_signal_self(RADIUS_SIGNAL_SELF_HUP);
752 * Hack a "main" HUP thingy
754 if (strcmp(argv[0], "main.log") == 0) {
759 cs = cf_section_find("modules");
760 if (!cs) return CMD_FAIL;
762 mi = module_find(cs, argv[0]);
764 cprintf_error(listener, "No such module \"%s\"\n", argv[0]);
768 if ((mi->entry->module->type & RLM_TYPE_HUP_SAFE) == 0) {
769 cprintf_error(listener, "Module %s cannot be hup'd\n",
774 if (!module_hup_module(mi->cs, mi, time(NULL))) {
775 cprintf_error(listener, "Failed to reload module\n");
779 snprintf(buffer, sizeof(buffer), "modules.%s.hup",
780 cf_section_name1(mi->cs));
781 exec_trigger(NULL, mi->cs, buffer, true);
786 static int command_terminate(UNUSED rad_listen_t *listener,
787 UNUSED int argc, UNUSED char *argv[])
789 radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
794 static int command_uptime(rad_listen_t *listener,
795 UNUSED int argc, UNUSED char *argv[])
799 CTIME_R(&fr_start_time, buffer, sizeof(buffer));
800 cprintf(listener, "Up since %s", buffer); /* no \r\n */
805 static int command_show_config(rad_listen_t *listener, int argc, char *argv[])
812 cprintf_error(listener, "No path was given\n");
816 ci = cf_reference_item(main_config.config, main_config.config, argv[0]);
817 if (!ci) return CMD_FAIL;
819 if (!cf_item_is_pair(ci)) return CMD_FAIL;
821 cp = cf_item_to_pair(ci);
822 value = cf_pair_value(cp);
823 if (!value) return CMD_FAIL;
825 cprintf(listener, "%s\n", value);
830 static char const tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
833 * FIXME: Recurse && indent?
835 static void cprint_conf_parser(rad_listen_t *listener, int indent, CONF_SECTION *cs,
840 char const *name1 = cf_section_name1(cs);
841 char const *name2 = cf_section_name2(cs);
842 CONF_PARSER const *variables = cf_section_parse_table(cs);
845 cprintf(listener, "%.*s%s %s {\n", indent, tabs, name1, name2);
847 cprintf(listener, "%.*s%s {\n", indent, tabs, name1);
855 if (variables) for (i = 0; variables[i].name != NULL; i++) {
860 * No base struct offset, data must be the pointer.
861 * If data doesn't exist, ignore the entry, there
862 * must be something wrong.
865 if (!variables[i].data) {
869 data = variables[i].data;
871 } else if (variables[i].data) {
872 data = variables[i].data;
875 data = (((char const *)base) + variables[i].offset);
879 * Ignore the various flags
881 switch (variables[i].type & 0xff) {
883 cprintf(listener, "%.*s%s = ?\n", indent, tabs,
887 case PW_TYPE_INTEGER:
888 cprintf(listener, "%.*s%s = %u\n", indent, tabs,
889 variables[i].name, *(int const *) data);
892 case PW_TYPE_IPV4_ADDR:
893 inet_ntop(AF_INET, data, buffer, sizeof(buffer));
896 case PW_TYPE_IPV6_ADDR:
897 inet_ntop(AF_INET6, data, buffer, sizeof(buffer));
900 case PW_TYPE_BOOLEAN:
901 cprintf(listener, "%.*s%s = %s\n", indent, tabs,
903 ((*(bool const *) data) == false) ? "no" : "yes");
907 case PW_TYPE_FILE_INPUT:
908 case PW_TYPE_FILE_OUTPUT:
910 * FIXME: Escape things in the string!
912 if (*(char const * const *) data) {
913 cprintf(listener, "%.*s%s = \"%s\"\n", indent, tabs,
914 variables[i].name, *(char const * const *) data);
916 cprintf(listener, "%.*s%s = \n", indent, tabs,
926 cprintf(listener, "%.*s}\n", indent, tabs);
929 static int command_show_module_config(rad_listen_t *listener, int argc, char *argv[])
932 module_instance_t *mi;
935 cprintf_error(listener, "No module name was given\n");
939 cs = cf_section_find("modules");
940 if (!cs) return CMD_FAIL;
942 mi = module_find(cs, argv[0]);
944 cprintf_error(listener, "No such module \"%s\"\n", argv[0]);
948 cprint_conf_parser(listener, 0, mi->cs, mi->insthandle);
953 static char const *method_names[MOD_COUNT] = {
965 static int command_show_module_methods(rad_listen_t *listener, int argc, char *argv[])
969 module_instance_t const *mi;
973 cprintf_error(listener, "No module name was given\n");
977 cs = cf_section_find("modules");
978 if (!cs) return CMD_FAIL;
980 mi = module_find(cs, argv[0]);
982 cprintf_error(listener, "No such module \"%s\"\n", argv[0]);
986 mod = mi->entry->module;
988 for (i = 0; i < MOD_COUNT; i++) {
989 if (mod->methods[i]) cprintf(listener, "%s\n", method_names[i]);
996 static int command_show_module_flags(rad_listen_t *listener, int argc, char *argv[])
999 module_instance_t const *mi;
1000 module_t const *mod;
1003 cprintf_error(listener, "No module name was given\n");
1007 cs = cf_section_find("modules");
1008 if (!cs) return CMD_FAIL;
1010 mi = module_find(cs, argv[0]);
1012 cprintf_error(listener, "No such module \"%s\"\n", argv[0]);
1016 mod = mi->entry->module;
1018 if ((mod->type & RLM_TYPE_THREAD_UNSAFE) != 0)
1019 cprintf(listener, "thread-unsafe\n");
1021 if ((mod->type & RLM_TYPE_HUP_SAFE) != 0)
1022 cprintf(listener, "reload-on-hup\n");
1027 static int command_show_module_status(rad_listen_t *listener, int argc, char *argv[])
1030 const module_instance_t *mi;
1033 cprintf_error(listener, "No module name was given\n");
1037 cs = cf_section_find("modules");
1038 if (!cs) return CMD_FAIL;
1040 mi = module_find(cs, argv[0]);
1042 cprintf_error(listener, "No such module \"%s\"\n", argv[0]);
1047 cprintf(listener, "alive\n");
1049 cprintf(listener, "%s\n", fr_int2str(mod_rcode_table, mi->code, "<invalid>"));
1058 * Show all loaded modules
1060 static int command_show_modules(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
1062 CONF_SECTION *cs, *subcs;
1064 cs = cf_section_find("modules");
1065 if (!cs) return CMD_FAIL;
1068 while ((subcs = cf_subsection_find_next(cs, subcs, NULL)) != NULL) {
1069 char const *name1 = cf_section_name1(subcs);
1070 char const *name2 = cf_section_name2(subcs);
1072 module_instance_t *mi;
1075 mi = module_find(cs, name2);
1078 cprintf(listener, "%s (%s)\n", name2, name1);
1080 mi = module_find(cs, name1);
1083 cprintf(listener, "%s\n", name1);
1091 static int command_show_home_servers(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
1094 home_server_t *home;
1095 char const *type, *state, *proto;
1099 for (i = 0; i < 256; i++) {
1100 home = home_server_bynumber(i);
1104 * Internal "virtual" home server.
1106 if (home->ipaddr.af == AF_UNSPEC) continue;
1108 if (home->type == HOME_TYPE_AUTH) {
1111 } else if (home->type == HOME_TYPE_ACCT) {
1114 } else if (home->type == HOME_TYPE_AUTH_ACCT) {
1118 } else if (home->type == HOME_TYPE_COA) {
1124 if (home->proto == IPPROTO_UDP) {
1128 else if (home->proto == IPPROTO_TCP) {
1134 if (home->state == HOME_STATE_ALIVE) {
1137 } else if (home->state == HOME_STATE_ZOMBIE) {
1140 } else if (home->state == HOME_STATE_IS_DEAD) {
1143 } else if (home->state == HOME_STATE_UNKNOWN) {
1144 time_t now = time(NULL);
1147 * We've recently received a packet, so
1148 * the home server seems to be alive.
1150 * The *reported* state changes because
1151 * the internal state machine NEEDS THE
1152 * RIGHT STATE. However, reporting that
1153 * to the admin will confuse them.
1154 * So... we lie. No, that dress doesn't
1155 * make you look fat...
1157 if ((home->last_packet_recv + (int)home->ping_interval) >= now) {
1165 cprintf(listener, "%s\t%d\t%s\t%s\t%s\t%d\n",
1166 ip_ntoh(&home->ipaddr, buffer, sizeof(buffer)),
1167 home->port, proto, type, state,
1168 home->currently_outstanding);
1175 static int command_show_clients(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
1181 for (i = 0; i < 256; i++) {
1182 client = client_findbynumber(NULL, i);
1185 ip_ntoh(&client->ipaddr, buffer, sizeof(buffer));
1187 if (((client->ipaddr.af == AF_INET) &&
1188 (client->ipaddr.prefix != 32)) ||
1189 ((client->ipaddr.af == AF_INET6) &&
1190 (client->ipaddr.prefix != 128))) {
1191 cprintf(listener, "%s/%d\n", buffer, client->ipaddr.prefix);
1193 cprintf(listener, "%s\n", buffer);
1201 static int command_show_version(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
1203 cprintf(listener, "%s\n", radiusd_version);
1207 static int command_debug_level(rad_listen_t *listener, int argc, char *argv[])
1212 cprintf_error(listener, "Must specify <number>\n");
1216 number = atoi(argv[0]);
1217 if ((number < 0) || (number > 4)) {
1218 cprintf_error(listener, "<number> must be between 0 and 4\n");
1222 fr_debug_lvl = rad_debug_lvl = number;
1227 static char debug_log_file_buffer[1024];
1229 static int command_debug_file(rad_listen_t *listener, int argc, char *argv[])
1231 if (rad_debug_lvl && default_log.dst == L_DST_STDOUT) {
1232 cprintf_error(listener, "Cannot redirect debug logs to a file when already in debugging mode.\n");
1236 if ((argc > 0) && (strchr(argv[0], FR_DIR_SEP) != NULL)) {
1237 cprintf_error(listener, "Cannot direct debug logs to absolute path.\n");
1240 default_log.debug_file = NULL;
1242 if (argc == 0) return CMD_OK;
1245 * This looks weird, but it's here to avoid locking
1246 * a mutex for every log message.
1248 memset(debug_log_file_buffer, 0, sizeof(debug_log_file_buffer));
1251 * Debug files always go to the logging directory.
1253 snprintf(debug_log_file_buffer, sizeof(debug_log_file_buffer),
1254 "%s/%s", radlog_dir, argv[0]);
1256 default_log.debug_file = &debug_log_file_buffer[0];
1261 extern fr_cond_t *debug_condition;
1262 static int command_debug_condition(rad_listen_t *listener, int argc, char *argv[])
1267 fr_cond_t *new_condition = NULL;
1268 char *p, buffer[1024];
1274 TALLOC_FREE(debug_condition);
1275 debug_condition = NULL;
1279 if (!((argc == 1) &&
1280 ((argv[0][0] == '"') || (argv[0][0] == '\'')))) {
1283 for (i = 0; i < argc; i++) {
1286 len = strlcpy(p, argv[i], buffer + sizeof(buffer) - p);
1294 * Backwards compatibility. De-escape the string.
1306 error = "Unexpected end of string";
1307 slen = -strlen(argv[0]);
1315 error = "Unexpected text after end of string";
1316 slen = -(p - argv[0]);
1337 slen = fr_condition_tokenize(NULL, NULL, p, &new_condition, &error, FR_COND_ONE_PASS);
1339 char *spaces, *text;
1342 fr_canonicalize_error(NULL, &spaces, &text, slen, p);
1344 ERROR("Parse error in condition");
1346 ERROR("%s^ %s", spaces, error);
1348 cprintf_error(listener, "Parse error in condition \"%s\": %s\n", p, error);
1350 talloc_free(spaces);
1355 (void) modcall_pass2_condition(new_condition);
1358 * Delete old condition.
1360 * This is thread-safe because the condition is evaluated
1361 * in the main server thread, along with this code.
1363 TALLOC_FREE(debug_condition);
1364 debug_condition = new_condition;
1369 static int command_show_debug_condition(rad_listen_t *listener,
1370 UNUSED int argc, UNUSED char *argv[])
1374 if (!debug_condition) {
1375 cprintf(listener, "\n");
1379 fr_cond_sprint(buffer, sizeof(buffer), debug_condition);
1381 cprintf(listener, "%s\n", buffer);
1386 static int command_show_debug_file(rad_listen_t *listener,
1387 UNUSED int argc, UNUSED char *argv[])
1389 if (!default_log.debug_file) return CMD_FAIL;
1391 cprintf(listener, "%s\n", default_log.debug_file);
1396 static int command_show_debug_level(rad_listen_t *listener,
1397 UNUSED int argc, UNUSED char *argv[])
1399 cprintf(listener, "%d\n", rad_debug_lvl);
1404 static RADCLIENT *get_client(rad_listen_t *listener, int argc, char *argv[])
1409 int proto = IPPROTO_UDP;
1410 RADCLIENT_LIST *list = NULL;
1413 cprintf_error(listener, "Must specify <ipaddr>\n");
1418 * First arg is IP address.
1420 if (ip_hton(&ipaddr, AF_UNSPEC, argv[0], false) < 0) {
1421 cprintf_error(listener, "Failed parsing IP address; %s\n",
1427 while (myarg < argc) {
1428 if (strcmp(argv[myarg], "udp") == 0) {
1429 proto = IPPROTO_UDP;
1435 if (strcmp(argv[myarg], "tcp") == 0) {
1436 proto = IPPROTO_TCP;
1442 if (strcmp(argv[myarg], "listen") == 0) {
1443 uint16_t server_port;
1444 fr_ipaddr_t server_ipaddr;
1446 if ((argc - myarg) < 2) {
1447 cprintf_error(listener, "Must specify listen <ipaddr> <port>\n");
1451 if (ip_hton(&server_ipaddr, ipaddr.af, argv[myarg + 1], false) < 0) {
1452 cprintf_error(listener, "Failed parsing IP address; %s\n",
1457 server_port = atoi(argv[myarg + 2]);
1459 list = listener_find_client_list(&server_ipaddr, server_port, proto);
1461 cprintf_error(listener, "No such listener %s %s\n", argv[myarg + 1], argv[myarg + 2]);
1468 cprintf_error(listener, "Unknown argument %s.\n", argv[myarg]);
1472 client = client_find(list, &ipaddr, proto);
1474 cprintf_error(listener, "No such client\n");
1482 static home_server_t *get_home_server(rad_listen_t *listener, int argc,
1483 char *argv[], int *last)
1486 home_server_t *home;
1488 int proto = IPPROTO_UDP;
1492 cprintf_error(listener, "Must specify <ipaddr> <port> [udp|tcp]\n");
1496 if (ip_hton(&ipaddr, AF_UNSPEC, argv[0], false) < 0) {
1497 cprintf_error(listener, "Failed parsing IP address; %s\n",
1502 port = atoi(argv[1]);
1506 while (myarg < argc) {
1507 if (strcmp(argv[myarg], "udp") == 0) {
1508 proto = IPPROTO_UDP;
1514 if (strcmp(argv[myarg], "tcp") == 0) {
1515 proto = IPPROTO_TCP;
1522 * Unknown argument. Leave it for the caller.
1527 home = home_server_find(&ipaddr, port, proto);
1529 cprintf_error(listener, "No such home server\n");
1533 if (last) *last = myarg;
1538 static int command_set_home_server_state(rad_listen_t *listener, int argc, char *argv[])
1541 home_server_t *home;
1544 cprintf_error(listener, "Must specify <ipaddr> <port> [udp|tcp] <state>\n");
1548 home = get_home_server(listener, argc, argv, &last);
1553 if (strcmp(argv[last], "alive") == 0) {
1554 revive_home_server(home);
1556 } else if (strcmp(argv[last], "dead") == 0) {
1559 gettimeofday(&now, NULL); /* we do this WAY too ofetn */
1560 mark_home_server_dead(home, &now);
1563 cprintf_error(listener, "Unknown state \"%s\"\n", argv[last]);
1570 static int command_show_home_server_state(rad_listen_t *listener, int argc, char *argv[])
1572 home_server_t *home;
1574 home = get_home_server(listener, argc, argv, NULL);
1575 if (!home) return CMD_FAIL;
1577 switch (home->state) {
1578 case HOME_STATE_ALIVE:
1579 cprintf(listener, "alive\n");
1582 case HOME_STATE_IS_DEAD:
1583 cprintf(listener, "dead\n");
1586 case HOME_STATE_ZOMBIE:
1587 cprintf(listener, "zombie\n");
1590 case HOME_STATE_UNKNOWN:
1591 cprintf(listener, "unknown\n");
1595 cprintf(listener, "invalid\n");
1604 * For encode/decode stuff
1606 static int null_socket_dencode(UNUSED rad_listen_t *listener, UNUSED REQUEST *request)
1611 static int null_socket_send(UNUSED rad_listen_t *listener, REQUEST *request)
1617 output_file = request_data_reference(request, (void *)null_socket_send, 0);
1619 ERROR("No output file for injected packet %d", request->number);
1623 fp = fopen(output_file, "w");
1625 ERROR("Failed to send injected file to %s: %s", output_file, fr_syserror(errno));
1629 if (request->reply->code != 0) {
1630 char const *what = "reply";
1634 if (request->reply->code < FR_MAX_PACKET_CODE) {
1635 what = fr_packet_codes[request->reply->code];
1638 fprintf(fp, "%s\n", what);
1640 if (rad_debug_lvl) {
1641 RDEBUG("Injected %s packet to host %s port 0 code=%d, id=%d", what,
1642 inet_ntop(request->reply->src_ipaddr.af,
1643 &request->reply->src_ipaddr.ipaddr,
1644 buffer, sizeof(buffer)),
1645 request->reply->code, request->reply->id);
1649 for (vp = fr_cursor_init(&cursor, &request->reply->vps);
1651 vp = fr_cursor_next(&cursor)) {
1652 vp_prints(buffer, sizeof(buffer), vp);
1653 fprintf(fp, "%s\n", buffer);
1654 RDEBUG("%s", buffer);
1663 static rad_listen_t *get_socket(rad_listen_t *listener, int argc,
1664 char *argv[], int *last)
1668 int proto = IPPROTO_UDP;
1672 cprintf_error(listener, "Must specify <ipaddr> <port> [udp|tcp]\n");
1676 if (ip_hton(&ipaddr, AF_UNSPEC, argv[0], false) < 0) {
1677 cprintf_error(listener, "Failed parsing IP address; %s\n",
1682 port = atoi(argv[1]);
1684 if (last) *last = 2;
1686 if (strcmp(argv[2], "udp") == 0) {
1687 proto = IPPROTO_UDP;
1688 if (last) *last = 3;
1691 if (strcmp(argv[2], "tcp") == 0) {
1692 proto = IPPROTO_TCP;
1693 if (last) *last = 3;
1698 sock = listener_find_byipaddr(&ipaddr, port, proto);
1700 cprintf_error(listener, "No such listen section\n");
1708 static int command_inject_to(rad_listen_t *listener, int argc, char *argv[])
1710 fr_command_socket_t *sock = listener->data;
1711 listen_socket_t *data;
1712 rad_listen_t *found;
1714 found = get_socket(listener, argc, argv, NULL);
1720 sock->inject_listener = found;
1721 sock->dst_ipaddr = data->my_ipaddr;
1722 sock->dst_port = data->my_port;
1727 static int command_inject_from(rad_listen_t *listener, int argc, char *argv[])
1730 fr_command_socket_t *sock = listener->data;
1733 cprintf_error(listener, "No <ipaddr> was given\n");
1737 if (!sock->inject_listener) {
1738 cprintf_error(listener, "You must specify \"inject to\" before using \"inject from\"\n");
1742 sock->src_ipaddr.af = AF_UNSPEC;
1743 if (ip_hton(&sock->src_ipaddr, AF_UNSPEC, argv[0], false) < 0) {
1744 cprintf_error(listener, "Failed parsing IP address; %s\n",
1749 client = client_listener_find(sock->inject_listener, &sock->src_ipaddr,
1752 cprintf_error(listener, "No such client %s\n", argv[0]);
1755 sock->inject_client = client;
1760 static int command_inject_file(rad_listen_t *listener, int argc, char *argv[])
1762 static int inject_id = 0;
1765 fr_command_socket_t *sock = listener->data;
1767 RADIUS_PACKET *packet;
1771 RAD_REQUEST_FUNP fun = NULL;
1775 cprintf_error(listener, "You must specify <input-file> <output-file>\n");
1779 if (!sock->inject_listener) {
1780 cprintf_error(listener, "You must specify \"inject to\" before using \"inject file\"\n");
1784 if (!sock->inject_client) {
1785 cprintf_error(listener, "You must specify \"inject from\" before using \"inject file\"\n");
1790 * Output files always go to the logging directory.
1792 snprintf(buffer, sizeof(buffer), "%s/%s", radlog_dir, argv[1]);
1794 fp = fopen(argv[0], "r");
1796 cprintf_error(listener, "Failed opening %s: %s\n",
1797 argv[0], fr_syserror(errno));
1801 ret = fr_pair_list_afrom_file(NULL, &vp, fp, &filedone);
1804 cprintf_error(listener, "Failed reading attributes from %s: %s\n",
1805 argv[0], fr_strerror());
1809 fake = talloc(NULL, rad_listen_t);
1810 memcpy(fake, sock->inject_listener, sizeof(*fake));
1813 * Re-write the IO for the listener.
1815 fake->encode = null_socket_dencode;
1816 fake->decode = null_socket_dencode;
1817 fake->send = null_socket_send;
1819 packet = rad_alloc(NULL, false);
1820 packet->src_ipaddr = sock->src_ipaddr;
1821 packet->src_port = 0;
1823 packet->dst_ipaddr = sock->dst_ipaddr;
1824 packet->dst_port = sock->dst_port;
1826 packet->id = inject_id++;
1828 if (fake->type == RAD_LISTEN_AUTH) {
1829 packet->code = PW_CODE_ACCESS_REQUEST;
1830 fun = rad_authenticate;
1833 #ifdef WITH_ACCOUNTING
1834 packet->code = PW_CODE_ACCOUNTING_REQUEST;
1835 fun = rad_accounting;
1837 cprintf_error(listener, "This server was built without accounting support.\n");
1844 if (rad_debug_lvl) {
1845 DEBUG("Injecting %s packet from host %s port 0 code=%d, id=%d",
1846 fr_packet_codes[packet->code],
1847 inet_ntop(packet->src_ipaddr.af,
1848 &packet->src_ipaddr.ipaddr,
1849 buffer, sizeof(buffer)),
1850 packet->code, packet->id);
1852 for (vp = fr_cursor_init(&cursor, &packet->vps);
1854 vp = fr_cursor_next(&cursor)) {
1855 vp_prints(buffer, sizeof(buffer), vp);
1856 DEBUG("\t%s", buffer);
1859 WARN("INJECTION IS LEAKING MEMORY!");
1862 if (!request_receive(NULL, fake, packet, sock->inject_client, fun)) {
1863 cprintf_error(listener, "Failed to inject request. See log file for details\n");
1871 * Remember what the output file is, and remember to
1872 * delete the fake listener when done.
1874 request_data_add(request, null_socket_send, 0, talloc_typed_strdup(NULL, buffer), true);
1875 request_data_add(request, null_socket_send, 1, fake, true);
1883 static fr_command_table_t command_table_inject[] = {
1885 "inject to <ipaddr> <port> - Inject packets to the destination IP and port.",
1886 command_inject_to, NULL },
1889 "inject from <ipaddr> - Inject packets as if they came from <ipaddr>",
1890 command_inject_from, NULL },
1893 "inject file <input-file> <output-file> - Inject packet from <input-file>, with results sent to <output-file>",
1894 command_inject_file, NULL },
1896 { NULL, 0, NULL, NULL, NULL }
1899 static fr_command_table_t command_table_debug[] = {
1900 { "condition", FR_WRITE,
1901 "debug condition [condition] - Enable debugging for requests matching [condition]",
1902 command_debug_condition, NULL },
1904 { "level", FR_WRITE,
1905 "debug level <number> - Set debug level to <number>. Higher is more debugging.",
1906 command_debug_level, NULL },
1909 "debug file [filename] - Send all debugging output to [filename]",
1910 command_debug_file, NULL },
1912 { NULL, 0, NULL, NULL, NULL }
1915 static fr_command_table_t command_table_show_debug[] = {
1916 { "condition", FR_READ,
1917 "show debug condition - Shows current debugging condition.",
1918 command_show_debug_condition, NULL },
1921 "show debug level - Shows current debugging level.",
1922 command_show_debug_level, NULL },
1925 "show debug file - Shows current debugging file.",
1926 command_show_debug_file, NULL },
1928 { NULL, 0, NULL, NULL, NULL }
1931 static fr_command_table_t command_table_show_module[] = {
1932 { "config", FR_READ,
1933 "show module config <module> - show configuration for given module",
1934 command_show_module_config, NULL },
1936 "show module flags <module> - show other module properties",
1937 command_show_module_flags, NULL },
1939 "show module list - shows list of loaded modules",
1940 command_show_modules, NULL },
1941 { "methods", FR_READ,
1942 "show module methods <module> - show sections where <module> may be used",
1943 command_show_module_methods, NULL },
1944 { "status", FR_READ,
1945 "show module status <module> - show the module status",
1946 command_show_module_status, NULL },
1948 { NULL, 0, NULL, NULL, NULL }
1951 static fr_command_table_t command_table_show_client[] = {
1953 "show client list - shows list of global clients",
1954 command_show_clients, NULL },
1956 { NULL, 0, NULL, NULL, NULL }
1960 static fr_command_table_t command_table_show_home[] = {
1962 "show home_server list - shows list of home servers",
1963 command_show_home_servers, NULL },
1965 "show home_server state <ipaddr> <port> [udp|tcp] - shows state of given home server",
1966 command_show_home_server_state, NULL },
1968 { NULL, 0, NULL, NULL, NULL }
1973 static fr_command_table_t command_table_show[] = {
1974 { "client", FR_READ,
1975 "show client <command> - do sub-command of client",
1976 NULL, command_table_show_client },
1977 { "config", FR_READ,
1978 "show config <path> - shows the value of configuration option <path>",
1979 command_show_config, NULL },
1981 "show debug <command> - show debug properties",
1982 NULL, command_table_show_debug },
1984 { "home_server", FR_READ,
1985 "show home_server <command> - do sub-command of home_server",
1986 NULL, command_table_show_home },
1988 { "module", FR_READ,
1989 "show module <command> - do sub-command of module",
1990 NULL, command_table_show_module },
1991 { "uptime", FR_READ,
1992 "show uptime - shows time at which server started",
1993 command_uptime, NULL },
1994 { "version", FR_READ,
1995 "show version - Prints version of the running server",
1996 command_show_version, NULL },
1997 { NULL, 0, NULL, NULL, NULL }
2001 static int command_set_module_config(rad_listen_t *listener, int argc, char *argv[])
2006 module_instance_t *mi;
2007 CONF_PARSER const *variables;
2011 cprintf_error(listener, "No module name or variable was given\n");
2015 cs = cf_section_find("modules");
2018 mi = module_find(cs, argv[0]);
2020 cprintf_error(listener, "No such module \"%s\"\n", argv[0]);
2024 if ((mi->entry->module->type & RLM_TYPE_HUP_SAFE) == 0) {
2025 cprintf_error(listener, "Cannot change configuration of module as it is cannot be HUP'd.\n");
2029 variables = cf_section_parse_table(mi->cs);
2031 cprintf_error(listener, "Cannot find configuration for module\n");
2036 for (i = 0; variables[i].name != NULL; i++) {
2038 * FIXME: Recurse into sub-types somehow...
2040 if (variables[i].type == PW_TYPE_SUBSECTION) continue;
2042 if (strcmp(variables[i].name, argv[1]) == 0) {
2049 cprintf_error(listener, "No such variable \"%s\"\n", argv[1]);
2053 i = rcode; /* just to be safe */
2056 * It's not part of the dynamic configuration. The module
2057 * needs to re-parse && validate things.
2059 if (variables[i].data) {
2060 cprintf_error(listener, "Variable cannot be dynamically updated\n");
2064 data = ((char *) mi->insthandle) + variables[i].offset;
2066 cp = cf_pair_find(mi->cs, argv[1]);
2070 * Replace the OLD value in the configuration file with
2073 * FIXME: Parse argv[2] depending on it's data type!
2074 * If it's a string, look for leading single/double quotes,
2075 * end then call tokenize functions???
2077 cf_pair_replace(mi->cs, cp, argv[2]);
2079 rcode = cf_item_parse(mi->cs, argv[1], variables[i].type, data, argv[2]);
2081 cprintf_error(listener, "Failed to parse value\n");
2088 static int command_set_module_status(rad_listen_t *listener, int argc, char *argv[])
2091 module_instance_t *mi;
2094 cprintf_error(listener, "No module name or status was given\n");
2098 cs = cf_section_find("modules");
2101 mi = module_find(cs, argv[0]);
2103 cprintf_error(listener, "No such module \"%s\"\n", argv[0]);
2108 if (strcmp(argv[1], "alive") == 0) {
2111 } else if (strcmp(argv[1], "dead") == 0) {
2112 mi->code = RLM_MODULE_FAIL;
2118 rcode = fr_str2int(mod_rcode_table, argv[1], -1);
2120 cprintf_error(listener, "Unknown status \"%s\"\n", argv[1]);
2132 static char const *elapsed_names[8] = {
2133 "1us", "10us", "100us", "1ms", "10ms", "100ms", "1s", "10s"
2137 #ifdef WITH_STATS_64BIT
2139 #define PU "%" PRIu64
2145 #define PU "%" PRIu32
2151 static int command_print_stats(rad_listen_t *listener, fr_stats_t *stats,
2152 int auth, int server)
2156 cprintf(listener, "requests\t" PU "\n", stats->total_requests);
2157 cprintf(listener, "responses\t" PU "\n", stats->total_responses);
2160 cprintf(listener, "accepts\t\t" PU "\n",
2161 stats->total_access_accepts);
2162 cprintf(listener, "rejects\t\t" PU "\n",
2163 stats->total_access_rejects);
2164 cprintf(listener, "challenges\t" PU "\n",
2165 stats->total_access_challenges);
2168 cprintf(listener, "dup\t\t" PU "\n", stats->total_dup_requests);
2169 cprintf(listener, "invalid\t\t" PU "\n", stats->total_invalid_requests);
2170 cprintf(listener, "malformed\t" PU "\n", stats->total_malformed_requests);
2171 cprintf(listener, "bad_authenticator\t" PU "\n", stats->total_bad_authenticators);
2172 cprintf(listener, "dropped\t\t" PU "\n", stats->total_packets_dropped);
2173 cprintf(listener, "unknown_types\t" PU "\n", stats->total_unknown_types);
2176 cprintf(listener, "timeouts\t" PU "\n", stats->total_timeouts);
2179 cprintf(listener, "last_packet\t%" PRId64 "\n", (int64_t) stats->last_packet);
2180 for (i = 0; i < 8; i++) {
2181 cprintf(listener, "elapsed.%s\t%u\n",
2182 elapsed_names[i], stats->elapsed[i]);
2189 #ifdef HAVE_PTHREAD_H
2190 static int command_stats_queue(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
2192 int array[RAD_LISTEN_MAX], pps[2];
2194 thread_pool_queue_stats(array, pps);
2196 cprintf(listener, "queue_len_internal\t" PU "\n", array[0]);
2197 cprintf(listener, "queue_len_proxy\t\t" PU "\n", array[1]);
2198 cprintf(listener, "queue_len_auth\t\t" PU "\n", array[2]);
2199 cprintf(listener, "queue_len_acct\t\t" PU "\n", array[3]);
2200 cprintf(listener, "queue_len_detail\t" PU "\n", array[4]);
2202 cprintf(listener, "queue_pps_in\t\t" PU "\n", pps[0]);
2203 cprintf(listener, "queue_pps_out\t\t" PU "\n", pps[1]);
2210 static int command_stats_memory(rad_listen_t *listener, int argc, char *argv[])
2213 if (!main_config.debug_memory || !main_config.memory_report) {
2214 cprintf(listener, "No memory debugging was enabled.\n");
2218 if (argc == 0) goto fail;
2220 if (strcmp(argv[0], "total") == 0) {
2221 cprintf(listener, "%zd\n", talloc_total_size(NULL));
2225 if (strcmp(argv[0], "blocks") == 0) {
2226 cprintf(listener, "%zd\n", talloc_total_blocks(NULL));
2230 if (strcmp(argv[0], "full") == 0) {
2231 cprintf(listener, "see stdout of the server for the full report.\n");
2232 fr_log_talloc_report(NULL);
2237 cprintf_error(listener, "Must use 'stats memory [blocks|full|total]'\n");
2243 static FR_NAME_NUMBER state_names[] = {
2244 { "unopened", STATE_UNOPENED },
2245 { "unlocked", STATE_UNLOCKED },
2246 { "header", STATE_HEADER },
2247 { "reading", STATE_READING },
2248 { "queued", STATE_QUEUED },
2249 { "running", STATE_RUNNING },
2250 { "no-reply", STATE_NO_REPLY },
2251 { "replied", STATE_REPLIED },
2256 static int command_stats_detail(rad_listen_t *listener, int argc, char *argv[])
2259 listen_detail_t *data, *needle;
2263 cprintf_error(listener, "Must specify <filename>\n");
2268 for (this = main_config.listen; this != NULL; this = this->next) {
2269 if (this->type != RAD_LISTEN_DETAIL) continue;
2271 needle = this->data;
2272 if (!strcmp(argv[0], needle->filename)) {
2279 cprintf_error(listener, "No detail file listener\n");
2283 cprintf(listener, "state\t%s\n",
2284 fr_int2str(state_names, data->state, "?"));
2286 if ((data->state == STATE_UNOPENED) ||
2287 (data->state == STATE_UNLOCKED)) {
2292 * Race conditions: file might not exist.
2294 if (stat(data->filename_work, &buf) < 0) {
2295 cprintf(listener, "packets\t0\n");
2296 cprintf(listener, "tries\t0\n");
2297 cprintf(listener, "offset\t0\n");
2298 cprintf(listener, "size\t0\n");
2302 cprintf(listener, "packets\t%d\n", data->packets);
2303 cprintf(listener, "tries\t%d\n", data->tries);
2304 cprintf(listener, "offset\t%u\n", (unsigned int) data->offset);
2305 cprintf(listener, "size\t%u\n", (unsigned int) buf.st_size);
2312 static int command_stats_home_server(rad_listen_t *listener, int argc, char *argv[])
2314 home_server_t *home;
2317 cprintf_error(listener, "Must specify [auth|acct|coa|disconnect] OR <ipaddr> <port>\n");
2322 if (strcmp(argv[0], "auth") == 0) {
2323 return command_print_stats(listener,
2324 &proxy_auth_stats, 1, 1);
2327 #ifdef WITH_ACCOUNTING
2328 if (strcmp(argv[0], "acct") == 0) {
2329 return command_print_stats(listener,
2330 &proxy_acct_stats, 0, 1);
2334 #ifdef WITH_ACCOUNTING
2335 if (strcmp(argv[0], "coa") == 0) {
2336 return command_print_stats(listener,
2337 &proxy_coa_stats, 0, 1);
2341 #ifdef WITH_ACCOUNTING
2342 if (strcmp(argv[0], "disconnect") == 0) {
2343 return command_print_stats(listener,
2344 &proxy_dsc_stats, 0, 1);
2348 cprintf_error(listener, "Should specify [auth|acct|coa|disconnect]\n");
2352 home = get_home_server(listener, argc, argv, NULL);
2353 if (!home) return 0;
2355 command_print_stats(listener, &home->stats,
2356 (home->type == HOME_TYPE_AUTH), 1);
2357 cprintf(listener, "outstanding\t%d\n", home->currently_outstanding);
2362 static int command_stats_client(rad_listen_t *listener, int argc, char *argv[])
2366 RADCLIENT *client, fake;
2369 cprintf_error(listener, "Must specify [auth/acct]\n");
2375 * Global statistics.
2377 fake.auth = radius_auth_stats;
2378 #ifdef WITH_ACCOUNTING
2379 fake.acct = radius_acct_stats;
2382 fake.coa = radius_coa_stats;
2383 fake.dsc = radius_dsc_stats;
2389 * Per-client statistics.
2391 client = get_client(listener, argc - 1, argv + 1);
2392 if (!client) return 0;
2395 if (strcmp(argv[0], "auth") == 0) {
2397 stats = &client->auth;
2399 } else if (strcmp(argv[0], "acct") == 0) {
2400 #ifdef WITH_ACCOUNTING
2402 stats = &client->acct;
2404 cprintf_error(listener, "This server was built without accounting support.\n");
2408 } else if (strcmp(argv[0], "coa") == 0) {
2411 stats = &client->coa;
2413 cprintf_error(listener, "This server was built without CoA support.\n");
2417 } else if (strcmp(argv[0], "disconnect") == 0) {
2420 stats = &client->dsc;
2422 cprintf_error(listener, "This server was built without CoA support.\n");
2427 cprintf_error(listener, "Unknown statistics type\n");
2432 * Global results for all client.
2435 #ifdef WITH_ACCOUNTING
2437 return command_print_stats(listener,
2438 &radius_acct_stats, auth, 0);
2441 return command_print_stats(listener, &radius_auth_stats, auth, 0);
2444 return command_print_stats(listener, stats, auth, 0);
2448 static int command_stats_socket(rad_listen_t *listener, int argc, char *argv[])
2453 sock = get_socket(listener, argc, argv, NULL);
2454 if (!sock) return 0;
2456 if (sock->type != RAD_LISTEN_AUTH) auth = false;
2458 return command_print_stats(listener, &sock->stats, auth, 0);
2460 #endif /* WITH_STATS */
2463 #ifdef WITH_DYNAMIC_CLIENTS
2464 static int command_add_client_file(rad_listen_t *listener, int argc, char *argv[])
2469 cprintf_error(listener, "<file> is required\n");
2474 * Read the file and generate the client.
2476 c = client_read(argv[0], false, false);
2478 cprintf_error(listener, "Unknown error reading client file.\n");
2482 if (!client_add(NULL, c)) {
2483 cprintf_error(listener, "Unknown error inserting new client.\n");
2492 static int command_del_client(rad_listen_t *listener, int argc, char *argv[])
2496 client = get_client(listener, argc, argv);
2497 if (!client) return 0;
2499 if (!client->dynamic) {
2500 cprintf_error(listener, "Client %s was not dynamically defined.\n", argv[0]);
2505 * DON'T delete it. Instead, mark it as "dead now". The
2506 * next time we receive a packet for the client, it will
2509 * If we don't receive a packet from it, the client
2510 * structure will stick around for a while. Oh well...
2512 client->lifetime = 1;
2518 static fr_command_table_t command_table_del_client[] = {
2519 { "ipaddr", FR_WRITE,
2520 "del client ipaddr <ipaddr> [udp|tcp] [listen <ipaddr> <port>] - Delete a dynamically created client",
2521 command_del_client, NULL },
2523 { NULL, 0, NULL, NULL, NULL }
2527 static fr_command_table_t command_table_del[] = {
2528 { "client", FR_WRITE,
2529 "del client <command> - Delete client configuration commands",
2530 NULL, command_table_del_client },
2532 { NULL, 0, NULL, NULL, NULL }
2536 static fr_command_table_t command_table_add_client[] = {
2538 "add client file <filename> - Add new client definition from <filename>",
2539 command_add_client_file, NULL },
2541 { NULL, 0, NULL, NULL, NULL }
2545 static fr_command_table_t command_table_add[] = {
2546 { "client", FR_WRITE,
2547 "add client <command> - Add client configuration commands",
2548 NULL, command_table_add_client },
2550 { NULL, 0, NULL, NULL, NULL }
2555 static fr_command_table_t command_table_set_home[] = {
2556 { "state", FR_WRITE,
2557 "set home_server state <ipaddr> <port> [udp|tcp] [alive|dead] - set state for given home server",
2558 command_set_home_server_state, NULL },
2560 { NULL, 0, NULL, NULL, NULL }
2564 static fr_command_table_t command_table_set_module[] = {
2565 { "config", FR_WRITE,
2566 "set module config <module> variable value - set configuration for <module>",
2567 command_set_module_config, NULL },
2569 { "status", FR_WRITE,
2570 "set module status <module> [alive|...] - set the module status to be alive (operating normally), or force a particular code (ok,fail, etc.)",
2571 command_set_module_status, NULL },
2573 { NULL, 0, NULL, NULL, NULL }
2577 static fr_command_table_t command_table_set[] = {
2578 { "module", FR_WRITE,
2579 "set module <command> - set module commands",
2580 NULL, command_table_set_module },
2582 { "home_server", FR_WRITE,
2583 "set home_server <command> - set home server commands",
2584 NULL, command_table_set_home },
2587 { NULL, 0, NULL, NULL, NULL }
2592 static fr_command_table_t command_table_stats[] = {
2593 { "client", FR_READ,
2594 "stats client [auth/acct] <ipaddr> [udp|tcp] [listen <ipaddr> <port>] "
2595 "- show statistics for given client, or for all clients (auth or acct)",
2596 command_stats_client, NULL },
2599 { "detail", FR_READ,
2600 "stats detail <filename> - show statistics for the given detail file",
2601 command_stats_detail, NULL },
2605 { "home_server", FR_READ,
2606 "stats home_server [<ipaddr>|auth|acct|coa|disconnect] <port> [udp|tcp] - show statistics for given home server (ipaddr and port), or for all home servers (auth or acct)",
2607 command_stats_home_server, NULL },
2610 #ifdef HAVE_PTHREAD_H
2612 "stats queue - show statistics for packet queues",
2613 command_stats_queue, NULL },
2616 { "socket", FR_READ,
2617 "stats socket <ipaddr> <port> [udp|tcp] "
2618 "- show statistics for given socket",
2619 command_stats_socket, NULL },
2622 { "memory", FR_READ,
2623 "stats memory [blocks|full|total] - show statistics on used memory",
2624 command_stats_memory, NULL },
2627 { NULL, 0, NULL, NULL, NULL }
2631 static fr_command_table_t command_table[] = {
2632 #ifdef WITH_DYNAMIC_CLIENTS
2633 { "add", FR_WRITE, NULL, NULL, command_table_add },
2635 { "debug", FR_WRITE,
2636 "debug <command> - debugging commands",
2637 NULL, command_table_debug },
2638 #ifdef WITH_DYNAMIC_CLIENTS
2639 { "del", FR_WRITE, NULL, NULL, command_table_del },
2642 "hup [module] - sends a HUP signal to the server, or optionally to one module",
2643 command_hup, NULL },
2644 { "inject", FR_WRITE,
2645 "inject <command> - commands to inject packets into a running server",
2646 NULL, command_table_inject },
2647 { "reconnect", FR_READ,
2648 "reconnect - reconnect to a running server",
2649 NULL, NULL }, /* just here for "help" */
2650 { "terminate", FR_WRITE,
2651 "terminate - terminates the server, and cause it to exit",
2652 command_terminate, NULL },
2653 { "set", FR_WRITE, NULL, NULL, command_table_set },
2654 { "show", FR_READ, NULL, NULL, command_table_show },
2656 { "stats", FR_READ, NULL, NULL, command_table_stats },
2659 { NULL, 0, NULL, NULL, NULL }
2663 static void command_socket_free(rad_listen_t *this)
2665 fr_command_socket_t *cmd = this->data;
2668 * If it's a TCP socket, don't do anything.
2670 if (cmd->magic != COMMAND_SOCKET_MAGIC) {
2674 if (!cmd->copy) return;
2680 * Parse the unix domain sockets.
2682 * FIXME: TCP + SSL, after RadSec is in.
2684 static int command_socket_parse_unix(CONF_SECTION *cs, rad_listen_t *this)
2686 fr_command_socket_t *sock;
2688 if (check_config) return 0;
2692 if (cf_section_parse(cs, sock, command_config) < 0) return -1;
2695 * Can't get uid or gid of connecting user, so can't do
2696 * peercred authentication.
2698 #ifndef HAVE_GETPEEREID
2699 if (sock->peercred && (sock->uid_name || sock->gid_name)) {
2700 ERROR("System does not support uid or gid authentication for sockets");
2705 sock->magic = COMMAND_SOCKET_MAGIC;
2707 if (sock->path) sock->copy = talloc_typed_strdup(sock, sock->path);
2709 if (sock->uid_name) {
2712 if (rad_getpwnam(cs, &pwd, sock->uid_name) < 0) {
2713 ERROR("Failed getting uid for %s: %s", sock->uid_name, fr_strerror());
2716 sock->uid = pwd->pw_uid;
2722 if (sock->gid_name) {
2723 if (rad_getgid(cs, &sock->gid, sock->gid_name) < 0) {
2724 ERROR("Failed getting gid for %s: %s", sock->gid_name, fr_strerror());
2731 if (!sock->mode_name) {
2732 sock->co.mode = FR_READ;
2734 sock->co.mode = fr_str2int(mode_names, sock->mode_name, 0);
2735 if (!sock->co.mode) {
2736 ERROR("Invalid mode name \"%s\"",
2742 if (sock->peercred) {
2743 this->fd = fr_server_domain_socket_peercred(sock->path, sock->uid, sock->gid);
2745 uid_t uid = sock->uid;
2746 gid_t gid = sock->gid;
2748 if (uid == ((uid_t)-1)) uid = 0;
2749 if (gid == ((gid_t)-1)) gid = 0;
2751 this->fd = fr_server_domain_socket_perm(sock->path, uid, gid);
2755 ERROR("Failed creating control socket \"%s\": %s", sock->path, fr_strerror());
2756 if (sock->copy) talloc_free(sock->copy);
2764 static int command_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
2767 CONF_PAIR const *cp;
2768 listen_socket_t *sock;
2770 cp = cf_pair_find(cs, "socket");
2771 if (cp) return command_socket_parse_unix(cs, this);
2773 rcode = common_socket_parse(cs, this);
2774 if (rcode < 0) return -1;
2779 "TLS is not supported for control sockets");
2785 if (sock->proto != IPPROTO_TCP) {
2787 "UDP is not supported for control sockets");
2794 static int command_socket_print(rad_listen_t const *this, char *buffer, size_t bufsize)
2796 fr_command_socket_t *sock = this->data;
2798 if (sock->magic != COMMAND_SOCKET_MAGIC) {
2799 return common_socket_print(this, buffer, bufsize);
2802 snprintf(buffer, bufsize, "command file %s", sock->path);
2808 * String split routine. Splits an input string IN PLACE
2809 * into pieces, based on spaces.
2811 static int str2argvX(char *str, char **argv, int max_argc)
2816 if (argc >= max_argc) return argc;
2819 * Chop out comments early.
2826 while ((*str == ' ') ||
2829 (*str == '\n')) *(str++) = '\0';
2831 if (!*str) return argc;
2835 if ((*str == '\'') || (*str == '"')) {
2848 * Handle \" and nothing else.
2863 (*str != '\n')) str++;
2869 static void print_help(rad_listen_t *listener, int argc, char *argv[],
2870 fr_command_table_t *table, int recursive)
2874 /* this should never happen, but if it does then just return gracefully */
2877 for (i = 0; table[i].command != NULL; i++) {
2879 if (strcmp(table[i].command, argv[0]) == 0) {
2880 if (table[i].table) {
2881 print_help(listener, argc - 1, argv + 1, table[i].table, recursive);
2883 if (table[i].help) {
2884 cprintf(listener, "%s\n", table[i].help);
2893 if (table[i].help) {
2894 cprintf(listener, "%s\n",
2897 cprintf(listener, "%s <command> - do sub-command of %s\n",
2898 table[i].command, table[i].command);
2901 if (recursive && table[i].table) {
2902 print_help(listener, 0, NULL, table[i].table, recursive);
2907 #define MAX_ARGV (16)
2910 * Check if an incoming request is "ok"
2912 * It takes packets, not requests. It sees if the packet looks
2913 * OK. If so, it does a number of sanity checks on it.
2915 static int command_domain_recv_co(rad_listen_t *listener, fr_cs_buffer_t *co)
2921 fr_channel_type_t channel;
2922 char *my_argv[MAX_ARGV], **argv;
2923 fr_command_table_t *table;
2926 r = fr_channel_drain(listener->fd, &channel, co->buffer, sizeof(co->buffer) - 1, &command, co->offset);
2930 command_close_socket(listener);
2935 * We need more data. Go read it.
2937 if (channel == FR_CHANNEL_WANT_MORE) {
2944 DEBUG("radmin> %s", command);
2946 argc = str2argvX((char *) command, my_argv, MAX_ARGV);
2947 if (argc == 0) goto do_next; /* empty strings are OK */
2950 cprintf_error(listener, "Failed parsing command.\n");
2956 for (len = 0; len <= co->offset; len++) {
2957 if (command[len] < 0x20) {
2958 command[len] = '\0';
2964 * Hard-code exit && quit.
2966 if ((strcmp(argv[0], "exit") == 0) ||
2967 (strcmp(argv[0], "quit") == 0)) goto do_close;
2969 table = command_table;
2972 for (i = 0; table[i].command != NULL; i++) {
2973 if (strcmp(table[i].command, argv[0]) == 0) {
2975 * Check permissions.
2977 if (((co->mode & FR_WRITE) == 0) &&
2978 ((table[i].mode & FR_WRITE) != 0)) {
2979 cprintf_error(listener, "You do not have write permission. See \"mode = rw\" in the \"listen\" section for this socket.\n");
2983 if (table[i].table) {
2985 * This is the last argument, but
2986 * there's a sub-table. Print help.
2990 table = table[i].table;
2996 table = table[i].table;
3000 if ((argc == 2) && (strcmp(argv[1], "?") == 0)) goto do_help;
3002 if (!table[i].func) {
3003 cprintf_error(listener, "Invalid command\n");
3007 status = table[i].func(listener, argc - 1, argv + 1);
3016 if ((strcmp(argv[0], "help") == 0) ||
3017 (strcmp(argv[0], "?") == 0)) {
3021 if ((argc > 1) && (strcmp(argv[1], "-r") == 0)) {
3029 print_help(listener, argc - 1, argv + 1, table, recursive);
3033 cprintf_error(listener, "Unknown command \"%s\"\n",
3038 r = fr_channel_write(listener->fd, FR_CHANNEL_CMD_STATUS, &status, sizeof(status));
3039 if (r <= 0) goto do_close;
3046 * Write 32-bit magic number && version information.
3048 static int command_write_magic(int newfd,
3052 listen_socket_t *sock
3057 fr_channel_type_t channel;
3060 r = fr_channel_read(newfd, &channel, buffer, 8);
3063 ERROR("Cannot talk to socket: %s",
3064 fr_syserror(errno));
3068 magic = htonl(0xf7eead16);
3069 if ((r != 8) || (channel != FR_CHANNEL_INIT_ACK) ||
3070 (memcmp(&magic, &buffer, sizeof(magic)) != 0)) {
3071 ERROR("Incompatible versions");
3075 r = fr_channel_write(newfd, FR_CHANNEL_INIT_ACK, buffer, 8);
3076 if (r <= 0) goto do_close;
3080 * Write an initial challenge
3086 co = talloc_zero(sock, fr_cs_buffer_t);
3087 sock->packet = (void *) co;
3089 for (i = 0; i < 16; i++) {
3090 co->buffer[i] = fr_rand();
3093 r = fr_channel_write(newfd, FR_CHANNEL_AUTH_CHALLENGE, co->buffer, 16);
3094 if (r <= 0) goto do_close;
3102 static int command_tcp_recv(rad_listen_t *this)
3105 listen_socket_t *sock = this->data;
3106 fr_cs_buffer_t *co = (void *) sock->packet;
3107 fr_channel_type_t channel;
3111 command_close_socket(this);
3116 uint8_t expected[16];
3118 r = fr_channel_read(this->fd, &channel, co->buffer, 16);
3119 if ((r != 16) || (channel != FR_CHANNEL_AUTH_RESPONSE)) {
3123 fr_hmac_md5(expected, (void const *) sock->client->secret,
3124 strlen(sock->client->secret),
3125 (uint8_t *) co->buffer, 16);
3127 if (rad_digest_cmp(expected,
3128 (uint8_t *) co->buffer + 16, 16 != 0)) {
3129 ERROR("radmin failed challenge: Closing socket");
3137 return command_domain_recv_co(this, co);
3142 * Should never be called. The functions should just call write().
3144 static int command_tcp_send(UNUSED rad_listen_t *listener, UNUSED REQUEST *request)
3150 static int command_domain_recv(rad_listen_t *listener)
3152 fr_command_socket_t *sock = listener->data;
3154 return command_domain_recv_co(listener, &sock->co);
3157 static int command_domain_accept(rad_listen_t *listener)
3162 struct sockaddr_storage src;
3163 fr_command_socket_t *sock = listener->data;
3165 salen = sizeof(src);
3167 DEBUG2(" ... new connection request on command socket");
3169 newfd = accept(listener->fd, (struct sockaddr *) &src, &salen);
3172 * Non-blocking sockets must handle this.
3174 if (errno == EWOULDBLOCK) {
3178 DEBUG2(" ... failed to accept connection");
3182 #ifdef HAVE_GETPEEREID
3184 * Perform user authentication.
3186 if (sock->peercred && (sock->uid_name || sock->gid_name)) {
3190 if (getpeereid(newfd, &uid, &gid) < 0) {
3191 ERROR("Failed getting peer credentials for %s: %s",
3192 sock->path, fr_syserror(errno));
3198 * Only do UID checking if the caller is
3199 * non-root. The superuser can do anything, so
3200 * we might as well let them.
3204 * Allow entry if UID or GID matches.
3206 if (sock->uid_name && (sock->uid == uid)) break;
3207 if (sock->gid_name && (sock->gid == gid)) break;
3209 if (sock->uid_name && (sock->uid != uid)) {
3210 ERROR("Unauthorized connection to %s from uid %ld",
3212 sock->path, (long int) uid);
3217 if (sock->gid_name && (sock->gid != gid)) {
3218 ERROR("Unauthorized connection to %s from gid %ld",
3219 sock->path, (long int) gid);
3228 if (command_write_magic(newfd, NULL) < 0) {
3234 * Add the new listener.
3236 this = listen_alloc(listener, listener->type);
3237 if (!this) return 0;
3240 * Copy everything, including the pointer to the socket
3244 memcpy(this, listener, sizeof(*this));
3245 this->status = RAD_LISTEN_STATUS_INIT;
3247 this->data = sock; /* fix it back */
3249 sock->magic = COMMAND_SOCKET_MAGIC;
3250 sock->user[0] = '\0';
3251 sock->path = ((fr_command_socket_t *) listener->data)->path;
3252 sock->co.offset = 0;
3253 sock->co.mode = ((fr_command_socket_t *) listener->data)->co.mode;
3256 this->recv = command_domain_recv;
3259 * Tell the event loop that we have a new FD
3261 radius_update_listener(this);
3268 * Send an authentication response packet
3270 static int command_domain_send(UNUSED rad_listen_t *listener,
3271 UNUSED REQUEST *request)
3277 static int command_socket_encode(UNUSED rad_listen_t *listener,
3278 UNUSED REQUEST *request)
3284 static int command_socket_decode(UNUSED rad_listen_t *listener,
3285 UNUSED REQUEST *request)
3290 #endif /* WITH_COMMAND_SOCKET */