Nmap Development mailing list archives

Re: [SoC] NSE pretty debugging methods


From: "Duilio Protti" <dprotti () fceia unr edu ar>
Date: Sun, 25 Mar 2007 13:11:01 -0300 (ART)


Hi, I have and idea for a pretty printer for NSE which in fact is more
general than is needed to print lua_State structures, but of course it
could be used to implement that in particular (probably the proposed
general pretty printer should go in libnmap)

The idea surrounds from PP module used in ML programs. The signature of
this module is very small (you can find an API description at
http://www.dina.dk/~sestoft/mosmllib/PP.html):

(* PP -- pretty-printing -- from the SML/NJ library *)

type ppconsumer = { consumer  : string -> unit,
                    linewidth : int,
                    flush     : unit -> unit }

datatype break_style =
    CONSISTENT
  | INCONSISTENT

val mk_ppstream    : ppconsumer -> ppstream
val dest_ppstream  : ppstream -> ppconsumer
val add_break      : ppstream -> int * int -> unit
val add_newline    : ppstream -> unit
val add_string     : ppstream -> string -> unit
val begin_block    : ppstream -> break_style -> int -> unit
val end_block      : ppstream -> unit
val clear_ppstream : ppstream -> unit
val flush_ppstream : ppstream -> unit
val with_pp        : ppconsumer -> (ppstream -> unit) -> unit
val pp_to_string   : int -> (ppstream -> 'a -> unit) -> 'a -> string


Aside from the specific details of what that CONSISTENT means and why you
would like something like mk_ppstream, the key point is: the programmer
groups related things together within blocks. Inside a block, it produce
output without worrying about the context where it is.

That is, programmers doesn't make formatting, they just group things
together. Pretty printer takes care of doing formatting. Programmers
doesn't even know where they are sending output, they just put things in a
stream (a ppstream). Later they can control things as to where the output
stream will be redirected and other details.

To understand how it works, suppose we want to show something like a
network with a list of host, and each host with a list of ports where
there could be some interesting things to say. The output is something
like:

Net 192.168.0.0
  Host 192.168.0.1
    T80: some observation,
         probably very long
    U139:
    T10000:
  Host 192.168.0.2
    T22:
    T25: some other observation


Using PP module in ML this can be done as:

type octet = int
type IPAddress = octet * octet * octet * octet

(* This is not the better datatype possible but hey! It's an example :-) *)
datatype
example = Net  of IPAddress * example list
        | Host of IPAddress * (port * string list) list
and port =
    TCP of int
  | UDP of int

(* Let's make the pretty printer. 'pps' is the PP.ppstream *)
fun prettyPrint pps example =
    let
        open PP

        fun ppExample (Net (ip,exs)) =
            (add_string pps "Net ";
             ppIP ip;
             add_break pps (0, 2);
             begin_block pps CONSISTENT 0;
             List.map ppExample exs;
             end_block pps)
          | ppExample (Host (ip,ports)) =
            (add_string pps "Host ";
             ppIP ip;
             add_break pps (0, 2);
             begin_block pps CONSISTENT 0;
             ppPorts ports;
             end_block pps;
             add_newline pps)
        and ppPorts ((port, msgs)::ports) =
            (ppPort port;
             add_string pps ": ";
             begin_block pps CONSISTENT 0;
             List.map ppPortMsg msgs;
             end_block pps;
             if ports <> [] then add_newline pps else ();
             ppPorts ports)
          | ppPorts [] = ()
        and ppPort (TCP n) =
            add_string pps ("T" ^ (Int.toString n))
          | ppPort (UDP n) =
            add_string pps ("U" ^ (Int.toString n))
        and ppPortMsg s =
            (add_string pps s;
             add_newline pps)
        and ppIP (a,b,c,d) =
            add_string pps (Int.toString a
                            ^ "." ^ Int.toString b
                            ^ "." ^ Int.toString c
                            ^ "." ^ Int.toString d)
    in
        begin_block pps INCONSISTENT 0;
        ppExample example;
        end_block pps
    end

val msg1 = "this is a message"
val msg2 = "this is another message"
val sample1 = Net ((192,168,0,0),
                   [Host ((192,168,0,1),
                          [(TCP 80,[msg1,msg2]), (UDP 139,[]), (TCP
10000,[])]),
                    Host ((192,168,0,2),
                          [(TCP 22,[]), (TCP 25,[])])])

(* '15' is the lineWidth. If nesting groups try to indent deeper than
   that, the library will take care of make a clever reordering of the
   output in order to show things nicely *)
val output1 = PP.pp_to_string 15 prettyPrint sample1
val _ = print (output1 ^ "\n")


The output for this program is:

Net 192.168.0.0
  Host 192.168.0.1
    T80: this is a message
         this is another message

    U139:
    T10000:
  Host 192.168.0.2
    T22:
    T25:

The good news is that we never produce the required tabs and spaces
directly, but rather we just mark the logical structure of the stream to
be printed, and the library makes all that job for us. The PP module of ML
is just an example, but the important thing is that this philosophy for
pretty printing presents a good separation of concerns, and while it can
be used to pretty print debugging objects, it's not restricted to that.

Finally, as you can imagine, it can be implemented completely in Lua, but
probably would be more useful if it's offered to the entire Nmap
infrastructure through C++ methods in the planned libnmap.

What do you think, guys?

Cheers,
Duilio Protti.

http://www.fceia.unr.edu.ar/~dprotti/



_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://SecLists.Org


Current thread: