Nmap Development mailing list archives
Re: Nmap Attack Scripting Language (NASL)
From: doug () hcsw org
Date: Tue, 23 May 2006 20:28:43 -0700
Hello nmap-dev, On Mon, May 22, 2006 at 06:20:26PM -0700 or thereabouts, Fyodor wrote:
For what it is worth, we're currently looking at LUA as the embedded scripting language of choice. Anyone have experience in this area?
I've looked into Lua a bit and I'm fairly impressed with what I see. The system is compact, well designed, and well documented. Lua was obviously *heavily* inspired by the Scheme language and contains many of Scheme's features that I consider vital for any modern scripting language: * Lexical scoping, functions as first class citizens, etc. * Incremental, user controllable Garbage Collection: http://www.lua.org/manual/5.1/manual.html#2.10 * Proper tail call optimisation: http://www.lua.org/manual/5.1/manual.html#2.5.8 * User customisable control flow. Lua calls these "coroutines" and places emphasis on their use in a cooperative multithreading system but really these coroutines are just slightly limited Scheme "continuations". (Lua's coroutines aren't re-invocable like Scheme continuations are): http://www.lua.org/manual/5.1/manual.html#2.11 I do have a couple small concerns with Lua, however: * Internally it looks as though *all* numbers are stored as double precision floating point values - even, for instance, array indices: http://www.lua.org/pil/2.3.html Using floating point values makes bitwise operations much more complex; see my next point. It does seem as though Lua's primary number type might be changable by modifying this line in the Lua source: typedef double lua_Number; Although changing double to, for example long, leaves us with no floating point capabilities whatsoever! * And Lua itself doesn't provide much functionality for dealing with binary data. Shifts, bitwise and/or/xor/complement, and other primitives will probably have to be added. * Considering the fact that Lua is very simple and doesn't use some advanced byte code optimisation techniques found in decent Scheme compilers, I would expect a Scheme implementation to have a performance advantage over Lua in most cases. However, with Nmap and, in general, any network bound program, the way network I/O is handled is *BY FAR* the most important determinate of performance. Most of these aren't major problems and some would likely have to be overcome with Scheme as well. The fact that I'm concerned about some of these at all might give away the fact that normally I'm a common lisp programmer and am used to the most powerful system available. ;) As Fyodor touches upon in his requirements document, it is vital that any scripting language handle many different types of network I/O asynchronously. This is definitley the most difficult part of the scripting language design and will have to be carefuly thought out beforehand. There are couple fundamentally different ways to do it: * "By hand". Most of Nmap uses carefuly designed C data structures and never does blocking calls until a main select() loop or some such (done inside Nsock, for example). Nsock, Nmap's library for doing exactly this, works very well and was what I used for the rDNS system. In theory, we could just give a script Nsock bindings but that might not make for easy-to-write scripts! * A "callback" interface would also be possible. The user could define a number of standard named functions in Lua or whatever and a callback engine would have to be created in C that takes care of all blocking I/O and calls the user-defined Lua functions as appropriate. This is, sort of, the way that version detection works. * In theory, C threads could be used. This has the advantage that scripts can be written simply and directly using normal blocking connect(), read(), and write() (whatever their counterparts in Lua will be), calls. The disadvantage is that C threads are usually very "heavy" and require lots of memory and CPU time overhead. This is a problem since we might be creating, destroying, and switching between, hundreds of probes per second. Also, decent threading can be be tough to do portably; decent thread scheduling even more so. (GNU pth is, IMO, the best implementation and I usually use it over POSIX). * Finally, a continuation style could be used that makes it feel, to the Lua (or whatever) programmer that they are using direct blocking calls. connect, read, write, etc would have to be defined so that when they are called they put a descriptor into a "read queue" or a "connect queue", store the current continuation and resume another "scheduling continuation" which continues processing continuations until its "run queue" is empty then do a big select() call on all collected descriptors. Then, once select() returns, the offending descriptors would have to be found and their associated continuations, well, continued. I have done empirical tests with Scheme continuations in the past and I've found that each continuation takes only a few hundred memory cells (typically 8 to 32 bytes each) and creation/destruction/context switching is negligible. I can't speak for Lua "coroutines" but suspect they are roughly comparable. In my personal opinion, Lua's biggest flaw is that it doesn't use S-expression scheme/lisp syntax. This makes some things that Scheme/lisp programmers take for granted impossible: macros, symbolic computation, "code as data", etc, etc, etc. That said, I think Lua is the best tool available for this task because of exactly one feature: its lack of S-expression scheme/lisp syntax. :) If we want this scripting interface to be as widely accesible as possible (which I believe we do) we want a straightforward easy programmer interface. Do we really want to be fielding questions on nmap-dev like "lol buds what's a cadr!??! k thxbye"? Besides, as any scheme/lisp programmer knows, when you're dealing with S-expressions, syntax is irrelevant! If you really want to code in S-expressions (who can blame you?), writing a compiler is trivial: ;; Simple Scheme to Lua compiler. Only supports basic ;; arithmetic, '=, and 'if for now. Infix limitations also apply. (define (scm->lua f) (cond ((pair? f) (case (car f) ((set!) (string-append (scm->lua (cadr f)) " = " (scm->lua (caddr f)))) ((+ - / *) (string-append "(" (scm->lua (cadr f)) " " (atom->string (car f)) " " (scm->lua (caddr f)) ")")) ((if) (string-append "if " (scm->lua (cadr f)) " then\n" (scm->lua (caddr f)) "\nend")) ((=) (string-append (scm->lua (cadr f)) " == " (scm->lua (caddr f)))))) (else (atom->string f)))) Now we can compile scheme forms like so:
(scm->lua '(if (= a 1) (set! b (+ (- b 2) (* a 3)))))
"if a == 1 then\nb = ((b - 2) + (a * 3))\nend" Doug
_______________________________________________ Sent through the nmap-dev mailing list http://cgi.insecure.org/mailman/listinfo/nmap-dev
Current thread:
- Nmap Attack Scripting Language (NASL) Fyodor (May 22)
- Re: Nmap Attack Scripting Language (NASL) doug (May 23)
- <Possible follow-ups>
- RE: Nmap Attack Scripting Language (NASL) Arun Vishwanathan (May 22)
- RE: Nmap Attack Scripting Language (NASL) Brandon Enright (May 22)
- RE: Nmap Attack Scripting Language (NASL) Arun Vishwanathan (May 22)
- Re: Nmap Attack Scripting Language (NASL) Paul Rigor (May 22)
- Re: Nmap Attack Scripting Language (NASL) David Warde-Farley (May 22)
- Re: Nmap Attack Scripting Language (NASL) Paul Rigor (May 22)
- Re: Nmap Attack Scripting Language (NASL) Fyodor (May 23)
- Re: Nmap Attack Scripting Language (NASL) Paul Rigor (May 22)
- RE: Nmap Attack Scripting Language (NASL) Arun Vishwanathan (May 22)
- RE: Nmap Attack Scripting Language (NASL) Arun Vishwanathan (May 22)
- RE: Nmap Attack Scripting Language (NASL) Arun Vishwanathan (May 23)