r4469: Version n + 1 of the pidl ethereal parser generator. This version is
authorTim Potter <tpot@samba.org>
Sun, 2 Jan 2005 00:00:43 +0000 (00:00 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:07:54 +0000 (13:07 -0500)
based on the idea of manipulating the .c and .h files generated by
parser.pm with perl regexps and glueing it all together to make an
ethereal plugin.

I thought this was a pretty crazy idea to start off with but it has
turned out to be not as complicated as I thought and has the huge advantage
of not duplicating any of the difficult code in parser.pm.
(This used to be commit 7007522f83740f41f9a47f5ad5942ea46320d405)

source4/build/pidl/eparser.pm
source4/build/pidl/pidl.pl

index a6f44b5b14a4d4df9430caeed987fbab394fb72a..09e2855e231ad2db0a1e603ca0dd0ec662f31c29 100644 (file)
@@ -689,47 +689,17 @@ sub ParseFunctionPull($)
        my($fn) = shift;
        my $static = fn_prefix($fn);
 
-       pidl "/*\n\n";
-       pidl IdlDump::DumpFunction($fn);
-       pidl "*/\n\n";
-
        # request function
        pidl "int $fn->{NAME}_rqst(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, guint8 *drep)\n{\n";
 
-       pidl "\tstruct ndr_pull *ndr = ndr_pull_init(tvb, offset, pinfo, drep);\n";
-       pidl "\tstruct $fn->{NAME} *r = (struct $fn->{NAME} *)g_malloc(sizeof(struct $fn->{NAME}));\n\n";
-
-       # declare any internal pointers we need
-       foreach my $e (@{$fn->{DATA}}) {
-               if (util::need_wire_pointer($e) &&
-                   util::has_property($e, "in")) {
-                       pidl "\tuint32_t _ptr_$e->{NAME};\n";
-               }
-       }
-
-       pidl "\n\tZERO_STRUCTP(r);\n\n";
+       pidl "\tstruct pidl_pull *ndr = pidl_pull_init(tvb, offset, pinfo, drep);\n";
+       pidl "\tstruct $fn->{NAME} *r = talloc_p(NULL, struct $fn->{NAME});\n";
+       pidl "\tpidl_tree ptree;\n\n";
 
-       # auto-init the out section of a structure. I originally argued that
-       # this was a bad idea as it hides bugs, but coping correctly
-       # with initialisation and not wiping ref vars is turning
-       # out to be too tricky (tridge)
-       foreach my $e (@{$fn->{DATA}}) {
-               if (util::has_property($e, "out")) {
-                       pidl "\tZERO_STRUCT(r->out);\n\n";
-                       last;
-               }
-       }
+       pidl "\tptree.proto_tree = tree;\n";
+       pidl "\tptree.subtree_list = NULL;\n\n";
 
-       foreach my $e (@{$fn->{DATA}}) {
-               if (util::has_property($e, "in")) {
-                       ParseFunctionElementPull($e, "in");
-               }
-               # we need to allocate any reference output variables, so that
-               # a dcerpc backend can be sure they are non-null
-               if (util::has_property($e, "out") && util::has_property($e, "ref")) {
-                       AllocateRefVars($e);
-               }
-       }
+       pidl "\tndr_pull_$fn->{NAME}(ndr, NDR_IN, &ptree, r);\n";
 
        pidl "\n\treturn ndr->offset;\n";
        pidl "}\n\n";
@@ -737,28 +707,14 @@ sub ParseFunctionPull($)
        # response function
        pidl "int $fn->{NAME}_resp(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, guint8 *drep)\n{\n";
 
-       pidl "\tstruct ndr_pull *ndr = ndr_pull_init(tvb, offset, pinfo, drep);\n";
-       pidl "\tstruct $fn->{NAME} *r = (struct $fn->{NAME} *)g_malloc(sizeof(struct $fn->{NAME}));\n\n";
-
-       # declare any internal pointers we need
-       foreach my $e (@{$fn->{DATA}}) {
-               if (util::need_wire_pointer($e) &&
-                   util::has_property($e, "out")) {
-                       pidl "\tuint32_t _ptr_$e->{NAME};\n";
-               }
-       }
-
-       pidl "\tZERO_STRUCTP(r);\n\n";
+       pidl "\tstruct pidl_pull *ndr = pidl_pull_init(tvb, offset, pinfo, drep);\n";
+       pidl "\tstruct $fn->{NAME} *r = talloc_p(NULL, struct $fn->{NAME});\n";
+       pidl "\tpidl_tree ptree;\n\n";
 
-       foreach my $e (@{$fn->{DATA}}) {
-               if (util::has_property($e, "out")) {
-                       ParseFunctionElementPull($e, "out");
-               }
-       }
+       pidl "\tptree.proto_tree = tree;\n";
+       pidl "\tptree.subtree_list = NULL;\n\n";
 
-       if ($fn->{RETURN_TYPE} && $fn->{RETURN_TYPE} ne "void") {
-               pidl "\tndr_pull_$fn->{RETURN_TYPE}(ndr, tree, hf_rc, &r->out.result);\n";
-       }
+       pidl "\tndr_pull_$fn->{NAME}(ndr, NDR_OUT, &ptree, r);\n";
 
        pidl "\n\treturn ndr->offset;\n";
        pidl "}\n\n";
@@ -809,7 +765,6 @@ sub ParseInterface($)
        }
 
        FunctionTable($interface);
