From 3d0e6b3835379d545189563ce25ffe37ed340703 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 23 Nov 2003 13:44:19 +0000 Subject: added a tool called 'ndrdump' that allows you to dump NDR data according to the current IDL taking the data from a file. In combination with a little hack to ethereal to extract data this is a quite powerful IDL development tool. (This used to be commit 229a325c3cf0d4dc1e910ed32e1d7391040aeba1) --- source4/Makefile.in | 7 ++ source4/build/pidl/header.pm | 2 + source4/build/pidl/parser.pm | 42 +++++++++- source4/librpc/ndr/ndr.c | 2 +- source4/librpc/rpc/dcerpc.h | 17 ++++ source4/utils/ndrdump.c | 179 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 246 insertions(+), 3 deletions(-) create mode 100644 source4/utils/ndrdump.c diff --git a/source4/Makefile.in b/source4/Makefile.in index eec2447f5a..2484b80a0f 100644 --- a/source4/Makefile.in +++ b/source4/Makefile.in @@ -458,6 +458,9 @@ SMBTORTURE_OBJ1 = torture/torture.o torture/torture_util.o torture/nbio.o tortur SMBTORTURE_OBJ = $(SMBTORTURE_OBJ1) \ $(LIBSMB_OBJ) $(LIBDFS_OBJ) $(PARAM_OBJ) $(LIB_OBJ) +NDRDUMP_OBJ = utils/ndrdump.o utils/rewrite.o \ + $(LIBSMB_OBJ) $(LIBDFS_OBJ) $(PARAM_OBJ) $(LIB_OBJ) + MASKTEST_OBJ = torture/masktest.o $(LIBSMB_OBJ) $(PARAM_OBJ) \ $(LIB_OBJ) libcli/raw/clirewrite.o @@ -795,6 +798,10 @@ bin/smbtorture@EXEEXT@: $(SMBTORTURE_OBJ) bin/.dummy @echo Linking $@ @$(CC) $(FLAGS) -o $@ $(SMBTORTURE_OBJ) $(LDFLAGS) $(LIBS) +bin/ndrdump@EXEEXT@: $(NDRDUMP_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(NDRDUMP_OBJ) $(LDFLAGS) $(LIBS) + bin/gentest@EXEEXT@: $(GENTEST_OBJ) bin/.dummy @echo Linking $@ @$(CC) $(FLAGS) -o $@ $(GENTEST_OBJ) $(LDFLAGS) $(LIBS) diff --git a/source4/build/pidl/header.pm b/source4/build/pidl/header.pm index d6584a05ad..5a75e4c9b4 100644 --- a/source4/build/pidl/header.pm +++ b/source4/build/pidl/header.pm @@ -240,6 +240,8 @@ sub HeaderInterface($) $res .= "#define DCERPC_$name\_NAME \"$interface->{NAME}\"\n\n"; } + $res .= "extern struct dcerpc_interface_table dcerpc_table_$interface->{NAME};\n\n"; + foreach my $d (@{$data}) { if ($d->{TYPE} eq "FUNCTION") { my $u_name = uc $d->{NAME}; diff --git a/source4/build/pidl/parser.pm b/source4/build/pidl/parser.pm index 59ff73b8e6..b92729a80a 100644 --- a/source4/build/pidl/parser.pm +++ b/source4/build/pidl/parser.pm @@ -406,7 +406,12 @@ sub ParseElementPullSwitch($$$$) pidl "\tif (($ndr_flags) & NDR_SCALARS) {\n"; pidl "\t\t $e2->{TYPE} _level;\n"; pidl "\t\tNDR_CHECK(ndr_pull_$e2->{TYPE}(ndr, &_level));\n"; - pidl "\t\tif (_level != $switch_var) return ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value %u in $e->{NAME}\");\n"; + if ($switch_var =~ /r->in/) { + pidl "\t\tif (!(ndr->flags & LIBNDR_FLAG_REF_ALLOC) && _level != $switch_var) {\n"; + } else { + pidl "\t\tif (_level != $switch_var) {\n"; + } + pidl "\t\t\treturn ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value %u in $e->{NAME}\");\t\t} else { $switch_var = _level; }\n"; pidl "\t}\n"; } @@ -1210,6 +1215,37 @@ sub ParseFunctionPull($) pidl "\n\treturn NT_STATUS_OK;\n}\n\n"; } +##################################################################### +# produce a function call table +sub FunctionTable($) +{ + my($interface) = shift; + my($data) = $interface->{DATA}; + my $count = 0; + + foreach my $d (@{$data}) { + if ($d->{TYPE} eq "FUNCTION") { $count++; } + } + + + pidl "static const struct dcerpc_interface_call calls[] = {\n"; + foreach my $d (@{$data}) { + if ($d->{TYPE} eq "FUNCTION") { + pidl "\t{\n"; + pidl "\t\t\"$d->{NAME}\",\n"; + pidl "\t\tsizeof(struct $d->{NAME}),\n"; + pidl "\t\t(ndr_push_flags_fn_t) ndr_push_$d->{NAME},\n"; + pidl "\t\t(ndr_pull_flags_fn_t) ndr_pull_$d->{NAME},\n"; + pidl "\t\t(ndr_print_function_t) ndr_print_$d->{NAME}\n"; + pidl "\t},\n"; + } + } + pidl "\t{ NULL, 0, NULL, NULL }\n};\n\n"; + + pidl "\nstruct dcerpc_interface_table dcerpc_table_$interface->{NAME} = {\"$interface->{NAME}\", $count,calls};\n\n"; +} + + ##################################################################### # parse the interface definitions sub ParseInterface($) @@ -1247,6 +1283,9 @@ sub ParseInterface($) ParseFunctionPrint($d); } } + + FunctionTable($interface); + } sub NeededFunction($) @@ -1310,7 +1349,6 @@ sub BuildNeeded($) } } - ##################################################################### # parse a parsed IDL structure back into an IDL file sub Parse($$) diff --git a/source4/librpc/ndr/ndr.c b/source4/librpc/ndr/ndr.c index bce4759d8a..00f8eaed46 100644 --- a/source4/librpc/ndr/ndr.c +++ b/source4/librpc/ndr/ndr.c @@ -309,7 +309,7 @@ void ndr_print_array(struct ndr_print *ndr, const char *name, void *base, -static void ndr_print_debug_helper(struct ndr_print *ndr, const char *format, ...) +void ndr_print_debug_helper(struct ndr_print *ndr, const char *format, ...) { va_list ap; char *s = NULL; diff --git a/source4/librpc/rpc/dcerpc.h b/source4/librpc/rpc/dcerpc.h index ec6189302a..6ba0f8429a 100644 --- a/source4/librpc/rpc/dcerpc.h +++ b/source4/librpc/rpc/dcerpc.h @@ -48,3 +48,20 @@ struct dcerpc_pipe { #define DCERPC_DEBUG_VALIDATE_IN 4 #define DCERPC_DEBUG_VALIDATE_OUT 8 #define DCERPC_DEBUG_VALIDATE_BOTH (DCERPC_DEBUG_VALIDATE_IN | DCERPC_DEBUG_VALIDATE_OUT) + +/* + this is used to find pointers to calls +*/ +struct dcerpc_interface_call { + const char *name; + size_t struct_size; + NTSTATUS (*ndr_push)(struct ndr_push *, int , void *); + NTSTATUS (*ndr_pull)(struct ndr_pull *, int , void *); + void (*ndr_print)(struct ndr_print *, const char *, int, void *); +}; + +struct dcerpc_interface_table { + const char *name; + uint32 num_calls; + const struct dcerpc_interface_call *calls; +}; diff --git a/source4/utils/ndrdump.c b/source4/utils/ndrdump.c new file mode 100644 index 0000000000..c81a9948f3 --- /dev/null +++ b/source4/utils/ndrdump.c @@ -0,0 +1,179 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Tridgell 2003 + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +struct dcerpc_interface_table *pipes[] = { + &dcerpc_table_samr, + &dcerpc_table_lsarpc, + &dcerpc_table_netdfs, + NULL +}; + +static struct dcerpc_interface_table *find_pipe(const char *pipe_name) +{ + int i; + for (i=0;pipes[i];i++) { + if (strcmp(pipes[i]->name, pipe_name) == 0) { + break; + } + } + if (!pipes[i]) { + printf("pipe '%s' not in table\n", pipe_name); + exit(1); + } + return pipes[i]; +} + +static const struct dcerpc_interface_call *find_function( + const struct dcerpc_interface_table *p, + const char *function) +{ + int i; + for (i=0;inum_calls;i++) { + if (strcmp(p->calls[i].name, function) == 0) { + break; + } + } + if (i == p->num_calls) { + printf("Function '%s' not found\n", function); + exit(1); + } + return &p->calls[i]; +} + +static void usage(void) +{ + printf("Usage: ndrdump \n"); +} + + +static void show_pipes(void) +{ + int i; + usage(); + printf("\nYou must specify a pipe\n"); + printf("known pipes are:\n"); + for (i=0;pipes[i];i++) { + printf("\t%s\n", pipes[i]->name); + } + exit(1); +} + +static void show_functions(const struct dcerpc_interface_table *p) +{ + int i; + usage(); + printf("\nYou must specify a function\n"); + printf("known functions on '%s' are:\n", p->name); + for (i=0;inum_calls;i++) { + printf("\t0x%02x (%2d) %s\n", i, i, p->calls[i].name); + } + exit(1); +} + +int main(int argc, char *argv[]) +{ + const struct dcerpc_interface_table *p; + const struct dcerpc_interface_call *f; + const char *pipe_name, *function, *inout, *filename; + char *data; + size_t size; + DATA_BLOB blob; + struct ndr_pull *ndr; + TALLOC_CTX *mem_ctx; + int flags; + NTSTATUS status; + void *st; + struct ndr_print pr; + + DEBUGLEVEL = 10; + + setup_logging("smbtorture", DEBUG_STDOUT); + + if (argc < 2) { + show_pipes(); + exit(1); + } + + pipe_name = argv[1]; + + p = find_pipe(pipe_name); + + if (argc < 5) { + show_functions(p); + exit(1); + } + + function = argv[2]; + inout = argv[3]; + filename = argv[4]; + + if (strcmp(inout, "in") == 0 || + strcmp(inout, "request") == 0) { + flags = NDR_IN; + } else if (strcmp(inout, "out") == 0 || + strcmp(inout, "response") == 0) { + flags = NDR_OUT; + } else { + printf("Bad inout value '%s'\n", inout); + exit(1); + } + + f = find_function(p, function); + + data = file_load(filename, &size); + if (!data) { + perror(filename); + exit(1); + } + + blob.data = data; + blob.length = size; + + mem_ctx = talloc_init("ndrdump"); + + ndr = ndr_pull_init_blob(&blob, mem_ctx); + + st = talloc_zero(mem_ctx, f->struct_size); + if (!st) { + printf("Unable to allocate %d bytes\n", f->struct_size); + exit(1); + } + + if (flags == NDR_OUT) { + ndr->flags |= LIBNDR_FLAG_REF_ALLOC; + } + + status = f->ndr_pull(ndr, flags, st); + + printf("pull returned %s\n", nt_errstr(status)); + + if (ndr->offset != ndr->data_size) { + printf("WARNING! %d unread bytes\n", ndr->data_size - ndr->offset); + } + + pr.mem_ctx = mem_ctx; + pr.print = ndr_print_debug_helper; + pr.depth = 1; + f->ndr_print(&pr, function, flags, st); + + return 0; +} -- cgit