summaryrefslogtreecommitdiff
path: root/utils/rpcscan.c
diff options
context:
space:
mode:
authorJustus Winter <4winter@informatik.uni-hamburg.de>2015-05-29 16:37:00 +0200
committerJustus Winter <4winter@informatik.uni-hamburg.de>2015-06-30 23:23:52 +0200
commit64f99d303557e991149bd2d4e6085e9f46efb3ef (patch)
tree12e0ce5fddc738abceabc66ad6494f77a62262d6 /utils/rpcscan.c
parent74fb5209f06edcf0b52bdf44ba805cb5f6cd41c8 (diff)
utils/rpcscan: new utility
* utils/rpcscan.c: New file. * utils/Makefile: Add `rpcscan'.
Diffstat (limited to 'utils/rpcscan.c')
-rw-r--r--utils/rpcscan.c404
1 files changed, 404 insertions, 0 deletions
diff --git a/utils/rpcscan.c b/utils/rpcscan.c
new file mode 100644
index 00000000..2270ea8a
--- /dev/null
+++ b/utils/rpcscan.c
@@ -0,0 +1,404 @@
+/* An RPC scanner for the Hurd.
+
+ Copyright (C) 2015 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <mach.h>
+#include <hurd.h>
+#include <hurd/ihash.h>
+#include <mach/message.h>
+#include <unistd.h>
+#include <argp.h>
+#include <error.h>
+#include <string.h>
+#include <version.h>
+#include <sys/wait.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <argz.h>
+
+#include "msgids.h"
+
+/* Send messages with arbitrary message ids. */
+struct Request {
+ mach_msg_header_t Head;
+} RequestTemplate;
+
+struct Reply {
+ mach_msg_header_t Head;
+ mach_msg_type_t RetCodeType;
+ kern_return_t RetCode;
+ char Body[4096];
+};
+
+union {
+ struct Request Request;
+ struct Reply Reply;
+} Message;
+
+error_t
+setup (mach_port_t request, mach_msg_type_name_t requestType)
+{
+ RequestTemplate.Head.msgh_remote_port = request;
+ if (! MACH_PORT_VALID (RequestTemplate.Head.msgh_local_port))
+ RequestTemplate.Head.msgh_local_port = mach_reply_port ();
+ RequestTemplate.Head.msgh_bits =
+ MACH_MSGH_BITS (requestType, MACH_MSG_TYPE_MAKE_SEND_ONCE);
+ return 0;
+}
+
+error_t
+send (mach_msg_id_t msgid)
+{
+ error_t err;
+ Message.Request = RequestTemplate;
+ Message.Request.Head.msgh_id = msgid;
+ err = mach_msg (&Message.Request.Head,
+ MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE,
+ sizeof Message.Request,
+ sizeof Message.Reply,
+ Message.Request.Head.msgh_local_port,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ if (err)
+ return err;
+
+ /* XXX typecheck */
+ return Message.Reply.RetCode;
+}
+
+typedef error_t (*setup_function_t) ();
+setup_function_t setup_target;
+void *setup_argument;
+
+error_t
+setup_task_target (void)
+{
+ error_t err;
+ static task_t task;
+ static mach_msg_type_name_t taskType = MACH_MSG_TYPE_COPY_SEND;
+
+ if (MACH_PORT_VALID (task))
+ {
+ task_terminate (task);
+ mach_port_deallocate (mach_task_self (), task);
+ }
+
+ err = task_create (mach_task_self (), 0, &task);
+ if (err)
+ return err;
+
+ return setup (task, taskType);
+}
+
+error_t
+setup_thread_target (void)
+{
+ error_t err;
+ static task_t task;
+ static thread_t thread;
+
+ if (MACH_PORT_VALID (thread))
+ {
+ thread_terminate (thread);
+ mach_port_deallocate (mach_task_self (), thread);
+ }
+
+ if (MACH_PORT_VALID (task))
+ {
+ task_terminate (task);
+ mach_port_deallocate (mach_task_self (), task);
+ }
+
+ err = task_create (mach_task_self (), 0, &task);
+ if (err)
+ return err;
+
+ err = thread_create (task, &thread);
+ if (err)
+ return err;
+
+ return setup (thread, MACH_MSG_TYPE_COPY_SEND);
+}
+
+error_t
+setup_proc_target (void)
+{
+ error_t err;
+ static task_t task;
+ static process_t proc, target;
+ static mach_msg_type_name_t targetType = MACH_MSG_TYPE_COPY_SEND;
+
+ if (! MACH_PORT_VALID (proc))
+ proc = getproc ();
+ if (MACH_PORT_VALID (task))
+ mach_port_deallocate (mach_task_self (), task);
+ if (MACH_PORT_VALID (target))
+ mach_port_deallocate (mach_task_self (), target);
+
+ err = task_create (mach_task_self (), 0, &task);
+ if (err)
+ return err;
+
+ err = proc_task2proc (proc, task, &target);
+ if (err)
+ return err;
+
+ return setup (target, targetType);
+}
+
+error_t
+setup_auth_target (void)
+{
+ static auth_t auth;
+ static mach_msg_type_name_t authType = MACH_MSG_TYPE_COPY_SEND;
+
+ if (MACH_PORT_VALID (auth))
+ mach_port_deallocate (mach_task_self (), auth);
+
+ auth = getauth ();
+ if (! MACH_PORT_VALID (auth))
+ return errno;
+
+ return setup (auth, authType);
+}
+
+error_t
+setup_hurd_target (void)
+{
+ char *name = (char *) setup_argument;
+ mach_port_t request;
+ mach_msg_type_name_t requestType;
+
+ request = file_name_lookup (name, 0, 0);
+ if (! MACH_PORT_VALID (request))
+ return errno;
+ requestType = MACH_MSG_TYPE_COPY_SEND;
+
+ return setup (request, requestType);
+}
+
+task_t extract_target_task;
+mach_port_t extract_target_port;
+mach_msg_type_name_t extract_target_type;
+
+error_t
+setup_extract_target (void)
+{
+ error_t err;
+ char *name = (char *) setup_argument;
+ mach_port_t request;
+ mach_msg_type_name_t requestType;
+
+ err = mach_port_extract_right (extract_target_task,
+ extract_target_port,
+ extract_target_type,
+ &request,
+ &requestType);
+ if (err)
+ error (1, err, "mach_port_extract_right");
+ if (err)
+ return err;
+ requestType = MACH_MSG_TYPE_COPY_SEND;
+ return setup (request, requestType);
+}
+
+const char *argp_program_version = STANDARD_HURD_VERSION (rpcscan);
+
+char **cmd_argv;
+int verbose;
+int numeric;
+int subsystem;
+
+#define OPT_TARGET_TASK -1
+#define OPT_TARGET_THREAD -2
+#define OPT_TARGET_PROC -3
+#define OPT_TARGET_AUTH -4
+#define OPT_TARGET_EXTRACT -5
+
+static const struct argp_option options[] =
+{
+ {NULL, 0, NULL, OPTION_DOC, "Target selection", 1},
+ {"task", OPT_TARGET_TASK, NULL, 0, "target a task port", 1},
+ {"thread", OPT_TARGET_THREAD, NULL, 0, "target a thread port", 1},
+ {"proc", OPT_TARGET_PROC, NULL, 0, "target a proc port", 1},
+ {"auth", OPT_TARGET_AUTH, NULL, 0, "target an auth port", 1},
+ {"extract", OPT_TARGET_EXTRACT, "PID.PORT", 0, "target port PORT of PID", 1},
+
+ {NULL, 0, NULL, OPTION_DOC, "Options", 2},
+ {"verbose", 'v', NULL, 0, "be verbose", 2},
+ {"numeric", 'n', NULL, 0, "show numeric message ids", 2},
+ {"subsystem", 's', NULL, 0, "show subsystem names", 2},
+ {0}
+};
+
+static const char args_doc[] = "[FILE]";
+static const char doc[] = "Scan a given Mach port.";
+
+/* Parse our options... */
+error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ error_t err;
+ switch (key)
+ {
+ case 'v':
+ verbose = 1;
+ break;
+
+ case 'n':
+ numeric = 1;
+ break;
+
+ case 's':
+ subsystem = 1;
+ break;
+
+#define SELECT_TARGET(target) \
+ if (setup_target) \
+ argp_error (state, "Multiple targets specified."); \
+ setup_target = target;
+
+ case OPT_TARGET_TASK:
+ SELECT_TARGET (setup_task_target);
+ break;
+
+ case OPT_TARGET_THREAD:
+ SELECT_TARGET (setup_thread_target);
+ break;
+
+ case OPT_TARGET_PROC:
+ SELECT_TARGET (setup_proc_target);
+ break;
+
+ case OPT_TARGET_AUTH:
+ SELECT_TARGET (setup_auth_target);
+ break;
+
+ case OPT_TARGET_EXTRACT:;
+ process_t proc;
+ pid_t pid;
+ char *end;
+
+ pid = strtol (arg, &end, 10);
+ if (arg == end || *end != '.')
+ argp_error (state, "Expected format PID.PORT, got `%s'.", arg);
+
+ arg = end + 1;
+ extract_target_port = strtol (arg, &end, 10);
+ if (arg == end || *end != '\0')
+ argp_error (state, "Expected format PORT, got `%s'.", arg);
+
+ proc = getproc ();
+ err = proc_pid2task (proc, pid, &extract_target_task);
+ if (err)
+ argp_failure (state, 1, err,
+ "Could not get task of process %d", pid);
+
+ extract_target_type = MACH_MSG_TYPE_COPY_SEND; /* XXX */
+ SELECT_TARGET (setup_extract_target);
+ break;
+
+ case ARGP_KEY_ARG:
+ SELECT_TARGET (setup_hurd_target);
+ setup_argument = arg;
+ break;
+#undef SELECT_TARGET
+
+ case ARGP_KEY_NO_ARGS:
+ if (setup_target == NULL)
+ argp_usage (state);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+const struct argp_child children[] =
+ {
+ { .argp=&msgid_argp, },
+ { 0 }
+ };
+
+const struct argp argp = { options, parse_opt, args_doc, doc, &children };
+
+void
+format_msgid (char *buf, size_t len, mach_msg_id_t id)
+{
+ if (numeric)
+ numeric:
+ snprintf (buf, len, "%d", id);
+ else
+ {
+ const struct msgid_info *info;
+ info = msgid_info (id);
+ if (info == NULL)
+ goto numeric;
+
+ if (subsystem)
+ snprintf (buf, len, "%s/%s", info->subsystem, info->name);
+ else
+ snprintf (buf, len, "%s", info->name);
+ }
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_msg_id_t msgid;
+
+ /* Parse our arguments. */
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+ err = setup_target ();
+ if (err)
+ /* Initial setup failed. Bail out. */
+ error (1, err, "%s",
+ setup_target == setup_hurd_target? (char *) setup_argument: "setup");
+
+ for (msgid = 0; msgid < 500000; msgid++)
+ {
+ err = send (msgid);
+ switch (err)
+ {
+ case MACH_SEND_INVALID_DEST:
+ err = setup_target ();
+ if (err)
+ error (1, err, "setup");
+ msgid--; /* redo */
+ continue;
+
+ case MIG_BAD_ID:
+ /* do nothing */
+ break;
+
+ default:;
+ char buf[80];
+ format_msgid (buf, sizeof buf, msgid);
+ if (verbose)
+ error (0, err, "%s", buf);
+ else
+ fprintf (stdout, "%s\n", buf);
+ }
+ }
+
+ return EXIT_SUCCESS;
+}