-
 }
 
 sub type2ft($)
@@ -990,7 +945,7 @@ sub ParseHeader($$)
 
 #####################################################################
 # parse a parsed IDL structure back into an IDL file
-sub Parse($$)
+sub OldParse($$)
 {
        my($idl) = shift;
        my($filename) = shift;
@@ -1132,4 +1087,309 @@ sub Parse($$)
        close(OUT);
 }
 
+#####################################################################
+# rewrite autogenerated header file
+sub RewriteHeader($$$)
+{
+    my($idl) = shift;
+    my($input) = shift;
+    my($output) = shift;
+
+    %needed = ();
+
+    # Open files
+
+    open(IN, "<$input") || die "can't open $input for reading";
+    open(OUT, ">$output") || die "can't open $output for writing";    
+   
+    # Read in entire file
+
+    undef $/;
+
+    while(<IN>) {
+
+       # Not interested in ndr_push or ndr_print routines as they
+       # define structures we aren't interested in.
+
+       s/^NTSTATUS ndr_push.*?;\n//smg;
+       s/^void ndr_print.*?;\n//smg;
+
+       # Get rid of async send and receive function.
+
+       s/^NTSTATUS dcerpc_.*?;\n//smg;
+       s/^struct rpc_request.*?;\n\n//smg;
+
+       # Rewrite librpc includes
+
+       s/^\#include \"librpc\/gen_ndr\/ndr_(.*?).h\"$/\#include \"packet-dcerpc-$1.h\"/smg;
+
+       # Convert samba fixed width types to stdint types
+
+       s/((u)?int)([0-9]+)/$1$3_t/smg;
+
+       # Rename struct ndr_pull to struct pidl_pull
+
+       s/struct ndr_pull \*ndr/struct pidl_pull \*ndr/smg;
+
+       # Change prototypes for public functions
+
+       s/(struct pidl_pull \*ndr, int ndr_flags)/$1, pidl_tree *tree/smg;
+
+       pidl $_;
+    }
+
+    close(OUT);   
+}
+
+#####################################################################
+# rewrite autogenerated C file
+sub RewriteC($$$)
+{
+    my($idl) = shift;
+    my($input) = shift;
+    my($output) = shift;
+
+    # Open files
+
+    open(IN, "<$input") || die "can't open $input for reading";
+    open(OUT, ">$output") || die "can't open $output for writing";    
+    
+    # Get name of module
+
+    foreach my $x (@{$idl}) {
+       if ($x->{TYPE} eq "INTERFACE") { 
+           ModuleHeader($x);
+           $module = $x->{NAME};
+           BuildNeeded($x);
+       }
+    }
+
+    pidl "#include \"eparser.h\"\n\n";
+
+    pidl "extern const value_string NT_errors[];\n\n";
+
+    # Declarations for hf variables
+
+    pidl "static int hf_opnum = -1;\n";
+    pidl "static int hf_ptr = -1;\n";
+    pidl "static int hf_array_size = -1;\n";
+    pidl "static int hf_result_NTSTATUS = -1;\n";
+
+    foreach my $y (keys(%needed)) {
+       pidl "static int $y = -1;\n", if $y =~ /^hf_/;
+    }
+
+    pidl "\n";
+
+    foreach my $y (keys(%needed)) {
+       pidl "static gint $y = -1;\n", if $y =~ /^ett_/;
+    }
+
+    pidl "\n";
+
+    # Read in entire file for post-processing
+
+    undef $/;
+
+    while(<IN>) {
+
+       # Ethereal take care of this for us.  It also makes the other
+       # regular expressions easier to write and understand. 
+
+       s/NDR_CHECK\((.*)\)/$1/g;
+
+       # We're not interested in ndr_print or ndr_push functions.
+
+       s/^(static )?NTSTATUS (ndr_push[^\(]+).*?^\}\n\n//smg;
+       s/^void (ndr_print[^\(]+).*?^\}\n\n//smg;
+
+       # Get rid of dcerpc interface structures and functions
+
+       s/^static const struct dcerpc_interface_call .*?^\};\n\n//smg;  
+       s/^static const char \* const ([a-z]+)_endpoint_strings.*?^\};\n\n//smg;
+       s/^static const struct dcerpc_endpoint_list .*?^\};\n\n\n//smg; 
+       s/^const struct dcerpc_interface_table .*?^\};\n\n//smg;        
+       s/^static NTSTATUS dcerpc_ndr_([a-z]+)_init.*?^\}\n\n//smg;     
+       s/^NTSTATUS dcerpc_([a-z]+)_init.*?^\}\n\n//smg;        
+
+       # Include packet-dcerpc-foo.h instead of ndr_foo.h
+
+       s/^\#include \".*?ndr_(.*?).h\"$/\#include \"packet-dcerpc-$1.h\"/smg;
+
+       # Call ethereal wrapper for ndr_pull_ptr() function.
+
+       s/(ndr_pull_ptr\(ndr, ([^\)]*?)\);)/ndr_pull_ptr(ndr, tree, hf_ptr, $2);/smg;
+
+       # Wrap ndr_pull_array_size() - generate wrapper that won't get
+       # caught by the regex for wrapping scalar values below (i.e
+       # the leading space in front of the first parameter).
+
+       s/(ndr_pull_array_size\(ndr, ([^\)]*?)\);)/ndr_pull_array_size( ndr, tree, $2);/smg;
+
+       # Add tree argument to ndr_pull_array()
+
+       s/(ndr_pull_array([^\(]*?)\(ndr, (NDR_[^,]*?), ([^\)].*?)\);)/ndr_pull_array$2( ndr, $3, tree, $4);/smg;
+
+
+       # Call ethereal wrappers for pull of scalar values in
+       # structures and functions:
+       #
+       # ndr_pull_uint32(ndr, &r->in.access_mask);
+       # ndr_pull_uint32(ndr, &r->idx);
+
+       s/(ndr_pull_([^\)]*?)\(ndr, (&?r->((in|out)\.)?([^\)]*?))\);)/ndr_pull_$2(ndr, tree, hf_$6_$2, $3);/smg;
+
+       # Pull of "internal" scalars like array sizes, levels, etcetera.
+
+       s/(ndr_pull_(uint32|uint16)\(ndr, (&_([^\)]*?))\);)/ndr_pull_$2(ndr, tree, hf_$4, $3);/smg;
+
+       # Call ethereal wrappers for pull of buffers in structures and
+       # functions:
+       #
+       # ndr_pull_string(ndr, NDR_SCALARS|NDR_BUFFERS, &r->command);
+       # ndr_pull_atsvc_enum_ctr(ndr, NDR_SCALARS|NDR_BUFFERS, r->in.ctr);
+
+       s/(ndr_pull_([^\)]*?)\(ndr, (NDR_[^,]*?), ([^\(].*?)\);)/ndr_pull_$2(ndr, $3, get_subtree(tree, \"$2\", ndr, ett_$2), $4);/smg;
+
+       # Add proto_tree parameter to pull functions:
+       #
+       # static NTSTATUS ndr_pull_atsvc_JobInfo(struct ndr_pull *ndr, int ndr_flags, struct atsvc_JobInfo *r)
+
+       s/^((static )?NTSTATUS ndr_pull_([^\(]*?)\(struct ndr_pull \*ndr, int (ndr_)?flags)/$1, proto_tree \*tree/smg;
+
+       # Get rid of ndr_pull_error() calls.  Ethereal should take
+       # care of buffer overruns and inconsistent array sizes for us.
+
+       s/(return ndr_pull_error([^;]*?);)/return NT_STATUS_OK; \/\/ $1/smg;
+
+       # Rename proto_tree args to pidl_tree
+
+       s/(int (ndr_)?flags), proto_tree \*tree/$1, pidl_tree \*tree/smg;
+
+       # Rename struct ndr_pull to struct pidl_pull
+
+       s/struct ndr_pull \*ndr/struct pidl_pull \*ndr/smg;
+
+       pidl $_;
+    }
+
+    # Function call table
+
+    foreach my $x (@{$idl}) {
+       if ($x->{TYPE} eq "INTERFACE") { 
+           foreach my $y (@{$x->{"INHERITED_DATA"}}) {
+               ($y->{TYPE} eq "FUNCTION") && ParseFunctionPull($y);
+           }
+
+           FunctionTable($x);
+       }
+    }
+
+    # Ethereal protocol registration
+
+    pidl "int proto_dcerpc_pidl_$module = -1;\n\n";
+
+    pidl "static gint ett_dcerpc_$module = -1;\n\n";
+
+    if (defined($if_uuid)) {
+
+       pidl "static e_uuid_t uuid_dcerpc_$module = {\n";
+       pidl "\t0x" . substr($if_uuid, 1, 8);
+       pidl ", 0x" . substr($if_uuid, 10, 4);
+       pidl ", 0x" . substr($if_uuid, 15, 4) . ",\n";
+       pidl "\t{ 0x" . substr($if_uuid, 20, 2);
+       pidl ", 0x" . substr($if_uuid, 22, 2);
+       pidl ", 0x" . substr($if_uuid, 25, 2);
+       pidl ", 0x" . substr($if_uuid, 27, 2);
+       pidl ", 0x" . substr($if_uuid, 29, 2);
+       pidl ", 0x" . substr($if_uuid, 31, 2);
+       pidl ", 0x" . substr($if_uuid, 33, 2);
+       pidl ", 0x" . substr($if_uuid, 35, 2) . " }\n";
+       pidl "};\n\n";
+    }
+
+    if (defined($if_version)) {
+       pidl "static guint16 ver_dcerpc_$module = " . $if_version . ";\n\n";
+    }
+
+    pidl "void proto_register_dcerpc_pidl_$module(void)\n";
+    pidl "{\n";
+
+    pidl "\tstatic hf_register_info hf[] = {\n";
+    pidl "\t{ &hf_opnum, { \"Operation\", \"$module.opnum\", FT_UINT16, BASE_DEC, NULL, 0x0, \"Operation\", HFILL }},\n";
+       pidl "\t{ &hf_result_NTSTATUS, { \"Return code\", \"$module.rc\", FT_UINT32, BASE_HEX, VALS(NT_errors), 0x0, \"Return status code\", HFILL }},\n";
+    pidl "\t{ &hf_ptr, { \"Pointer\", \"$module.ptr\", FT_UINT32, BASE_HEX, NULL, 0x0, \"Pointer\", HFILL }},\n";
+
+    foreach my $x (keys(%needed)) {
+       next, if !($x =~ /^hf_/);
+       pidl "\t{ &$x,\n";
+       pidl "\t  { \"$needed{$x}{name}\", \"$x\", $needed{$x}{ft}, $needed{$x}{base}, NULL, 0, \"$x\", HFILL }},\n";
+    }
+
+    pidl "\t};\n\n";
+
+    pidl "\tstatic gint *ett[] = {\n";
+    pidl "\t\t&ett_dcerpc_$module,\n";
+    foreach my $x (keys(%needed)) {
+       pidl "\t\t&$x,\n", if $x =~ /^ett_/;
+    }
+    pidl "\t};\n\n";
+    
+    if (defined($if_uuid)) {
+
+       pidl "\tproto_dcerpc_pidl_$module = proto_register_protocol(\"pidl_$module\", \"pidl_$module\", \"pidl_$module\");\n\n";
+
+       pidl "\tproto_register_field_array(proto_dcerpc_pidl_$module, hf, array_length (hf));\n";
+       pidl "\tproto_register_subtree_array(ett, array_length(ett));\n";
+
+       pidl "}\n\n";
+
+       pidl "void proto_reg_handoff_dcerpc_pidl_$module(void)\n";
+       pidl "{\n";
+       pidl "\tdcerpc_init_uuid(proto_dcerpc_pidl_$module, ett_dcerpc_$module, \n";
+       pidl "\t\t&uuid_dcerpc_$module, ver_dcerpc_$module, \n";
+       pidl "\t\tdcerpc_dissectors, hf_opnum);\n";
+       pidl "}\n";
+
+    } else {
+
+       pidl "\tint proto_dcerpc;\n\n";
+       pidl "\tproto_dcerpc = proto_get_id_by_filter_name(\"dcerpc\");\n";
+       pidl "\tproto_register_field_array(proto_dcerpc, hf, array_length(hf));\n";
+       pidl "\tproto_register_subtree_array(ett, array_length(ett));\n";
+
+       pidl "}\n";
+
+    }
+
+    close(OUT);   
+}
+
+#####################################################################
+# parse a parsed IDL structure back into an IDL file
+sub Parse($$)
+{
+       my($idl) = shift;
+       my($filename) = shift;
+
+       %needed = ();           # Clear after generating header file
+
+       open(OUT, ">$filename") || die "can't open $filename";    
+
+       # Look for name of module
+
+       foreach my $x (@{$idl}) {
+
+           if ($x->{TYPE} eq "INTERFACE") { 
+               ModuleHeader($x);
+               $module = $x->{NAME};
+               BuildNeeded($x);
+           }
+       }
+       
+       pidl "/* parser auto-generated by pidl */\n\n";
+
+       close(OUT);
+}
+
 1;
index c430c7523645d6665b53e26bc727c10ab1e45fe4..6d0ff6908193d85b4f1162afc40c97607b3f84b3 100755 (executable)
@@ -205,15 +205,23 @@ $dcom
        }
 
        if ($opt_eparser) {
-               my($parser) = dirname($output) . "/packet-dcerpc-$basename.c";
-               IdlEParser::Parse($pidl, $parser);
-               $parser = dirname($output) . "/packet-dcerpc-proto-$basename.h";
-               IdlEParser::ParseHeader($pidl, $parser);
-               my($header) = dirname($output) . "/packet-dcerpc-proto.h";
-               open(OUT, ">>$header") || die "can't open $header";
-               print OUT "#include \"ndr_$basename.h\"\n";
-               print OUT "#include \"packet-dcerpc-proto-$basename.h\"\n";
-               close(OUT);
+
+         # Generate regular .c and .h files for marshaling and
+         # unmarshaling.
+
+         my($parser) = util::ChangeExtension($output, ".c");
+         IdlParser::Parse($pidl, $parser);
+
+         my($header) = util::ChangeExtension($output, ".h");
+         util::FileSave($header, IdlHeader::Parse($pidl));
+
+         # Postprocess to produce ethereal parsers.
+
+         my($eparser) = dirname($output) . "/packet-dcerpc-$basename.c";
+         IdlEParser::RewriteC($pidl, $parser, $eparser);
+
+         my($eparserhdr) = dirname($output) . "/packet-dcerpc-$basename.h";
+         IdlEParser::RewriteHeader($pidl, $header, $eparserhdr);
        }
 
        if ($opt_swig) {