From 566a246f2c2881f1598d376032f9f73427790feb Mon Sep 17 00:00:00 2001
From: Justus Winter <4winter@informatik.uni-hamburg.de>
Date: Fri, 29 May 2015 16:37:00 +0200
Subject: [PATCH hurd 2/2] utils/rpcscan: new utility
* utils/rpcscan.c: New file.
* utils/Makefile: Add `rpcscan'.
---
utils/Makefile | 6 +-
utils/rpcscan.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 407 insertions(+), 3 deletions(-)
create mode 100644 utils/rpcscan.c
diff --git a/utils/Makefile b/utils/Makefile
index 9ad2a40..955789b 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -22,7 +22,7 @@ targets = shd ps settrans showtrans syncfs fsysopts \
storeinfo login w uptime ids loginpr sush vmstat portinfo \
devprobe vminfo addauth rmauth unsu setauth ftpcp ftpdir storecat \
storeread msgport rpctrace mount gcore fakeauth fakeroot remap \
- umount nullauth
+ umount nullauth rpcscan
special-targets = loginpr sush uptime fakeroot remap
SRCS = shd.c ps.c settrans.c syncfs.c showtrans.c addauth.c rmauth.c \
@@ -31,7 +31,7 @@ SRCS = shd.c ps.c settrans.c syncfs.c showtrans.c addauth.c rmauth.c \
parse.c frobauth.c frobauth-mod.c setauth.c pids.c nonsugid.c \
unsu.c ftpcp.c ftpdir.c storeread.c storecat.c msgport.c \
rpctrace.c mount.c gcore.c fakeauth.c fakeroot.sh remap.sh \
- nullauth.c match-options.c msgids.c
+ nullauth.c match-options.c msgids.c rpcscan.c
OBJS = $(filter-out %.sh,$(SRCS:.c=.o))
HURDLIBS = ps ihash store fshelp ports ftpconn shouldbeinlibc
@@ -68,7 +68,7 @@ ps w ids settrans syncfs showtrans fsysopts storeinfo login vmstat portinfo \
$(filter-out $(special-targets), $(targets)): %: %.o
rpctrace: ../libports/libports.a
-rpctrace: msgids.o \
+rpctrace rpcscan: msgids.o \
../libihash/libihash.a
msgids-CPPFLAGS = -DDATADIR=\"${datadir}\"
diff --git a/utils/rpcscan.c b/utils/rpcscan.c
new file mode 100644
index 0000000..e32c016
--- /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 . */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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, char **envp)
+{
+ 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;
+}
--
2.1.4