diff --git a/.hgsigs b/.hgsigs
index 7301d8ffd9bb74bca250422734153730d09ef0c7..37d15d29ee45030fdbb1b2e1bec12f837273682b 100644
--- a/.hgsigs
+++ b/.hgsigs
@@ -3,3 +3,4 @@
 d8a91f28e741bb8af8975658a8a5a539107c1ea2 0 iD8DBQBGGjz5Xuj/Xz2aQ+IRAmNHAJ9ttQ67KKbqYse4af4hgwdYPWxdiwCgg1+4HS9sREyplo/0GMS8XOfSnck=
 105de7dc59f734c36488fd77926ca65b675bf541 0 iD8DBQBG6nscXuj/Xz2aQ+IRAkQbAJ47GgD2QrQdAq/R+GxYuqTuJs3khQCfd8w+LKvcyWLdl+sbYNjXjPnhTpk=
 9642ec17c7b8b63297bf9372f2d8084053304e79 0 iEYEABECAAYFAkmQI/4ACgkQXuj/Xz2aQ+IGVQCdH2MFM/AmFknON6PGBy5jtYmBXi8AoImp0HqW0KDawiRhw8N3oAy9bSd5
+8ac76c4dfc2ea44482d2e68a9877a80ae433467e 0 iEYEABECAAYFAksmWiwACgkQXuj/Xz2aQ+J/QwCgsEQnlxjr2HfXsOELtE6wRvH7BaMAn2tAfl9Z1JanqS7YvP1yXtTCaQJx
diff --git a/.hgtags b/.hgtags
index ab21a3e5114a0de8fcd976b057ce8ac208785728..27f749e3c3b8f88336c306f55420e62889c49b23 100644
--- a/.hgtags
+++ b/.hgtags
@@ -498,3 +498,4 @@ a1ee8c68a0023e998b3353060d2b774ea541cdf1 v2.0.0.6
 86518fb3c5addf2721c5a9e818ab21908fc1f7ab v2.0.0.10
 ebe234b803cd48882a082ec18a5743a36ca1db13 v2.0.0.11
 834b9f8a3c230c0c8ba5a66ea6b9147c9f64d390 v2.0.1
+62d7ebd04e57fb33d152e6cf8d2b37c91d1a6364 v2.1.0
diff --git a/bin/UTscapy.bat b/bin/UTscapy.bat
new file mode 100755
index 0000000000000000000000000000000000000000..8f86ff0d1c8efc701035a3e453ca1d68cad5cd2e
--- /dev/null
+++ b/bin/UTscapy.bat
@@ -0,0 +1,2 @@
+@REM Use Python to run the UTscapy script from the current directory, passing all parameters
+@python %~dp0\UTscapy %*
diff --git a/bin/scapy.bat b/bin/scapy.bat
new file mode 100755
index 0000000000000000000000000000000000000000..613370a5e2a666e5007fdc346c5b3618a2087e63
--- /dev/null
+++ b/bin/scapy.bat
@@ -0,0 +1,2 @@
+@REM Use Python to run the Scapy script from the current directory, passing all parameters
+@python %~dp0\scapy %*
diff --git a/doc/scapy/extending.rst b/doc/scapy/extending.rst
index 0a9ecd900259c9d4cb2f1339931522da2ff516e4..6be0bd0d94e4c4b705be5c6f4befa64bfcbeb754 100644
--- a/doc/scapy/extending.rst
+++ b/doc/scapy/extending.rst
@@ -57,7 +57,7 @@ Here is another tool that will constantly monitor all interfaces on a machine an
     
     sniff(prn=arp_monitor_callback, filter="arp", store=0)
 
-For a real life example, you can check `Wifitap <http://sid.rstack.org/index.php/Wifitap_EN>`_.
+For a real life example, you can check `Wifitap <http://sid.rstack.org/static/articles/w/i/f/Wifitap_EN_9613.html>`_.
 
 
 Extending Scapy with add-ons
diff --git a/doc/scapy/installation.rst b/doc/scapy/installation.rst
index eca90591020470d31503fe4447b281f245e56cfd..ae29033a350c9a2933f7de34ed0adee897a5926e 100644
--- a/doc/scapy/installation.rst
+++ b/doc/scapy/installation.rst
@@ -44,7 +44,7 @@ Make sure you have Python installed before you go on.
 Latest release
 --------------
 
-Download the `latest version <http://scapy.net>`_ to a temporary directory and install it in the standard `distutils <http://docs.python.org/inst/inst.html>`_ way::
+Download the `latest release <http://scapy.net>`_ to a temporary directory and install it in the standard `distutils <http://docs.python.org/inst/inst.html>`_ way::
 
 $ cd /tmp
 $ wget scapy.net 
@@ -55,8 +55,14 @@ $ sudo python setup.py install
 Alternatively, you can make the zip file executable, move it to a directory in your PATH and run it directly::
 
 $ chmod +x scapy-latest.zip
-$ mv scapy-latest.zip /usr/local/bin/scapy
-$ sudo scapy
+$ sudo ./scapy-latest.zip
+
+.. note::
+
+   To make a zip executable, some bytes have been added before the zip header.
+   Most zip programs handle this, but not all. If your zip program complains
+   about the zip file to be corrupted, either change it, or download a 
+   non-executable zip at http://hg.secdev.org/scapy/archive/tip.zip
  
 Current development version
 ----------------------------
@@ -164,6 +170,7 @@ Here are the topics involved and some examples that you can use to try if your i
 
   .. code-block:: python 
   
+     >>> load_module("nmap")
      >>> nmap_fp("192.168.0.1")
      Begin emission:
      Finished to send 8 packets.
@@ -199,7 +206,9 @@ $ sudo apt-get install tcpdump graphviz imagemagick python-gnuplot python-crypto
 Fedora
 ------
 
-Here's how to install Scapy on Fedora 9::
+Here's how to install Scapy on Fedora 9:
+
+.. code-block:: text
 
     # yum install mercurial python-devel
     # cd /tmp
@@ -207,7 +216,9 @@ Here's how to install Scapy on Fedora 9::
     # cd scapy
     # python setup.py install
     
-Some optional packages::
+Some optional packages:
+
+.. code-block:: text
 
     # yum install graphviz python-crypto sox PyX gnuplot numpy
     # cd /tmp
@@ -349,11 +360,10 @@ Windows
 
 .. sectionauthor:: Dirk Loss <mail at dirk-loss.de>
 
-Scapy is primarily being developed for Unix-like systems and works best on those platforms. But a special port (Scapy-win) exists that allows you to use nearly all of Scapy's features on your Windows machine as well.
+Scapy is primarily being developed for Unix-like systems and works best on those platforms. But the latest version of Scapy supports Windows out-of-the-box. So you can use nearly all of Scapy's features on your Windows machine as well.
 
 .. note::
-
-   At the moment, only Scapy v1.2.x works on Windows. Scapy v2 might be ported in the future.
+   If you update from Scapy-win v1.2.0.2 to Scapy v2 remember to use ``from scapy.all import *`` instead of ``from scapy import *``.
 
 .. image:: graphics/scapy-win-screenshot1.png
    :scale: 80
@@ -361,21 +371,21 @@ Scapy is primarily being developed for Unix-like systems and works best on those
 
 You need the following software packages in order to install Scapy on Windows:
 
-  * `Python <http://www.python.org>`_: `python-2.5.2.msi <http://www.python.org/ftp/python/2.5.2/python-2.5.2.msi>`_. I'm using Python 2.5. Scapy-win will work with Python 2.4 as well, but you will need all third-party extensions on this page compiled for v2.4.
-  * `Scapy-win <http://hg.secdev.org/scapy-win>`_: `latest version from the Mercurial repository <http://hg.secdev.org/scapy-win/raw-file/tip/scapy.py>`_. Right click and save to ``C:\Python25\Lib\site-packages\scapy.py``, or adjust to match your Python install directory.
-  * `pywin32 <http://python.net/crew/mhammond/win32/Downloads.html>`_: `pywin32-210.win32-py2.5.exe <http://surfnet.dl.sourceforge.net/sourceforge/pywin32/pywin32-210.win32-py2.5.exe>`_ 
-  * `WinPcap <http://www.winpcap.org/>`_: `WinPcap_4_0_2.exe <http://www.winpcap.org/install/bin/WinPcap_4_0_2.exe>`_. Or if you want to use the ethernet vendor database to resolve MAC addresses, download `Wireshark <http://www.wireshark.org/>`_ which already includes WinPcap.
-  * `pypcap <http://code.google.com/p/pypcap/>`_: `pcap-1.1-scapy.win32-py2.5.exe <http://www.secdev.org/projects/scapy/files/pcap-1.1-scapy.win32-py2.5.exe>`_. This is a *special version for Scapy*, as the original leads to some timing problems. For background info look on the `Wiki <http://trac.secdev.org/scapy/wiki/PypcapScapyWin>`_
-  * `libdnet <http://code.google.com/p/libdnet/>`_:  `dnet-1.12.win32-py2.5.exe <http://libdnet.googlecode.com/files/dnet-1.12.win32-py2.5.exe>`_
+  * `Python <http://www.python.org>`_: `python-2.5.4.msi <http://www.python.org/ftp/python/2.5.4/python-2.5.4.msi>`_. `python-2.6.3.msi <http://www.python.org/ftp/python/2.6.3/python-2.6.3.msi>`_. After installation, add the Python installation directory and its \Scripts subdirectory to your PATH. Depending on your Python version, the defaults would be ``C:\Python25`` and ``C:\Python25\Scripts`` or ``C:\Python26`` and ``C:\Python26\Scripts`` respectively.
+  * `Scapy <http://www.secdev.org/projects/scapy/>`_: `latest version from the Mercurial repository <http://scapy.net>`_. Unzip the archive, open a command prompt in that directory and run "python setup.py install".
+  * `pywin32 <http://python.net/crew/mhammond/win32/Downloads.html>`_: `pywin32-214.win32-py2.5.exe <http://surfnet.dl.sourceforge.net/sourceforge/pywin32/pywin32-214.win32-py2.5.exe>`_ `pywin32-214.win32-py2.6.exe <http://downloads.sourceforge.net/project/pywin32/pywin32/Build%20214/pywin32-214.win32-py2.6.exe>`_
+  * `WinPcap <http://www.winpcap.org/>`_: `WinPcap_4_1_1.exe <http://www.winpcap.org/install/bin/WinPcap_4_1_1.exe>`_. You might want to choose "[x] Automatically start the WinPcap driver at boot time", so that non-privileged users can sniff, especially under Vista and Windows 7. If you want to use the ethernet vendor database to resolve MAC addresses or use the ``wireshark()`` command, download `Wireshark <http://www.wireshark.org/>`_ which already includes WinPcap. 
+  * `pypcap <http://code.google.com/p/pypcap/>`_: `pcap-1.1-scapy-20090720.win32-py25.exe <http://www.secdev.org/projects/scapy/files/pcap-1.1-scapy-20090720.win32-py2.5.exe>`_ `pcap-1.1-scapy-20090720.win32-py2.6.exe <http://www.secdev.org/projects/scapy/files/pcap-1.1-scapy-20090720.win32-py2.6.exe>`_. This is a *special version for Scapy*, as the original leads to some timing problems. Now works on Vista and Windows 7, too. Under Vista/Win7 please right-click on the installer and choose "Run as administrator".
+  * `libdnet <http://code.google.com/p/libdnet/>`_:  `dnet-1.12.win32-py2.5.exe <http://libdnet.googlecode.com/files/dnet-1.12.win32-py2.5.exe>`_ `dnet-1.12.win32-py2.6.exe <http://www.secdev.org/projects/scapy/files/dnet-1.12.win32-py2.6.exe>`_. Under Vista/Win7 please right-click on the installer and choose "Run as administrator"
   * `pyreadline <http://ipython.scipy.org/moin/PyReadline/Intro>`_: `pyreadline-1.5-win32-setup.exe <http://ipython.scipy.org/dist/pyreadline-1.5-win32-setup.exe>`_
 
 Just download the files and run the setup program. Choosing the default installation options should be safe.
 
-For your convenience direct links are given to the versions I used (for Python 2.5). If these links do not work or if you are using a different Python version, just visit the homepage of the respective package and look for a Windows binary. As a last resort, search the web for the filename.
+For your convenience direct links are given to the versions I used (for Python 2.5 and Python 2.6). If these links do not work or if you are using a different Python version, just visit the homepage of the respective package and look for a Windows binary. As a last resort, search the web for the filename.
 
-After all packages are installed, open a command prompt (cmd.exe), change to the directory containing scapy.py and run Scapy with ``python scapy.py`` (or just ``scapy.py``). For usage information see the interactive demo and the other documents on Scapy's homepage.
+After all packages are installed, open a command prompt (cmd.exe) and run Scapy by typing ``scapy``. If you have set the PATH correctly, this will find a little batch file in your ``C:\Python26\Scripts`` directory and instruct the Python interpreter to load Scapy.
 
-If really nothing seems to work, consider skipping the Windows version and using Scapy from a Linux Live CD -- either in a virtual machine on your Windows host or by booting from CDROM: Scapy is already included in grml and BackTrack for example. While using the Live CD you can easily upgrade to the lastest Scapy version (for Unix) by typing ``cd /tmp && wget scapy.net``.
+If really nothing seems to work, consider skipping the Windows version and using Scapy from a Linux Live CD -- either in a virtual machine on your Windows host or by booting from CDROM: An older version of Scapy is already included in grml and BackTrack for example. While using the Live CD you can easily upgrade to the lastest Scapy version by typing ``cd /tmp && wget scapy.net``.
 
 Optional packages
 ^^^^^^^^^^^^^^^^^
@@ -383,29 +393,29 @@ Optional packages
 Plotting (``plot``)
 
  * `GnuPlot <http://www.gnuplot.info/>`_: `gp420win32.zip <http://downloads.sourceforge.net/gnuplot/gp420win32.zip>`_. Extract the zip file (e.g. to ``c:\gnuplot``) and add the ``gnuplot\bin`` directory to your PATH.
- * `Numeric <http://numpy.scipy.org/>`_: `Numeric-24.2.win32-py2.5.exe <http://biopython.org/DIST/Numeric-24.2.win32-py2.5.exe>`_ . Gnuplot-py needs Numeric.
- * `Gnuplot-py <http://gnuplot-py.sourceforge.net/>`_: `gnuplot-py-1.7.zip <http://mesh.dl.sourceforge.net/sourceforge/gnuplot-py/gnuplot-py-1.7.zip>`_. Extract to temp dir, open command prompt, change to tempdir and type ``python setup.py install``.
+ * `NumPy <http://numpy.scipy.org/>`_: `numpy-1.3.0-win32-superpack-python2.5.exe <http://downloads.sourceforge.net/project/numpy/NumPy/1.3.0/numpy-1.3.0-win32-superpack-python2.5.exe>`_ `numpy-1.3.0-win32-superpack-python2.6.exe <http://downloads.sourceforge.net/project/numpy/NumPy/1.3.0/numpy-1.3.0-win32-superpack-python2.6.exe>`_. Gnuplot-py 1.8 needs NumPy.
+ * `Gnuplot-py <http://gnuplot-py.sourceforge.net/>`_: `gnuplot-py-1.8.zip <http://downloads.sourceforge.net/project/gnuplot-py/Gnuplot-py/1.8/gnuplot-py-1.8.zip>`_. Extract to temp dir, open command prompt, change to tempdir and type ``python setup.py install``.
 
 2D Graphics (``psdump``, ``pdfdump``)
 
- * `PyX <http://pyx.sourceforge.net/>`_: `PyX-0.10.tar.gz `PyX-0.10.tar.gz <http://mesh.dl.sourceforge.net/sourceforge/pyx/PyX-0.10.tar.gz>`_. Extract to temp dir, open command prompt, change to tempdir and type ``python setup.py install``
- * `MikTeX <http://miktex.org/>`_: `basic-miktex-2.6.2742.exe (52 MB) <http://prdownloads.sourceforge.net/miktex/basic-miktex-2.6.2742.exe?download>`_. PyX needs a LaTeX installation. Choose an installation directory WITHOUT spaces (e.g. ``C:\MikTex2.6`` and add the ``(INSTALLDIR)\miktex\bin`` subdirectory to your PATH.
+ * `PyX <http://pyx.sourceforge.net/>`_: `PyX-0.10.tar.gz <http://mesh.dl.sourceforge.net/sourceforge/pyx/PyX-0.10.tar.gz>`_. Extract to temp dir, open command prompt, change to tempdir and type ``python setup.py install``
+ * `MikTeX <http://miktex.org/>`_: `Basic MiKTeX 2.8 Installer <http://miktex.org/2.8/setup>`_. PyX needs a LaTeX installation. Choose an installation directory WITHOUT spaces (e.g. ``C:\MikTex2.8`` and add the ``(INSTALLDIR)\miktex\bin`` subdirectory to your PATH.
 
 Graphs (conversations)
 
- * `Graphviz <http://www.graphviz.org/>`_: `graphviz-2.12.exe <http://www.graphviz.org/pub/graphviz/stable/windows/graphviz-2.12.exe>`_. Add ``(INSTALLDIR)\ATT\Graphviz\bin`` to your PATH.
+ * `Graphviz <http://www.graphviz.org/>`_: `graphviz-2.24.exe <http://www.graphviz.org/pub/graphviz/stable/windows/graphviz-2.24.msi>`_. Add ``(INSTALLDIR)\ATT\Graphviz\bin`` to your PATH.
 
 3D Graphics (trace3d)
 
- * `VPython <http://www.vpython.org/>`_: `VPython-Win-Py2.5-3.2.11.exe <http://www.vpython.org/download/VPython-Win-Py2.5-3.2.11.exe>`_ 
+ * `VPython <http://www.vpython.org/>`_: `VPython-Win-Py2.5-3.2.11.exe <http://www.vpython.org/download/VPython-Win-Py2.5-3.2.11.exe>`_. No binary installer for Python 2.6 seems to be available yet.
 
 WEP decryption
 
- * `PyCrypto <http://www.dlitz.net/software/pycrypto/>`_: `pycrypto-2.0.1.win32-py2.5.zip <http://www.voidspace.org.uk/cgi-bin/voidspace/downman.py?file=pycrypto-2.0.1.win32-py2.5.zip>`_ 
+ * `PyCrypto <http://www.dlitz.net/software/pycrypto/>`_: `pycrypto-2.0.1.win32-py2.5.zip <http://www.voidspace.org.uk/cgi-bin/voidspace/downman.py?file=pycrypto-2.0.1.win32-py2.5.zip>`_ `pycrypto-2.0.1.win32-py2.6.exe <http://www.voidspace.org.uk/downloads/pycrypto-2.0.1.win32-py2.6.exe>`_
 
 Fingerprinting
 
-  * `Nmap <http://nmap.org>`_. `nmap-4.20-setup.exe <http://download.insecure.org/nmap/dist/nmap-4.20-setup.exe>`_. If you use the default installation directory, Scapy-win should automatically find the fingerprints file.
+  * `Nmap <http://nmap.org>`_. `nmap-4.20-setup.exe <http://download.insecure.org/nmap/dist-old/nmap-4.20-setup.exe>`_. If you use the default installation directory, Scapy should automatically find the fingerprints file.
   * Queso: `queso-980922.tar.gz <http://www.packetstormsecurity.org/UNIX/scanners/queso-980922.tar.gz>`_. Extract the tar.gz file (e.g. using `7-Zip <http://www.7-zip.org/>`_) and put ``queso.conf`` into your Scapy directory
 
 
diff --git a/doc/scapy/usage.rst b/doc/scapy/usage.rst
index cec4f6f9bef8ae2cb105b6fe225605f9cb0a7ae4..3bde23e416b5173e604d74ae6c834c2979c1d26c 100644
--- a/doc/scapy/usage.rst
+++ b/doc/scapy/usage.rst
@@ -9,19 +9,21 @@ Scapy's interactive shell is run in a terminal session. Root privileges are need
 send the packets, so we're using ``sudo`` here::
   
     $ sudo scapy
-    Welcome to Scapy (2.0.0.10 beta)
+    Welcome to Scapy (2.0.1-dev)
     >>> 
 
 On Windows, please open a command prompt (``cmd.exe``) and make sure that you have 
 administrator privileges::
 
-    C:\scapy> python scapy.py
-    Welcome to Scapy (1.2.0.2-win)
-    >>> 
+    C:\>scapy
+    INFO: No IPv6 support in kernel
+    WARNING: No route found for IPv6 destination :: (no default route?)
+    Welcome to Scapy (2.0.1-dev)
+    >>>
 
 If you do not have all optional packages installed, Scapy will inform you that 
 some features will not be available:: 
-
+                                 
     INFO: Can't import python gnuplot wrapper . Won't be able to plot.
     INFO: Can't import PyX. Won't be able to use psdump() or pdfdump().
 
@@ -170,7 +172,7 @@ For the moment, we have only generated one packet. Let see how to specify sets o
 
     >>> a=IP(dst="www.slashdot.org/30")
     >>> a
-    <IP dst= |>
+    <IP  dst=Net('www.slashdot.org/30') |>
     >>> [p for p in a]
     [<IP dst=66.35.250.148 |>, <IP dst=66.35.250.149 |>,
      <IP dst=66.35.250.150 |>, <IP dst=66.35.250.151 |>]
@@ -298,7 +300,7 @@ Now, let's try to do some fun things. The sr() function is for sending packets a
 .. index::
    single: DNS, Etherleak
 
-A DNS query (``rd`` = recursion desired). Note the non-null padding coming from my Linksys having the Etherleak flaw::
+A DNS query (``rd`` = recursion desired). The host 192.168.5.1 is my DNS server. Note the non-null padding coming from my Linksys having the Etherleak flaw::
 
     >>> sr1(IP(dst="192.168.5.1")/UDP()/DNS(rd=1,qd=DNSQR(qname="www.slashdot.org")))
     Begin emission:
@@ -405,12 +407,12 @@ The above example will even print the ICMP error type if the ICMP packet was rec
 
 For larger scans, we could be interested in displaying only certain responses. The example below will only display packets with the “SA” flag set::
 
-    >>> ans.nsummary(lfilter = lambda (s,r): r.sprintf("%TCP.flags%") ====== "SA")
+    >>> ans.nsummary(lfilter = lambda (s,r): r.sprintf("%TCP.flags%") == "SA")
     0003 IP / TCP 192.168.1.100:ftp_data > 192.168.1.1:https S ======> IP / TCP 192.168.1.1:https > 192.168.1.100:ftp_data SA
 
 In case we want to do some expert analysis of responses, we can use the following command to indicate which ports are open::
 
-    >>> ans.summary(lfilter = lambda (s,r): r.sprintf("%TCP.flags%") ====== "SA",prn=lambda(s,r):r.sprintf("%TCP.sport% is open"))
+    >>> ans.summary(lfilter = lambda (s,r): r.sprintf("%TCP.flags%") == "SA",prn=lambda(s,r):r.sprintf("%TCP.sport% is open"))
     https is open
 
 Again, for larger scans we can build a table of open ports::
@@ -627,6 +629,7 @@ We can sniff and do passive OS fingerprinting::
      seq=2023566040L ack=0L dataofs=10L reserved=0L flags=SEC window=5840
      chksum=0x570c urgptr=0 options=[('Timestamp', (342940201L, 0L)), ('MSS', 1460),
      ('NOP', ()), ('SAckOK', ''), ('WScale', 0)] |>>>
+    >>> load_module("p0f")
     >>> p0f(p)
     (1.0, ['Linux 2.4.2 - 2.4.14 (1)'])
     >>> a=sniff(prn=prnp0f)
@@ -797,7 +800,7 @@ Using the ``export_object()`` function, Scapy can export a base64 encoded Python
     WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6
     ...
 
-The output above can be reimported back into Skype using ``import_object()``::
+The output above can be reimported back into Scapy using ``import_object()``::
 
     >>> new_pkt = import_object()
     eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST
@@ -1487,11 +1490,15 @@ Once we obtain a reasonable number of responses we can start analyzing collected
 nmap_fp
 ^^^^^^^
 
-If you have nmap installed you can use it's active os fingerprinting database with Scapy. First make sure that version 1 of signature database is located in the path specified by::
+Nmap fingerprinting (the old "1st generation" one that was done by Nmap up to v4.20) is supported in Scapy. In Scapy v2 you have to load an extension module first::
+
+    >>> load_module("nmap")
+
+If you have Nmap installed you can use it's active os fingerprinting database with Scapy. Make sure that version 1 of signature database is located in the path specified by::
 
     >>> conf.nmap_base
 
-Scapy includes a built-in ``nmap_fp()`` function which implements same probes as in Nmap's OS Detection engine::
+Then you can use the ``nmap_fp()`` function which implements same probes as in Nmap's OS Detection engine::
 
     >>> nmap_fp("192.168.1.1",oport=443,cport=1)
     Begin emission:
diff --git a/graphics/ATMT_HelloWorld.png b/graphics/ATMT_HelloWorld.png
deleted file mode 100644
index e5b5ddb188a64bba6f71d07ff850d4f9601a0ce5..0000000000000000000000000000000000000000
Binary files a/graphics/ATMT_HelloWorld.png and /dev/null differ
diff --git a/graphics/ATMT_TFTP_read.png b/graphics/ATMT_TFTP_read.png
deleted file mode 100644
index 50621d976ac1addc852537ac49343ffd0e9e7209..0000000000000000000000000000000000000000
Binary files a/graphics/ATMT_TFTP_read.png and /dev/null differ
diff --git a/run_scapy.bat b/run_scapy.bat
new file mode 100644
index 0000000000000000000000000000000000000000..ba19aa5e9445a1d453c1f00c0d9e4e6a5bf1590a
--- /dev/null
+++ b/run_scapy.bat
@@ -0,0 +1,3 @@
+@echo off
+set PYTHONPATH=%cd% 
+python -m scapy.__init__
diff --git a/scapy/ansmachine.py b/scapy/ansmachine.py
index 816a0205f19ac1b722b6c47f21ef4550f1e1263c..88c7f5d2e55d1b4d9ed98a5c817a979c5fc33aa4 100644
--- a/scapy/ansmachine.py
+++ b/scapy/ansmachine.py
@@ -24,7 +24,7 @@ class AnsweringMachine(object):
     function_name = ""
     filter = None
     sniff_options = { "store":0 }
-    sniff_options_list = [ "store", "iface", "count", "promisc", "filter", "type", "prn" ]
+    sniff_options_list = [ "store", "iface", "count", "promisc", "filter", "type", "prn", "stop_filter" ]
     send_options = { "verbose":0 }
     send_options_list = ["iface", "inter", "loop", "verbose"]
     send_function = staticmethod(send)
diff --git a/scapy/arch/__init__.py b/scapy/arch/__init__.py
index b46309f9a8962ef12e5024120e77a692695988b7..0963c8d0bf45bf04abd4e6bc76200c8f69065aa6 100644
--- a/scapy/arch/__init__.py
+++ b/scapy/arch/__init__.py
@@ -45,8 +45,9 @@ FREEBSD=sys.platform.startswith("freebsd")
 NETBSD = sys.platform.startswith("netbsd")
 DARWIN=sys.platform.startswith("darwin")
 SOLARIS=sys.platform.startswith("sunos")
+WINDOWS=sys.platform.startswith("win32")
 
-X86_64 = (os.uname()[4] == 'x86_64')
+X86_64 = not WINDOWS and (os.uname()[4] == 'x86_64')
 
 
 # Next step is to import following architecture specific functions:
@@ -70,7 +71,9 @@ elif OPENBSD or FREEBSD or NETBSD or DARWIN:
     from bsd import *
 elif SOLARIS:
     from solaris import *
-    
+elif WINDOWS:
+    from windows import *
+
 if scapy.config.conf.iface is None:
     scapy.config.conf.iface = LOOPBACK_NAME
 
diff --git a/scapy/arch/linux.py b/scapy/arch/linux.py
index 68089bd00b718721cf1851756d2a5b010354f4f9..4f5bb13155b2d1121ae58e614229780bace397a5 100644
--- a/scapy/arch/linux.py
+++ b/scapy/arch/linux.py
@@ -66,13 +66,13 @@ RTF_REJECT = 0x0200
 
 LOOPBACK_NAME="lo"
 
-with os.popen("tcpdump -V 2> /dev/null") as f:
-    if f.close() >> 8 == 0x7f:
+with os.popen("tcpdump -V 2> /dev/null") as _f:
+    if _f.close() >> 8 == 0x7f:
         log_loading.warning("Failed to execute tcpdump. Check it is installed and in the PATH")
         TCPDUMP=0
     else:
         TCPDUMP=1
-        
+del(_f)
     
 
 def get_if_raw_hwaddr(iff):
@@ -271,11 +271,16 @@ def get_if(iff,cmd):
 def get_if_index(iff):
     return int(struct.unpack("I",get_if(iff, SIOCGIFINDEX)[16:20])[0])
 
-def get_last_packet_timestamp(sock):
-    ts = ioctl(sock, SIOCGSTAMP, "12345678")
-    s,us = struct.unpack("II",ts)
-    return s+us/1000000.0
-
+if os.uname()[4] == 'x86_64':
+    def get_last_packet_timestamp(sock):
+        ts = ioctl(sock, SIOCGSTAMP, "1234567890123456")
+        s,us = struct.unpack("QQ",ts)
+        return s+us/1000000.0
+else:
+    def get_last_packet_timestamp(sock):
+        ts = ioctl(sock, SIOCGSTAMP, "12345678")
+        s,us = struct.unpack("II",ts)
+        return s+us/1000000.0
 
 
 def _flush_fd(fd):
@@ -333,7 +338,7 @@ class L3PacketSocket(SuperSocket):
             for i in self.iff:
                 set_promisc(self.ins, i, 0)
         SuperSocket.close(self)
-    def recv(self, x):
+    def recv(self, x=MTU):
         pkt, sa_ll = self.ins.recvfrom(x)
         if sa_ll[2] == socket.PACKET_OUTGOING:
             return None
@@ -371,8 +376,8 @@ class L3PacketSocket(SuperSocket):
         self.outs.bind(sdto)
         sn = self.outs.getsockname()
         ll = lambda x:x
-        if sn[3] in (ARPHDR_PPP,ARPHDR_TUN):
-            sdto = (iff, ETH_P_IP)
+        if type(x) in conf.l3types:
+            sdto = (iff, conf.l3types[type(x)])
         if sn[3] in conf.l2types:
             ll = lambda x:conf.l2types[sn[3]]()/x
         try:
@@ -382,7 +387,7 @@ class L3PacketSocket(SuperSocket):
         except socket.error,msg:
             x.sent_time = time.time()  # bad approximation
             if conf.auto_fragment and msg[0] == 90:
-                for p in fragment(x):
+                for p in x.fragment():
                     self.outs.sendto(str(ll(p)), sdto)
             else:
                 raise
@@ -419,7 +424,7 @@ class L2Socket(SuperSocket):
             self.LL = conf.default_l2
             warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s" % (sa_ll[0],sa_ll[1],sa_ll[3],self.LL.name))
             
-    def recv(self, x):
+    def recv(self, x=MTU):
         pkt, sa_ll = self.ins.recvfrom(x)
         if sa_ll[2] == socket.PACKET_OUTGOING:
             return None
diff --git a/scapy/arch/pcapdnet.py b/scapy/arch/pcapdnet.py
index d5b70d5433e4f329a615525587ba6a0d71c04816..60fe74258636181cd7972bb2f246d4bb055b86de 100644
--- a/scapy/arch/pcapdnet.py
+++ b/scapy/arch/pcapdnet.py
@@ -3,15 +3,18 @@
 ## Copyright (C) Philippe Biondi <phil@secdev.org>
 ## This program is published under a GPLv2 license
 
-import time,struct
-from fcntl import ioctl
+import time,struct,sys
+if not sys.platform.startswith("win"):
+    from fcntl import ioctl
 from scapy.data import *
 from scapy.config import conf
 from scapy.utils import warning
 from scapy.supersocket import SuperSocket
+from scapy.error import Scapy_Exception
 import scapy.arch
 
 
+
 if conf.use_pcap:    
 
 
@@ -34,7 +37,17 @@ if conf.use_pcap:
         BIOCIMMEDIATE=-2147204496
 
         if hasattr(pcap,"pcap"): # python-pypcap
-            open_pcap = pcap.pcap
+            class _PcapWrapper_pypcap:
+                def __init__(self, device, snaplen, promisc, to_ms):
+                    # Normal pypcap module has no timeout parameter,
+                    # only the specially patched "scapy" variant has.                 
+                    if "scapy" in pcap.__version__.lower():
+                        self.pcap = pcap.pcap(device, snaplen, promisc, immediate=1, timeout_ms=to_ms)
+                    else:
+                        self.pcap = pcap.pcap(device, snaplen, promisc, immediate=1)                    
+                def __getattr__(self, attr):
+                    return getattr(self.pcap, attr)
+            open_pcap = lambda *args,**kargs: _PcapWrapper_pypcap(*args,**kargs)
         elif hasattr(pcap,"pcapObject"): # python-libpcap
             class _PcapWrapper_libpcap:
                 def __init__(self, *args, **kargs):
@@ -70,8 +83,10 @@ if conf.use_pcap:
                 def __getattr__(self, attr):
                     return getattr(self.pcap, attr)
             open_pcap = lambda *args,**kargs: _PcapWrapper_pcapy(*args,**kargs)
+
         
-    
+        class PcapTimeoutElapsed(Scapy_Exception):
+            pass
     
         class L2pcapListenSocket(SuperSocket):
             desc = "read packets at layer 2 using libpcap"
@@ -101,7 +116,7 @@ if conf.use_pcap:
             def close(self):
                 del(self.ins)
                 
-            def recv(self, x):
+            def recv(self, x=MTU):
                 ll = self.ins.datalink()
                 if ll in conf.l2types:
                     cls = conf.l2types[ll]
@@ -114,6 +129,8 @@ if conf.use_pcap:
                     pkt = self.ins.next()
                     if pkt is not None:
                         ts,pkt = pkt
+                    if scapy.arch.WINDOWS and pkt is None:
+                        raise PcapTimeoutElapsed
                 
                 try:
                     pkt = cls(pkt)
@@ -291,7 +308,7 @@ if conf.use_pcap and conf.use_dnet:
             if filter:
                 self.ins.setfilter(filter)
             self.outs = dnet.eth(iface)
-        def recv(self,x):
+        def recv(self,x=MTU):
             ll = self.ins.datalink()
             if ll in conf.l2types:
                 cls = conf.l2types[ll]
diff --git a/scapy/arch/windows/__init__.py b/scapy/arch/windows/__init__.py
new file mode 100755
index 0000000000000000000000000000000000000000..f80eaf2b726edceafd89ae1b3857a17d95353210
--- /dev/null
+++ b/scapy/arch/windows/__init__.py
@@ -0,0 +1,539 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+import os,re,sys,socket,time
+from glob import glob
+from scapy.config import conf,ConfClass
+from scapy.error import Scapy_Exception,log_loading,log_runtime
+from scapy.utils import atol, inet_aton, inet_ntoa, PcapReader
+from scapy.base_classes import Gen, Net, SetGen
+import scapy.plist as plist
+from scapy.sendrecv import debug, srp1
+from scapy.layers.l2 import Ether, ARP
+from scapy.data import MTU, ETHER_BROADCAST, ETH_P_ARP
+
+conf.use_pcap = 1
+conf.use_dnet = 1
+from scapy.arch import pcapdnet
+from scapy.arch.pcapdnet import *
+
+LOOPBACK_NAME="lo0"
+WINDOWS = True
+
+
+def _where(filename, dirs=[], env="PATH"):
+    """Find file in current dir or system path"""
+    if not isinstance(dirs, list):
+        dirs = [dirs]
+    if glob(filename):
+        return filename
+    paths = [os.curdir] + os.environ[env].split(os.path.pathsep) + dirs
+    for path in paths:
+        for match in glob(os.path.join(path, filename)):
+            if match:
+                return os.path.normpath(match)
+    raise IOError("File not found: %s" % filename)
+
+def win_find_exe(filename, installsubdir=None, env="ProgramFiles"):
+    """Find executable in current dir, system path or given ProgramFiles subdir"""
+    for fn in [filename, filename+".exe"]:
+        try:
+            if installsubdir is None:
+                path = _where(fn)
+            else:
+                path = _where(fn, dirs=[os.path.join(os.environ[env], installsubdir)])
+        except IOError:
+            path = filename
+        else:
+            break        
+    return path
+
+
+class WinProgPath(ConfClass):
+    _default = "<System default>"
+    # We try some magic to find the appropriate executables
+    pdfreader = win_find_exe("AcroRd32") 
+    psreader = win_find_exe("gsview32.exe", "Ghostgum/gsview")
+    dot = win_find_exe("dot", "ATT/Graphviz/bin")
+    tcpdump = win_find_exe("windump")
+    tcpreplay = win_find_exe("tcpreplay")
+    display = _default
+    hexedit = win_find_exe("hexer")
+    wireshark = win_find_exe("wireshark", "wireshark")
+
+conf.prog = WinProgPath()
+
+
+
+import _winreg
+
+
+    
+class PcapNameNotFoundError(Scapy_Exception):
+    pass    
+
+class NetworkInterface(object):
+    """A network interface of your local host"""
+    
+    def __init__(self, dnetdict=None):
+        self.name = None
+        self.ip = None
+        self.mac = None
+        self.pcap_name = None
+        self.win_name = None
+        self.uuid = None
+        self.dnetdict = dnetdict
+        if dnetdict is not None:
+            self.update(dnetdict)
+        
+    def update(self, dnetdict):
+        """Update info about network interface according to given dnet dictionary"""
+        self.name = dnetdict["name"]
+        # Other attributes are optional
+        try:
+            self.ip = socket.inet_ntoa(dnetdict["addr"].ip)
+        except (KeyError, AttributeError, NameError):
+            pass
+        try:
+            self.mac = dnetdict["link_addr"]
+        except KeyError:
+            pass
+        self._update_pcapdata()
+    
+    def _update_pcapdata(self):
+        """Supplement more info from pypcap and the Windows registry"""
+        
+        # XXX: We try eth0 - eth29 by bruteforce and match by IP address, 
+        # because only the IP is available in both pypcap and dnet.
+        # This may not work with unorthodox network configurations and is
+        # slow because we have to walk through the Windows registry.
+        for n in range(30):
+            guess = "eth%s" % n
+            win_name = pcapdnet.pcap.ex_name(guess)
+            if win_name.endswith("}"):
+                try:
+                    uuid = win_name[win_name.index("{"):win_name.index("}")+1]
+                    keyname = r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\%s" % uuid
+                    try:
+                        key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keyname)
+                    except WindowsError:
+                        log_loading.debug("Couldn't open 'HKEY_LOCAL_MACHINE\\%s' (for guessed pcap iface name '%s')." % (keyname, guess))
+                        continue
+                    try:    
+                        fixed_ip = _winreg.QueryValueEx(key, "IPAddress")[0][0].encode("utf-8")
+                    except (WindowsError, UnicodeDecodeError, IndexError):
+                        fixed_ip = None
+                    try:
+                        dhcp_ip = _winreg.QueryValueEx(key, "DhcpIPAddress")[0].encode("utf-8")
+                    except (WindowsError, UnicodeDecodeError, IndexError):
+                        dhcp_ip = None
+                    # "0.0.0.0" or None means the value is not set (at least not correctly).
+                    # If both fixed_ip and dhcp_ip are set, fixed_ip takes precedence 
+                    if fixed_ip is not None and fixed_ip != "0.0.0.0":
+                        ip = fixed_ip
+                    elif dhcp_ip is not None and dhcp_ip != "0.0.0.0":
+                        ip = dhcp_ip
+                    else:
+                        continue
+                except IOError:
+                    continue
+                else:
+                    if ip == self.ip:
+                        self.pcap_name = guess
+                        self.win_name = win_name
+                        self.uuid = uuid
+                        break
+        else:
+            raise PcapNameNotFoundError
+    
+    def __repr__(self):
+        return "<%s: %s %s %s pcap_name=%s win_name=%s>" % (self.__class__.__name__,
+                     self.name, self.ip, self.mac, self.pcap_name, self.win_name)
+
+from UserDict import IterableUserDict
+
+class NetworkInterfaceDict(IterableUserDict):
+    """Store information about network interfaces and convert between names""" 
+    
+    def load_from_dnet(self):
+        """Populate interface table via dnet"""
+        for i in pcapdnet.dnet.intf():
+            try:
+                # XXX: Only Ethernet for the moment: localhost is not supported by dnet and pcap
+                # We only take interfaces that have an IP address, because the IP
+                # is used for the mapping between dnet and pcap interface names
+                # and this significantly improves Scapy's startup performance
+                if i["name"].startswith("eth") and "addr" in i:
+                    self.data[i["name"]] = NetworkInterface(i)
+            except (KeyError, PcapNameNotFoundError):
+                pass
+        if len(self.data) == 0:
+            log_loading.warning("No match between your pcap and dnet network interfaces found. "
+                                "You probably won't be able to send packets. "
+                                "Deactivating unneeded interfaces and restarting Scapy might help.")
+    
+    def pcap_name(self, devname):
+        """Return pypcap device name for given libdnet/Scapy device name
+        
+        This mapping is necessary because pypcap numbers the devices differently."""
+        
+        try:
+            pcap_name = self.data[devname].pcap_name
+        except KeyError:
+            raise ValueError("Unknown network interface %r" % devname)
+        else:
+            return pcap_name
+            
+    def devname(self, pcap_name):
+        """Return libdnet/Scapy device name for given pypcap device name
+        
+        This mapping is necessary because pypcap numbers the devices differently."""
+        
+        for devname, iface in self.items():
+            if iface.pcap_name == pcap_name:
+                return iface.name
+        raise ValueError("Unknown pypcap network interface %r" % pcap_name)
+    
+    def show(self, resolve_mac=True):
+        """Print list of available network interfaces in human readable form"""
+        print "%s  %s  %s" % ("IFACE".ljust(5), "IP".ljust(15), "MAC")
+        for iface_name in sorted(self.data.keys()):
+            dev = self.data[iface_name]
+            mac = str(dev.mac)
+            if resolve_mac:
+                mac = conf.manufdb._resolve_MAC(mac)
+            print "%s  %s  %s" % (str(dev.name).ljust(5), str(dev.ip).ljust(15), mac)     
+            
+ifaces = NetworkInterfaceDict()
+ifaces.load_from_dnet()
+
+def pcap_name(devname):
+    """Return pypcap device name for given libdnet/Scapy device name"""  
+    try:
+        pcap_name = ifaces.pcap_name(devname)
+    except ValueError:
+        # pcap.pcap() will choose a sensible default for sniffing if iface=None
+        pcap_name = None
+    return pcap_name            
+
+def devname(pcap_name):
+    """Return libdnet/Scapy device name for given pypcap device name"""
+    return ifaces.devname(pcap_name)
+    
+def show_interfaces(resolve_mac=True):
+    """Print list of available network interfaces"""
+    return ifaces.show(resolve_mac)
+
+_orig_open_pcap = pcapdnet.open_pcap
+pcapdnet.open_pcap = lambda iface,*args,**kargs: _orig_open_pcap(pcap_name(iface),*args,**kargs)
+
+def read_routes():
+    ok = 0
+    routes = []
+    ip = '(\d+\.\d+\.\d+\.\d+)'
+    # On Vista and Windows 7 the gateway can be IP or 'On-link'.
+    # But the exact 'On-link' string depends on the locale, so we allow any text.
+    gw_pattern = '(.+)'
+    metric_pattern = "(\d+)"
+    delim = "\s+"        # The columns are separated by whitespace
+    netstat_line = delim.join([ip, ip, gw_pattern, ip, metric_pattern])
+    pattern = re.compile(netstat_line)
+    f=os.popen("netstat -rn")
+    for l in f.readlines():
+        match = re.search(pattern,l)
+        if match:
+            dest   = match.group(1)
+            mask   = match.group(2)
+            gw     = match.group(3)
+            netif  = match.group(4)
+            metric = match.group(5)
+            try:
+                intf = pcapdnet.dnet.intf().get_dst(pcapdnet.dnet.addr(type=2, addrtxt=dest))
+            except OSError:
+                log_loading.warning("Building Scapy's routing table: Couldn't get outgoing interface for destination %s" % dest)
+                continue               
+            if not intf.has_key("addr"):
+                break
+            addr = str(intf["addr"])
+            addr = addr.split("/")[0]
+            
+            dest = atol(dest)
+            mask = atol(mask)
+            # If the gateway is no IP we assume it's on-link
+            gw_ipmatch = re.search('\d+\.\d+\.\d+\.\d+', gw)
+            if gw_ipmatch:
+                gw = gw_ipmatch.group(0)
+            else:
+                gw = netif
+            routes.append((dest,mask,gw, str(intf["name"]), addr))
+    f.close()
+    return routes
+
+def read_routes6():
+    return []
+
+def getmacbyip(ip, chainCC=0):
+    """Return MAC address corresponding to a given IP address"""
+    if isinstance(ip,Net):
+        ip = iter(ip).next()
+    tmp = map(ord, inet_aton(ip))
+    if (tmp[0] & 0xf0) == 0xe0: # mcast @
+        return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3])
+    iff,a,gw = conf.route.route(ip)
+    if ( (iff == LOOPBACK_NAME) or (ip == conf.route.get_if_bcast(iff)) ):
+        return "ff:ff:ff:ff:ff:ff"
+    # Windows uses local IP instead of 0.0.0.0 to represent locally reachable addresses
+    ifip = str(pcapdnet.dnet.intf().get(iff)['addr'])
+    if gw != ifip.split('/')[0]:
+        ip = gw
+
+    mac = conf.netcache.arp_cache.get(ip)
+    if mac:
+        return mac
+
+    res = srp1(Ether(dst=ETHER_BROADCAST)/ARP(op="who-has", pdst=ip),
+               type=ETH_P_ARP,
+               iface = iff,
+               timeout=2,
+               verbose=0,
+               chainCC=chainCC,
+               nofilter=1)
+    if res is not None:
+        mac = res.payload.hwsrc
+        conf.netcache.arp_cache[ip] = mac
+        return mac
+    return None
+
+import scapy.layers.l2
+scapy.layers.l2.getmacbyip = getmacbyip
+
+try:
+    import readline
+    console = readline.GetOutputFile()
+except (ImportError, AttributeError):
+    log_loading.info("Could not get readline console. Will not interpret ANSI color codes.") 
+else:
+    conf.readfunc = readline.rl.readline
+    orig_stdout = sys.stdout
+    sys.stdout = console
+
+
+
+
+
+def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, multi=0):
+    if not isinstance(pkt, Gen):
+        pkt = SetGen(pkt)
+        
+    if verbose is None:
+        verbose = conf.verb
+    debug.recv = plist.PacketList([],"Unanswered")
+    debug.sent = plist.PacketList([],"Sent")
+    debug.match = plist.SndRcvList([])
+    nbrecv=0
+    ans = []
+    # do it here to fix random fields, so that parent and child have the same
+    all_stimuli = tobesent = [p for p in pkt]
+    notans = len(tobesent)
+
+    hsent={}
+    for i in tobesent:
+        h = i.hashret()
+        if h in hsent:
+            hsent[h].append(i)
+        else:
+            hsent[h] = [i]
+    if retry < 0:
+        retry = -retry
+        autostop=retry
+    else:
+        autostop=0
+
+
+    while retry >= 0:
+        found=0
+    
+        if timeout < 0:
+            timeout = None
+
+        pid=1
+        try:
+            if WINDOWS or pid == 0:
+                try:
+                    try:
+                        i = 0
+                        if verbose:
+                            print "Begin emission:"
+                        for p in tobesent:
+                            pks.send(p)
+                            i += 1
+                            time.sleep(inter)
+                        if verbose:
+                            print "Finished to send %i packets." % i
+                    except SystemExit:
+                        pass
+                    except KeyboardInterrupt:
+                        pass
+                    except:
+                        log_runtime.exception("--- Error sending packets")
+                        log_runtime.info("--- Error sending packets")
+                finally:
+                    try:
+                        sent_times = [p.sent_time for p in all_stimuli if p.sent_time]
+                    except:
+                        pass
+            if WINDOWS or pid > 0:
+                # Timeout starts after last packet is sent (as in Unix version) 
+                if timeout:
+                    stoptime = time.time()+timeout
+                else:
+                    stoptime = 0
+                remaintime = None
+                inmask = [pks.ins.fd]
+                try:
+                    try:
+                        while 1:
+                            if stoptime:
+                                remaintime = stoptime-time.time()
+                                if remaintime <= 0:
+                                    break
+                            r = pks.recv(MTU)
+                            if r is None:
+                                continue
+                            ok = 0
+                            h = r.hashret()
+                            if h in hsent:
+                                hlst = hsent[h]
+                                for i in range(len(hlst)):
+                                    if r.answers(hlst[i]):
+                                        ans.append((hlst[i],r))
+                                        if verbose > 1:
+                                            os.write(1, "*")
+                                        ok = 1                                
+                                        if not multi:
+                                            del(hlst[i])
+                                            notans -= 1;
+                                        else:
+                                            if not hasattr(hlst[i], '_answered'):
+                                                notans -= 1;
+                                            hlst[i]._answered = 1;
+                                        break
+                            if notans == 0 and not multi:
+                                break
+                            if not ok:
+                                if verbose > 1:
+                                    os.write(1, ".")
+                                nbrecv += 1
+                                if conf.debug_match:
+                                    debug.recv.append(r)
+                    except KeyboardInterrupt:
+                        if chainCC:
+                            raise
+                finally:
+                    if WINDOWS:
+                        for p,t in zip(all_stimuli, sent_times):
+                            p.sent_time = t
+        finally:
+            pass
+
+        remain = reduce(list.__add__, hsent.values(), [])
+        if multi:
+            remain = filter(lambda p: not hasattr(p, '_answered'), remain);
+            
+        if autostop and len(remain) > 0 and len(remain) != len(tobesent):
+            retry = autostop
+            
+        tobesent = remain
+        if len(tobesent) == 0:
+            break
+        retry -= 1
+        
+    if conf.debug_match:
+        debug.sent=plist.PacketList(remain[:],"Sent")
+        debug.match=plist.SndRcvList(ans[:])
+
+    #clean the ans list to delete the field _answered
+    if (multi):
+        for s,r in ans:
+            if hasattr(s, '_answered'):
+                del(s._answered)
+    
+    if verbose:
+        print "\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans)
+    return plist.SndRcvList(ans),plist.PacketList(remain,"Unanswered")
+
+
+import scapy.sendrecv
+scapy.sendrecv.sndrcv = sndrcv
+
+def sniff(count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, *arg, **karg):
+    """Sniff packets
+sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets
+
+  count: number of packets to capture. 0 means infinity
+  store: wether to store sniffed packets or discard them
+    prn: function to apply to each packet. If something is returned,
+         it is displayed. Ex:
+         ex: prn = lambda x: x.summary()
+lfilter: python function applied to each packet to determine
+         if further action may be done
+         ex: lfilter = lambda x: x.haslayer(Padding)
+offline: pcap file to read packets from, instead of sniffing them
+timeout: stop sniffing after a given time (default: None)
+L2socket: use the provided L2socket
+    """
+    c = 0
+
+    if offline is None:
+        if L2socket is None:
+            L2socket = conf.L2listen
+        s = L2socket(type=ETH_P_ALL, *arg, **karg)
+    else:
+        s = PcapReader(offline)
+
+    lst = []
+    if timeout is not None:
+        stoptime = time.time()+timeout
+    remain = None
+    while 1:
+        try:
+            if timeout is not None:
+                remain = stoptime-time.time()
+                if remain <= 0:
+                    break
+
+            try:
+                p = s.recv(MTU)
+            except PcapTimeoutElapsed:
+                continue
+            if p is None:
+                break
+            if lfilter and not lfilter(p):
+                continue
+            if store:
+                lst.append(p)
+            c += 1
+            if prn:
+                r = prn(p)
+                if r is not None:
+                    print >> console, r
+            if count > 0 and c >= count:
+                break
+        except KeyboardInterrupt:
+            break
+    s.close()
+    return plist.PacketList(lst,"Sniffed")
+
+import scapy.sendrecv
+scapy.sendrecv.sniff = sniff
+
+def get_if_list():
+    return sorted(ifaces.keys())
+        
+def get_working_if():
+    try:
+        return devname(pcap.lookupdev())
+    except Exception:
+        return 'lo0'
diff --git a/scapy/asn1/asn1.py b/scapy/asn1/asn1.py
index c8c42f72d40ffe74382809d30bdf1f7a630b3549..f333cbe3ca646fa393556ff4a87e03920204fb25 100644
--- a/scapy/asn1/asn1.py
+++ b/scapy/asn1/asn1.py
@@ -20,6 +20,9 @@ class RandASN1Object(RandField):
         o = random.choice(self.objlist)
         if issubclass(o, ASN1_INTEGER):
             return o(int(random.gauss(0,1000)))
+        elif issubclass(o, ASN1_IPADDRESS):
+            z = RandIP()._fix()
+            return o(z)
         elif issubclass(o, ASN1_STRING):
             z = int(random.expovariate(0.05)+1)
             return o("".join([random.choice(self.chars) for i in range(z)]))
@@ -161,7 +164,9 @@ class ASN1_Class_UNIVERSAL(ASN1_Class):
     BMP_STRING = 30
     IPADDRESS = 0x40
     COUNTER32 = 0x41
+    GAUGE32 = 0x42
     TIME_TICKS = 0x43
+    SEP = 0x80
 
 class ASN1_Object_metaclass(type):
     def __new__(cls, name, bases, dct):
@@ -246,15 +251,27 @@ class ASN1_IPADDRESS(ASN1_STRING):
 class ASN1_UTC_TIME(ASN1_STRING):
     tag = ASN1_Class_UNIVERSAL.UTC_TIME
 
+class ASN1_GENERALIZED_TIME(ASN1_STRING):
+    tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME
+
 class ASN1_TIME_TICKS(ASN1_INTEGER):
     tag = ASN1_Class_UNIVERSAL.TIME_TICKS
 
 class ASN1_BOOLEAN(ASN1_INTEGER):
     tag = ASN1_Class_UNIVERSAL.BOOLEAN
+
+class ASN1_ENUMERATED(ASN1_INTEGER):
+    tag = ASN1_Class_UNIVERSAL.ENUMERATED
     
 class ASN1_NULL(ASN1_INTEGER):
     tag = ASN1_Class_UNIVERSAL.NULL
 
+class ASN1_SEP(ASN1_NULL):
+    tag = ASN1_Class_UNIVERSAL.SEP
+
+class ASN1_GAUGE32(ASN1_INTEGER):
+    tag = ASN1_Class_UNIVERSAL.GAUGE32
+    
 class ASN1_COUNTER32(ASN1_INTEGER):
     tag = ASN1_Class_UNIVERSAL.COUNTER32
     
@@ -276,6 +293,8 @@ class ASN1_OID(ASN1_Object):
         ASN1_Object.__init__(self, val)
     def __repr__(self):
         return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), conf.mib._oidname(self.val))
+    def __oidname__(self):
+        return '%s'%conf.mib._oidname(self.val)
     
 
 
diff --git a/scapy/asn1/ber.py b/scapy/asn1/ber.py
index 25c913dd63388d44329b4fca4159df82b260d56a..1e66ffd7dc5ad98fb18fe40d06c9cc4efddef0d3 100644
--- a/scapy/asn1/ber.py
+++ b/scapy/asn1/ber.py
@@ -223,6 +223,9 @@ class BERcodec_INTEGER(BERcodec_Object):
 class BERcodec_BOOLEAN(BERcodec_INTEGER):
     tag = ASN1_Class_UNIVERSAL.BOOLEAN
 
+class BERcodec_ENUMERATED(BERcodec_INTEGER):
+    tag = ASN1_Class_UNIVERSAL.ENUMERATED
+
 class BERcodec_NULL(BERcodec_INTEGER):
     tag = ASN1_Class_UNIVERSAL.NULL
     @classmethod
@@ -230,7 +233,10 @@ class BERcodec_NULL(BERcodec_INTEGER):
         if i == 0:
             return chr(cls.tag)+"\0"
         else:
-            return super(cls,cls).enc(i)
+            return BERcodec_INTEGER.enc(i)
+
+class BERcodec_SEP(BERcodec_NULL):
+    tag = ASN1_Class_UNIVERSAL.SEP
 
 class BERcodec_STRING(BERcodec_Object):
     tag = ASN1_Class_UNIVERSAL.STRING
@@ -254,6 +260,12 @@ class BERcodec_T61_STRING (BERcodec_STRING):
 class BERcodec_IA5_STRING(BERcodec_STRING):
     tag = ASN1_Class_UNIVERSAL.IA5_STRING
 
+class BERcodec_NUMERIC_STRING(BERcodec_STRING):
+    tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING
+
+class BERcodec_VIDEOTEX_STRING(BERcodec_STRING):
+    tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING
+
 class BERcodec_IPADDRESS(BERcodec_STRING):
     tag = ASN1_Class_UNIVERSAL.IPADDRESS
     
@@ -277,9 +289,15 @@ class BERcodec_IPADDRESS(BERcodec_STRING):
 class BERcodec_UTC_TIME(BERcodec_STRING):
     tag = ASN1_Class_UNIVERSAL.UTC_TIME
 
+class BERcodec_GENERALIZED_TIME(BERcodec_STRING):
+    tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME
+
 class BERcodec_TIME_TICKS(BERcodec_INTEGER):
     tag = ASN1_Class_UNIVERSAL.TIME_TICKS
 
+class BERcodec_GAUGE32(BERcodec_INTEGER):
+    tag = ASN1_Class_UNIVERSAL.GAUGE32
+
 class BERcodec_COUNTER32(BERcodec_INTEGER):
     tag = ASN1_Class_UNIVERSAL.COUNTER32
 
diff --git a/scapy/asn1fields.py b/scapy/asn1fields.py
index 83722125e8b6298d606d83cfc6e7c1d6d0ac4ec0..c32fbe31b6bcb86fa6116478d217ec48a05e63d2 100644
--- a/scapy/asn1fields.py
+++ b/scapy/asn1fields.py
@@ -123,6 +123,9 @@ class ASN1F_BOOLEAN(ASN1F_field):
 class ASN1F_NULL(ASN1F_INTEGER):
     ASN1_tag= ASN1_Class_UNIVERSAL.NULL
 
+class ASN1F_SEP(ASN1F_NULL):
+    ASN1_tag= ASN1_Class_UNIVERSAL.SEP
+
 class ASN1F_enum_INTEGER(ASN1F_INTEGER):
     def __init__(self, name, default, enum):
         ASN1F_INTEGER.__init__(self, name, default)
@@ -155,6 +158,9 @@ class ASN1F_enum_INTEGER(ASN1F_INTEGER):
         else:
             return self.i2repr_one(pkt,x)
 
+class ASN1F_ENUMERATED(ASN1F_enum_INTEGER):
+    ASN1_tag = ASN1_Class_UNIVERSAL.ENUMERATED
+
 class ASN1F_STRING(ASN1F_field):
     ASN1_tag = ASN1_Class_UNIVERSAL.STRING
     def randval(self):
@@ -175,6 +181,9 @@ class ASN1F_TIME_TICKS(ASN1F_INTEGER):
 class ASN1F_UTC_TIME(ASN1F_STRING):
     ASN1_tag = ASN1_Class_UNIVERSAL.UTC_TIME
 
+class ASN1F_GENERALIZED_TIME(ASN1F_STRING):
+    ASN1_tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME
+
 class ASN1F_OID(ASN1F_field):
     ASN1_tag = ASN1_Class_UNIVERSAL.OID
     def randval(self):
diff --git a/scapy/automaton.py b/scapy/automaton.py
index b5be2b1430919ee2c71dd7714e124cc31ba530e6..332709d31d2fcc3b268d896672177423af6b56d1 100644
--- a/scapy/automaton.py
+++ b/scapy/automaton.py
@@ -3,13 +3,59 @@
 ## Copyright (C) Philippe Biondi <phil@secdev.org>
 ## This program is published under a GPLv2 license
 
-import types,itertools,time
+from __future__ import with_statement
+import types,itertools,time,os,sys,socket
 from select import select
+from collections import deque
+import thread
 from config import conf
 from utils import do_graph
 from error import log_interactive
 from plist import PacketList
 from data import MTU
+from supersocket import SuperSocket
+
+class ObjectPipe:
+    def __init__(self):
+        self.rd,self.wr = os.pipe()
+        self.queue = deque()
+    def fileno(self):
+        return self.rd
+    def send(self, obj):
+        self.queue.append(obj)
+        os.write(self.wr,"X")
+    def recv(self, n=0):
+        os.read(self.rd,1)
+        return self.queue.popleft()
+
+
+class Message:
+    def __init__(self, **args):
+        self.__dict__.update(args)
+    def __repr__(self):
+        return "<Message %s>" % " ".join("%s=%r"%(k,v)
+                                         for (k,v) in self.__dict__.iteritems()
+                                         if not k.startswith("_"))
+
+class _instance_state:
+    def __init__(self, instance):
+        self.im_self = instance.im_self
+        self.im_func = instance.im_func
+        self.im_class = instance.im_class
+    def __getattr__(self, attr):
+        return getattr(self.im_func, attr)
+
+    def __call__(self, *args, **kargs):
+        return self.im_func(self.im_self, *args, **kargs)
+    def breaks(self):
+        return self.im_self.add_breakpoints(self.im_func)
+    def intercepts(self):
+        return self.im_self.add_interception_points(self.im_func)
+    def unbreaks(self):
+        return self.im_self.remove_breakpoints(self.im_func)
+    def unintercepts(self):
+        return self.im_self.remove_interception_points(self.im_func)
+        
 
 ##############
 ## Automata ##
@@ -21,6 +67,7 @@ class ATMT:
     CONDITION = "Condition"
     RECV = "Receive condition"
     TIMEOUT = "Timeout condition"
+    IOEVENT = "I/O event"
 
     class NewStateRequested(Exception):
         def __init__(self, state_func, automaton, *args, **kargs):
@@ -40,6 +87,8 @@ class ATMT:
             return self
         def run(self):
             return self.func(self.automaton, *self.args, **self.kargs)
+        def __repr__(self):
+            return "NewStateRequested(%s)" % self.state
 
     @staticmethod
     def state(initial=0,final=0,error=0):
@@ -89,6 +138,17 @@ class ATMT:
             return f
         return deco
     @staticmethod
+    def ioevent(state, name, prio=0, as_supersocket=None):
+        def deco(f, state=state):
+            f.atmt_type = ATMT.IOEVENT
+            f.atmt_state = state.atmt_state
+            f.atmt_condname = f.func_name
+            f.atmt_ioname = name
+            f.atmt_prio = prio
+            f.atmt_as_supersocket = as_supersocket
+            return f
+        return deco
+    @staticmethod
     def timeout(state, timeout):
         def deco(f, state=state, timeout=timeout):
             f.atmt_type = ATMT.TIMEOUT
@@ -98,6 +158,50 @@ class ATMT:
             return f
         return deco
 
+class _ATMT_Command:
+    RUN = "RUN"
+    NEXT = "NEXT"
+    FREEZE = "FREEZE"
+    STOP = "STOP"
+    END = "END"
+    EXCEPTION = "EXCEPTION"
+    SINGLESTEP = "SINGLESTEP"
+    BREAKPOINT = "BREAKPOINT"
+    INTERCEPT = "INTERCEPT"
+    ACCEPT = "ACCEPT"
+    REPLACE = "REPLACE"
+    REJECT = "REJECT"
+
+class _ATMT_supersocket(SuperSocket):
+    def __init__(self, name, ioevent, automaton, proto, args, kargs):
+        self.name = name
+        self.ioevent = ioevent
+        self.proto = proto
+        self.spa,self.spb = socket.socketpair(socket.AF_UNIX, socket.SOCK_DGRAM)
+        kargs["external_fd"] = {ioevent:self.spb}
+        self.atmt = automaton(*args, **kargs)
+        self.atmt.runbg()
+    def fileno(self):
+        return self.spa.fileno()
+    def send(self, s):
+        if type(s) is not str:
+            s = str(s)
+        return self.spa.send(s)
+    def recv(self, n=MTU):
+        r = self.spa.recv(n)
+        if self.proto is not None:
+            r = self.proto(r)
+        return r
+    def close(self):
+        pass
+
+class _ATMT_to_supersocket:
+    def __init__(self, name, ioevent, automaton):
+        self.name = name
+        self.ioevent = ioevent
+        self.automaton = automaton
+    def __call__(self, proto, *args, **kargs):
+        return _ATMT_supersocket(self.name, self.ioevent, self.automaton, proto, args, kargs)
 
 class Automaton_metaclass(type):
     def __new__(cls, name, bases, dct):
@@ -106,9 +210,12 @@ class Automaton_metaclass(type):
         cls.state = None
         cls.recv_conditions={}
         cls.conditions={}
+        cls.ioevents={}
         cls.timeout={}
         cls.actions={}
         cls.initial_states=[]
+        cls.ionames = []
+        cls.iosupersockets = []
 
         members = {}
         classes = [cls]
@@ -127,11 +234,12 @@ class Automaton_metaclass(type):
                 s = m.atmt_state
                 cls.states[s] = m
                 cls.recv_conditions[s]=[]
+                cls.ioevents[s]=[]
                 cls.conditions[s]=[]
                 cls.timeout[s]=[]
                 if m.atmt_initial:
                     cls.initial_states.append(m)
-            elif m.atmt_type in [ATMT.CONDITION, ATMT.RECV, ATMT.TIMEOUT]:
+            elif m.atmt_type in [ATMT.CONDITION, ATMT.RECV, ATMT.TIMEOUT, ATMT.IOEVENT]:
                 cls.actions[m.atmt_condname] = []
     
         for m in decorated:
@@ -139,6 +247,11 @@ class Automaton_metaclass(type):
                 cls.conditions[m.atmt_state].append(m)
             elif m.atmt_type == ATMT.RECV:
                 cls.recv_conditions[m.atmt_state].append(m)
+            elif m.atmt_type == ATMT.IOEVENT:
+                cls.ioevents[m.atmt_state].append(m)
+                cls.ionames.append(m.atmt_ioname)
+                if m.atmt_as_supersocket is not None:
+                    cls.iosupersockets.append(m)
             elif m.atmt_type == ATMT.TIMEOUT:
                 cls.timeout[m.atmt_state].append((m.atmt_timeout, m))
             elif m.atmt_type == ATMT.ACTION:
@@ -150,14 +263,17 @@ class Automaton_metaclass(type):
             v.sort(lambda (t1,f1),(t2,f2): cmp(t1,t2))
             v.append((None, None))
         for v in itertools.chain(cls.conditions.itervalues(),
-                                 cls.recv_conditions.itervalues()):
+                                 cls.recv_conditions.itervalues(),
+                                 cls.ioevents.itervalues()):
             v.sort(lambda c1,c2: cmp(c1.atmt_prio,c2.atmt_prio))
         for condname,actlst in cls.actions.iteritems():
             actlst.sort(lambda c1,c2: cmp(c1.atmt_cond[condname], c2.atmt_cond[condname]))
 
+        for ioev in cls.iosupersockets:
+            setattr(cls, ioev.atmt_as_supersocket, _ATMT_to_supersocket(ioev.atmt_as_supersocket, ioev.atmt_ioname, cls))
+
         return cls
 
-        
     def graph(self, **kargs):
         s = 'digraph "%s" {\n'  % self.__class__.__name__
         
@@ -177,7 +293,9 @@ class Automaton_metaclass(type):
                     s += '\t"%s" -> "%s" [ color=green ];\n' % (st.atmt_state,n)
             
 
-        for c,k,v in [("purple",k,v) for k,v in self.conditions.items()]+[("red",k,v) for k,v in self.recv_conditions.items()]:
+        for c,k,v in ([("purple",k,v) for k,v in self.conditions.items()]+
+                      [("red",k,v) for k,v in self.recv_conditions.items()]+
+                      [("orange",k,v) for k,v in self.ioevents.items()]):
             for f in v:
                 for n in f.func_code.co_names+f.func_code.co_consts:
                     if n in self.states:
@@ -203,37 +321,170 @@ class Automaton_metaclass(type):
 class Automaton:
     __metaclass__ = Automaton_metaclass
 
-    def __init__(self, *args, **kargs):
-        self.debug_level=0
-        self.init_args=args
-        self.init_kargs=kargs
-        self.parse_args(*args, **kargs)
+    ## Methods to overload
+    def parse_args(self, debug=0, store=1, **kargs):
+        self.debug_level=debug
+        self.socket_kargs = kargs
+        self.store_packets = store        
 
-    def debug(self, lvl, msg):
-        if self.debug_level >= lvl:
-            log_interactive.debug(msg)
-            
+    def master_filter(self, pkt):
+        return True
 
+    def my_send(self, pkt):
+        self.send_sock.send(pkt)
 
 
-    class ErrorState(Exception):
-        def __init__(self, msg, result=None):
+    ## Utility classes and exceptions
+    class _IO_fdwrapper:
+        def __init__(self,rd,wr):
+            if rd is not None and type(rd) is not int:
+                rd = rd.fileno()
+            if wr is not None and type(wr) is not int:
+                wr = wr.fileno()
+            self.rd = rd
+            self.wr = wr
+        def fileno(self):
+            return self.rd
+        def read(self, n=65535):
+            return os.read(self.rd, n)
+        def write(self, msg):
+            return os.write(self.wr,msg)
+        def recv(self, n=65535):
+            return self.read(n)        
+        def send(self, msg):
+            return self.write(msg)
+
+    class _IO_mixer:
+        def __init__(self,rd,wr):
+            self.rd = rd
+            self.wr = wr
+        def fileno(self):
+            if type(self.rd) is int:
+                return self.rd
+            return self.rd.fileno()
+        def recv(self, n=None):
+            return self.rd.recv(n)
+        def read(self, n=None):
+            return self.rd.recv(n)        
+        def send(self, msg):
+            return self.wr.send(msg)
+        def write(self, msg):
+            return self.wr.send(msg)
+
+
+    class AutomatonException(Exception):
+        def __init__(self, msg, state=None, result=None):
             Exception.__init__(self, msg)
+            self.state = state
             self.result = result
-    class Stuck(ErrorState):
+
+    class AutomatonError(AutomatonException):
+        pass
+    class ErrorState(AutomatonException):
         pass
+    class Stuck(AutomatonException):
+        pass
+    class AutomatonStopped(AutomatonException):
+        pass
+    
+    class Breakpoint(AutomatonStopped):
+        pass
+    class Singlestep(AutomatonStopped):
+        pass
+    class InterceptionPoint(AutomatonStopped):
+        def __init__(self, msg, state=None, result=None, packet=None):
+            Automaton.AutomatonStopped.__init__(self, msg, state=state, result=result)
+            self.packet = packet
 
-    def parse_args(self, debug=0, store=1, **kargs):
-        self.debug_level=debug
-        self.socket_kargs = kargs
-        self.store_packets = store
+    class CommandMessage(AutomatonException):
+        pass
+
+
+    ## Services
+    def debug(self, lvl, msg):
+        if self.debug_level >= lvl:
+            log_interactive.debug(msg)            
+
+    def send(self, pkt):
+        if self.state.state in self.interception_points:
+            self.debug(3,"INTERCEPT: packet intercepted: %s" % pkt.summary())
+            self.intercepted_packet = pkt
+            cmd = Message(type = _ATMT_Command.INTERCEPT, state=self.state, pkt=pkt)
+            self.cmdout.send(cmd)
+            cmd = self.cmdin.recv()
+            self.intercepted_packet = None
+            if cmd.type == _ATMT_Command.REJECT:
+                self.debug(3,"INTERCEPT: packet rejected")
+                return
+            elif cmd.type == _ATMT_Command.REPLACE:
+                pkt = cmd.pkt
+                self.debug(3,"INTERCEPT: packet replaced by: %s" % pkt.summary())
+            elif cmd.type == _ATMT_Command.ACCEPT:
+                self.debug(3,"INTERCEPT: packet accepted")
+            else:
+                raise self.AutomatonError("INTERCEPT: unkown verdict: %r" % cmd.type)
+        self.my_send(pkt)
+        self.debug(3,"SENT : %s" % pkt.summary())
+        self.packets.append(pkt.copy())
+
+
+    ## Internals
+    def __init__(self, *args, **kargs):
+        external_fd = kargs.pop("external_fd",{})
+        self.send_sock_class = kargs.pop("ll", conf.L3socket)
+        self.started = thread.allocate_lock()
+        self.threadid = None
+        self.breakpointed = None
+        self.breakpoints = set()
+        self.interception_points = set()
+        self.intercepted_packet = None
+        self.debug_level=0
+        self.init_args=args
+        self.init_kargs=kargs
+        self.io = type.__new__(type, "IOnamespace",(),{})
+        self.oi = type.__new__(type, "IOnamespace",(),{})
+        self.cmdin = ObjectPipe()
+        self.cmdout = ObjectPipe()
+        self.ioin = {}
+        self.ioout = {}
+        for n in self.ionames:
+            extfd = external_fd.get(n)
+            if type(extfd) is not tuple:
+                extfd = (extfd,extfd)
+            ioin,ioout = extfd                
+            if ioin is None:
+                ioin = ObjectPipe()
+            elif type(ioin) is not types.InstanceType:
+                ioin = self._IO_fdwrapper(ioin,None)
+            if ioout is None:
+                ioout = ObjectPipe()
+            elif type(ioout) is not types.InstanceType:
+                ioout = self._IO_fdwrapper(None,ioout)
+
+            self.ioin[n] = ioin
+            self.ioout[n] = ioout 
+            ioin.ioname = n
+            ioout.ioname = n
+            setattr(self.io, n, self._IO_mixer(ioout,ioin))
+            setattr(self.oi, n, self._IO_mixer(ioin,ioout))
+
+        for stname in self.states:
+            setattr(self, stname, 
+                    _instance_state(getattr(self, stname)))
         
+        self.parse_args(*args, **kargs)
 
-    def master_filter(self, pkt):
-        return True
+        self.start()
+
+    def __iter__(self):
+        return self        
+
+    def __del__(self):
+        self.stop()
 
-    def run_condition(self, cond, *args, **kargs):
+    def _run_condition(self, cond, *args, **kargs):
         try:
+            self.debug(5, "Trying %s [%s]" % (cond.atmt_type, cond.atmt_condname))
             cond(self,*args, **kargs)
         except ATMT.NewStateRequested, state_req:
             self.debug(2, "%s [%s] taken to state [%s]" % (cond.atmt_type, cond.atmt_condname, state_req.state))
@@ -243,33 +494,88 @@ class Automaton:
                 self.debug(2, "   + Running action [%s]" % action.func_name)
                 action(self, *state_req.action_args, **state_req.action_kargs)
             raise
+        except Exception,e:
+            self.debug(2, "%s [%s] raised exception [%s]" % (cond.atmt_type, cond.atmt_condname, e))
+            raise
         else:
             self.debug(2, "%s [%s] not taken" % (cond.atmt_type, cond.atmt_condname))
-            
 
-    def run(self, *args, **kargs):
-        # Update default parameters
-        a = args+self.init_args[len(args):]
-        k = self.init_kargs
-        k.update(kargs)
-        self.parse_args(*a,**k)
-
-        # Start the automaton
-        self.state=self.initial_states[0](self)
-        self.send_sock = conf.L3socket()
-        l = conf.L2listen(**self.socket_kargs)
-        self.packets = PacketList(name="session[%s]"%self.__class__.__name__)
-        while 1:
+    def _do_start(self, *args, **kargs):
+        
+        thread.start_new_thread(self._do_control, args, kargs)
+
+
+    def _do_control(self, *args, **kargs):
+        with self.started:
+            self.threadid = thread.get_ident()
+
+            # Update default parameters
+            a = args+self.init_args[len(args):]
+            k = self.init_kargs.copy()
+            k.update(kargs)
+            self.parse_args(*a,**k)
+    
+            # Start the automaton
+            self.state=self.initial_states[0](self)
+            self.send_sock = self.send_sock_class()
+            self.listen_sock = conf.L2listen(**self.socket_kargs)
+            self.packets = PacketList(name="session[%s]"%self.__class__.__name__)
+
+            singlestep = True
+            iterator = self._do_iter()
+            self.debug(3, "Starting control thread [tid=%i]" % self.threadid)
+            try:
+                while True:
+                    c = self.cmdin.recv()
+                    self.debug(5, "Received command %s" % c.type)
+                    if c.type == _ATMT_Command.RUN:
+                        singlestep = False
+                    elif c.type == _ATMT_Command.NEXT:
+                        singlestep = True
+                    elif c.type == _ATMT_Command.FREEZE:
+                        continue
+                    elif c.type == _ATMT_Command.STOP:
+                        break
+                    while True:
+                        state = iterator.next()
+                        if isinstance(state, self.CommandMessage):
+                            break
+                        elif isinstance(state, self.Breakpoint):
+                            c = Message(type=_ATMT_Command.BREAKPOINT,state=state)
+                            self.cmdout.send(c)
+                            break
+                        if singlestep:
+                            c = Message(type=_ATMT_Command.SINGLESTEP,state=state)
+                            self.cmdout.send(c)
+                            break
+            except StopIteration,e:
+                c = Message(type=_ATMT_Command.END, result=e.args[0])
+                self.cmdout.send(c)
+            except Exception,e:
+                self.debug(3, "Transfering exception [%s] from tid=%i"% (e,self.threadid))
+                m = Message(type = _ATMT_Command.EXCEPTION, exception=e, exc_info=sys.exc_info())
+                self.cmdout.send(m)        
+            self.debug(3, "Stopping control thread (tid=%i)"%self.threadid)
+            self.threadid = None
+    
+    def _do_iter(self):
+        while True:
             try:
                 self.debug(1, "## state=[%s]" % self.state.state)
-
+    
                 # Entering a new state. First, call new state function
+                if self.state.state in self.breakpoints and self.state.state != self.breakpointed: 
+                    self.breakpointed = self.state.state
+                    yield self.Breakpoint("breakpoint triggered on state %s" % self.state.state,
+                                          state = self.state.state)
+                self.breakpointed = None
                 state_output = self.state.run()
                 if self.state.error:
-                    raise self.ErrorState("Reached %s: [%r]" % (self.state.state, state_output), result=state_output)
+                    raise self.ErrorState("Reached %s: [%r]" % (self.state.state, state_output), 
+                                          result=state_output, state=self.state.state)
                 if self.state.final:
-                    return state_output
-
+                    raise StopIteration(state_output)
+    
                 if state_output is None:
                     state_output = ()
                 elif type(state_output) is not list:
@@ -277,54 +583,146 @@ class Automaton:
                 
                 # Then check immediate conditions
                 for cond in self.conditions[self.state.state]:
-                    self.run_condition(cond, *state_output)
-
+                    self._run_condition(cond, *state_output)
+    
                 # If still there and no conditions left, we are stuck!
-                if ( len(self.recv_conditions[self.state.state]) == 0
-                     and len(self.timeout[self.state.state]) == 1 ):
-                    raise self.Stuck("stuck in [%s]" % self.state.state,result=state_output)
-
+                if ( len(self.recv_conditions[self.state.state]) == 0 and
+                     len(self.ioevents[self.state.state]) == 0 and
+                     len(self.timeout[self.state.state]) == 1 ):
+                    raise self.Stuck("stuck in [%s]" % self.state.state,
+                                     state=self.state.state, result=state_output)
+    
                 # Finally listen and pay attention to timeouts
                 expirations = iter(self.timeout[self.state.state])
                 next_timeout,timeout_func = expirations.next()
                 t0 = time.time()
                 
+                fds = [self.cmdin]
+                if len(self.recv_conditions[self.state.state]) > 0:
+                    fds.append(self.listen_sock)
+                for ioev in self.ioevents[self.state.state]:
+                    fds.append(self.ioin[ioev.atmt_ioname])
                 while 1:
                     t = time.time()-t0
                     if next_timeout is not None:
                         if next_timeout <= t:
-                            self.run_condition(timeout_func, *state_output)
+                            self._run_condition(timeout_func, *state_output)
                             next_timeout,timeout_func = expirations.next()
                     if next_timeout is None:
                         remain = None
                     else:
                         remain = next_timeout-t
     
-                    r,_,_ = select([l],[],[],remain)
-                    if l in r:
-                        pkt = l.recv(MTU)
-                        if pkt is not None:
-                            if self.master_filter(pkt):
-                                self.debug(3, "RECVD: %s" % pkt.summary())
-                                for rcvcond in self.recv_conditions[self.state.state]:
-                                    self.run_condition(rcvcond, pkt, *state_output)
-                            else:
-                                self.debug(4, "FILTR: %s" % pkt.summary())
-
+                    self.debug(5, "Select on %r" % fds)
+                    r,_,_ = select(fds,[],[],remain)
+                    self.debug(5, "Selected %r" % r)
+                    for fd in r:
+                        self.debug(5, "Looking at %r" % fd)
+                        if fd == self.cmdin:
+                            yield self.CommandMessage("Received command message")
+                        elif fd == self.listen_sock:
+                            pkt = self.listen_sock.recv(MTU)
+                            if pkt is not None:
+                                if self.master_filter(pkt):
+                                    self.debug(3, "RECVD: %s" % pkt.summary())
+                                    for rcvcond in self.recv_conditions[self.state.state]:
+                                        self._run_condition(rcvcond, pkt, *state_output)
+                                else:
+                                    self.debug(4, "FILTR: %s" % pkt.summary())
+                        else:
+                            self.debug(3, "IOEVENT on %s" % fd.ioname)
+                            for ioevt in self.ioevents[self.state.state]:
+                                if ioevt.atmt_ioname == fd.ioname:
+                                    self._run_condition(ioevt, fd, *state_output)
+    
             except ATMT.NewStateRequested,state_req:
                 self.debug(2, "switching from [%s] to [%s]" % (self.state.state,state_req.state))
                 self.state = state_req
+                yield state_req
+
+    ## Public API
+    def add_interception_points(self, *ipts):
+        for ipt in ipts:
+            if hasattr(ipt,"atmt_state"):
+                ipt = ipt.atmt_state
+            self.interception_points.add(ipt)
+        
+    def remove_interception_points(self, *ipts):
+        for ipt in ipts:
+            if hasattr(ipt,"atmt_state"):
+                ipt = ipt.atmt_state
+            self.interception_points.discard(ipt)
+
+    def add_breakpoints(self, *bps):
+        for bp in bps:
+            if hasattr(bp,"atmt_state"):
+                bp = bp.atmt_state
+            self.breakpoints.add(bp)
+
+    def remove_breakpoints(self, *bps):
+        for bp in bps:
+            if hasattr(bp,"atmt_state"):
+                bp = bp.atmt_state
+            self.breakpoints.discard(bp)
+
+    def start(self, *args, **kargs):
+        if not self.started.locked():
+            self._do_start(*args, **kargs)
+        
+    def run(self, resume=None, wait=True):
+        if resume is None:
+            resume = Message(type = _ATMT_Command.RUN)
+        self.cmdin.send(resume)
+        if wait:
+            try:
+                c = self.cmdout.recv()
             except KeyboardInterrupt:
-                self.debug(1,"Interrupted by user")
-                break
+                self.cmdin.send(Message(type = _ATMT_Command.FREEZE))
+                return
+            if c.type == _ATMT_Command.END:
+                return c.result
+            elif c.type == _ATMT_Command.INTERCEPT:
+                raise self.InterceptionPoint("packet intercepted", state=c.state.state, packet=c.pkt)
+            elif c.type == _ATMT_Command.SINGLESTEP:
+                raise self.Singlestep("singlestep state=[%s]"%c.state.state, state=c.state.state)
+            elif c.type == _ATMT_Command.BREAKPOINT:
+                raise self.Breakpoint("breakpoint triggered on state [%s]"%c.state.state, state=c.state.state)
+            elif c.type == _ATMT_Command.EXCEPTION:
+                raise c.exc_info[0],c.exc_info[1],c.exc_info[2]
+
+    def runbg(self, resume=None, wait=False):
+        self.run(resume, wait)
+
+    def next(self):
+        return self.run(resume = Message(type=_ATMT_Command.NEXT))
+
+    def stop(self):
+        self.cmdin.send(Message(type=_ATMT_Command.STOP))
+        with self.started:
+            # Flush command pipes
+            while True:
+                r,_,_ = select([self.cmdin, self.cmdout],[],[],0)
+                if not r:
+                    break
+                for fd in r:
+                    fd.recv()
+                
+    def restart(self, *args, **kargs):
+        self.stop()
+        self.start(*args, **kargs)
+
+    def accept_packet(self, pkt=None, wait=False):
+        rsm = Message()
+        if pkt is None:
+            rsm.type = _ATMT_Command.ACCEPT
+        else:
+            rsm.type = _ATMT_Command.REPLACE
+            rsm.pkt = pkt
+        return self.run(resume=rsm, wait=wait)
 
-    def my_send(self, pkt):
-        self.send_sock.send(pkt)
-
-    def send(self, pkt):
-        self.my_send(pkt)
-        self.debug(3,"SENT : %s" % pkt.summary())
-        self.packets.append(pkt.copy())
+    def reject_packet(self, wait=False):
+        rsm = Message(type = _ATMT_Command.REJECT)
+        return self.run(resume=rsm, wait=wait)
 
+    
 
-        
diff --git a/scapy/base_classes.py b/scapy/base_classes.py
index 6d36053c021c16386dc593b9037da18273863ae6..c2fa4b73aa9611f6ddd6a296d1092b988047770c 100644
--- a/scapy/base_classes.py
+++ b/scapy/base_classes.py
@@ -46,28 +46,34 @@ class Net(Gen):
     """Generate a list of IPs from a network address or a name"""
     name = "ip"
     ipaddress = re.compile(r"^(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)(/[0-3]?[0-9])?$")
-    def __init__(self, net):
-        self.repr=net
 
+    @staticmethod
+    def _parse_digit(a,netmask):
+        netmask = min(8,max(netmask,0))
+        if a == "*":
+            a = (0,256)
+        elif a.find("-") >= 0:
+            x,y = map(int,a.split("-"))
+            if x > y:
+                y = x
+            a = (x &  (0xffL<<netmask) , max(y, (x | (0xffL>>(8-netmask))))+1)
+        else:
+            a = (int(a) & (0xffL<<netmask),(int(a) | (0xffL>>(8-netmask)))+1)
+        return a
+
+    @classmethod
+    def _parse_net(cls, net):
         tmp=net.split('/')+["32"]
-        if not self.ipaddress.match(net):
+        if not cls.ipaddress.match(net):
             tmp[0]=socket.gethostbyname(tmp[0])
         netmask = int(tmp[1])
+        return map(lambda x,y: cls._parse_digit(x,y), tmp[0].split("."), map(lambda x,nm=netmask: x-nm, (8,16,24,32))),netmask
+
+    def __init__(self, net):
+        self.repr=net
+        self.parsed,self.netmask = self._parse_net(net)
 
-        def parse_digit(a,netmask):
-            netmask = min(8,max(netmask,0))
-            if a == "*":
-                a = (0,256)
-            elif a.find("-") >= 0:
-                x,y = map(int,a.split("-"))
-                if x > y:
-                    y = x
-                a = (x &  (0xffL<<netmask) , max(y, (x | (0xffL>>(8-netmask))))+1)
-            else:
-                a = (int(a) & (0xffL<<netmask),(int(a) | (0xffL>>(8-netmask)))+1)
-            return a
 
-        self.parsed = map(lambda x,y: parse_digit(x,y), tmp[0].split("."), map(lambda x,nm=netmask: x-nm, (8,16,24,32)))
                                                                                                
     def __iter__(self):
         for d in xrange(*self.parsed[3]):
@@ -83,6 +89,24 @@ class Net(Gen):
                           
     def __repr__(self):
         return "Net(%r)" % self.repr
+    def __eq__(self, other):
+        if hasattr(other, "parsed"):
+            p2 = other.parsed
+        else:
+            p2,nm2 = self._parse_net(other)
+        return self.parsed == p2
+    def __contains__(self, other):
+        if hasattr(other, "parsed"):
+            p2 = other.parsed
+        else:
+            p2,nm2 = self._parse_net(other)
+        for (a1,b1),(a2,b2) in zip(self.parsed,p2):
+            if a1 > a2 or b1 < b2:
+                return False
+        return True
+    def __rcontains__(self, other):        
+        return self in self.__class__(other)
+        
 
 class OID(Gen):
     name = "OID"
diff --git a/scapy/config.py b/scapy/config.py
index 5efd3b92dc7a81e87a015a205527c1aa3e6d8b98..af1b1e0c889bd8eb810d17281acae101edb21599 100644
--- a/scapy/config.py
+++ b/scapy/config.py
@@ -3,7 +3,7 @@
 ## Copyright (C) Philippe Biondi <phil@secdev.org>
 ## This program is published under a GPLv2 license
 
-import os,time,socket
+import os,time,socket,sys
 from data import *
 import base_classes
 import themes
@@ -33,6 +33,23 @@ class ConfClass(object):
                     r = r[:wlen-3]+"..."
                 s += "%-10s = %s\n" % (i, r)
         return s[:-1]
+
+class Interceptor(object):
+    def __init__(self, name, default, hook, args=None, kargs=None):
+        self.name = name
+        self.intname = "_intercepted_%s" % name
+        self.default=default
+        self.hook = hook
+        self.args = args if args is not None else []
+        self.kargs = kargs if kargs is not None else {}
+    def __get__(self, obj, typ=None):
+        if not hasattr(obj, self.intname):
+            setattr(obj, self.intname, self.default)
+        return getattr(obj, self.intname)
+    def __set__(self, obj, val):
+        setattr(obj, self.intname, val)
+        self.hook(self.name, val, *self.args, **self.kargs)
+
     
 class ProgPath(ConfClass):
     pdfreader = "acroread"
@@ -43,21 +60,35 @@ class ProgPath(ConfClass):
     tcpreplay = "tcpreplay"
     hexedit = "hexer"
     wireshark = "wireshark"
-    
-class Resolve:
+
+
+class ConfigFieldList:
     def __init__(self):
-        self.fields = {}
+        self.fields = set()
+        self.layers = set()
+    @staticmethod
+    def _is_field(f):
+        return hasattr(f, "owners")
+    def _recalc_layer_list(self):
+        self.layers = set([owner for f in self.fields for owner in f.owners])
     def add(self, *flds):
-        for fld in flds:
-            self.fields[fld]=None
+        self.fields |= set([f for f in flds if self._is_field(f)])
+        self._recalc_layer_list()
     def remove(self, *flds):
-        for fld in flds:
-            if fld in self.fields:
-                del(self.fields[fld])
+        self.fields -= set(flds)
+        self._recalc_layer_list()
     def __contains__(self, elt):
+        if isinstance(elt, base_classes.Packet_metaclass):
+            return elt in self.layers
         return elt in self.fields
     def __repr__(self):
-        return "<Resolve [%s]>" %  " ".join(str(x) for x in self.fields)
+        return "<%s [%s]>" %  (self.__class__.__name__," ".join(str(x) for x in self.fields))
+
+class Emphasize(ConfigFieldList):
+    pass
+
+class Resolve(ConfigFieldList):
+    pass
     
 
 class Num2Layer:
@@ -237,6 +268,22 @@ class LogLevel(object):
         obj._logLevel = val
         
 
+
+def _prompt_changer(attr,val):
+    prompt = conf.prompt
+    try:
+        ct = val
+        if isinstance(ct, AnsiColorTheme) and ct.prompt(""):
+            ## ^A and ^B delimit invisible caracters for readline to count right.
+            ## And we need ct.prompt() to do change something or else ^A and ^B will be
+            ## displayed
+             prompt = "\001%s\002" % ct.prompt("\002"+prompt+"\001")
+        else:
+            prompt = ct.prompt(prompt)
+    except:
+        pass
+    sys.ps1 = prompt
+
 class Conf(ConfClass):
     """This object contains the configuration of scapy.
 session  : filename where the session will be saved
@@ -264,11 +311,12 @@ noenum    : holds list of enum fields for which conversion to string should NOT
 AS_resolver: choose the AS resolver class to use
 extensions_paths: path or list of paths where extensions are to be looked for
 """
-    version = "2.0.1-dev"
+    version = "2.1.0-dev"
     session = ""
     interactive = False
     stealth = "not implemented"
     iface = None
+    readfunc = None
     layers = LayersList()
     commands = CommandsList()
     logLevel = LogLevel()
@@ -281,6 +329,7 @@ extensions_paths: path or list of paths where extensions are to be looked for
     promisc = 1
     sniff_promisc = 1
     raw_layer = None
+    raw_summary = False
     default_l2 = None
     l2types = Num2Layer()
     l3types = Num2Layer()
@@ -296,11 +345,12 @@ extensions_paths: path or list of paths where extensions are to be looked for
     route6 = None # Filed by route6.py
     auto_fragment = 1
     debug_dissector = 0
-    color_theme = themes.DefaultTheme()
+    color_theme = Interceptor("color_theme", themes.NoTheme(), _prompt_changer)
     warning_threshold = 5
     prog = ProgPath()
     resolve = Resolve()
     noenum = Resolve()
+    emph = Emphasize()
     use_pcap = False
     use_dnet = False
     ipv6_enabled = socket.has_ipv6
@@ -309,14 +359,14 @@ extensions_paths: path or list of paths where extensions are to be looked for
     services_tcp = TCP_SERVICES
     services_udp = UDP_SERVICES
     extensions_paths = "."
-    manufdb = load_manuf("/usr/share/wireshark/manuf")
+    manufdb = MANUFDB
     stats_classic_protocols = []
     stats_dot11_protocols = []
     temp_files = []
     netcache = NetCache()
     load_layers = ["l2", "inet", "dhcp", "dns", "dot11", "gprs", "hsrp", "inet6", "ir", "isakmp", "l2tp",
                    "mgcp", "mobileip", "netbios", "netflow", "ntp", "ppp", "radius", "rip", "rtp",
-                   "sebek", "skinny", "smb", "snmp", "tftp", "x509", "bluetooth", "dhcp6", "llmnr" ]
+                   "sebek", "skinny", "smb", "snmp", "tftp", "x509", "bluetooth", "dhcp6", "llmnr", "sctp", "vrrp" ]
     
 
 if not Conf.ipv6_enabled:
@@ -329,4 +379,3 @@ if not Conf.ipv6_enabled:
 conf=Conf()
 conf.logLevel=30 # 30=Warning
 
-
diff --git a/scapy/crypto/__init__.py b/scapy/crypto/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..0fdc3e489b5afd6594b6fb1ce28a9be633ea6c16
--- /dev/null
+++ b/scapy/crypto/__init__.py
@@ -0,0 +1,6 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Arnaud Ebalard <arno@natisbad.org>
+## This program is published under a GPLv2 license
+
+__all__ = ["cert"]
diff --git a/scapy/crypto/cert.py b/scapy/crypto/cert.py
new file mode 100644
index 0000000000000000000000000000000000000000..dd50e777a167c8e42965dff6eb3d3c65f12fef79
--- /dev/null
+++ b/scapy/crypto/cert.py
@@ -0,0 +1,2470 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Arnaud Ebalard <arno@natisbad.org>
+## This program is published under a GPLv2 license
+
+import os, sys, math, socket, struct, sha, hmac, string, time
+import random, popen2, tempfile
+from scapy.utils import strxor
+try:
+    HAS_HASHLIB=True
+    import hashlib
+except:
+    HAS_HASHLIB=False
+
+from Crypto.PublicKey import *
+from Crypto.Cipher import *
+from Crypto.Hash import *
+
+# Maximum allowed size in bytes for a certificate file, to avoid
+# loading huge file when importing a cert
+MAX_KEY_SIZE=50*1024
+MAX_CERT_SIZE=50*1024
+MAX_CRL_SIZE=10*1024*1024   # some are that big
+
+#####################################################################
+# Some helpers
+#####################################################################
+
+def warning(m):
+    print "WARNING: %s" % m
+
+def randstring(l):
+    """
+    Returns a random string of length l (l >= 0)
+    """
+    tmp = map(lambda x: struct.pack("B", random.randrange(0, 256, 1)), [""]*l)
+    return "".join(tmp)
+
+def zerofree_randstring(l):
+    """
+    Returns a random string of length l (l >= 0) without zero in it. 
+    """
+    tmp = map(lambda x: struct.pack("B", random.randrange(1, 256, 1)), [""]*l)
+    return "".join(tmp)
+
+def strand(s1, s2):
+    """
+    Returns the binary AND of the 2 provided strings s1 and s2. s1 and s2
+    must be of same length.
+    """
+    return "".join(map(lambda x,y:chr(ord(x)&ord(y)), s1, s2))
+
+# OS2IP function defined in RFC 3447 for octet string to integer conversion
+def pkcs_os2ip(x):
+    """
+    Accepts a byte string as input parameter and return the associated long
+    value:
+
+    Input : x        octet string to be converted
+
+    Output: x        corresponding nonnegative integer
+
+    Reverse function is pkcs_i2osp()
+    """
+    return RSA.number.bytes_to_long(x) 
+
+# IP2OS function defined in RFC 3447 for octet string to integer conversion
+def pkcs_i2osp(x,xLen):
+    """
+    Converts a long (the first parameter) to the associated byte string
+    representation of length l (second parameter). Basically, the length
+    parameters allow the function to perform the associated padding.
+
+    Input : x        nonnegative integer to be converted
+            xLen     intended length of the resulting octet string
+
+    Output: x        corresponding nonnegative integer
+
+    Reverse function is pkcs_os2ip().
+    """
+    z = RSA.number.long_to_bytes(x)
+    padlen = max(0, xLen-len(z))
+    return '\x00'*padlen + z
+
+# for every hash function a tuple is provided, giving access to 
+# - hash output length in byte
+# - associated hash function that take data to be hashed as parameter
+#   XXX I do not provide update() at the moment.
+# - DER encoding of the leading bits of digestInfo (the hash value
+#   will be concatenated to create the complete digestInfo).
+# 
+# Notes:
+# - MD4 asn.1 value should be verified. Also, as stated in 
+#   PKCS#1 v2.1, MD4 should not be used.
+# - hashlib is available from http://code.krypto.org/python/hashlib/
+# - 'tls' one is the concatenation of both md5 and sha1 hashes used
+#   by SSL/TLS when signing/verifying things
+_hashFuncParams = {
+    "md2"    : (16, 
+                lambda x: MD2.new(x).digest(), 
+                '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10'),
+    "md4"    : (16, 
+                lambda x: MD4.new(x).digest(), 
+                '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x04\x05\x00\x04\x10'), # is that right ?
+    "md5"    : (16, 
+                lambda x: MD5.new(x).digest(), 
+                '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'),
+    "sha1"   : (20,
+                lambda x: SHA.new(x).digest(), 
+                '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'),
+    "tls"    : (36,
+                lambda x: MD5.new(x).digest() + SHA.new(x).digest(),
+                '') }
+
+if HAS_HASHLIB:
+    _hashFuncParams["sha224"] = (28, 
+                lambda x: hashlib.sha224(x).digest(),
+                '\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c')
+    _hashFuncParams["sha256"] = (32, 
+                lambda x: hashlib.sha256(x).digest(), 
+                '\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20')
+    _hashFuncParams["sha384"] = (48, 
+                lambda x: hashlib.sha384(x).digest(),
+               '\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30')
+    _hashFuncParams["sha512"] = (64, 
+               lambda x: hashlib.sha512(x).digest(),
+               '\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40')
+else:
+    warning("hashlib support is not available. Consider installing it")
+    warning("if you need sha224, sha256, sha384 and sha512 algs.")
+    
+def pkcs_mgf1(mgfSeed, maskLen, h):
+    """
+    Implements generic MGF1 Mask Generation function as described in
+    Appendix B.2.1 of RFC 3447. The hash function is passed by name.
+    valid values are 'md2', 'md4', 'md5', 'sha1', 'tls, 'sha256',
+    'sha384' and 'sha512'. Returns None on error.
+
+    Input:
+       mgfSeed: seed from which mask is generated, an octet string
+       maskLen: intended length in octets of the mask, at most 2^32 * hLen
+                hLen (see below)
+       h      : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
+                'sha256', 'sha384'). hLen denotes the length in octets of
+                the hash function output.
+
+    Output:
+       an octet string of length maskLen
+    """
+
+    # steps are those of Appendix B.2.1
+    if not _hashFuncParams.has_key(h):
+        warning("pkcs_mgf1: invalid hash (%s) provided")
+        return None
+    hLen = _hashFuncParams[h][0]
+    hFunc = _hashFuncParams[h][1]
+    if maskLen > 2**32 * hLen:                               # 1)
+        warning("pkcs_mgf1: maskLen > 2**32 * hLen")         
+        return None
+    T = ""                                                   # 2)
+    maxCounter = math.ceil(float(maskLen) / float(hLen))     # 3)
+    counter = 0
+    while counter < maxCounter:
+        C = pkcs_i2osp(counter, 4)
+        T += hFunc(mgfSeed + C)
+        counter += 1
+    return T[:maskLen]
+
+
+def pkcs_emsa_pss_encode(M, emBits, h, mgf, sLen): 
+    """
+    Implements EMSA-PSS-ENCODE() function described in Sect. 9.1.1 of RFC 3447
+
+    Input:
+       M     : message to be encoded, an octet string
+       emBits: maximal bit length of the integer resulting of pkcs_os2ip(EM),
+               where EM is the encoded message, output of the function.
+       h     : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
+               'sha256', 'sha384'). hLen denotes the length in octets of
+               the hash function output. 
+       mgf   : the mask generation function f : seed, maskLen -> mask
+       sLen  : intended length in octets of the salt
+
+    Output:
+       encoded message, an octet string of length emLen = ceil(emBits/8)
+
+    On error, None is returned.
+    """
+
+    # 1) is not done
+    hLen = _hashFuncParams[h][0]                             # 2)
+    hFunc = _hashFuncParams[h][1]
+    mHash = hFunc(M)
+    emLen = int(math.ceil(emBits/8.))
+    if emLen < hLen + sLen + 2:                              # 3)
+        warning("encoding error (emLen < hLen + sLen + 2)")
+        return None
+    salt = randstring(sLen)                                  # 4)
+    MPrime = '\x00'*8 + mHash + salt                         # 5)
+    H = hFunc(MPrime)                                        # 6)
+    PS = '\x00'*(emLen - sLen - hLen - 2)                    # 7)
+    DB = PS + '\x01' + salt                                  # 8)
+    dbMask = mgf(H, emLen - hLen - 1)                        # 9)
+    maskedDB = strxor(DB, dbMask)                            # 10)
+    l = (8*emLen - emBits)/8                                 # 11)
+    rem = 8*emLen - emBits - 8*l # additionnal bits
+    andMask = l*'\x00'
+    if rem:
+        j = chr(reduce(lambda x,y: x+y, map(lambda x: 1<<x, range(8-rem))))
+        andMask += j
+        l += 1
+    maskedDB = strand(maskedDB[:l], andMask) + maskedDB[l:]
+    EM = maskedDB + H + '\xbc'                               # 12)
+    return EM                                                # 13)
+
+
+def pkcs_emsa_pss_verify(M, EM, emBits, h, mgf, sLen):
+    """
+    Implements EMSA-PSS-VERIFY() function described in Sect. 9.1.2 of RFC 3447
+
+    Input:
+       M     : message to be encoded, an octet string
+       EM    : encoded message, an octet string of length emLen = ceil(emBits/8)
+       emBits: maximal bit length of the integer resulting of pkcs_os2ip(EM)
+       h     : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
+               'sha256', 'sha384'). hLen denotes the length in octets of
+               the hash function output.
+       mgf   : the mask generation function f : seed, maskLen -> mask
+       sLen  : intended length in octets of the salt
+
+    Output:
+       True if the verification is ok, False otherwise.
+    """
+    
+    # 1) is not done
+    hLen = _hashFuncParams[h][0]                             # 2)
+    hFunc = _hashFuncParams[h][1]
+    mHash = hFunc(M)
+    emLen = int(math.ceil(emBits/8.))                        # 3)
+    if emLen < hLen + sLen + 2:
+        return False
+    if EM[-1] != '\xbc':                                     # 4)
+        return False
+    l = emLen - hLen - 1                                     # 5)
+    maskedDB = EM[:l]
+    H = EM[l:l+hLen]
+    l = (8*emLen - emBits)/8                                 # 6)
+    rem = 8*emLen - emBits - 8*l # additionnal bits
+    andMask = l*'\xff'
+    if rem:
+        val = reduce(lambda x,y: x+y, map(lambda x: 1<<x, range(8-rem)))
+        j = chr(~val & 0xff)
+        andMask += j
+        l += 1
+    if strand(maskedDB[:l], andMask) != '\x00'*l:
+        return False
+    dbMask = mgf(H, emLen - hLen - 1)                        # 7)
+    DB = strxor(maskedDB, dbMask)                            # 8)
+    l = (8*emLen - emBits)/8                                 # 9)
+    rem = 8*emLen - emBits - 8*l # additionnal bits
+    andMask = l*'\x00'
+    if rem:
+        j = chr(reduce(lambda x,y: x+y, map(lambda x: 1<<x, range(8-rem))))
+        andMask += j
+        l += 1
+    DB = strand(DB[:l], andMask) + DB[l:]
+    l = emLen - hLen - sLen - 1                              # 10)
+    if DB[:l] != '\x00'*(l-1) + '\x01':
+        return False
+    salt = DB[-sLen:]                                        # 11)
+    MPrime = '\x00'*8 + mHash + salt                         # 12)
+    HPrime = hFunc(MPrime)                                   # 13)
+    return H == HPrime                                       # 14)
+
+
+def pkcs_emsa_pkcs1_v1_5_encode(M, emLen, h): # section 9.2 of RFC 3447
+    """
+    Implements EMSA-PKCS1-V1_5-ENCODE() function described in Sect.
+    9.2 of RFC 3447.
+
+    Input:
+       M    : message to be encode, an octet string
+       emLen: intended length in octets of the encoded message, at least
+              tLen + 11, where tLen is the octet length of the DER encoding
+              T of a certain value computed during the encoding operation.
+       h    : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
+              'sha256', 'sha384'). hLen denotes the length in octets of
+              the hash function output.
+
+    Output:
+       encoded message, an octet string of length emLen
+
+    On error, None is returned.
+    """
+    hLen = _hashFuncParams[h][0]                             # 1)
+    hFunc = _hashFuncParams[h][1]
+    H = hFunc(M)
+    hLeadingDigestInfo = _hashFuncParams[h][2]               # 2)
+    T = hLeadingDigestInfo + H
+    tLen = len(T)
+    if emLen < tLen + 11:                                    # 3)
+        warning("pkcs_emsa_pkcs1_v1_5_encode: intended encoded message length too short")
+        return None
+    PS = '\xff'*(emLen - tLen - 3)                           # 4)
+    EM = '\x00' + '\x01' + PS + '\x00' + T                   # 5)
+    return EM                                                # 6)
+
+
+# XXX should add other pgf1 instance in a better fashion.
+
+def create_ca_file(anchor_list, filename):
+    """
+    Concatenate all the certificates (PEM format for the export) in
+    'anchor_list' and write the result to file 'filename'. On success
+    'filename' is returned, None otherwise.
+
+    If you are used to OpenSSL tools, this function builds a CAfile
+    that can be used for certificate and CRL check.
+
+    Also see create_temporary_ca_file().
+    """
+    try:
+        f = open(filename, "w")
+        for a in anchor_list:
+            s = a.output(fmt="PEM")
+            f.write(s)
+        f.close()
+    except:
+        return None
+    return filename
+
+def create_temporary_ca_file(anchor_list):
+    """
+    Concatenate all the certificates (PEM format for the export) in
+    'anchor_list' and write the result to file to a temporary file
+    using mkstemp() from tempfile module. On success 'filename' is
+    returned, None otherwise.
+
+    If you are used to OpenSSL tools, this function builds a CAfile
+    that can be used for certificate and CRL check.
+
+    Also see create_temporary_ca_file().
+    """
+    try:
+        f, fname = tempfile.mkstemp()
+        for a in anchor_list:
+            s = a.output(fmt="PEM")
+            l = os.write(f, s)
+        os.close(f)
+    except:
+        return None
+    return fname
+
+def create_temporary_ca_path(anchor_list, folder):
+    """
+    Create a CA path folder as defined in OpenSSL terminology, by
+    storing all certificates in 'anchor_list' list in PEM format
+    under provided 'folder' and then creating the associated links
+    using the hash as usually done by c_rehash.
+
+    Note that you can also include CRL in 'anchor_list'. In that
+    case, they will also be stored under 'folder' and associated
+    links will be created.
+
+    In folder, the files are created with names of the form
+    0...ZZ.pem. If you provide an empty list, folder will be created
+    if it does not already exist, but that's all.
+
+    The number of certificates written to folder is returned on
+    success, None on error.
+    """
+    # We should probably avoid writing duplicate anchors and also
+    # check if they are all certs.
+    try:
+        if not os.path.isdir(folder):
+            os.makedirs(folder)
+    except:
+        return None
+    
+    l = len(anchor_list)
+    if l == 0:
+        return None
+    fmtstr = "%%0%sd.pem" % math.ceil(math.log(l, 10))
+    i = 0
+    try:
+        for a in anchor_list:
+            fname = os.path.join(folder, fmtstr % i)
+            f = open(fname, "w")
+            s = a.output(fmt="PEM")
+            f.write(s)
+            f.close()
+            i += 1
+    except:
+        return None
+
+    r,w=popen2.popen2("c_rehash %s" % folder)
+    r.close(); w.close()
+
+    return l
+
+
+#####################################################################
+# Public Key Cryptography related stuff
+#####################################################################
+
+class OSSLHelper:
+    def _apply_ossl_cmd(self, osslcmd, rawdata):
+	r,w=popen2.popen2(osslcmd)
+	w.write(rawdata)
+	w.close()
+	res = r.read()
+	r.close()
+	return res
+
+class _EncryptAndVerify:
+    ### Below are encryption methods
+
+    def _rsaep(self, m):
+        """
+        Internal method providing raw RSA encryption, i.e. simple modular
+        exponentiation of the given message representative 'm', a long
+        between 0 and n-1.
+
+        This is the encryption primitive RSAEP described in PKCS#1 v2.1,
+        i.e. RFC 3447 Sect. 5.1.1.
+
+        Input:
+           m: message representative, a long between 0 and n-1, where
+              n is the key modulus.
+
+        Output:
+           ciphertext representative, a long between 0 and n-1
+
+        Not intended to be used directly. Please, see encrypt() method.
+        """
+
+        n = self.modulus
+        if type(m) is int:
+            m = long(m)
+        if type(m) is not long or m > n-1:
+            warning("Key._rsaep() expects a long between 0 and n-1")
+            return None
+
+        return self.key.encrypt(m, "")[0]
+
+
+    def _rsaes_pkcs1_v1_5_encrypt(self, M):
+        """
+        Implements RSAES-PKCS1-V1_5-ENCRYPT() function described in section
+        7.2.1 of RFC 3447.
+
+        Input:
+           M: message to be encrypted, an octet string of length mLen, where
+              mLen <= k - 11 (k denotes the length in octets of the key modulus)
+
+        Output:
+           ciphertext, an octet string of length k
+
+        On error, None is returned.
+        """
+
+        # 1) Length checking
+        mLen = len(M)
+        k = self.modulusLen / 8
+        if mLen > k - 11:
+            warning("Key._rsaes_pkcs1_v1_5_encrypt(): message too "
+                    "long (%d > %d - 11)" % (mLen, k))
+            return None
+
+        # 2) EME-PKCS1-v1_5 encoding
+        PS = zerofree_randstring(k - mLen - 3)      # 2.a)
+        EM = '\x00' + '\x02' + PS + '\x00' + M      # 2.b)
+
+        # 3) RSA encryption
+        m = pkcs_os2ip(EM)                          # 3.a)
+        c = self._rsaep(m)                          # 3.b)
+        C = pkcs_i2osp(c, k)                        # 3.c)
+
+        return C                                    # 4)
+
+
+    def _rsaes_oaep_encrypt(self, M, h=None, mgf=None, L=None):
+        """
+        Internal method providing RSAES-OAEP-ENCRYPT as defined in Sect.
+        7.1.1 of RFC 3447. Not intended to be used directly. Please, see
+        encrypt() method for type "OAEP".
+
+
+        Input:
+           M  : message to be encrypted, an octet string of length mLen
+                where mLen <= k - 2*hLen - 2 (k denotes the length in octets
+                of the RSA modulus and hLen the length in octets of the hash
+                function output)
+           h  : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
+                'sha256', 'sha384'). hLen denotes the length in octets of
+                the hash function output. 'sha1' is used by default if not
+                provided.
+           mgf: the mask generation function f : seed, maskLen -> mask
+           L  : optional label to be associated with the message; the default
+                value for L, if not provided is the empty string
+
+        Output:
+           ciphertext, an octet string of length k
+
+        On error, None is returned.
+        """
+        # The steps below are the one described in Sect. 7.1.1 of RFC 3447.
+        # 1) Length Checking
+                                                    # 1.a) is not done
+        mLen = len(M)
+        if h is None:
+            h = "sha1"
+        if not _hashFuncParams.has_key(h):
+            warning("Key._rsaes_oaep_encrypt(): unknown hash function %s.", h)
+            return None
+        hLen = _hashFuncParams[h][0]
+        hFun = _hashFuncParams[h][1]
+        k = self.modulusLen / 8
+        if mLen > k - 2*hLen - 2:                   # 1.b)
+            warning("Key._rsaes_oaep_encrypt(): message too long.")
+            return None
+        
+        # 2) EME-OAEP encoding
+        if L is None:                               # 2.a)
+            L = ""
+        lHash = hFun(L)
+        PS = '\x00'*(k - mLen - 2*hLen - 2)         # 2.b)
+        DB = lHash + PS + '\x01' + M                # 2.c)
+        seed = randstring(hLen)                     # 2.d)
+        if mgf is None:                             # 2.e)
+            mgf = lambda x,y: pkcs_mgf1(x,y,h)
+        dbMask = mgf(seed, k - hLen - 1)
+        maskedDB = strxor(DB, dbMask)               # 2.f)
+        seedMask = mgf(maskedDB, hLen)              # 2.g)
+        maskedSeed = strxor(seed, seedMask)         # 2.h)
+        EM = '\x00' + maskedSeed + maskedDB         # 2.i)
+
+        # 3) RSA Encryption
+        m = pkcs_os2ip(EM)                          # 3.a)
+        c = self._rsaep(m)                          # 3.b)
+        C = pkcs_i2osp(c, k)                        # 3.c)
+
+        return C                                    # 4)
+
+
+    def encrypt(self, m, t=None, h=None, mgf=None, L=None):
+        """
+        Encrypt message 'm' using 't' encryption scheme where 't' can be:
+
+        - None: the message 'm' is directly applied the RSAEP encryption
+                primitive, as described in PKCS#1 v2.1, i.e. RFC 3447
+                Sect 5.1.1. Simply put, the message undergo a modular
+                exponentiation using the public key. Additionnal method
+                parameters are just ignored.
+
+        - 'pkcs': the message 'm' is applied RSAES-PKCS1-V1_5-ENCRYPT encryption
+                scheme as described in section 7.2.1 of RFC 3447. In that
+                context, other parameters ('h', 'mgf', 'l') are not used.
+
+        - 'oaep': the message 'm' is applied the RSAES-OAEP-ENCRYPT encryption
+                scheme, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
+                7.1.1. In that context,
+
+                o 'h' parameter provides the name of the hash method to use.
+                  Possible values are "md2", "md4", "md5", "sha1", "tls",
+                  "sha224", "sha256", "sha384" and "sha512". if none is provided,
+                  sha1 is used.
+
+                o 'mgf' is the mask generation function. By default, mgf
+                  is derived from the provided hash function using the
+                  generic MGF1 (see pkcs_mgf1() for details).
+
+                o 'L' is the optional label to be associated with the
+                  message. If not provided, the default value is used, i.e
+                  the empty string. No check is done on the input limitation
+                  of the hash function regarding the size of 'L' (for
+                  instance, 2^61 - 1 for SHA-1). You have been warned.
+        """
+
+        if t is None: # Raw encryption
+            m = pkcs_os2ip(m)
+            c = self._rsaep(m)
+            return pkcs_i2osp(c, self.modulusLen/8)
+        
+        elif t == "pkcs":
+            return self._rsaes_pkcs1_v1_5_encrypt(m)
+        
+        elif t == "oaep":
+            return self._rsaes_oaep_encrypt(m, h, mgf, L)
+
+        else:
+            warning("Key.encrypt(): Unknown encryption type (%s) provided" % t)
+            return None
+
+    ### Below are verification related methods
+
+    def _rsavp1(self, s):
+        """
+        Internal method providing raw RSA verification, i.e. simple modular
+        exponentiation of the given signature representative 'c', an integer
+        between 0 and n-1.
+
+        This is the signature verification primitive RSAVP1 described in
+        PKCS#1 v2.1, i.e. RFC 3447 Sect. 5.2.2.
+
+        Input:
+          s: signature representative, an integer between 0 and n-1,
+             where n is the key modulus.
+
+        Output:
+           message representative, an integer between 0 and n-1
+
+        Not intended to be used directly. Please, see verify() method.
+        """
+        return self._rsaep(s)
+
+    def _rsassa_pss_verify(self, M, S, h=None, mgf=None, sLen=None):
+        """
+        Implements RSASSA-PSS-VERIFY() function described in Sect 8.1.2
+        of RFC 3447
+
+        Input:
+           M: message whose signature is to be verified
+           S: signature to be verified, an octet string of length k, where k
+              is the length in octets of the RSA modulus n.
+
+        Output:
+           True is the signature is valid. False otherwise.
+        """
+
+        # Set default parameters if not provided
+        if h is None: # By default, sha1
+            h = "sha1"
+        if not _hashFuncParams.has_key(h):
+            warning("Key._rsassa_pss_verify(): unknown hash function "
+                    "provided (%s)" % h)
+            return False
+        if mgf is None: # use mgf1 with underlying hash function
+            mgf = lambda x,y: pkcs_mgf1(x, y, h)
+        if sLen is None: # use Hash output length (A.2.3 of RFC 3447)
+            hLen = _hashFuncParams[h][0]
+            sLen = hLen
+
+        # 1) Length checking
+        modBits = self.modulusLen
+        k = modBits / 8
+        if len(S) != k:
+            return False
+
+        # 2) RSA verification
+        s = pkcs_os2ip(S)                           # 2.a)
+        m = self._rsavp1(s)                         # 2.b)
+        emLen = math.ceil((modBits - 1) / 8.)       # 2.c)
+        EM = pkcs_i2osp(m, emLen) 
+
+        # 3) EMSA-PSS verification
+        Result = pkcs_emsa_pss_verify(M, EM, modBits - 1, h, mgf, sLen)
+
+        return Result                               # 4)
+
+
+    def _rsassa_pkcs1_v1_5_verify(self, M, S, h):
+        """
+        Implements RSASSA-PKCS1-v1_5-VERIFY() function as described in
+        Sect. 8.2.2 of RFC 3447.
+
+        Input:
+           M: message whose signature is to be verified, an octet string
+           S: signature to be verified, an octet string of length k, where
+              k is the length in octets of the RSA modulus n
+           h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
+                'sha256', 'sha384').
+           
+        Output:
+           True if the signature is valid. False otherwise.
+        """
+
+        # 1) Length checking
+        k = self.modulusLen / 8
+        if len(S) != k:
+            warning("invalid signature (len(S) != k)")
+            return False
+
+        # 2) RSA verification
+        s = pkcs_os2ip(S)                           # 2.a)
+        m = self._rsavp1(s)                         # 2.b)
+        EM = pkcs_i2osp(m, k)                       # 2.c)
+
+        # 3) EMSA-PKCS1-v1_5 encoding
+        EMPrime = pkcs_emsa_pkcs1_v1_5_encode(M, k, h)
+        if EMPrime is None:
+            warning("Key._rsassa_pkcs1_v1_5_verify(): unable to encode.")
+            return False
+
+        # 4) Comparison
+        return EM == EMPrime
+
+
+    def verify(self, M, S, t=None, h=None, mgf=None, sLen=None):
+        """
+        Verify alleged signature 'S' is indeed the signature of message 'M' using
+        't' signature scheme where 't' can be:
+
+        - None: the alleged signature 'S' is directly applied the RSAVP1 signature
+                primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
+                5.2.1. Simply put, the provided signature is applied a moular
+                exponentiation using the public key. Then, a comparison of the
+                result is done against 'M'. On match, True is returned.
+                Additionnal method parameters are just ignored.
+
+        - 'pkcs': the alleged signature 'S' and message 'M' are applied
+                RSASSA-PKCS1-v1_5-VERIFY signature verification scheme as
+                described in Sect. 8.2.2 of RFC 3447. In that context,
+                the hash function name is passed using 'h'. Possible values are
+                "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384"
+                and "sha512". If none is provided, sha1 is used. Other additionnal
+                parameters are ignored.
+
+        - 'pss': the alleged signature 'S' and message 'M' are applied
+                RSASSA-PSS-VERIFY signature scheme as described in Sect. 8.1.2.
+                of RFC 3447. In that context,
+
+                o 'h' parameter provides the name of the hash method to use.
+                   Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224",
+                   "sha256", "sha384" and "sha512". if none is provided, sha1
+                   is used. 
+
+                o 'mgf' is the mask generation function. By default, mgf
+                   is derived from the provided hash function using the
+                   generic MGF1 (see pkcs_mgf1() for details).
+
+                o 'sLen' is the length in octet of the salt. You can overload the
+                  default value (the octet length of the hash value for provided
+                  algorithm) by providing another one with that parameter.
+        """
+        if t is None: # RSAVP1
+            S = pkcs_os2ip(S)
+            n = self.modulus
+            if S > n-1:
+                warning("Signature to be verified is too long for key modulus")
+                return False
+            m = self._rsavp1(S)
+            if m is None:
+                return False
+            l = int(math.ceil(math.log(m, 2) / 8.)) # Hack
+            m = pkcs_i2osp(m, l)
+            return M == m
+
+        elif t == "pkcs": # RSASSA-PKCS1-v1_5-VERIFY
+            if h is None:
+                h = "sha1"
+            return self._rsassa_pkcs1_v1_5_verify(M, S, h)
+
+        elif t == "pss": # RSASSA-PSS-VERIFY
+            return self._rsassa_pss_verify(M, S, h, mgf, sLen)
+
+        else:
+            warning("Key.verify(): Unknown signature type (%s) provided" % t)
+            return None
+    
+class _DecryptAndSignMethods(OSSLHelper):
+    ### Below are decryption related methods. Encryption ones are inherited
+    ### from PubKey
+
+    def _rsadp(self, c):
+        """
+        Internal method providing raw RSA decryption, i.e. simple modular
+        exponentiation of the given ciphertext representative 'c', a long
+        between 0 and n-1.
+
+        This is the decryption primitive RSADP described in PKCS#1 v2.1,
+        i.e. RFC 3447 Sect. 5.1.2.
+
+        Input:
+           c: ciphertest representative, a long between 0 and n-1, where
+              n is the key modulus.
+
+        Output:
+           ciphertext representative, a long between 0 and n-1
+
+        Not intended to be used directly. Please, see encrypt() method.
+        """
+
+        n = self.modulus
+        if type(c) is int:
+            c = long(c)        
+        if type(c) is not long or c > n-1:
+            warning("Key._rsaep() expects a long between 0 and n-1")
+            return None
+
+        return self.key.decrypt(c)    
+
+
+    def _rsaes_pkcs1_v1_5_decrypt(self, C):
+        """
+        Implements RSAES-PKCS1-V1_5-DECRYPT() function described in section
+        7.2.2 of RFC 3447.
+
+        Input:
+           C: ciphertext to be decrypted, an octet string of length k, where
+              k is the length in octets of the RSA modulus n.
+
+        Output:
+           an octet string of length k at most k - 11
+
+        on error, None is returned.
+        """
+        
+        # 1) Length checking
+        cLen = len(C)
+        k = self.modulusLen / 8
+        if cLen != k or k < 11:
+            warning("Key._rsaes_pkcs1_v1_5_decrypt() decryption error "
+                    "(cLen != k or k < 11)")
+            return None
+
+        # 2) RSA decryption
+        c = pkcs_os2ip(C)                           # 2.a)
+        m = self._rsadp(c)                          # 2.b)
+        EM = pkcs_i2osp(m, k)                       # 2.c)
+
+        # 3) EME-PKCS1-v1_5 decoding
+
+        # I am aware of the note at the end of 7.2.2 regarding error
+        # conditions reporting but the one provided below are for _local_
+        # debugging purposes. --arno
+        
+        if EM[0] != '\x00':
+            warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
+                    "(first byte is not 0x00)")
+            return None
+
+        if EM[1] != '\x02':
+            warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
+                    "(second byte is not 0x02)")
+            return None
+
+        tmp = EM[2:].split('\x00', 1)
+        if len(tmp) != 2:
+            warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
+                    "(no 0x00 to separate PS from M)")
+            return None
+
+        PS, M = tmp
+        if len(PS) < 8:
+            warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
+                    "(PS is less than 8 byte long)")
+            return None
+
+        return M                                    # 4)
+
+
+    def _rsaes_oaep_decrypt(self, C, h=None, mgf=None, L=None):
+        """
+        Internal method providing RSAES-OAEP-DECRYPT as defined in Sect.
+        7.1.2 of RFC 3447. Not intended to be used directly. Please, see
+        encrypt() method for type "OAEP".
+
+
+        Input:
+           C  : ciphertext to be decrypted, an octet string of length k, where
+                k = 2*hLen + 2 (k denotes the length in octets of the RSA modulus
+                and hLen the length in octets of the hash function output)
+           h  : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
+                'sha256', 'sha384'). 'sha1' is used if none is provided.
+           mgf: the mask generation function f : seed, maskLen -> mask
+           L  : optional label whose association with the message is to be
+                verified; the default value for L, if not provided is the empty
+                string.
+
+        Output:
+           message, an octet string of length k mLen, where mLen <= k - 2*hLen - 2
+
+        On error, None is returned.
+        """
+        # The steps below are the one described in Sect. 7.1.2 of RFC 3447.
+
+        # 1) Length Checking
+                                                    # 1.a) is not done
+        if h is None:
+            h = "sha1"
+        if not _hashFuncParams.has_key(h):
+            warning("Key._rsaes_oaep_decrypt(): unknown hash function %s.", h)
+            return None
+        hLen = _hashFuncParams[h][0]
+        hFun = _hashFuncParams[h][1]
+        k = self.modulusLen / 8
+        cLen = len(C)
+        if cLen != k:                               # 1.b)
+            warning("Key._rsaes_oaep_decrypt(): decryption error. "
+                    "(cLen != k)")
+            return None
+        if k < 2*hLen + 2:
+            warning("Key._rsaes_oaep_decrypt(): decryption error. "
+                    "(k < 2*hLen + 2)")
+            return None
+
+        # 2) RSA decryption
+        c = pkcs_os2ip(C)                           # 2.a)
+        m = self._rsadp(c)                          # 2.b)
+        EM = pkcs_i2osp(m, k)                       # 2.c)
+
+        # 3) EME-OAEP decoding
+        if L is None:                               # 3.a)
+            L = ""
+        lHash = hFun(L)
+        Y = EM[:1]                                  # 3.b)
+        if Y != '\x00':
+            warning("Key._rsaes_oaep_decrypt(): decryption error. "
+                    "(Y is not zero)")
+            return None
+        maskedSeed = EM[1:1+hLen]
+        maskedDB = EM[1+hLen:]
+        if mgf is None:
+            mgf = lambda x,y: pkcs_mgf1(x, y, h)
+        seedMask = mgf(maskedDB, hLen)              # 3.c)
+        seed = strxor(maskedSeed, seedMask)         # 3.d)
+        dbMask = mgf(seed, k - hLen - 1)            # 3.e)
+        DB = strxor(maskedDB, dbMask)               # 3.f)
+
+        # I am aware of the note at the end of 7.1.2 regarding error
+        # conditions reporting but the one provided below are for _local_
+        # debugging purposes. --arno
+
+        lHashPrime = DB[:hLen]                      # 3.g)
+        tmp = DB[hLen:].split('\x01', 1)
+        if len(tmp) != 2:
+            warning("Key._rsaes_oaep_decrypt(): decryption error. "
+                    "(0x01 separator not found)")
+            return None
+        PS, M = tmp
+        if PS != '\x00'*len(PS):
+            warning("Key._rsaes_oaep_decrypt(): decryption error. "
+                    "(invalid padding string)")
+            return None
+        if lHash != lHashPrime:
+            warning("Key._rsaes_oaep_decrypt(): decryption error. "
+                    "(invalid hash)")
+            return None            
+        return M                                    # 4)
+
+
+    def decrypt(self, C, t=None, h=None, mgf=None, L=None):
+        """
+        Decrypt ciphertext 'C' using 't' decryption scheme where 't' can be:
+
+        - None: the ciphertext 'C' is directly applied the RSADP decryption
+                primitive, as described in PKCS#1 v2.1, i.e. RFC 3447
+                Sect 5.1.2. Simply, put the message undergo a modular
+                exponentiation using the private key. Additionnal method
+                parameters are just ignored.
+
+        - 'pkcs': the ciphertext 'C' is applied RSAES-PKCS1-V1_5-DECRYPT
+                decryption scheme as described in section 7.2.2 of RFC 3447.
+                In that context, other parameters ('h', 'mgf', 'l') are not
+                used.
+
+        - 'oaep': the ciphertext 'C' is applied the RSAES-OAEP-DECRYPT decryption
+                scheme, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
+                7.1.2. In that context,
+
+                o 'h' parameter provides the name of the hash method to use.
+                  Possible values are "md2", "md4", "md5", "sha1", "tls",
+                  "sha224", "sha256", "sha384" and "sha512". if none is provided,
+                  sha1 is used by default.
+
+                o 'mgf' is the mask generation function. By default, mgf
+                  is derived from the provided hash function using the
+                  generic MGF1 (see pkcs_mgf1() for details).
+
+                o 'L' is the optional label to be associated with the
+                  message. If not provided, the default value is used, i.e
+                  the empty string. No check is done on the input limitation
+                  of the hash function regarding the size of 'L' (for
+                  instance, 2^61 - 1 for SHA-1). You have been warned.        
+        """
+        if t is None:
+            C = pkcs_os2ip(C)
+            c = self._rsadp(C)
+            l = int(math.ceil(math.log(c, 2) / 8.)) # Hack
+            return pkcs_i2osp(c, l)
+
+        elif t == "pkcs":
+            return self._rsaes_pkcs1_v1_5_decrypt(C)
+
+        elif t == "oaep":
+            return self._rsaes_oaep_decrypt(C, h, mgf, L)
+
+        else:
+            warning("Key.decrypt(): Unknown decryption type (%s) provided" % t)
+            return None
+
+    ### Below are signature related methods. Verification ones are inherited from
+    ### PubKey
+
+    def _rsasp1(self, m):
+        """
+        Internal method providing raw RSA signature, i.e. simple modular
+        exponentiation of the given message representative 'm', an integer
+        between 0 and n-1.
+
+        This is the signature primitive RSASP1 described in PKCS#1 v2.1,
+        i.e. RFC 3447 Sect. 5.2.1.
+
+        Input:
+           m: message representative, an integer between 0 and n-1, where
+              n is the key modulus.
+
+        Output:
+           signature representative, an integer between 0 and n-1
+
+        Not intended to be used directly. Please, see sign() method.
+        """
+        return self._rsadp(m)
+
+
+    def _rsassa_pss_sign(self, M, h=None, mgf=None, sLen=None):
+        """
+        Implements RSASSA-PSS-SIGN() function described in Sect. 8.1.1 of
+        RFC 3447.
+
+        Input:
+           M: message to be signed, an octet string
+
+        Output:
+           signature, an octet string of length k, where k is the length in
+           octets of the RSA modulus n.
+
+        On error, None is returned.
+        """
+
+        # Set default parameters if not provided
+        if h is None: # By default, sha1
+            h = "sha1"
+        if not _hashFuncParams.has_key(h):
+            warning("Key._rsassa_pss_sign(): unknown hash function "
+                    "provided (%s)" % h)
+            return None
+        if mgf is None: # use mgf1 with underlying hash function
+            mgf = lambda x,y: pkcs_mgf1(x, y, h)
+        if sLen is None: # use Hash output length (A.2.3 of RFC 3447)
+            hLen = _hashFuncParams[h][0]
+            sLen = hLen
+
+        # 1) EMSA-PSS encoding
+        modBits = self.modulusLen
+        k = modBits / 8
+        EM = pkcs_emsa_pss_encode(M, modBits - 1, h, mgf, sLen)
+        if EM is None:
+            warning("Key._rsassa_pss_sign(): unable to encode")
+            return None
+
+        # 2) RSA signature
+        m = pkcs_os2ip(EM)                          # 2.a)
+        s = self._rsasp1(m)                         # 2.b)
+        S = pkcs_i2osp(s, k)                        # 2.c)
+
+        return S                                    # 3)
+
+
+    def _rsassa_pkcs1_v1_5_sign(self, M, h):
+        """
+        Implements RSASSA-PKCS1-v1_5-SIGN() function as described in
+        Sect. 8.2.1 of RFC 3447.
+
+        Input:
+           M: message to be signed, an octet string
+           h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls'
+                'sha256', 'sha384').
+           
+        Output:
+           the signature, an octet string.
+        """
+        
+        # 1) EMSA-PKCS1-v1_5 encoding
+        k = self.modulusLen / 8
+        EM = pkcs_emsa_pkcs1_v1_5_encode(M, k, h)
+        if EM is None:
+            warning("Key._rsassa_pkcs1_v1_5_sign(): unable to encode")
+            return None
+
+        # 2) RSA signature
+        m = pkcs_os2ip(EM)                          # 2.a)
+        s = self._rsasp1(m)                         # 2.b)
+        S = pkcs_i2osp(s, k)                        # 2.c)
+
+        return S                                    # 3)
+
+
+    def sign(self, M, t=None, h=None, mgf=None, sLen=None):
+        """
+        Sign message 'M' using 't' signature scheme where 't' can be:
+
+        - None: the message 'M' is directly applied the RSASP1 signature
+                primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
+                5.2.1. Simply put, the message undergo a modular exponentiation
+                using the private key. Additionnal method parameters are just
+                ignored.
+
+        - 'pkcs': the message 'M' is applied RSASSA-PKCS1-v1_5-SIGN signature
+                scheme as described in Sect. 8.2.1 of RFC 3447. In that context,
+                the hash function name is passed using 'h'. Possible values are
+                "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384"
+                and "sha512". If none is provided, sha1 is used. Other additionnal 
+                parameters are ignored.
+
+        - 'pss' : the message 'M' is applied RSASSA-PSS-SIGN signature scheme as
+                described in Sect. 8.1.1. of RFC 3447. In that context,
+
+                o 'h' parameter provides the name of the hash method to use.
+                   Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224",
+                   "sha256", "sha384" and "sha512". if none is provided, sha1
+                   is used. 
+
+                o 'mgf' is the mask generation function. By default, mgf
+                   is derived from the provided hash function using the
+                   generic MGF1 (see pkcs_mgf1() for details).
+
+                o 'sLen' is the length in octet of the salt. You can overload the
+                  default value (the octet length of the hash value for provided
+                  algorithm) by providing another one with that parameter.
+        """
+
+        if t is None: # RSASP1
+            M = pkcs_os2ip(M)
+            n = self.modulus
+            if M > n-1:
+                warning("Message to be signed is too long for key modulus")
+                return None
+            s = self._rsasp1(M)
+            if s is None:
+                return None
+            return pkcs_i2osp(s, self.modulusLen/8)
+        
+        elif t == "pkcs": # RSASSA-PKCS1-v1_5-SIGN
+            if h is None:
+                h = "sha1"
+            return self._rsassa_pkcs1_v1_5_sign(M, h)
+        
+        elif t == "pss": # RSASSA-PSS-SIGN
+            return self._rsassa_pss_sign(M, h, mgf, sLen)
+
+        else:
+            warning("Key.sign(): Unknown signature type (%s) provided" % t)
+            return None
+
+
+
+
+class PubKey(OSSLHelper, _EncryptAndVerify):
+    # Below are the fields we recognize in the -text output of openssl
+    # and from which we extract information. We expect them in that
+    # order. Number of spaces does matter.
+    possible_fields = [ "Modulus (",
+                        "Exponent:" ]
+    possible_fields_count = len(possible_fields)
+    
+    def __init__(self, keypath):
+        error_msg = "Unable to import key."
+
+        # XXX Temporary hack to use PubKey inside Cert
+        if type(keypath) is tuple:
+            e, m, mLen = keypath
+            self.modulus = m
+            self.modulusLen = mLen
+            self.pubExp = e
+            return
+
+        fields_dict = {}
+        for k in self.possible_fields:
+            fields_dict[k] = None
+
+        self.keypath = None
+        rawkey = None
+
+        if (not '\x00' in keypath) and os.path.isfile(keypath): # file
+            self.keypath = keypath
+            key_size = os.path.getsize(keypath)
+            if key_size > MAX_KEY_SIZE:
+                raise Exception(error_msg)
+            try:
+                f = open(keypath)
+                rawkey = f.read()
+                f.close()
+            except:
+    		raise Exception(error_msg)     
+        else:
+            rawkey = keypath
+
+	if rawkey is None:
+	    raise Exception(error_msg)
+
+	self.rawkey = rawkey
+
+        # Let's try to get file format : PEM or DER.
+        fmtstr = 'openssl rsa -text -pubin -inform %s -noout '
+        convertstr = 'openssl rsa -pubin -inform %s -outform %s 2>/dev/null'
+        key_header = "-----BEGIN PUBLIC KEY-----"
+        key_footer = "-----END PUBLIC KEY-----"
+        l = rawkey.split(key_header, 1)
+        if len(l) == 2: # looks like PEM
+            tmp = l[1]
+            l = tmp.split(key_footer, 1)
+            if len(l) == 2:
+                tmp = l[0]
+                rawkey = "%s%s%s\n" % (key_header, tmp, key_footer)
+            else:
+                raise Exception(error_msg)
+            r,w,e = popen2.popen3(fmtstr % "PEM")
+            w.write(rawkey)
+            w.close()
+            textkey = r.read()
+            r.close()
+            res = e.read()
+            e.close()
+            if res == '':
+                self.format = "PEM"
+                self.pemkey = rawkey
+                self.textkey = textkey
+                cmd = convertstr % ("PEM", "DER")
+                self.derkey = self._apply_ossl_cmd(cmd, rawkey)
+            else:
+                raise Exception(error_msg)
+        else: # not PEM, try DER
+            r,w,e = popen2.popen3(fmtstr % "DER")            
+            w.write(rawkey)
+            w.close()
+            textkey = r.read()
+            r.close()
+            res = e.read()
+	    if res == '':
+		self.format = "DER"
+                self.derkey = rawkey
+                self.textkey = textkey
+                cmd = convertstr % ("DER", "PEM")
+                self.pemkey = self._apply_ossl_cmd(cmd, rawkey)
+                cmd = convertstr % ("DER", "DER")
+                self.derkey = self._apply_ossl_cmd(cmd, rawkey)                
+	    else:
+                try: # Perhaps it is a cert
+                    c = Cert(keypath)
+                except:
+                    raise Exception(error_msg)
+                # TODO:
+                # Reconstruct a key (der and pem) and provide:
+                # self.format
+                # self.derkey
+                # self.pemkey
+                # self.textkey
+                # self.keypath
+
+        self.osslcmdbase = 'openssl rsa -pubin -inform %s ' % self.format
+
+        self.keypath = keypath
+
+        # Parse the -text output of openssl to make things available
+        l = self.textkey.split('\n', 1)
+        if len(l) != 2:
+            raise Exception(error_msg)
+        cur, tmp = l
+        i = 0
+        k = self.possible_fields[i] # Modulus (
+        cur = cur[len(k):] + '\n'
+        while k:
+            l = tmp.split('\n', 1)
+            if len(l) != 2: # Over
+                fields_dict[k] = cur
+                break
+            l, tmp = l
+
+            newkey = 0
+            # skip fields we have already seen, this is the purpose of 'i'
+            for j in range(i, self.possible_fields_count):
+                f = self.possible_fields[j]
+                if l.startswith(f):
+                    fields_dict[k] = cur
+                    cur = l[len(f):] + '\n'
+                    k = f
+                    newkey = 1
+                    i = j+1
+                    break
+            if newkey == 1:
+                continue
+            cur += l + '\n'
+
+        # modulus and modulus length
+        v = fields_dict["Modulus ("]
+        self.modulusLen = None
+        if v:
+            v, rem = v.split(' bit):', 1)
+            self.modulusLen = int(v)
+            rem = rem.replace('\n','').replace(' ','').replace(':','')
+            self.modulus = long(rem, 16)
+        if self.modulus is None:
+            raise Exception(error_msg)
+        
+        # public exponent
+        v = fields_dict["Exponent:"]
+        self.pubExp = None
+        if v:
+            self.pubExp = long(v.split('(', 1)[0])
+        if self.pubExp is None:
+            raise Exception(error_msg)
+
+        self.key = RSA.construct((self.modulus, self.pubExp, ))
+
+    def __str__(self):
+        return self.derkey
+
+
+class Key(OSSLHelper, _DecryptAndSignMethods, _EncryptAndVerify):
+    # Below are the fields we recognize in the -text output of openssl
+    # and from which we extract information. We expect them in that
+    # order. Number of spaces does matter.
+    possible_fields = [ "Private-Key: (",
+                        "modulus:",
+                        "publicExponent:",
+                        "privateExponent:",
+                        "prime1:",
+                        "prime2:",
+                        "exponent1:",
+                        "exponent2:",
+                        "coefficient:" ]
+    possible_fields_count = len(possible_fields)
+    
+    def __init__(self, keypath):
+        error_msg = "Unable to import key."
+
+        fields_dict = {}
+        for k in self.possible_fields:
+            fields_dict[k] = None
+
+        self.keypath = None
+        rawkey = None
+
+        if (not '\x00' in keypath) and os.path.isfile(keypath):
+            self.keypath = keypath
+            key_size = os.path.getsize(keypath)
+            if key_size > MAX_KEY_SIZE:
+                raise Exception(error_msg)
+            try:
+                f = open(keypath)
+                rawkey = f.read()
+                f.close()
+            except:
+    		raise Exception(error_msg)     
+        else:
+            rawkey = keypath
+
+	if rawkey is None:
+	    raise Exception(error_msg)
+
+	self.rawkey = rawkey
+
+        # Let's try to get file format : PEM or DER.
+        fmtstr = 'openssl rsa -text -inform %s -noout '
+        convertstr = 'openssl rsa -inform %s -outform %s 2>/dev/null'
+        key_header = "-----BEGIN RSA PRIVATE KEY-----"
+        key_footer = "-----END RSA PRIVATE KEY-----"
+        l = rawkey.split(key_header, 1)
+        if len(l) == 2: # looks like PEM
+            tmp = l[1]
+            l = tmp.split(key_footer, 1)
+            if len(l) == 2:
+                tmp = l[0]
+                rawkey = "%s%s%s\n" % (key_header, tmp, key_footer)
+            else:
+                raise Exception(error_msg)
+            r,w,e = popen2.popen3(fmtstr % "PEM")
+            w.write(rawkey)
+            w.close()
+            textkey = r.read()
+            r.close()
+            res = e.read()
+            e.close()
+            if res == '':
+                self.format = "PEM"
+                self.pemkey = rawkey
+                self.textkey = textkey
+                cmd = convertstr % ("PEM", "DER")
+                self.derkey = self._apply_ossl_cmd(cmd, rawkey)
+            else:
+                raise Exception(error_msg)
+        else: # not PEM, try DER
+            r,w,e = popen2.popen3(fmtstr % "DER")            
+            w.write(rawkey)
+            w.close()
+            textkey = r.read()
+            r.close()
+            res = e.read()
+	    if res == '':
+		self.format = "DER"
+                self.derkey = rawkey
+                self.textkey = textkey
+                cmd = convertstr % ("DER", "PEM")
+                self.pemkey = self._apply_ossl_cmd(cmd, rawkey)
+                cmd = convertstr % ("DER", "DER")
+                self.derkey = self._apply_ossl_cmd(cmd, rawkey)
+	    else:
+		raise Exception(error_msg)     
+
+        self.osslcmdbase = 'openssl rsa -inform %s ' % self.format
+
+        r,w,e = popen2.popen3('openssl asn1parse -inform DER ')
+        w.write(self.derkey)
+        w.close()
+        self.asn1parsekey = r.read()
+        r.close()
+        res = e.read()
+        e.close()
+        if res != '':
+            raise Exception(error_msg)
+
+        self.keypath = keypath
+
+        # Parse the -text output of openssl to make things available
+        l = self.textkey.split('\n', 1)
+        if len(l) != 2:
+            raise Exception(error_msg)
+        cur, tmp = l
+        i = 0
+        k = self.possible_fields[i] # Private-Key: (
+        cur = cur[len(k):] + '\n'
+        while k:
+            l = tmp.split('\n', 1)
+            if len(l) != 2: # Over
+                fields_dict[k] = cur
+                break
+            l, tmp = l
+
+            newkey = 0
+            # skip fields we have already seen, this is the purpose of 'i'
+            for j in range(i, self.possible_fields_count):
+                f = self.possible_fields[j]
+                if l.startswith(f):
+                    fields_dict[k] = cur
+                    cur = l[len(f):] + '\n'
+                    k = f
+                    newkey = 1
+                    i = j+1
+                    break
+            if newkey == 1:
+                continue
+            cur += l + '\n'
+
+        # modulus length
+        v = fields_dict["Private-Key: ("]
+        self.modulusLen = None
+        if v:
+            self.modulusLen = int(v.split(' bit', 1)[0])
+        if self.modulusLen is None:
+            raise Exception(error_msg)
+        
+        # public exponent
+        v = fields_dict["publicExponent:"]
+        self.pubExp = None
+        if v:
+            self.pubExp = long(v.split('(', 1)[0])
+        if self.pubExp is None:
+            raise Exception(error_msg)
+
+        tmp = {}
+        for k in ["modulus:", "privateExponent:", "prime1:", "prime2:",
+                  "exponent1:", "exponent2:", "coefficient:"]:
+            v = fields_dict[k]
+            if v:
+                s = v.replace('\n', '').replace(' ', '').replace(':', '')
+                tmp[k] = long(s, 16)
+            else:
+                raise Exception(error_msg)
+
+        self.modulus     = tmp["modulus:"]
+        self.privExp     = tmp["privateExponent:"]
+        self.prime1      = tmp["prime1:"]
+        self.prime2      = tmp["prime2:"] 
+        self.exponent1   = tmp["exponent1:"]
+        self.exponent2   = tmp["exponent2:"]
+        self.coefficient = tmp["coefficient:"]
+
+        self.key = RSA.construct((self.modulus, self.pubExp, self.privExp))
+
+    def __str__(self):
+        return self.derkey
+
+
+# We inherit from PubKey to get access to all encryption and verification
+# methods. To have that working, we simply need Cert to provide 
+# modulusLen and key attribute.
+# XXX Yes, it is a hack.
+class Cert(OSSLHelper, _EncryptAndVerify):
+    # Below are the fields we recognize in the -text output of openssl
+    # and from which we extract information. We expect them in that
+    # order. Number of spaces does matter.
+    possible_fields = [ "        Version:",
+                        "        Serial Number:",
+                        "        Signature Algorithm:",
+                        "        Issuer:",
+                        "            Not Before:",
+                        "            Not After :",
+                        "        Subject:",
+                        "            Public Key Algorithm:",
+                        "                Modulus (",
+                        "                Exponent:",
+                        "            X509v3 Subject Key Identifier:",
+                        "            X509v3 Authority Key Identifier:",
+                        "                keyid:",
+                        "                DirName:",
+                        "                serial:",
+                        "            X509v3 Basic Constraints:",
+                        "            X509v3 Key Usage:",
+                        "            X509v3 Extended Key Usage:",
+                        "            X509v3 CRL Distribution Points:",
+                        "            Authority Information Access:",
+                        "    Signature Algorithm:" ]
+    possible_fields_count = len(possible_fields)
+    
+    def __init__(self, certpath):
+        error_msg = "Unable to import certificate."
+
+        fields_dict = {}
+        for k in self.possible_fields:
+            fields_dict[k] = None
+
+        self.certpath = None
+        rawcert = None
+
+        if (not '\x00' in certpath) and os.path.isfile(certpath): # file
+            self.certpath = certpath
+            cert_size = os.path.getsize(certpath)
+            if cert_size > MAX_CERT_SIZE:
+                raise Exception(error_msg)
+            try:
+                f = open(certpath)
+                rawcert = f.read()
+                f.close()
+            except:
+    		raise Exception(error_msg)     
+        else:
+            rawcert = certpath
+            
+	if rawcert is None:
+	    raise Exception(error_msg)
+
+	self.rawcert = rawcert
+
+        # Let's try to get file format : PEM or DER.
+        fmtstr = 'openssl x509 -text -inform %s -noout '
+        convertstr = 'openssl x509 -inform %s -outform %s '
+        cert_header = "-----BEGIN CERTIFICATE-----"
+        cert_footer = "-----END CERTIFICATE-----"
+        l = rawcert.split(cert_header, 1)
+        if len(l) == 2: # looks like PEM
+            tmp = l[1]
+            l = tmp.split(cert_footer, 1)
+            if len(l) == 2:
+                tmp = l[0]
+                rawcert = "%s%s%s\n" % (cert_header, tmp, cert_footer)
+            else:
+                raise Exception(error_msg)
+            r,w,e = popen2.popen3(fmtstr % "PEM")
+            w.write(rawcert)
+            w.close()
+            textcert = r.read()
+            r.close()
+            res = e.read()
+            e.close()
+            if res == '':
+                self.format = "PEM"
+                self.pemcert = rawcert
+                self.textcert = textcert
+                cmd = convertstr % ("PEM", "DER")
+                self.dercert = self._apply_ossl_cmd(cmd, rawcert)
+            else:
+                raise Exception(error_msg)
+        else: # not PEM, try DER
+            r,w,e = popen2.popen3(fmtstr % "DER")            
+            w.write(rawcert)
+            w.close()
+            textcert = r.read()
+            r.close()
+            res = e.read()
+	    if res == '':
+		self.format = "DER"
+                self.dercert = rawcert
+                self.textcert = textcert
+                cmd = convertstr % ("DER", "PEM")
+                self.pemcert = self._apply_ossl_cmd(cmd, rawcert)
+                cmd = convertstr % ("DER", "DER")                
+                self.dercert = self._apply_ossl_cmd(cmd, rawcert)
+	    else:
+		raise Exception(error_msg)
+
+        self.osslcmdbase = 'openssl x509 -inform %s ' % self.format
+                                                  
+        r,w,e = popen2.popen3('openssl asn1parse -inform DER ')
+        w.write(self.dercert)
+        w.close()
+        self.asn1parsecert = r.read()
+        r.close()
+        res = e.read()
+        e.close()
+        if res != '':
+            raise Exception(error_msg)
+        
+        # Grab _raw_ X509v3 Authority Key Identifier, if any.
+        tmp = self.asn1parsecert.split(":X509v3 Authority Key Identifier", 1)
+        self.authorityKeyID = None
+        if len(tmp) == 2:
+            tmp = tmp[1]
+            tmp = tmp.split("[HEX DUMP]:", 1)[1]
+            self.authorityKeyID=tmp.split('\n',1)[0]
+
+        # Grab _raw_ X509v3 Subject Key Identifier, if any.
+        tmp = self.asn1parsecert.split(":X509v3 Subject Key Identifier", 1)
+        self.subjectKeyID = None
+        if len(tmp) == 2:
+            tmp = tmp[1]
+            tmp = tmp.split("[HEX DUMP]:", 1)[1]
+            self.subjectKeyID=tmp.split('\n',1)[0]            
+
+        # Get tbsCertificate using the worst hack. output of asn1parse
+        # looks like that:
+        #
+        # 0:d=0  hl=4 l=1298 cons: SEQUENCE          
+        # 4:d=1  hl=4 l=1018 cons: SEQUENCE          
+        # ...
+        #
+        l1,l2 = self.asn1parsecert.split('\n', 2)[:2]
+        hl1 = int(l1.split("hl=",1)[1].split("l=",1)[0])
+        rem = l2.split("hl=",1)[1]
+        hl2, rem = rem.split("l=",1)
+        hl2 = int(hl2)
+        l = int(rem.split("cons",1)[0])
+        self.tbsCertificate = self.dercert[hl1:hl1+hl2+l]
+
+        # Parse the -text output of openssl to make things available
+        tmp = self.textcert.split('\n', 2)[2]
+        l = tmp.split('\n', 1)
+        if len(l) != 2:
+            raise Exception(error_msg)
+        cur, tmp = l
+        i = 0
+        k = self.possible_fields[i] # Version:
+        cur = cur[len(k):] + '\n'
+        while k:
+            l = tmp.split('\n', 1)
+            if len(l) != 2: # Over
+                fields_dict[k] = cur
+                break
+            l, tmp = l
+
+            newkey = 0
+            # skip fields we have already seen, this is the purpose of 'i'
+            for j in range(i, self.possible_fields_count):
+                f = self.possible_fields[j]
+                if l.startswith(f):
+                    fields_dict[k] = cur
+                    cur = l[len(f):] + '\n'
+                    k = f
+                    newkey = 1
+                    i = j+1
+                    break
+            if newkey == 1:
+                continue
+            cur += l + '\n'
+
+        # version
+        v = fields_dict["        Version:"]
+        self.version = None
+        if v:
+            self.version = int(v[1:2])
+        if self.version is None:
+            raise Exception(error_msg)
+
+        # serial number
+        v = fields_dict["        Serial Number:"]
+        self.serial = None
+        if v:
+            v = v.replace('\n', '').strip()
+            if "0x" in v:
+                v = v.split("0x", 1)[1].split(')', 1)[0]
+            v = v.replace(':', '').upper()
+            if len(v) % 2:
+                v = '0' + v
+            self.serial = v
+        if self.serial is None:
+            raise Exception(error_msg)
+
+        # Signature Algorithm        
+        v = fields_dict["        Signature Algorithm:"]
+        self.sigAlg = None
+        if v:
+            v = v.split('\n',1)[0]
+            v = v.strip()
+            self.sigAlg = v
+        if self.sigAlg is None:
+            raise Exception(error_msg)
+        
+        # issuer
+        v = fields_dict["        Issuer:"]
+        self.issuer = None
+        if v:
+            v = v.split('\n',1)[0]
+            v = v.strip()
+            self.issuer = v
+        if self.issuer is None:
+            raise Exception(error_msg)
+
+        # not before
+        v = fields_dict["            Not Before:"]
+        self.notBefore_str = None
+        if v:
+            v = v.split('\n',1)[0]
+            v = v.strip()
+            self.notBefore_str = v
+        if self.notBefore_str is None:
+            raise Exception(error_msg)
+        self.notBefore = time.strptime(self.notBefore_str,
+                                       "%b %d %H:%M:%S %Y %Z")
+        self.notBefore_str_simple = time.strftime("%x", self.notBefore)
+        
+        # not after
+        v = fields_dict["            Not After :"]
+        self.notAfter_str = None
+        if v:
+            v = v.split('\n',1)[0]
+            v = v.strip()
+            self.notAfter_str = v
+        if self.notAfter_str is None:
+            raise Exception(error_msg)
+        self.notAfter = time.strptime(self.notAfter_str,
+                                      "%b %d %H:%M:%S %Y %Z")
+        self.notAfter_str_simple = time.strftime("%x", self.notAfter)
+        
+        # subject
+        v = fields_dict["        Subject:"]
+        self.subject = None
+        if v:
+            v = v.split('\n',1)[0]
+            v = v.strip()
+            self.subject = v
+        if self.subject is None:
+            raise Exception(error_msg)
+        
+        # Public Key Algorithm
+        v = fields_dict["            Public Key Algorithm:"]
+        self.pubKeyAlg = None
+        if v:
+            v = v.split('\n',1)[0]
+            v = v.strip()
+            self.pubKeyAlg = v
+        if self.pubKeyAlg is None:
+            raise Exception(error_msg)
+        
+        # Modulus
+        v = fields_dict["                Modulus ("]
+        self.modulus = None
+        if v:
+            v,t = v.split(' bit):',1)
+            self.modulusLen = int(v)
+            t = t.replace(' ', '').replace('\n', ''). replace(':', '')
+            self.modulus_hexdump = t
+            self.modulus = long(t, 16)
+        if self.modulus is None:
+            raise Exception(error_msg)
+
+        # Exponent
+        v = fields_dict["                Exponent:"]
+        self.exponent = None
+        if v:
+            v = v.split('(',1)[0]
+            self.exponent = long(v)
+        if self.exponent is None:
+            raise Exception(error_msg)
+
+        # Public Key instance
+        self.key = RSA.construct((self.modulus, self.exponent, ))
+        
+        # Subject Key Identifier
+
+        # Authority Key Identifier: keyid, dirname and serial
+        self.authorityKeyID_keyid   = None
+        self.authorityKeyID_dirname = None
+        self.authorityKeyID_serial  = None
+        if self.authorityKeyID: # (hex version already done using asn1parse)
+            v = fields_dict["                keyid:"]
+            if v:
+                v = v.split('\n',1)[0]
+                v = v.strip().replace(':', '')
+                self.authorityKeyID_keyid = v
+            v = fields_dict["                DirName:"]
+            if v:
+                v = v.split('\n',1)[0]
+                self.authorityKeyID_dirname = v
+            v = fields_dict["                serial:"]
+            if v:
+                v = v.split('\n',1)[0]
+                v = v.strip().replace(':', '')
+                self.authorityKeyID_serial = v                
+
+        # Basic constraints
+        self.basicConstraintsCritical = False
+        self.basicConstraints=None
+        v = fields_dict["            X509v3 Basic Constraints:"]
+        if v:
+            self.basicConstraints = {}
+            v,t = v.split('\n',2)[:2]
+            if "critical" in v:
+                self.basicConstraintsCritical = True
+            if "CA:" in t:
+                self.basicConstraints["CA"] = t.split('CA:')[1][:4] == "TRUE"
+            if "pathlen:" in t:
+                self.basicConstraints["pathlen"] = int(t.split('pathlen:')[1])
+
+        # X509v3 Key Usage
+        self.keyUsage = []
+        v = fields_dict["            X509v3 Key Usage:"]
+        if v:	
+            # man 5 x509v3_config
+            ku_mapping = {"Digital Signature": "digitalSignature",
+                          "Non Repudiation": "nonRepudiation",
+                          "Key Encipherment": "keyEncipherment",
+                          "Data Encipherment": "dataEncipherment",
+                          "Key Agreement": "keyAgreement",
+                          "Certificate Sign": "keyCertSign",
+                          "CRL Sign": "cRLSign",
+                          "Encipher Only": "encipherOnly",
+                          "Decipher Only": "decipherOnly"}
+            v = v.split('\n',2)[1]
+            l = map(lambda x: x.strip(), v.split(','))
+            while l:
+                c = l.pop()
+                if ku_mapping.has_key(c):
+                    self.keyUsage.append(ku_mapping[c])
+                else:
+                    self.keyUsage.append(c) # Add it anyway
+                    print "Found unknown X509v3 Key Usage: '%s'" % c
+                    print "Report it to arno (at) natisbad.org for addition"
+
+        # X509v3 Extended Key Usage
+        self.extKeyUsage = []
+        v = fields_dict["            X509v3 Extended Key Usage:"]
+        if v:	
+            # man 5 x509v3_config:
+            eku_mapping = {"TLS Web Server Authentication": "serverAuth",
+                           "TLS Web Client Authentication": "clientAuth",
+                           "Code Signing": "codeSigning",
+                           "E-mail Protection": "emailProtection",
+                           "Time Stamping": "timeStamping",
+                           "Microsoft Individual Code Signing": "msCodeInd",
+                           "Microsoft Commercial Code Signing": "msCodeCom",
+                           "Microsoft Trust List Signing": "msCTLSign",
+                           "Microsoft Encrypted File System": "msEFS",
+                           "Microsoft Server Gated Crypto": "msSGC",
+                           "Netscape Server Gated Crypto": "nsSGC",
+                           "IPSec End System": "iPsecEndSystem",
+                           "IPSec Tunnel": "iPsecTunnel",
+                           "IPSec User": "iPsecUser"}
+            v = v.split('\n',2)[1]
+            l = map(lambda x: x.strip(), v.split(','))
+            while l:
+                c = l.pop()
+                if eku_mapping.has_key(c):
+                    self.extKeyUsage.append(eku_mapping[c])
+                else:
+                    self.extKeyUsage.append(c) # Add it anyway
+                    print "Found unknown X509v3 Extended Key Usage: '%s'" % c
+                    print "Report it to arno (at) natisbad.org for addition"
+
+        # CRL Distribution points
+        self.cRLDistributionPoints = []
+        v = fields_dict["            X509v3 CRL Distribution Points:"]
+        if v:
+            v = v.split("\n\n", 1)[0]
+            v = v.split("URI:")[1:]
+            self.CRLDistributionPoints = map(lambda x: x.strip(), v)
+            
+        # Authority Information Access: list of tuples ("method", "location")
+        self.authorityInfoAccess = []
+        v = fields_dict["            Authority Information Access:"]
+        if v:
+            v = v.split("\n\n", 1)[0]
+            v = v.split("\n")[1:]
+            for e in v:
+                method, location = map(lambda x: x.strip(), e.split(" - ", 1))
+                self.authorityInfoAccess.append((method, location))
+
+        # signature field
+        v = fields_dict["    Signature Algorithm:" ]
+        self.sig = None
+        if v:
+            v = v.split('\n',1)[1]
+            v = v.replace(' ', '').replace('\n', '')
+            self.sig = "".join(map(lambda x: chr(int(x, 16)), v.split(':')))
+            self.sigLen = len(self.sig)
+        if self.sig is None:
+            raise Exception(error_msg)
+
+    def isIssuerCert(self, other):
+        """
+        True if 'other' issued 'self', i.e.:
+          - self.issuer == other.subject
+          - self is signed by other
+        """
+        # XXX should be done on raw values, instead of their textual repr
+        if self.issuer != other.subject:
+            return False
+
+        # Sanity check regarding modulus length and the
+        # signature length
+        keyLen = (other.modulusLen + 7)/8
+        if keyLen != self.sigLen:
+            return False
+
+        unenc = other.encrypt(self.sig) # public key encryption, i.e. decrypt
+
+        # XXX Check block type (00 or 01 and type of padding)
+        unenc = unenc[1:]
+        if not '\x00' in unenc:
+            return False
+        pos = unenc.index('\x00')
+        unenc = unenc[pos+1:]
+
+        found = None
+        for k in _hashFuncParams.keys():
+            if self.sigAlg.startswith(k):
+                found = k
+                break
+        if not found:
+            return False
+        hlen, hfunc, digestInfo =  _hashFuncParams[k]
+        
+        if len(unenc) != (hlen+len(digestInfo)):
+            return False
+
+        if not unenc.startswith(digestInfo):
+            return False
+
+        h = unenc[-hlen:]
+        myh = hfunc(self.tbsCertificate)
+
+        return h == myh
+
+    def chain(self, certlist):
+        """
+        Construct the chain of certificates leading from 'self' to the
+        self signed root using the certificates in 'certlist'. If the
+        list does not provide all the required certs to go to the root
+        the function returns a incomplete chain starting with the
+        certificate. This fact can be tested by tchecking if the last
+        certificate of the returned chain is self signed (if c is the
+        result, c[-1].isSelfSigned())
+        """
+        d = {}
+        for c in certlist:
+            # XXX we should check if we have duplicate
+            d[c.subject] = c
+        res = [self]
+        cur = self
+        while not cur.isSelfSigned():
+            if d.has_key(cur.issuer):
+                possible_issuer = d[cur.issuer]
+                if cur.isIssuerCert(possible_issuer):
+                    res.append(possible_issuer)
+                    cur = possible_issuer
+                else:
+                    break
+        return res
+
+    def remainingDays(self, now=None):
+        """
+        Based on the value of notBefore field, returns the number of
+        days the certificate will still be valid. The date used for the
+        comparison is the current and local date, as returned by 
+        time.localtime(), except if 'now' argument is provided another
+        one. 'now' argument can be given as either a time tuple or a string
+        representing the date. Accepted format for the string version
+        are:
+        
+         - '%b %d %H:%M:%S %Y %Z' e.g. 'Jan 30 07:38:59 2008 GMT'
+         - '%m/%d/%y' e.g. '01/30/08' (less precise)
+
+        If the certificate is no more valid at the date considered, then,
+        a negative value is returned representing the number of days
+        since it has expired.
+        
+        The number of days is returned as a float to deal with the unlikely
+        case of certificates that are still just valid.
+        """
+        if now is None:
+            now = time.localtime()
+        elif type(now) is str:
+            try:
+                if '/' in now:
+                    now = time.strptime(now, '%m/%d/%y')
+                else:
+                    now = time.strptime(now, '%b %d %H:%M:%S %Y %Z')
+            except:
+                warning("Bad time string provided '%s'. Using current time" % now)
+                now = time.localtime()
+
+        now = time.mktime(now)
+        nft = time.mktime(self.notAfter)
+        diff = (nft - now)/(24.*3600)
+        return diff
+
+
+    # return SHA-1 hash of cert embedded public key
+    # !! At the moment, the trailing 0 is in the hashed string if any
+    def keyHash(self):
+	m = self.modulus_hexdump
+        res = []
+        i = 0
+        l = len(m)
+        while i<l: # get a string version of modulus
+            res.append(struct.pack("B", int(m[i:i+2], 16)))
+            i += 2
+        return sha.new("".join(res)).digest()    
+
+    def output(self, fmt="DER"):
+        if fmt == "DER":
+            return self.dercert
+        elif fmt == "PEM":
+            return self.pemcert
+        elif fmt == "TXT":
+            return self.textcert
+
+    def export(self, filename, fmt="DER"):
+        """
+        Export certificate in 'fmt' format (PEM, DER or TXT) to file 'filename'
+        """
+        f = open(filename, "wb")
+        f.write(self.output(fmt))
+        f.close()
+
+    def isSelfSigned(self):
+        """
+        Return True if the certificate is self signed:
+          - issuer and subject are the same
+          - the signature of the certificate is valid.
+        """
+        if self.issuer == self.subject:
+            return self.isIssuerCert(self)
+        return False
+
+    # Print main informations stored in certificate
+    def show(self):
+        print "Serial: %s" % self.serial
+        print "Issuer: " + self.issuer
+        print "Subject: " + self.subject
+        print "Validity: %s to %s" % (self.notBefore_str_simple,
+                                      self.notAfter_str_simple)
+
+    def __repr__(self):
+        return "[X.509 Cert. Subject:%s, Issuer:%s]" % (self.subject, self.issuer)
+
+    def __str__(self):
+        return self.dercert
+
+    def verifychain(self, anchors, untrusted=None):
+        """
+        Perform verification of certificate chains for that certificate. The
+        behavior of verifychain method is mapped (and also based) on openssl
+        verify userland tool (man 1 verify).
+        A list of anchors is required. untrusted parameter can be provided 
+        a list of untrusted certificates that can be used to reconstruct the
+        chain.
+
+        If you have a lot of certificates to verify against the same
+        list of anchor, consider constructing this list as a cafile
+        and use .verifychain_from_cafile() instead.
+        """
+        cafile = create_temporary_ca_file(anchors)
+        if not cafile:
+            return False
+        untrusted_file = None
+        if untrusted:
+            untrusted_file = create_temporary_ca_file(untrusted) # hack
+            if not untrusted_file:
+                os.unlink(cafile)
+                return False
+        res = self.verifychain_from_cafile(cafile, 
+                                           untrusted_file=untrusted_file)
+        os.unlink(cafile)
+        if untrusted_file:
+            os.unlink(untrusted_file)
+        return res
+
+    def verifychain_from_cafile(self, cafile, untrusted_file=None):
+        """
+        Does the same job as .verifychain() but using the list of anchors
+        from the cafile. This is useful (because more efficient) if
+        you have a lot of certificates to verify do it that way: it
+        avoids the creation of a cafile from anchors at each call.
+
+        As for .verifychain(), a list of untrusted certificates can be
+        passed (as a file, this time)
+        """
+        u = ""
+        if untrusted_file:
+            u = "-untrusted %s" % untrusted_file
+        try:
+            cmd = "openssl verify -CAfile %s %s " % (cafile, u)
+            pemcert = self.output(fmt="PEM")
+            cmdres = self._apply_ossl_cmd(cmd, pemcert)
+        except:
+            return False
+        return cmdres.endswith("\nOK\n") or cmdres.endswith(": OK\n")
+
+    def verifychain_from_capath(self, capath, untrusted_file=None):
+        """
+        Does the same job as .verifychain_from_cafile() but using the list
+        of anchors in capath directory. The directory should contain
+        certificates files in PEM format with associated links as
+        created using c_rehash utility (man c_rehash).
+
+        As for .verifychain_from_cafile(), a list of untrusted certificates
+        can be passed as a file (concatenation of the certificates in
+        PEM format)
+        """
+        u = ""
+        if untrusted_file:
+            u = "-untrusted %s" % untrusted_file
+        try:
+            cmd = "openssl verify -CApath %s %s " % (capath, u)
+            pemcert = self.output(fmt="PEM")
+            cmdres = self._apply_ossl_cmd(cmd, pemcert)
+        except:
+            return False
+        return cmdres.endswith("\nOK\n") or cmdres.endswith(": OK\n")
+
+    def is_revoked(self, crl_list):
+        """
+        Given a list of trusted CRL (their signature has already been
+        verified with trusted anchors), this function returns True if
+        the certificate is marked as revoked by one of those CRL.
+
+        Note that if the Certificate was on hold in a previous CRL and
+        is now valid again in a new CRL and bot are in the list, it
+        will be considered revoked: this is because _all_ CRLs are 
+        checked (not only the freshest) and revocation status is not
+        handled.
+
+        Also note that the check on the issuer is performed on the
+        Authority Key Identifier if available in _both_ the CRL and the
+        Cert. Otherwise, the issuers are simply compared.
+        """
+        for c in crl_list:
+            if (self.authorityKeyID is not None and 
+                c.authorityKeyID is not None and
+                self.authorityKeyID == c.authorityKeyID):
+                return self.serial in map(lambda x: x[0], c.revoked_cert_serials)
+            elif (self.issuer == c.issuer):
+                return self.serial in map(lambda x: x[0], c.revoked_cert_serials)
+        return False
+
+def print_chain(l):
+    llen = len(l) - 1
+    if llen < 0:
+        return ""
+    c = l[llen]
+    llen -= 1
+    s = "_ "
+    if not c.isSelfSigned():
+        s = "_ ... [Missing Root]\n"
+    else:
+        s += "%s [Self Signed]\n" % c.subject
+    i = 1
+    while (llen != -1):
+        c = l[llen]
+        s += "%s\_ %s" % (" "*i, c.subject)
+        if llen != 0:
+            s += "\n"
+        i += 2
+        llen -= 1
+    print s
+
+# import popen2
+# a=popen2.Popen3("openssl crl -text -inform DER -noout ", capturestderr=True)
+# a.tochild.write(open("samples/klasa1.crl").read())
+# a.tochild.close()
+# a.poll()
+
+class CRL(OSSLHelper):
+    # Below are the fields we recognize in the -text output of openssl
+    # and from which we extract information. We expect them in that
+    # order. Number of spaces does matter.
+    possible_fields = [ "        Version",
+                        "        Signature Algorithm:",
+                        "        Issuer:",
+                        "        Last Update:",
+                        "        Next Update:",
+                        "        CRL extensions:",
+                        "            X509v3 Issuer Alternative Name:",
+                        "            X509v3 Authority Key Identifier:", 
+                        "                keyid:",
+                        "                DirName:",
+                        "                serial:",
+                        "            X509v3 CRL Number:", 
+                        "Revoked Certificates:",
+                        "No Revoked Certificates.",
+                        "    Signature Algorithm:" ]
+    possible_fields_count = len(possible_fields)
+
+    def __init__(self, crlpath):
+        error_msg = "Unable to import CRL."
+
+        fields_dict = {}
+        for k in self.possible_fields:
+            fields_dict[k] = None
+
+        self.crlpath = None
+        rawcrl = None
+
+        if (not '\x00' in crlpath) and os.path.isfile(crlpath):
+            self.crlpath = crlpath
+            cert_size = os.path.getsize(crlpath)
+            if cert_size > MAX_CRL_SIZE:
+                raise Exception(error_msg)
+            try:
+                f = open(crlpath)
+                rawcrl = f.read()
+                f.close()
+            except:
+    		raise Exception(error_msg)     
+        else:
+            rawcrl = crlpath
+
+	if rawcrl is None:
+	    raise Exception(error_msg)
+
+	self.rawcrl = rawcrl
+
+        # Let's try to get file format : PEM or DER.
+        fmtstr = 'openssl crl -text -inform %s -noout '
+        convertstr = 'openssl crl -inform %s -outform %s '
+        crl_header = "-----BEGIN X509 CRL-----"
+        crl_footer = "-----END X509 CRL-----"
+        l = rawcrl.split(crl_header, 1)
+        if len(l) == 2: # looks like PEM
+            tmp = l[1]
+            l = tmp.split(crl_footer, 1)
+            if len(l) == 2:
+                tmp = l[0]
+                rawcrl = "%s%s%s\n" % (crl_header, tmp, crl_footer)
+            else:
+                raise Exception(error_msg)
+            r,w,e = popen2.popen3(fmtstr % "PEM")
+            w.write(rawcrl)
+            w.close()
+            textcrl = r.read()
+            r.close()
+            res = e.read()
+            e.close()
+            if res == '':
+                self.format = "PEM"
+                self.pemcrl = rawcrl
+                self.textcrl = textcrl
+                cmd = convertstr % ("PEM", "DER")
+                self.dercrl = self._apply_ossl_cmd(cmd, rawcrl)
+            else:
+                raise Exception(error_msg)
+        else: # not PEM, try DER
+            r,w,e = popen2.popen3(fmtstr % "DER")            
+            w.write(rawcrl)
+            w.close()
+            textcrl = r.read()
+            r.close()
+            res = e.read()
+	    if res == '':
+		self.format = "DER"
+                self.dercrl = rawcrl
+                self.textcrl = textcrl
+                cmd = convertstr % ("DER", "PEM")
+                self.pemcrl = self._apply_ossl_cmd(cmd, rawcrl)
+                cmd = convertstr % ("DER", "DER")
+                self.dercrl = self._apply_ossl_cmd(cmd, rawcrl)
+	    else:
+		raise Exception(error_msg)
+
+        self.osslcmdbase = 'openssl crl -inform %s ' % self.format
+
+        r,w,e = popen2.popen3('openssl asn1parse -inform DER ')
+        w.write(self.dercrl)
+        w.close()
+        self.asn1parsecrl = r.read()
+        r.close()
+        res = e.read()
+        e.close()
+        if res != '':
+            raise Exception(error_msg)
+
+        # Grab _raw_ X509v3 Authority Key Identifier, if any.
+        tmp = self.asn1parsecrl.split(":X509v3 Authority Key Identifier", 1)
+        self.authorityKeyID = None
+        if len(tmp) == 2:
+            tmp = tmp[1]
+            tmp = tmp.split("[HEX DUMP]:", 1)[1]
+            self.authorityKeyID=tmp.split('\n',1)[0]
+
+        # Parse the -text output of openssl to make things available
+        tmp = self.textcrl.split('\n', 1)[1]
+        l = tmp.split('\n', 1)
+        if len(l) != 2:
+            raise Exception(error_msg)
+        cur, tmp = l
+        i = 0
+        k = self.possible_fields[i] # Version
+        cur = cur[len(k):] + '\n'
+        while k:
+            l = tmp.split('\n', 1)
+            if len(l) != 2: # Over
+                fields_dict[k] = cur
+                break
+            l, tmp = l
+
+            newkey = 0
+            # skip fields we have already seen, this is the purpose of 'i'
+            for j in range(i, self.possible_fields_count):
+                f = self.possible_fields[j]
+                if l.startswith(f):
+                    fields_dict[k] = cur
+                    cur = l[len(f):] + '\n'
+                    k = f
+                    newkey = 1
+                    i = j+1
+                    break
+            if newkey == 1:
+                continue
+            cur += l + '\n'
+
+        # version
+        v = fields_dict["        Version"]
+        self.version = None
+        if v:
+            self.version = int(v[1:2])
+        if self.version is None:
+            raise Exception(error_msg)
+
+        # signature algorithm
+        v = fields_dict["        Signature Algorithm:"]
+        self.sigAlg = None
+        if v:
+            v = v.split('\n',1)[0]
+            v = v.strip()
+            self.sigAlg = v
+        if self.sigAlg is None:
+            raise Exception(error_msg)
+
+        # issuer
+        v = fields_dict["        Issuer:"]
+        self.issuer = None
+        if v:
+            v = v.split('\n',1)[0]
+            v = v.strip()
+            self.issuer = v
+        if self.issuer is None:
+            raise Exception(error_msg)
+
+        # last update
+        v = fields_dict["        Last Update:"]
+        self.lastUpdate_str = None
+        if v:
+            v = v.split('\n',1)[0]
+            v = v.strip()
+            self.lastUpdate_str = v
+        if self.lastUpdate_str is None:
+            raise Exception(error_msg)
+        self.lastUpdate = time.strptime(self.lastUpdate_str,
+                                       "%b %d %H:%M:%S %Y %Z")
+        self.lastUpdate_str_simple = time.strftime("%x", self.lastUpdate)
+
+        # next update
+        v = fields_dict["        Next Update:"]
+        self.nextUpdate_str = None
+        if v:
+            v = v.split('\n',1)[0]
+            v = v.strip()
+            self.nextUpdate_str = v
+        if self.nextUpdate_str is None:
+            raise Exception(error_msg)
+        self.nextUpdate = time.strptime(self.nextUpdate_str,
+                                       "%b %d %H:%M:%S %Y %Z")
+        self.nextUpdate_str_simple = time.strftime("%x", self.nextUpdate)
+        
+        # XXX Do something for Issuer Alternative Name
+
+        # Authority Key Identifier: keyid, dirname and serial
+        self.authorityKeyID_keyid   = None
+        self.authorityKeyID_dirname = None
+        self.authorityKeyID_serial  = None
+        if self.authorityKeyID: # (hex version already done using asn1parse)
+            v = fields_dict["                keyid:"]
+            if v:
+                v = v.split('\n',1)[0]
+                v = v.strip().replace(':', '')
+                self.authorityKeyID_keyid = v
+            v = fields_dict["                DirName:"]
+            if v:
+                v = v.split('\n',1)[0]
+                self.authorityKeyID_dirname = v
+            v = fields_dict["                serial:"]
+            if v:
+                v = v.split('\n',1)[0]
+                v = v.strip().replace(':', '')
+                self.authorityKeyID_serial = v
+
+        # number
+        v = fields_dict["            X509v3 CRL Number:"]
+        self.number = None
+        if v:
+            v = v.split('\n',2)[1]
+            v = v.strip()
+            self.number = int(v)
+
+        # Get the list of serial numbers of revoked certificates
+        self.revoked_cert_serials = []
+        v = fields_dict["Revoked Certificates:"]
+        t = fields_dict["No Revoked Certificates."]
+        if (t is None and v is not None):
+            v = v.split("Serial Number: ")[1:]
+            for r in v:
+                s,d = r.split('\n', 1)
+                s = s.split('\n', 1)[0]
+                d = d.split("Revocation Date:", 1)[1]
+                d = time.strptime(d.strip(), "%b %d %H:%M:%S %Y %Z")
+                self.revoked_cert_serials.append((s,d))
+
+        # signature field
+        v = fields_dict["    Signature Algorithm:" ]
+        self.sig = None
+        if v:
+            v = v.split('\n',1)[1]
+            v = v.replace(' ', '').replace('\n', '')
+            self.sig = "".join(map(lambda x: chr(int(x, 16)), v.split(':')))
+            self.sigLen = len(self.sig)
+        if self.sig is None:
+            raise Exception(error_msg)
+
+    def __str__(self):
+        return self.dercrl
+        
+    # Print main informations stored in CRL
+    def show(self):
+        print "Version: %d" % self.version
+        print "sigAlg: " + self.sigAlg
+        print "Issuer: " + self.issuer
+        print "lastUpdate: %s" % self.lastUpdate_str_simple
+        print "nextUpdate: %s" % self.nextUpdate_str_simple
+
+    def verify(self, anchors):
+        """
+        Return True if the CRL is signed by one of the provided
+        anchors. False on error (invalid signature, missing anchorand, ...)
+        """
+        cafile = create_temporary_ca_file(anchors)
+        if cafile is None:
+            return False
+        try:
+            cmd = self.osslcmdbase + '-noout -CAfile %s 2>&1' % cafile
+            cmdres = self._apply_ossl_cmd(cmd, self.rawcrl)
+        except:
+            os.unlink(cafile)
+            return False
+        os.unlink(cafile)
+        return "verify OK" in cmdres
+
+
+    
+
diff --git a/scapy/data.py b/scapy/data.py
index 018b06297e8e1308dc9b5a36ba66474ff28eea72..820232f92d7af69913f303a644c115df4307d407 100644
--- a/scapy/data.py
+++ b/scapy/data.py
@@ -3,7 +3,7 @@
 ## Copyright (C) Philippe Biondi <phil@secdev.org>
 ## This program is published under a GPLv2 license
 
-import re
+import os,sys,re
 from dadict import DADict
 from error import log_loading
 
@@ -44,7 +44,9 @@ IPV6_ADDR_UNSPECIFIED = 0x10000
 
 
 
-MTU = 1600
+MTU = 0x7fff # a.k.a give me all you have
+
+WINDOWS=sys.platform.startswith("win")
 
  
 # file parsing to get some values :
@@ -68,11 +70,9 @@ def load_protocols(filename):
             except Exception,e:
                 log_loading.info("Couldn't parse file [%s]: line [%r] (%s)" % (filename,l,e))
     except IOError:
-        log_loading.info("Can't open /etc/protocols file")
+        log_loading.info("Can't open %s file" % filename)
     return dct
 
-IP_PROTOS=load_protocols("/etc/protocols")
-
 def load_ethertypes(filename):
     spaces = re.compile("[ \t]+|\n")
     dct = DADict(_name=filename)
@@ -97,8 +97,6 @@ def load_ethertypes(filename):
         pass
     return dct
 
-ETHER_TYPES=load_ethertypes("/etc/ethertypes")
-
 def load_services(filename):
     spaces = re.compile("[ \t]+|\n")
     tdct=DADict(_name="%s-tcp"%filename)
@@ -127,7 +125,6 @@ def load_services(filename):
         log_loading.info("Can't open /etc/services file")
     return tdct,udct
 
-TCP_SERVICES,UDP_SERVICES=load_services("/etc/services")
 
 class ManufDA(DADict):
     def fixname(self, val):
@@ -172,6 +169,17 @@ def load_manuf(filename):
     
 
 
+if WINDOWS:
+    ETHER_TYPES=load_ethertypes("ethertypes")
+    IP_PROTOS=load_protocols(os.environ["SystemRoot"]+"\system32\drivers\etc\protocol")
+    TCP_SERVICES,UDP_SERVICES=load_services(os.environ["SystemRoot"] + "\system32\drivers\etc\services")
+    MANUFDB = load_manuf(os.environ["ProgramFiles"] + "\\wireshark\\manuf")
+else:
+    IP_PROTOS=load_protocols("/etc/protocols")
+    ETHER_TYPES=load_ethertypes("/etc/ethertypes")
+    TCP_SERVICES,UDP_SERVICES=load_services("/etc/services")
+    MANUFDB = load_manuf("/usr/share/wireshark/wireshark/manuf")
+
 
 
 #####################
diff --git a/scapy/fields.py b/scapy/fields.py
index a8793d1f628b626747f3fe3dbcc9c9bea47dffa5..7989a6737d4a106808023077b1c2dcd2655f0d57 100644
--- a/scapy/fields.py
+++ b/scapy/fields.py
@@ -1,4 +1,4 @@
-# This file is part of Scapy
+## This file is part of Scapy
 ## See http://www.secdev.org/projects/scapy for more informations
 ## Copyright (C) Philippe Biondi <phil@secdev.org>
 ## This program is published under a GPLv2 license
@@ -154,6 +154,11 @@ class PadField:
         self._align = align
         self._padwith = padwith or ""
 
+    def getfield(self, pkt, s):
+        x = self._fld.getfield(pkt,s)
+        padlen = ((self._align - (len(x[1]) % self._align)) % self._align)
+        return x[0][padlen:], x[1]
+
     def addfield(self, pkt, s, val):
         sval = self._fld.addfield(pkt, "", val)
         return s+sval+struct.pack("%is" % (-len(sval)%self._align), self._padwith)
@@ -249,10 +254,12 @@ class ByteField(Field):
         
 class XByteField(ByteField):
     def i2repr(self, pkt, x):
-        if x is None:
-            x = 0
         return lhex(self.i2h(pkt, x))
 
+class OByteField(ByteField):
+    def i2repr(self, pkt, x):
+        return "%03o"%self.i2h(pkt, x)
+
 class X3BytesField(XByteField):
     def __init__(self, name, default):
         Field.__init__(self, name, default, "!I")
@@ -272,8 +279,6 @@ class LEShortField(Field):
 
 class XShortField(ShortField):
     def i2repr(self, pkt, x):
-        if x is None:
-            x = 0
         return lhex(self.i2h(pkt, x))
 
 
@@ -299,8 +304,6 @@ class LESignedIntField(Field):
 
 class XIntField(IntField):
     def i2repr(self, pkt, x):
-        if x is None:
-            x = 0
         return lhex(self.i2h(pkt, x))
 
 
@@ -310,8 +313,6 @@ class LongField(Field):
 
 class XLongField(LongField):
     def i2repr(self, pkt, x):
-        if x is None:
-            x = 0
         return lhex(self.i2h(pkt, x))
 
 class IEEEFloatField(Field):
@@ -463,6 +464,19 @@ class StrFixedLenField(StrField):
             l = RandNum(0,200)
         return RandBin(l)
 
+class StrFixedLenEnumField(StrFixedLenField):
+    def __init__(self, name, default, length=None, enum=None, length_from=None):
+        StrFixedLenField.__init__(self, name, default, length=length, length_from=length_from)
+        self.enum = enum
+    def i2repr(self, pkt, v):
+        r = v.rstrip("\0")
+        rr = repr(r)
+        if v in self.enum:
+            rr = "%s (%s)" % (rr, self.enum[v])
+        elif r in self.enum:
+            rr = "%s (%s)" % (rr, self.enum[r])
+        return rr
+
 class NetBIOSNameField(StrFixedLenField):
     def __init__(self, name, default, length=31):
         StrFixedLenField.__init__(self, name, default, length)
diff --git a/scapy/layers/all.py b/scapy/layers/all.py
index f6518cd3a5133ee3057b7318751898fdf2737681..6251c2a594fe6c00cfc7be66889ea6a4308dcc6a 100644
--- a/scapy/layers/all.py
+++ b/scapy/layers/all.py
@@ -11,9 +11,9 @@ def _import_star(m):
     for k,v in mod.__dict__.iteritems():
         globals()[k] = v
 
-for l in conf.load_layers:
-    log_loading.debug("Loading layer %s" % l)
-    _import_star(l)
+for _l in conf.load_layers:
+    log_loading.debug("Loading layer %s" % _l)
+    _import_star(_l)
 
 
 
diff --git a/scapy/layers/bluetooth.py b/scapy/layers/bluetooth.py
index cfa874f534a37916820299d8a5f5edaede14945b..89757a98daeb7901c65e25148bd20d8aa8c858a0 100644
--- a/scapy/layers/bluetooth.py
+++ b/scapy/layers/bluetooth.py
@@ -9,6 +9,7 @@ from scapy.config import conf
 from scapy.packet import *
 from scapy.fields import *
 from scapy.supersocket import SuperSocket
+from scapy.data import MTU
 
 
 class HCI_Hdr(Packet):
@@ -163,7 +164,7 @@ class BluetoothL2CAPSocket(SuperSocket):
         
         self.ins = self.outs = s
 
-    def recv(self, x):
+    def recv(self, x=MTU):
         return L2CAP_CmdHdr(self.ins.recv(x))
     
 
@@ -189,7 +190,7 @@ class BluetoothHCISocket(SuperSocket):
 def srbt(peer, pkts, inter=0.1, *args, **kargs):
     """send and receive using a bluetooth socket"""
     s = conf.BTsocket(peer=peer)
-    a,b,c=sndrcv(s,pkts,inter=inter,*args,**kargs)
+    a,b = sndrcv(s,pkts,inter=inter,*args,**kargs)
     s.close()
     return a,b
 
diff --git a/scapy/layers/dot11.py b/scapy/layers/dot11.py
index 5c3aec63e2dd5969c200940e9f773228b621cf64..5fbf5121f0f435282dabd889e8f65cea542c9c27 100644
--- a/scapy/layers/dot11.py
+++ b/scapy/layers/dot11.py
@@ -263,8 +263,8 @@ class Dot11AssoResp(Packet):
 class Dot11ReassoReq(Packet):
     name = "802.11 Reassociation Request"
     fields_desc = [ FlagsField("cap", 0, 16, capability_list),
-                    MACField("current_AP", ETHER_ANY),
-                    LEShortField("listen_interval", 0x00c8) ]
+                    LEShortField("listen_interval", 0x00c8),
+                    MACField("current_AP", ETHER_ANY) ]
 
 
 class Dot11ReassoResp(Dot11AssoResp):
diff --git a/scapy/layers/inet.py b/scapy/layers/inet.py
index 15897eb2d32b28428253e2f31f77f761c3db3210..db36d0b8370e27e3076da7f16ad2651975817dcd 100644
--- a/scapy/layers/inet.py
+++ b/scapy/layers/inet.py
@@ -14,6 +14,7 @@ from scapy.packet import *
 from scapy.volatile import *
 from scapy.sendrecv import sr,sr1,srp1
 from scapy.plist import PacketList,SndRcvList
+from scapy.automaton import Automaton,ATMT
 
 import scapy.as_resolvers
 
@@ -387,7 +388,35 @@ class IP(Packet, IPTools):
             s += " frag:%i" % self.frag
         return s
                  
-    
+    def fragment(self, fragsize=1480):
+        """Fragment IP datagrams"""
+        fragsize = (fragsize+7)/8*8
+        lst = []
+        fnb = 0
+        fl = self
+        while fl.underlayer is not None:
+            fnb += 1
+            fl = fl.underlayer
+        
+        for p in fl:
+            s = str(p[fnb].payload)
+            nb = (len(s)+fragsize-1)/fragsize
+            for i in range(nb):            
+                q = p.copy()
+                del(q[fnb].payload)
+                del(q[fnb].chksum)
+                del(q[fnb].len)
+                if i == nb-1:
+                    q[IP].flags &= ~1
+                else:
+                    q[IP].flags |= 1 
+                q[IP].frag = i*fragsize/8
+                r = Raw(load=s[i*fragsize:(i+1)*fragsize])
+                r.overload_fields = p[IP].payload.overload_fields.copy()
+                q.add_payload(r)
+                lst.append(q)
+        return lst
+
 
 class TCP(Packet):
     name = "TCP"
@@ -421,8 +450,8 @@ class TCP(Packet):
                                      ln)
                 ck=checksum(psdhdr+p)
                 p = p[:16]+struct.pack("!H", ck)+p[18:]
-            elif conf.ipv6_enabled and isinstance(self.underlayer, inet6.IPv6) or isinstance(self.underlayer, inet6._IPv6ExtHdr):
-                ck = inet6.in6_chksum(socket.IPPROTO_TCP, self.underlayer, p)
+            elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr):
+                ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_TCP, self.underlayer, p)
                 p = p[:16]+struct.pack("!H", ck)+p[18:]
             else:
                 warning("No IP underlayer to compute checksum. Leaving null.")
@@ -445,7 +474,7 @@ class TCP(Packet):
     def mysummary(self):
         if isinstance(self.underlayer, IP):
             return self.underlayer.sprintf("TCP %IP.src%:%TCP.sport% > %IP.dst%:%TCP.dport% %TCP.flags%")
-        elif conf.ipv6_enabled and isinstance(self.underlayer, inet6.IPv6):
+        elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6):
             return self.underlayer.sprintf("TCP %IPv6.src%:%TCP.sport% > %IPv6.dst%:%TCP.dport% %TCP.flags%")
         else:
             return self.sprintf("TCP %TCP.sport% > %TCP.dport% %TCP.flags%")
@@ -475,8 +504,8 @@ class UDP(Packet):
                                      ln)
                 ck=checksum(psdhdr+p)
                 p = p[:6]+struct.pack("!H", ck)+p[8:]
-            elif isinstance(self.underlayer, inet6.IPv6) or isinstance(self.underlayer, inet6._IPv6ExtHdr):
-                ck = inet6.in6_chksum(socket.IPPROTO_UDP, self.underlayer, p)
+            elif isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr):
+                ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_UDP, self.underlayer, p)
                 p = p[:6]+struct.pack("!H", ck)+p[8:]
             else:
                 warning("No IP underlayer to compute checksum. Leaving null.")
@@ -496,7 +525,7 @@ class UDP(Packet):
     def mysummary(self):
         if isinstance(self.underlayer, IP):
             return self.underlayer.sprintf("UDP %IP.src%:%UDP.sport% > %IP.dst%:%UDP.dport%")
-        elif isinstance(self.underlayer, inet6.IPv6):
+        elif isinstance(self.underlayer, scapy.layers.inet6.IPv6):
             return self.underlayer.sprintf("UDP %IPv6.src%:%UDP.sport% > %IPv6.dst%:%UDP.dport%")
         else:
             return self.sprintf("UDP %UDP.sport% > %UDP.dport%")    
@@ -569,7 +598,9 @@ class ICMP(Packet):
         return p
     
     def hashret(self):
-        return struct.pack("HH",self.id,self.seq)+self.payload.hashret()
+        if self.type in [0,8,13,14,15,16,17,18]:
+            return struct.pack("HH",self.id,self.seq)+self.payload.hashret()
+        return self.payload.hashret()
     def answers(self, other):
         if not isinstance(other,ICMP):
             return 0
@@ -998,7 +1029,7 @@ class TracerouteResult(SndRcvList):
         while 1:
             if visual.scene.kb.keys:
                 k = visual.scene.kb.getkey()
-                if k == "esc":
+                if k == "esc" or k == "q":
                     break
             if visual.scene.mouse.events:
                 ev = visual.scene.mouse.getevent()
@@ -1084,8 +1115,8 @@ class TracerouteResult(SndRcvList):
         ports = {}
         ports_done = {}
         for s,r in self.res:
-            r = r[IP] or (conf.ipv6_enabled and r[inet6.IPv6]) or r
-            s = s[IP] or (conf.ipv6_enabled and s[inet6.IPv6]) or s
+            r = r.getlayer(IP) or (conf.ipv6_enabled and r[scapy.layers.inet6.IPv6]) or r
+            s = s.getlayer(IP) or (conf.ipv6_enabled and s[scapy.layers.inet6.IPv6]) or s
             ips[r.src] = None
             if TCP in s:
                 trace_id = (s.src,s.dst,6,s.dport)
@@ -1096,8 +1127,8 @@ class TracerouteResult(SndRcvList):
             else:
                 trace_id = (s.src,s.dst,s.proto,0)
             trace = rt.get(trace_id,{})
-            ttl = conf.ipv6_enabled and inet6.IPv6 in s and s.hlim or s.ttl
-            if not (ICMP in r and r[ICMP].type == 11) and not (conf.ipv6_enabled and inet6.IPv6 in r and ICMPv6TimeExceeded in r):
+            ttl = conf.ipv6_enabled and scapy.layers.inet6.IPv6 in s and s.hlim or s.ttl
+            if not (ICMP in r and r[ICMP].type == 11) and not (conf.ipv6_enabled and scapy.layers.inet6.IPv6 in r and ICMPv6TimeExceeded in r):
                 if trace_id in ports_done:
                     continue
                 ports_done[trace_id] = None
@@ -1274,6 +1305,133 @@ traceroute(target, [maxttl=30,] [dport=80,] [sport=80,] [verbose=conf.verb]) ->
 
 
 
+#############################
+## Simple TCP client stack ##
+#############################
+
+class TCP_client(Automaton):
+    
+    def parse_args(self, ip, port, *args, **kargs):
+        self.dst = ip
+        self.dport = port
+        self.sport = random.randrange(0,2**16)
+        self.l4 = IP(dst=ip)/TCP(sport=self.sport, dport=self.dport, flags=0,
+                                 seq=random.randrange(0,2**32))
+        self.src = self.l4.src
+        self.swin=self.l4[TCP].window
+        self.dwin=1
+        self.rcvbuf=""
+        bpf = "host %s  and host %s and port %i and port %i" % (self.src,
+                                                                self.dst,
+                                                                self.sport,
+                                                                self.dport)
+
+#        bpf=None
+        Automaton.parse_args(self, filter=bpf, **kargs)
+
+    
+    def master_filter(self, pkt):
+        return (IP in pkt and
+                pkt[IP].src == self.dst and
+                pkt[IP].dst == self.src and
+                TCP in pkt and
+                pkt[TCP].sport == self.dport and
+                pkt[TCP].dport == self.sport and
+                self.l4[TCP].seq >= pkt[TCP].ack and # XXX: seq/ack 2^32 wrap up
+                ((self.l4[TCP].ack == 0) or (self.l4[TCP].ack <= pkt[TCP].seq <= self.l4[TCP].ack+self.swin)) )
+
+
+    @ATMT.state(initial=1)
+    def START(self):
+        pass
+
+    @ATMT.state()
+    def SYN_SENT(self):
+        pass
+    
+    @ATMT.state()
+    def ESTABLISHED(self):
+        pass
+
+    @ATMT.state()
+    def LAST_ACK(self):
+        pass
+
+    @ATMT.state(final=1)
+    def CLOSED(self):
+        pass
+
+    
+    @ATMT.condition(START)
+    def connect(self):
+        raise self.SYN_SENT()
+    @ATMT.action(connect)
+    def send_syn(self):
+        self.l4[TCP].flags = "S"
+        self.send(self.l4)
+        self.l4[TCP].seq += 1
+
+
+    @ATMT.receive_condition(SYN_SENT)
+    def synack_received(self, pkt):
+        if pkt[TCP].flags & 0x3f == 0x12:
+            raise self.ESTABLISHED().action_parameters(pkt)
+    @ATMT.action(synack_received)
+    def send_ack_of_synack(self, pkt):
+        self.l4[TCP].ack = pkt[TCP].seq+1
+        self.l4[TCP].flags = "A"
+        self.send(self.l4)
+
+    @ATMT.receive_condition(ESTABLISHED)
+    def incoming_data_received(self, pkt):
+        if not isinstance(pkt[TCP].payload, NoPayload) and not isinstance(pkt[TCP].payload, Padding):
+            raise self.ESTABLISHED().action_parameters(pkt)
+    @ATMT.action(incoming_data_received)
+    def receive_data(self,pkt):
+        data = str(pkt[TCP].payload)
+        if data and self.l4[TCP].ack == pkt[TCP].seq:
+            self.l4[TCP].ack += len(data)
+            self.l4[TCP].flags = "A"
+            self.send(self.l4)
+            self.rcvbuf += data
+            if pkt[TCP].flags & 8 != 0: #PUSH
+                self.oi.tcp.send(self.rcvbuf)
+                self.rcvbuf = ""
+    
+    @ATMT.ioevent(ESTABLISHED,name="tcp", as_supersocket="tcplink")
+    def outgoing_data_received(self, fd):
+        raise self.ESTABLISHED().action_parameters(fd.recv())
+    @ATMT.action(outgoing_data_received)
+    def send_data(self, d):
+        self.l4[TCP].flags = "PA"
+        self.send(self.l4/d)
+        self.l4[TCP].seq += len(d)
+        
+    
+    @ATMT.receive_condition(ESTABLISHED)
+    def reset_received(self, pkt):
+        if pkt[TCP].flags & 4 != 0:
+            raise self.CLOSED()
+
+    @ATMT.receive_condition(ESTABLISHED)
+    def fin_received(self, pkt):
+        if pkt[TCP].flags & 0x1 == 1:
+            raise self.LAST_ACK().action_parameters(pkt)
+    @ATMT.action(fin_received)
+    def send_finack(self, pkt):
+        self.l4[TCP].flags = "FA"
+        self.l4[TCP].ack = pkt[TCP].seq+1
+        self.send(self.l4)
+        self.l4[TCP].seq += 1
+
+    @ATMT.receive_condition(LAST_ACK)
+    def ack_of_fin_received(self, pkt):
+        if pkt[TCP].flags & 0x3f == 0x10:
+            raise self.CLOSED()
+
+
+
+
 #####################
 ## Reporting stuff ##
 #####################
@@ -1382,4 +1540,4 @@ conf.stats_classic_protocols += [TCP,UDP,ICMP]
 conf.stats_dot11_protocols += [TCP,UDP,ICMP]
 
 if conf.ipv6_enabled:
-    from scapy.layers import inet6
+    import scapy.layers.inet6
diff --git a/scapy/layers/inet6.py b/scapy/layers/inet6.py
index 5466e985721c3bae2c465fd2c57beaf0338057f2..f30ac79a7f7aec004e74b9d64737b4f46ba676f4 100644
--- a/scapy/layers/inet6.py
+++ b/scapy/layers/inet6.py
@@ -23,6 +23,9 @@
 import socket
 if not socket.has_ipv6:
     raise socket.error("can't use AF_INET6, IPv6 is disabled")
+if not hasattr(socket, "IPPROTO_IPV6"):
+    # Workaround for http://bugs.python.org/issue6926
+    socket.IPPROTO_IPV6 = 41
 
 from scapy.config import conf
 from scapy.layers.l2 import *
@@ -206,6 +209,8 @@ class IP6Field(Field):
                 vaddr = in6_6to4ExtractAddr(x)
                 return "%s [6to4 GW: %s]" % (self.i2h(pkt, x), vaddr)
         return self.i2h(pkt, x)       # No specific information to return
+    def randval(self):
+        return RandIP6()
 
 class SourceIP6Field(IP6Field):
     def __init__(self, name, dstname):
@@ -2938,6 +2943,7 @@ conf.l3types.register(ETH_P_IPV6, IPv6)
 conf.l2types.register(31, IPv6)
 
 bind_layers(Ether,     IPv6,     type = 0x86dd )
+bind_layers(CookedLinux, IPv6,   proto = 0x86dd )
 bind_layers(IPerror6,  TCPerror, nh = socket.IPPROTO_TCP )
 bind_layers(IPerror6,  UDPerror, nh = socket.IPPROTO_UDP )
 bind_layers(IPv6,      TCP,      nh = socket.IPPROTO_TCP )
diff --git a/scapy/layers/isakmp.py b/scapy/layers/isakmp.py
index 8dd60ad8c069cad0a25764cb9c4fe19f05337473..ccbad10deb2a29906499abfb01122d423b20dc95 100644
--- a/scapy/layers/isakmp.py
+++ b/scapy/layers/isakmp.py
@@ -7,7 +7,8 @@ import struct
 from scapy.packet import *
 from scapy.fields import *
 from scapy.ansmachine import *
-from scapy.layers.inet import UDP
+from scapy.layers.inet import IP,UDP
+from scapy.sendrecv import sr
 
 
 # see http://www.iana.org/assignments/ipsec-registry for details
diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py
index 97ed5a1a3f986355ec1176441c0480c83d5079a1..6fd1141f51698bd5eea17f3a63b0fdcde5efbb72 100644
--- a/scapy/layers/l2.py
+++ b/scapy/layers/l2.py
@@ -348,22 +348,38 @@ class ARP(Packet):
                  
 conf.neighbor.register_l3(Ether, ARP, lambda l2,l3: getmacbyip(l3.pdst))
 
+class GRErouting(Packet):
+    name = "GRE routing informations"
+    fields_desc = [ ShortField("address_family",0),
+                    ByteField("SRE_offset", 0),
+                    FieldLenField("SRE_len", None, "routing_info", "B"),
+                    StrLenField("routing_info", "", "SRE_len"),
+                    ]
+
+
 class GRE(Packet):
     name = "GRE"
-    fields_desc = [ BitField("chksumpresent",0,1),
-                    BitField("reserved0",0,12),
+    fields_desc = [ BitField("chksum_present",0,1),
+                    BitField("routing_present",0,1),
+                    BitField("key_present",0,1),
+                    BitField("seqnum_present",0,1),
+                    BitField("strict_route_source",0,1),
+                    BitField("recursion control",0,3),
+                    BitField("flags",0,5),
                     BitField("version",0,3),
                     XShortEnumField("proto", 0x0000, ETHER_TYPES),
-                    ConditionalField(XShortField("chksum",None),lambda pkt:pkt.chksumpresent==1),
-                    ConditionalField(XShortField("reserved1",None),lambda pkt:pkt.chksumpresent==1),
+                    ConditionalField(XShortField("chksum",None), lambda pkt:pkt.chksum_present==1 or pkt.routing_present==1),
+                    ConditionalField(XShortField("offset",None), lambda pkt:pkt.chksum_present==1 or pkt.routing_present==1),
+                    ConditionalField(XIntField("key",None), lambda pkt:pkt.key_present==1),
+                    ConditionalField(XIntField("seqence_number",None), lambda pkt:pkt.seqnum_present==1),
                     ]
     def post_build(self, p, pay):
         p += pay
-        if self.chksumpresent and self.chksum is None:
+        if self.chksum_present and self.chksum is None:
             c = checksum(p)
             p = p[:4]+chr((c>>8)&0xff)+chr(c&0xff)+p[6:]
         return p
-            
+
 
 
 
@@ -384,6 +400,9 @@ bind_layers( GRE,           Dot1Q,         proto=33024)
 bind_layers( GRE,           Ether,         proto=1)
 bind_layers( GRE,           ARP,           proto=2054)
 bind_layers( GRE,           EAPOL,         proto=34958)
+bind_layers( GRE,           GRErouting,    { "routing_present" : 1 } )
+bind_layers( GRErouting,    Raw,           { "address_family" : 0, "SRE_len" : 0 })
+bind_layers( GRErouting,    GRErouting,    { } )
 bind_layers( EAPOL,         EAP,           type=0)
 bind_layers( LLC,           STP,           dsap=66, ssap=66, ctrl=3)
 bind_layers( LLC,           SNAP,          dsap=170, ssap=170, ctrl=3)
diff --git a/scapy/layers/sctp.py b/scapy/layers/sctp.py
new file mode 100644
index 0000000000000000000000000000000000000000..82e4e4fbd7dc69b5cd1307f9a2b105a096d2af5e
--- /dev/null
+++ b/scapy/layers/sctp.py
@@ -0,0 +1,433 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## Copyright (C) 6WIND <olivier.matz@6wind.com>
+## This program is published under a GPLv2 license
+
+import struct
+
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.inet import IP
+from scapy.layers.inet6 import IP6Field
+
+IPPROTO_SCTP=132
+
+# crc32-c (Castagnoli) (crc32c_poly=0x1EDC6F41)
+crc32c_table = [
+    0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
+    0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
+    0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
+    0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
+    0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
+    0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
+    0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
+    0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
+    0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
+    0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
+    0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
+    0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
+    0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
+    0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
+    0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
+    0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
+    0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
+    0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
+    0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
+    0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
+    0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
+    0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
+    0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
+    0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
+    0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
+    0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
+    0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
+    0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
+    0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
+    0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
+    0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
+    0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
+    0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
+    0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
+    0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
+    0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
+    0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
+    0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
+    0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
+    0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
+    0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
+    0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
+    0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
+    0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
+    0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
+    0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
+    0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
+    0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
+    0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
+    0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
+    0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
+    0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
+    0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
+    0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
+    0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
+    0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
+    0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
+    0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
+    0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
+    0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
+    0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
+    0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
+    0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
+    0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351,
+    ]
+
+def crc32c(buf):
+    crc = 0xffffffff
+    for c in buf:
+        crc = (crc>>8) ^ crc32c_table[(crc^(ord(c))) & 0xFF]
+    crc = (~crc) & 0xffffffff
+    # reverse endianness
+    return struct.unpack(">I",struct.pack("<I", crc))[0]
+
+# old checksum (RFC2960)
+"""
+BASE = 65521 # largest prime smaller than 65536
+def update_adler32(adler, buf):
+    s1 = adler & 0xffff
+    s2 = (adler >> 16) & 0xffff
+    print s1,s2
+
+    for c in buf:
+        print ord(c)
+        s1 = (s1 + ord(c)) % BASE
+        s2 = (s2 + s1) % BASE
+        print s1,s2
+    return (s2 << 16) + s1
+
+def sctp_checksum(buf):
+    return update_adler32(1, buf)
+"""
+
+sctpchunktypescls = {
+    0 : "SCTPChunkData",
+    1 : "SCTPChunkInit",
+    2 : "SCTPChunkInitAck",
+    3 : "SCTPChunkSACK",
+    4 : "SCTPChunkHeartbeatReq",
+    5 : "SCTPChunkHeartbeatAck",
+    6 : "SCTPChunkAbort",
+    7 : "SCTPChunkShutdown",
+    8 : "SCTPChunkShutdownAck",
+    9 : "SCTPChunkError",
+    10 : "SCTPChunkCookieEcho",
+    11 : "SCTPChunkCookieAck",
+    14 : "SCTPChunkShutdownComplete",
+    }
+
+sctpchunktypes = {
+    0 : "data",
+    1 : "init",
+    2 : "init-ack",
+    3 : "sack",
+    4 : "heartbeat-req",
+    5 : "heartbeat-ack",
+    6 : "abort",
+    7 : "shutdown",
+    8 : "shutdown-ack",
+    9 : "error",
+    10 : "cookie-echo",
+    11 : "cookie-ack",
+    14 : "shutdown-complete",
+    }
+
+sctpchunkparamtypescls = {
+    1 : "SCTPChunkParamHearbeatInfo",
+    5 : "SCTPChunkParamIPv4Addr",
+    6 : "SCTPChunkParamIPv6Addr",
+    7 : "SCTPChunkParamStateCookie",
+    8 : "SCTPChunkParamUnrocognizedParam",
+    9 : "SCTPChunkParamCookiePreservative",
+    11 : "SCTPChunkParamHostname",
+    12 : "SCTPChunkParamSupportedAddrTypes",
+    32768 : "SCTPChunkParamECNCapable",
+    49152 : "SCTPChunkParamFwdTSN",
+    49158 : "SCTPChunkParamAdaptationLayer",
+    }
+
+sctpchunkparamtypes = {
+    1 : "heartbeat-info",
+    5 : "IPv4",
+    6 : "IPv6",
+    7 : "state-cookie",
+    8 : "unrecognized-param",
+    9 : "cookie-preservative",
+    11 : "hostname",
+    12 : "addrtypes",
+    32768 : "ecn-capable",
+    49152 : "fwd-tsn-supported",
+    49158 : "adaptation-layer",
+    }
+
+############## SCTP header
+
+# Dummy class to guess payload type (variable parameters)
+class _SCTPChunkGuessPayload:
+    def default_payload_class(self,p):
+        if len(p) < 4:
+            return Padding
+        else:
+            t = ord(p[0])
+            return globals().get(sctpchunktypescls.get(t, "Raw"), Raw)
+
+
+class SCTP(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ShortField("sport", None),
+                    ShortField("dport", None),
+                    XIntField("tag", None),
+                    XIntField("chksum", None), ]
+    def answers(self, other):
+        if not isinstance(other, SCTP):
+            return 0
+        if conf.checkIPsrc:
+            if not ((self.sport == other.dport) and
+                    (self.dport == other.sport)):
+                return 0
+        return 1
+    def post_build(self, p, pay):
+        p += pay
+        if self.chksum is None:
+            crc = crc32c(str(p))
+            p = p[:8]+struct.pack(">I", crc)+p[12:]
+        return p
+
+############## SCTP Chunk variable params
+
+class ChunkParamField(PacketListField):
+    islist = 1
+    holds_packets=1
+    def __init__(self, name, default, count_from=None, length_from=None):
+        PacketListField.__init__(self, name, default, Raw, count_from=count_from, length_from=length_from)
+    def m2i(self, p, m):
+        cls = Raw
+        if len(m) >= 4:
+            t = ord(m[0]) * 256 + ord(m[1])
+            cls = globals().get(sctpchunkparamtypescls.get(t, "Raw"), Raw)
+        return cls(m)
+
+# dummy class to avoid Raw() after Chunk params
+class _SCTPChunkParam:
+    def extract_padding(self, s):
+        return "",s[:]
+
+class SCTPChunkParamHearbeatInfo(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 1, sctpchunkparamtypes),
+                    FieldLenField("len", None, length_of="data",
+                                  adjust = lambda pkt,x:x+4),
+                    PadField(StrLenField("data", "",
+                                         length_from=lambda pkt: pkt.len-4),
+                             4, padwith="\x00"),]
+
+class SCTPChunkParamIPv4Addr(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 5, sctpchunkparamtypes),
+                    ShortField("len", 8),
+                    IPField("addr","127.0.0.1"), ]
+
+class SCTPChunkParamIPv6Addr(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 6, sctpchunkparamtypes),
+                    ShortField("len", 20),
+                    IP6Field("addr","::1"), ]
+
+class SCTPChunkParamStateCookie(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 7, sctpchunkparamtypes),
+                    FieldLenField("len", None, length_of="cookie",
+                                  adjust = lambda pkt,x:x+4),
+                    PadField(StrLenField("cookie", "",
+                                         length_from=lambda pkt: pkt.len-4),
+                             4, padwith="\x00"),]
+
+class SCTPChunkParamUnrocognizedParam(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 8, sctpchunkparamtypes),
+                    FieldLenField("len", None, length_of="param",
+                                  adjust = lambda pkt,x:x+4),
+                    PadField(StrLenField("param", "",
+                                         length_from=lambda pkt: pkt.len-4),
+                             4, padwith="\x00"),]
+
+class SCTPChunkParamCookiePreservative(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 9, sctpchunkparamtypes),
+                    ShortField("len", 8),
+                    XIntField("sug_cookie_inc", None), ]
+
+class SCTPChunkParamHostname(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 11, sctpchunkparamtypes),
+                    FieldLenField("len", None, length_of="hostname",
+                                  adjust = lambda pkt,x:x+4),
+                    PadField(StrLenField("hostname", "",
+                                         length_from=lambda pkt: pkt.len-4),
+                             4, padwith="\x00"), ]
+
+class SCTPChunkParamSupportedAddrTypes(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 12, sctpchunkparamtypes),
+                    FieldLenField("len", None, length_of="addr_type_list",
+                                  adjust = lambda pkt,x:x+4),
+                    PadField(FieldListField("addr_type_list", [ "IPv4" ],
+                                            ShortEnumField("addr_type", 5, sctpchunkparamtypes),
+                                            length_from=lambda pkt: pkt.len-4),
+                             4, padwith="\x00"), ]
+
+class SCTPChunkParamECNCapable(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 32768, sctpchunkparamtypes),
+                    ShortField("len", 4), ]
+
+class SCTPChunkParamFwdTSN(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 49152, sctpchunkparamtypes),
+                    ShortField("len", 4), ]
+
+class SCTPChunkParamAdaptationLayer(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 49158, sctpchunkparamtypes),
+                    ShortField("len", 8),
+                    XIntField("indication", None), ]
+
+############## SCTP Chunks
+
+class SCTPChunkData(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ByteEnumField("type", 0, sctpchunktypes),
+                    BitField("reserved", None, 4),
+                    BitField("delay_sack", 0, 1),
+                    BitField("unordered", 0, 1),
+                    BitField("beginning", 0, 1),
+                    BitField("ending", 0, 1),
+                    FieldLenField("len", None, length_of="data", adjust = lambda pkt,x:x+16),
+                    XIntField("tsn", None),
+                    XShortField("stream_id", None),
+                    XShortField("stream_seq", None),
+                    XIntField("proto_id", None),
+                    PadField(StrLenField("data", None, length_from=lambda pkt: pkt.len-16),
+                             4, padwith="\x00"),
+                    ]
+
+class SCTPChunkInit(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ByteEnumField("type", 1, sctpchunktypes),
+                    XByteField("flags", None),
+                    FieldLenField("len", None, length_of="params", adjust = lambda pkt,x:x+20),
+                    XIntField("init_tag", None),
+                    IntField("a_rwnd", None),
+                    ShortField("n_out_streams", None),
+                    ShortField("n_in_streams", None),
+                    XIntField("init_tsn", None),
+                    ChunkParamField("params", None, length_from=lambda pkt:pkt.len-20),
+                    ]
+
+class SCTPChunkInitAck(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ByteEnumField("type", 2, sctpchunktypes),
+                    XByteField("flags", None),
+                    FieldLenField("len", None, length_of="params", adjust = lambda pkt,x:x+20),
+                    XIntField("init_tag", None),
+                    IntField("a_rwnd", None),
+                    ShortField("n_out_streams", None),
+                    ShortField("n_in_streams", None),
+                    XIntField("init_tsn", None),
+                    ChunkParamField("params", None, length_from=lambda pkt:pkt.len-20),
+                    ]
+
+class GapAckField(Field):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "4s")
+    def i2m(self, pkt, x):
+        if x is None:
+            return "\0\0\0\0"
+        sta, end = map(int, x.split(":"))
+        args = tuple([">HH", sta, end])
+        return struct.pack(*args)
+    def m2i(self, pkt, x):
+        return "%d:%d"%(struct.unpack(">HH", x))
+    def any2i(self, pkt, x):
+        if type(x) is tuple and len(x) == 2:
+            return "%d:%d"%(x)
+        return x
+
+class SCTPChunkSACK(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ByteEnumField("type", 3, sctpchunktypes),
+                    XByteField("flags", None),
+                    ShortField("len", None),
+                    XIntField("cumul_tsn_ack", None),
+                    IntField("a_rwnd", None),
+                    FieldLenField("n_gap_ack", None, count_of="gap_ack_list"),
+                    FieldLenField("n_dup_tsn", None, count_of="dup_tsn_list"),
+                    FieldListField("gap_ack_list", [ ], GapAckField("gap_ack", None), count_from=lambda pkt:pkt.n_gap_ack),
+                    FieldListField("dup_tsn_list", [ ], XIntField("dup_tsn", None), count_from=lambda pkt:pkt.n_dup_tsn),
+                    ]
+
+    def post_build(self, p, pay):
+        if self.len is None:
+            p = p[:2] + struct.pack(">H", len(p)) + p[4:]
+        return p+pay
+
+
+class SCTPChunkHeartbeatReq(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ByteEnumField("type", 4, sctpchunktypes),
+                    XByteField("flags", None),
+                    FieldLenField("len", None, length_of="params", adjust = lambda pkt,x:x+4),
+                    ChunkParamField("params", None, length_from=lambda pkt:pkt.len-4),
+                   ]
+
+class SCTPChunkHeartbeatAck(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ByteEnumField("type", 5, sctpchunktypes),
+                    XByteField("flags", None),
+                    FieldLenField("len", None, length_of="params", adjust = lambda pkt,x:x+4),
+                    ChunkParamField("params", None, length_from=lambda pkt:pkt.len-4),
+                   ]
+
+class SCTPChunkAbort(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ByteEnumField("type", 6, sctpchunktypes),
+                    BitField("reserved", None, 7),
+                    BitField("TCB", 0, 1),
+                    FieldLenField("len", None, length_of="error_causes", adjust = lambda pkt,x:x+4),
+                    PadField(StrLenField("error_causes", "", length_from=lambda pkt: pkt.len-4),
+                             4, padwith="\x00"),
+                   ]
+
+class SCTPChunkShutdown(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ByteEnumField("type", 7, sctpchunktypes),
+                    XByteField("flags", None),
+                    ShortField("len", 8),
+                    XIntField("cumul_tsn_ack", None),
+                   ]
+
+class SCTPChunkShutdownAck(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ByteEnumField("type", 8, sctpchunktypes),
+                    XByteField("flags", None),
+                    ShortField("len", 4),
+                   ]
+
+class SCTPChunkError(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ByteEnumField("type", 9, sctpchunktypes),
+                    XByteField("flags", None),
+                    FieldLenField("len", None, length_of="error_causes", adjust = lambda pkt,x:x+4),
+                    PadField(StrLenField("error_causes", "", length_from=lambda pkt: pkt.len-4),
+                             4, padwith="\x00"),
+                   ]
+
+class SCTPChunkCookieEcho(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ByteEnumField("type", 10, sctpchunktypes),
+                    XByteField("flags", None),
+                    FieldLenField("len", None, length_of="cookie", adjust = lambda pkt,x:x+4),
+                    PadField(StrLenField("cookie", "", length_from=lambda pkt: pkt.len-4),
+                             4, padwith="\x00"),
+                   ]
+
+class SCTPChunkCookieAck(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ByteEnumField("type", 11, sctpchunktypes),
+                    XByteField("flags", None),
+                    ShortField("len", 4),
+                   ]
+
+class SCTPChunkShutdownComplete(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ByteEnumField("type", 12, sctpchunktypes),
+                    BitField("reserved", None, 7),
+                    BitField("TCB", 0, 1),
+                    ShortField("len", 4),
+                    ]
+
+bind_layers( IP,           SCTP,          proto=IPPROTO_SCTP)
+
diff --git a/scapy/layers/vrrp.py b/scapy/layers/vrrp.py
new file mode 100644
index 0000000000000000000000000000000000000000..a08a42f7d0a8412fba881dc1e81820ff99cf4466
--- /dev/null
+++ b/scapy/layers/vrrp.py
@@ -0,0 +1,35 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## Copyright (C) 6WIND <olivier.matz@6wind.com>
+## This program is published under a GPLv2 license
+
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.inet import IP
+
+IPPROTO_VRRP=112
+
+# RFC 3768 - Virtual Router Redundancy Protocol (VRRP)
+class VRRP(Packet):
+    fields_desc = [
+        BitField("version" , 2, 4),
+        BitField("type" , 1, 4),
+        ByteField("vrid", 1),
+        ByteField("priority", 100),
+        FieldLenField("ipcount", None, count_of="addrlist", fmt="B"),
+        ByteField("authtype", 0),
+        ByteField("adv", 1),
+        XShortField("chksum", None),
+        FieldListField("addrlist", [], IPField("", "0.0.0.0"),
+                       count_from = lambda pkt: pkt.ipcount),
+        IntField("auth1", 0),
+        IntField("auth2", 0) ]
+
+    def post_build(self, p, pay):
+        if self.chksum is None:
+            ck = checksum(p)
+            p = p[:6]+chr(ck>>8)+chr(ck&0xff)+p[8:]
+        return p
+
+bind_layers( IP,            VRRP,          proto=IPPROTO_VRRP)
diff --git a/scapy/main.py b/scapy/main.py
index 4e60a340ae79de255524651461b852e64d456ae8..2aa1614628757dc04201bf39beced434bc3c5c70 100644
--- a/scapy/main.py
+++ b/scapy/main.py
@@ -41,7 +41,7 @@ def _usage():
 
 
 from config import conf
-from themes import ColorPrompt
+from themes import DefaultTheme
 
 
 ######################
@@ -295,8 +295,9 @@ def interact(mydict=None,argv=None,mybanner=None,loglevel=20):
         atexit.register(scapy_write_history_file,readline)
     
     atexit.register(scapy_delete_temp_files)
-    sys.ps1 = ColorPrompt()
-    code.interact(banner = the_banner % (conf.version), local=session)
+    conf.color_theme = DefaultTheme()
+    code.interact(banner = the_banner % (conf.version), 
+                  local=session, readfunc=conf.readfunc)
 
     if conf.session:
         save_session(conf.session, session)
diff --git a/scapy/modules/nmap.py b/scapy/modules/nmap.py
index 692e0730ecc2ed1325179bcc5e77cc014f542656..ffed334a63a80918f7e90391c4afa56b902677fa 100644
--- a/scapy/modules/nmap.py
+++ b/scapy/modules/nmap.py
@@ -3,10 +3,17 @@
 ## Copyright (C) Philippe Biondi <phil@secdev.org>
 ## This program is published under a GPLv2 license
 
+import os
+
 from scapy.data import KnowledgeBase
 from scapy.config import conf
+from scapy.arch import WINDOWS
+
 
-conf.nmap_base ="/usr/share/nmap/nmap-os-fingerprints"
+if WINDOWS:
+    conf.nmap_base=os.environ["ProgramFiles"] + "\\nmap\\nmap-os-fingerprints"
+else:
+    conf.nmap_base ="/usr/share/nmap/nmap-os-fingerprints"
 
 
 ######################
diff --git a/scapy/modules/p0f.py b/scapy/modules/p0f.py
index 28542edfa6f578048e882a24ccb42e2e662e12ed..ebcf68d05940be09298b95365ccd5d3692906b78 100644
--- a/scapy/modules/p0f.py
+++ b/scapy/modules/p0f.py
@@ -5,8 +5,13 @@
 
 from scapy.data import KnowledgeBase
 from scapy.config import conf
+from scapy.layers.inet import IP, TCP, TCPOptions
+from scapy.packet import NoPayload
 
 conf.p0f_base ="/etc/p0f/p0f.fp"
+conf.p0fa_base ="/etc/p0f/p0fa.fp"
+conf.p0fr_base ="/etc/p0f/p0fr.fp"
+conf.p0fo_base ="/etc/p0f/p0fo.fp"
 
 
 ###############
@@ -26,8 +31,6 @@ conf.p0f_base ="/etc/p0f/p0f.fp"
 # OS      - OS genre
 # details - OS description
 
-
-
 class p0fKnowledgeBase(KnowledgeBase):
     def __init__(self, filename):
         KnowledgeBase.__init__(self, filename)
@@ -46,7 +49,11 @@ class p0fKnowledgeBase(KnowledgeBase):
                 l = tuple(l.split(":"))
                 if len(l) < 8:
                     continue
-                li = map(int,l[1:4])
+                def a2i(x):
+                    if x.isdigit():
+                        return int(x)
+                    return x
+                li = map(a2i, l[1:4])
                 #if li[0] not in self.ttl_range:
                 #    self.ttl_range.append(li[0])
                 #    self.ttl_range.sort()
@@ -57,38 +64,70 @@ class p0fKnowledgeBase(KnowledgeBase):
         f.close()
 
 p0f_kdb = p0fKnowledgeBase(conf.p0f_base)
+p0fa_kdb = p0fKnowledgeBase(conf.p0fa_base)
+p0fr_kdb = p0fKnowledgeBase(conf.p0fr_base)
+p0fo_kdb = p0fKnowledgeBase(conf.p0fo_base)
 
+def p0f_selectdb(flags):
+    # tested flags: S, R, A
+    if flags & 0x16 == 0x2:
+        # SYN
+        return p0f_kdb
+    elif flags & 0x16 == 0x12:
+        # SYN/ACK
+        return p0fa_kdb
+    elif flags & 0x16 in [ 0x4, 0x14 ]:
+        # RST RST/ACK
+        return p0fr_kdb
+    elif flags & 0x16 == 0x10:
+        # ACK
+        return p0fo_kdb
+    else:
+        return None
 
 def packet2p0f(pkt):
+    pkt = pkt.copy()
+    pkt = pkt.__class__(str(pkt))
     while pkt.haslayer(IP) and pkt.haslayer(TCP):
         pkt = pkt.getlayer(IP)
         if isinstance(pkt.payload, TCP):
             break
         pkt = pkt.payload
-
+    
     if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP):
         raise TypeError("Not a TCP/IP packet")
-    if pkt.payload.flags & 0x13 != 0x02: #S,!A,!F
-        raise TypeError("Not a syn packet")
+    #if pkt.payload.flags & 0x7 != 0x02: #S,!F,!R
+    #    raise TypeError("Not a SYN or SYN/ACK packet")
+    
+    db = p0f_selectdb(pkt.payload.flags)
     
     #t = p0f_kdb.ttl_range[:]
     #t += [pkt.ttl]
     #t.sort()
     #ttl=t[t.index(pkt.ttl)+1]
     ttl = pkt.ttl
-
+    
     df = (pkt.flags & 2) / 2
     ss = len(pkt)
     # from p0f/config.h : PACKET_BIG = 100
     if ss > 100:
-        ss = 0
-
+        if db == p0fr_kdb:
+            # p0fr.fp: "Packet size may be wildcarded. The meaning of
+            #           wildcard is, however, hardcoded as 'size >
+            #           PACKET_BIG'"
+            ss = '*'
+        else:
+            ss = 0
+    if db == p0fo_kdb:
+        # p0fo.fp: "Packet size MUST be wildcarded."
+        ss = '*'
+    
     ooo = ""
     mss = -1
     qqT = False
     qqP = False
     #qqBroken = False
-    ilen = (pkt[TCP].dataofs << 2) - 20 # from p0f.c
+    ilen = (pkt.payload.dataofs << 2) - 20 # from p0f.c
     for option in pkt.payload.options:
         ilen -= 1
         if option[0] == "MSS":
@@ -118,60 +157,85 @@ def packet2p0f(pkt):
             if ilen > 0:
                 qqP = True
         else:
-            ooo += "?,"
+            if type(option[0]) is str:
+                ooo += "?%i," % TCPOptions[1][option[0]]
+            else:
+                ooo += "?%i," % option[0]
             # FIXME: ilen
     ooo = ooo[:-1]
     if ooo == "": ooo = "."
-
+    
     win = pkt.payload.window
     if mss != -1:
-        if win % mss == 0:
+        if mss != 0 and win % mss == 0:
             win = "S" + str(win/mss)
         elif win % (mss + 40) == 0:
             win = "T" + str(win/(mss+40))
-        win = str(win)
-
+    win = str(win)
+    
     qq = ""
-
+    
+    if db == p0fr_kdb:
+        if pkt.payload.flags & 0x10 == 0x10:
+            # p0fr.fp: "A new quirk, 'K', is introduced to denote
+            #           RST+ACK packets"
+            qq += "K"
+    # The two next cases should also be only for p0f*r*, but although
+    # it's not documented (or I have not noticed), p0f seems to
+    # support the '0' and 'Q' quirks on any databases (or at the least
+    # "classical" p0f.fp).
+    if pkt.payload.seq == pkt.payload.ack:
+        # p0fr.fp: "A new quirk, 'Q', is used to denote SEQ number
+        #           equal to ACK number."
+        qq += "Q"
+    if pkt.payload.seq == 0:
+        # p0fr.fp: "A new quirk, '0', is used to denote packets
+        #           with SEQ number set to 0."
+        qq += "0"
     if qqP:
         qq += "P"
-    if pkt[IP].id == 0:
+    if pkt.id == 0:
         qq += "Z"
-    if pkt[IP].options != '':
+    if pkt.options != []:
         qq += "I"
-    if pkt[TCP].urgptr != 0:
+    if pkt.payload.urgptr != 0:
         qq += "U"
-    if pkt[TCP].reserved != 0:
+    if pkt.payload.reserved != 0:
         qq += "X"
-    if pkt[TCP].ack != 0:
+    if pkt.payload.ack != 0:
         qq += "A"
     if qqT:
         qq += "T"
-    if pkt[TCP].flags & 40 != 0:
-        # U or P
-        qq += "F"
-    if not isinstance(pkt[TCP].payload, NoPayload):
+    if db == p0fo_kdb:
+        if pkt.payload.flags & 0x20 != 0:
+            # U
+            # p0fo.fp: "PUSH flag is excluded from 'F' quirk checks"
+            qq += "F"
+    else:
+        if pkt.payload.flags & 0x28 != 0:
+            # U or P
+            qq += "F"
+    if db != p0fo_kdb and not isinstance(pkt.payload.payload, NoPayload):
+        # p0fo.fp: "'D' quirk is not checked for."
         qq += "D"
-    # FIXME : "!" - broken options segment
+    # FIXME : "!" - broken options segment: not handled yet
 
     if qq == "":
         qq = "."
 
-    return (win,
-            ttl,
-            df,
-            ss,
-            ooo,
-            qq)
+    return (db, (win, ttl, df, ss, ooo, qq))
 
 def p0f_correl(x,y):
     d = 0
-    # wwww can be "*" or "%nn"
+    # wwww can be "*" or "%nn". "Tnn" and "Snn" should work fine with
+    # the x[0] == y[0] test.
     d += (x[0] == y[0] or y[0] == "*" or (y[0][0] == "%" and x[0].isdigit() and (int(x[0]) % int(y[0][1:])) == 0))
     # ttl
     d += (y[1] >= x[1] and y[1] - x[1] < 32)
-    for i in [2, 3, 5]:
-        d += (x[i] == y[i])
+    for i in [2, 5]:
+        d += (x[i] == y[i] or y[i] == '*')
+    # '*' has a special meaning for ss
+    d += x[3] == y[3]
     xopt = x[4].split(",")
     yopt = y[4].split(",")
     if len(xopt) == len(yopt):
@@ -192,31 +256,34 @@ def p0f_correl(x,y):
 
 @conf.commands.register
 def p0f(pkt):
-    """Passive OS fingerprinting: which OS emitted this TCP SYN ?
+    """Passive OS fingerprinting: which OS emitted this TCP packet ?
 p0f(packet) -> accuracy, [list of guesses]
 """
-    pb = p0f_kdb.get_base()
+    db, sig = packet2p0f(pkt)
+    if db:
+        pb = db.get_base()
+    else:
+        pb = []
     if not pb:
         warning("p0f base empty.")
         return []
-    s = len(pb[0][0])
+    #s = len(pb[0][0])
     r = []
-    sig = packet2p0f(pkt)
     max = len(sig[4].split(",")) + 5
     for b in pb:
         d = p0f_correl(sig,b)
         if d == max:
             r.append((b[6], b[7], b[1] - pkt[IP].ttl))
     return r
-            
 
 def prnp0f(pkt):
+    # we should print which DB we use
     try:
         r = p0f(pkt)
     except:
         return
     if r == []:
-        r = ("UNKNOWN", "[" + ":".join(map(str, packet2p0f(pkt))) + ":?:?]", None)
+        r = ("UNKNOWN", "[" + ":".join(map(str, packet2p0f(pkt)[1])) + ":?:?]", None)
     else:
         r = r[0]
     uptime = None
@@ -228,16 +295,16 @@ def prnp0f(pkt):
         uptime = None
     res = pkt.sprintf("%IP.src%:%TCP.sport% - " + r[0] + " " + r[1])
     if uptime is not None:
-        res += pkt.sprintf(" (up: " + str(uptime/3600) + " hrs)\n  -> %IP.dst%:%TCP.dport%")
+        res += pkt.sprintf(" (up: " + str(uptime/3600) + " hrs)\n  -> %IP.dst%:%TCP.dport% (%TCP.flags%)")
     else:
-        res += pkt.sprintf("\n  -> %IP.dst%:%TCP.dport%")
+        res += pkt.sprintf("\n  -> %IP.dst%:%TCP.dport% (%TCP.flags%)")
     if r[2] is not None:
         res += " (distance " + str(r[2]) + ")"
     print res
 
 @conf.commands.register
 def pkt2uptime(pkt, HZ=100):
-    """Calculate the date the machine which emitted the packet booted using TCP timestamp
+    """Calculate the date the machine which emitted the packet booted using TCP timestamp 
 pkt2uptime(pkt, [HZ=100])"""
     if not isinstance(pkt, Packet):
         raise TypeError("Not a TCP packet")
@@ -253,4 +320,219 @@ pkt2uptime(pkt, [HZ=100])"""
             return t
     raise TypeError("No timestamp option")
 
+def p0f_impersonate(pkt, osgenre=None, osdetails=None, signature=None,
+                    extrahops=0, mtu=1500, uptime=None):
+    """Modifies pkt so that p0f will think it has been sent by a
+specific OS.  If osdetails is None, then we randomly pick up a
+personality matching osgenre. If osgenre and signature are also None,
+we use a local signature (using p0f_getlocalsigs). If signature is
+specified (as a tuple), we use the signature.
+
+For now, only TCP Syn packets are supported.
+Some specifications of the p0f.fp file are not (yet) implemented."""
+    pkt = pkt.copy()
+    #pkt = pkt.__class__(str(pkt))
+    while pkt.haslayer(IP) and pkt.haslayer(TCP):
+        pkt = pkt.getlayer(IP)
+        if isinstance(pkt.payload, TCP):
+            break
+        pkt = pkt.payload
+    
+    if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP):
+        raise TypeError("Not a TCP/IP packet")
+    
+    if uptime is None:
+        uptime = random.randint(120,100*60*60*24*365)
+    
+    db = p0f_selectdb(pkt.payload.flags)
+    if osgenre:
+        pb = db.get_base()
+        if pb is None:
+            pb = []
+        pb = filter(lambda x: x[6] == osgenre, pb)
+        if osdetails:
+            pb = filter(lambda x: x[7] == osdetails, pb)
+    elif signature:
+        pb = [signature]
+    else:
+        pb = p0f_getlocalsigs()[db]
+    if db == p0fr_kdb:
+        # 'K' quirk <=> RST+ACK
+        if pkt.payload.flags & 0x4 == 0x4:
+            pb = filter(lambda x: 'K' in x[5], pb)
+        else:
+            pb = filter(lambda x: 'K' not in x[5], pb)
+    if not pb:
+        raise Scapy_Exception("No match in the p0f database")
+    pers = pb[random.randint(0, len(pb) - 1)]
+    
+    # options (we start with options because of MSS)
+    ## TODO: let the options already set if they are valid
+    options = []
+    if pers[4] != '.':
+        for opt in pers[4].split(','):
+            if opt[0] == 'M':
+                # MSS might have a maximum size because of window size
+                # specification
+                if pers[0][0] == 'S':
+                    maxmss = (2L**16-1) / int(pers[0][1:])
+                else:
+                    maxmss = (2L**16-1)
+                # If we have to randomly pick up a value, we cannot use
+                # scapy RandXXX() functions, because the value has to be
+                # set in case we need it for the window size value. That's
+                # why we use random.randint()
+                if opt[1:] == '*':
+                    options.append(('MSS', random.randint(1,maxmss)))
+                elif opt[1] == '%':
+                    coef = int(opt[2:])
+                    options.append(('MSS', coef*random.randint(1,maxmss/coef)))
+                else:
+                    options.append(('MSS', int(opt[1:])))
+            elif opt[0] == 'W':
+                if opt[1:] == '*':
+                    options.append(('WScale', RandByte()))
+                elif opt[1] == '%':
+                    coef = int(opt[2:])
+                    options.append(('WScale', coef*RandNum(min=1,
+                                                           max=(2L**8-1)/coef)))
+                else:
+                    options.append(('WScale', int(opt[1:])))
+            elif opt == 'T0':
+                options.append(('Timestamp', (0, 0)))
+            elif opt == 'T':
+                if 'T' in pers[5]:
+                    # FIXME: RandInt() here does not work (bug (?) in
+                    # TCPOptionsField.m2i often raises "OverflowError:
+                    # long int too large to convert to int" in:
+                    #    oval = struct.pack(ofmt, *oval)"
+                    # Actually, this is enough to often raise the error:
+                    #    struct.pack('I', RandInt())
+                    options.append(('Timestamp', (uptime, random.randint(1,2**32-1))))
+                else:
+                    options.append(('Timestamp', (uptime, 0)))
+            elif opt == 'S':
+                options.append(('SAckOK', ''))
+            elif opt == 'N':
+                options.append(('NOP', None))
+            elif opt == 'E':
+                options.append(('EOL', None))
+            elif opt[0] == '?':
+                if int(opt[1:]) in TCPOptions[0]:
+                    optname = TCPOptions[0][int(opt[1:])][0]
+                    optstruct = TCPOptions[0][int(opt[1:])][1]
+                    options.append((optname,
+                                    struct.unpack(optstruct,
+                                                  RandString(struct.calcsize(optstruct))._fix())))
+                else:
+                    options.append((int(opt[1:]), ''))
+            ## FIXME: qqP not handled
+            else:
+                warning("unhandled TCP option " + opt)
+            pkt.payload.options = options
+    
+    # window size
+    if pers[0] == '*':
+        pkt.payload.window = RandShort()
+    elif pers[0].isdigit():
+        pkt.payload.window = int(pers[0])
+    elif pers[0][0] == '%':
+        coef = int(pers[0][1:])
+        pkt.payload.window = coef * RandNum(min=1,max=(2L**16-1)/coef)
+    elif pers[0][0] == 'T':
+        pkt.payload.window = mtu * int(pers[0][1:])
+    elif pers[0][0] == 'S':
+        ## needs MSS set
+        MSS = filter(lambda x: x[0] == 'MSS', options)
+        if not filter(lambda x: x[0] == 'MSS', options):
+            raise Scapy_Exception("TCP window value requires MSS, and MSS option not set")
+        pkt.payload.window = filter(lambda x: x[0] == 'MSS', options)[0][1] * int(pers[0][1:])
+    else:
+        raise Scapy_Exception('Unhandled window size specification')
+    
+    # ttl
+    pkt.ttl = pers[1]-extrahops
+    # DF flag
+    pkt.flags |= (2 * pers[2])
+    ## FIXME: ss (packet size) not handled (how ? may be with D quirk
+    ## if present)
+    # Quirks
+    if pers[5] != '.':
+        for qq in pers[5]:
+            ## FIXME: not handled: P, I, X, !
+            # T handled with the Timestamp option
+            if qq == 'Z': pkt.id = 0
+            elif qq == 'U': pkt.payload.urgptr = RandShort()
+            elif qq == 'A': pkt.payload.ack = RandInt()
+            elif qq == 'F':
+                if db == p0fo_kdb:
+                    pkt.payload.flags |= 0x20 # U
+                else:
+                    pkt.payload.flags |= RandChoice(8, 32, 40) #P / U / PU
+            elif qq == 'D' and db != p0fo_kdb:
+                pkt /= Raw(load=RandString(random.randint(1, 10))) # XXX p0fo.fp
+            elif qq == 'Q': pkt.payload.seq = pkt.payload.ack
+            #elif qq == '0': pkt.payload.seq = 0
+        #if db == p0fr_kdb:
+        # '0' quirk is actually not only for p0fr.fp (see
+        # packet2p0f())
+    if '0' in pers[5]:
+        pkt.payload.seq = 0
+    elif pkt.payload.seq == 0:
+        pkt.payload.seq = RandInt()
+    
+    while pkt.underlayer:
+        pkt = pkt.underlayer
+    return pkt
+
+def p0f_getlocalsigs():
+    """This function returns a dictionary of signatures indexed by p0f
+db (e.g., p0f_kdb, p0fa_kdb, ...) for the local TCP/IP stack.
+
+You need to have your firewall at least accepting the TCP packets
+from/to a high port (30000 <= x <= 40000) on your loopback interface.
+
+Please note that the generated signatures come from the loopback
+interface and may (are likely to) be different than those generated on
+"normal" interfaces."""
+    pid = os.fork()
+    port = random.randint(30000, 40000)
+    if pid > 0:
+        # parent: sniff
+        result = {}
+        def addresult(res):
+            # TODO: wildcard window size in some cases? and maybe some
+            # other values?
+            if res[0] not in result:
+                result[res[0]] = [res[1]]
+            else:
+                if res[1] not in result[res[0]]:
+                    result[res[0]].append(res[1])
+        # XXX could we try with a "normal" interface using other hosts
+        iface = conf.route.route('127.0.0.1')[0]
+        # each packet is seen twice: S + RA, S + SA + A + FA + A
+        # XXX are the packets also seen twice on non Linux systems ?
+        count=14
+        pl = sniff(iface=iface, filter='tcp and port ' + str(port), count = count, timeout=3)
+        map(addresult, map(packet2p0f, pl))
+        os.waitpid(pid,0)
+    elif pid < 0:
+        log_runtime.error("fork error")
+    else:
+        # child: send
+        # XXX erk
+        time.sleep(1)
+        s1 = socket.socket(socket.AF_INET, type = socket.SOCK_STREAM)
+        # S & RA
+        try:
+            s1.connect(('127.0.0.1', port))
+        except socket.error:
+            pass
+        # S, SA, A, FA, A
+        s1.bind(('127.0.0.1', port))
+        s1.connect(('127.0.0.1', port))
+        # howto: get an RST w/o ACK packet
+        s1.close()
+        os._exit(0)
+    return result
 
diff --git a/scapy/packet.py b/scapy/packet.py
index b96f3d17af2ec1296093ec957f1be809108d750f..4e3c893c6ce6485a9503b8298d754ab613592250 100644
--- a/scapy/packet.py
+++ b/scapy/packet.py
@@ -17,6 +17,15 @@ except ImportError:
     pass
 
 
+class RawVal:
+    def __init__(self, val=""):
+        self.val = val
+    def __str__(self):
+        return str(self.val)
+    def __repr__(self):
+        return "<RawVal [%r]>" % self.val
+
+
 class Packet(BasePacket):
     __metaclass__ = Packet_metaclass
     name=None
@@ -226,7 +235,7 @@ class Packet(BasePacket):
                 val =  f.i2repr(self, self.overloaded_fields[f.name])
             else:
                 continue
-            if isinstance(f, Emph):
+            if isinstance(f, Emph) or f in conf.emph:
                 ncol = ct.emph_field_name
                 vcol = ct.emph_field_value
             else:
@@ -275,7 +284,11 @@ class Packet(BasePacket):
     def do_build(self):
         p=""
         for f in self.fields_desc:
-            p = f.addfield(self, p, self.getfieldval(f.name))
+            val = self.getfieldval(f.name)
+            if isinstance(val, RawVal):
+                p += str(val)
+            else:
+                p = f.addfield(self, p, val)
         return p
     
     def post_build(self, pkt, pay):
@@ -341,7 +354,7 @@ Creates an EPS file describing a packet. If filename is not provided a temporary
         if filename is None:
             fname = get_temp_file(autoext=".eps")
             canvas.writeEPSfile(fname)
-            os.system("%s '%s.eps' &" % (conf.prog.psreader,fname))
+            subprocess.Popen([conf.prog.psreader, fname+".eps"])
         else:
             canvas.writeEPSfile(filename)
 
@@ -352,7 +365,7 @@ Creates an EPS file describing a packet. If filename is not provided a temporary
         if filename is None:
             fname = get_temp_file(autoext=".pdf")
             canvas.writePDFfile(fname)
-            os.system("%s '%s.pdf' &" % (conf.prog.pdfreader,fname))
+            subprocess.Popen([conf.prog.pdfreader, fname+".pdf"])
         else:
             canvas.writePDFfile(filename)
 
@@ -468,7 +481,7 @@ Creates an EPS file describing a packet. If filename is not provided a temporary
             for fname, fval, fdump in fields:
                 col = forecolor.next()
                 ft = pyx.text.text(XSTART, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % tex_escape(fname.name))
-                if fval is not None:
+                if isinstance(fval, str):
                     if len(fval) > 18:
                         fval = fval[:18]+"[...]"
                 else:
@@ -689,11 +702,14 @@ Creates an EPS file describing a packet. If filename is not provided a temporary
         return self.payload.haslayer(cls)
     def getlayer(self, cls, nb=1, _track=None):
         """Return the nb^th layer that is an instance of cls."""
+        if type(cls) is int:
+            nb = cls+1
+            cls = None
         if type(cls) is str and "." in cls:
             ccls,fld = cls.split(".",1)
         else:
             ccls,fld = cls,None
-        if self.__class__ == cls or self.__class__.name == ccls:
+        if cls is None or self.__class__ == cls or self.__class__.name == ccls:
             if nb == 1:
                 if fld is None:
                     return self
@@ -716,6 +732,12 @@ Creates an EPS file describing a packet. If filename is not provided a temporary
                     nb = track[0]
         return self.payload.getlayer(cls,nb,_track=_track)
 
+    def firstlayer(self):
+        q = self
+        while q.underlayer is not None:
+            q = q.underlayer
+        return q
+
     def __getitem__(self, cls):
         if type(cls) is slice:
             lname = cls.start
@@ -748,6 +770,9 @@ Creates an EPS file describing a packet. If filename is not provided a temporary
 
     def route(self):
         return (None,None,None)
+
+    def fragment(self, *args, **kargs):
+        return self.payload.fragment(*args, **kargs)
     
 
     def display(self,*args,**kargs):  # Deprecated. Use show()
@@ -763,7 +788,7 @@ Creates an EPS file describing a packet. If filename is not provided a temporary
         for f in self.fields_desc:
             if isinstance(f, ConditionalField) and not f._evalcond(self):
                 continue
-            if isinstance(f, Emph):
+            if isinstance(f, Emph) or f in conf.emph:
                 ncol = ct.emph_field_name
                 vcol = ct.emph_field_value
             else:
@@ -897,9 +922,8 @@ A side effect is that, to obtain "{" and "}" characters, you must use
            mysummary() must be called if they are present."""
         return ""
 
-    def summary(self, intern=0):
-        """Prints a one line summary of a packet."""
-        found,s,needed = self.payload.summary(intern=1)
+    def _do_summary(self):
+        found,s,needed = self.payload._do_summary()
         if s:
             s = " / "+s
         ret = ""
@@ -912,11 +936,20 @@ A side effect is that, to obtain "{" and "}" characters, you must use
             found = 1
         if not ret:
             ret = self.__class__.__name__
+        if self.__class__ in conf.emph:
+            impf = []
+            for f in self.fields_desc:
+                if f in conf.emph:
+                    impf.append("%s=%s" % (f.name, f.i2repr(self, self.getfieldval(f.name))))
+            ret = "%s [%s]" % (ret," ".join(impf))
         ret = "%s%s" % (ret,s)
-        if intern:
-            return found,ret,needed
-        else:
-            return ret
+        return found,ret,needed
+
+    def summary(self, intern=0):
+        """Prints a one line summary of a packet."""
+        found,s,needed = self._do_summary()
+        return s
+
     
     def lastlayer(self,layer=None):
         """Returns the uppest layer of the packet"""
@@ -925,7 +958,11 @@ A side effect is that, to obtain "{" and "}" characters, you must use
     def decode_payload_as(self,cls):
         """Reassembles the payload and decode it using another packet class"""
         s = str(self.payload)
-        self.payload = cls(s)
+        self.payload = cls(s, _internal=1, _underlayer=self)
+        pp = self
+        while pp.underlayer is not None:
+            pp = pp.underlayer
+        self.payload.dissection_done(pp)
 
     def libnet(self):
         """Not ready yet. Should give the necessary C code that interfaces with libnet to recreate the packet"""
@@ -1025,6 +1062,8 @@ class NoPayload(Packet):
         if _track is not None:
             _track.append(nb)
         return None
+    def fragment(self, *args, **kargs):
+        raise Scapy_Exception("cannot fragment this packet")        
     def show(self, indent=3, lvl="", label_lvl=""):
         pass
     def sprintf(self, fmt, relax):
@@ -1032,7 +1071,7 @@ class NoPayload(Packet):
             return "??"
         else:
             raise Scapy_Exception("Format not found [%s]"%fmt)
-    def summary(self, intern=0):
+    def _do_summary(self):
         return 0,"",[]
     def lastlayer(self,layer):
         return layer
@@ -1053,6 +1092,14 @@ class Raw(Packet):
 #        t = self.load
 #        l = min(len(s), len(t))
 #        return  s[:l] == t[:l]
+    def mysummary(self):
+        cs = conf.raw_summary
+        if cs:
+            if callable(cs):
+                return "Raw %s" % cs(self.load)
+            else:
+                return "Raw %r" % self.load
+        return Packet.mysummary(self)
         
 class Padding(Raw):
     name = "Padding"
diff --git a/scapy/plist.py b/scapy/plist.py
index bc5c24f56ee399bc3c59f2b0578f8260199de89e..4cb01a7aa88eab6460773d8a590e1d14669f47e5 100644
--- a/scapy/plist.py
+++ b/scapy/plist.py
@@ -3,10 +3,11 @@
 ## Copyright (C) Philippe Biondi <phil@secdev.org>
 ## This program is published under a GPLv2 license
 
-import os
+import os,subprocess
 from config import conf
 from base_classes import BasePacket,BasePacketList
 from packet import Padding
+from collections import defaultdict
 
 from utils import do_graph,hexdump,make_table,make_lined_table,make_tex_table,get_temp_file
 
@@ -362,7 +363,7 @@ lfilter: truth function to apply to each packet to decide whether it will be dis
         if filename is None:
             filename = get_temp_file(autoext=".ps")
             d.writePSfile(filename)
-            os.system("%s %s.ps &" % (conf.prog.psreader,filename))
+            subprocess.Popen([conf.prog.psreader, filename+".ps"])
         else:
             d.writePSfile(filename)
         print
@@ -375,7 +376,7 @@ lfilter: truth function to apply to each packet to decide whether it will be dis
         if filename is None:
             filename = get_temp_file(autoext=".pdf")
             d.writePDFfile(filename)
-            os.system("%s %s.pdf &" % (conf.prog.pdfreader,filename))
+            subprocess.Popen([conf.prog.pdfreader, filename+".pdf"])
         else:
             d.writePDFfile(filename)
         print
@@ -406,6 +407,68 @@ lfilter: truth function to apply to each packet to decide whether it will be dis
         if multi:
             remain = filter(lambda x:not hasattr(x,"_answered"), remain)
         return SndRcvList(sr),PacketList(remain)
+
+    def sessions(self, session_extractor=None):
+        if session_extractor is None:
+            def session_extractor(p):
+                sess = "Other"
+                if 'Ether' in p:
+                    if 'IP' in p:
+                        if 'TCP' in p:
+                            sess = p.sprintf("TCP %IP.src%:%r,TCP.sport% > %IP.dst%:%r,TCP.dport%")
+                        elif 'UDP' in p:
+                            sess = p.sprintf("UDP %IP.src%:%r,UDP.sport% > %IP.dst%:%r,UDP.dport%")
+                        elif 'ICMP' in p:
+                            sess = p.sprintf("ICMP %IP.src% > %IP.dst% type=%r,ICMP.type% code=%r,ICMP.code% id=%ICMP.id%")
+                        else:
+                            sess = p.sprintf("IP %IP.src% > %IP.dst% proto=%IP.proto%")
+                    elif 'ARP' in p:
+                        sess = p.sprintf("ARP %ARP.psrc% > %ARP.pdst%")
+                    else:
+                        sess = p.sprintf("Ethernet type=%04xr,Ether.type%")
+                return sess
+        sessions = defaultdict(self.__class__)
+        for p in self.res:
+            sess = session_extractor(self._elt2pkt(p))
+            sessions[sess].append(p)
+        return dict(sessions)
+    
+    def replace(self, *args, **kargs):
+        """
+        lst.replace(<field>,[<oldvalue>,]<newvalue>)
+        lst.replace( (fld,[ov],nv),(fld,[ov,]nv),...)
+          if ov is None, all values are replaced
+        ex:
+          lst.replace( IP.src, "192.168.1.1", "10.0.0.1" )
+          lst.replace( IP.ttl, 64 )
+          lst.replace( (IP.ttl, 64), (TCP.sport, 666, 777), )
+        """
+        delete_checksums = kargs.get("delete_checksums",False)
+        x=PacketList(name="Replaced %s" % self.listname)
+        if type(args[0]) is not tuple:
+            args = (args,)
+        for p in self.res:
+            p = self._elt2pkt(p)
+            copied = False
+            for scheme in args:
+                fld = scheme[0]
+                old = scheme[1] # not used if len(scheme) == 2
+                new = scheme[-1]
+                for o in fld.owners:
+                    if o in p:
+                        if len(scheme) == 2 or p[o].getfieldval(fld.name) == old:
+                            if not copied:
+                                p = p.copy()
+                                if delete_checksums:
+                                    p.delete_checksums()
+                                copied = True
+                            setattr(p[o], fld.name, new)
+            x.append(p)
+        return x
+                
+            
+        
+    
         
 
 
diff --git a/scapy/pton_ntop.py b/scapy/pton_ntop.py
new file mode 100644
index 0000000000000000000000000000000000000000..8f83697d31d81f31e946bd3eb2ee6149047e11b4
--- /dev/null
+++ b/scapy/pton_ntop.py
@@ -0,0 +1,86 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+
+import socket,struct
+
+# These functions are missing when python is compiled
+# without IPv6 support, on Windows for instance
+
+def inet_pton(af, addr):
+    """Convert an IP address from text representation into binary form"""
+    if af == socket.AF_INET:
+        return inet_aton(addr)
+    elif af == socket.AF_INET6:
+        # IPv6: The use of "::" indicates one or more groups of 16 bits of zeros.
+        # We deal with this form of wildcard using a special marker. 
+        JOKER = "*"
+        while "::" in addr:
+            addr = addr.replace("::", ":" + JOKER + ":")
+        joker_pos = None 
+        
+        # The last part of an IPv6 address can be an IPv4 address
+        ipv4_addr = None
+        if "." in addr:
+            ipv4_addr = addr.split(":")[-1]
+           
+        result = ""
+        parts = addr.split(":")
+        for part in parts:
+            if part == JOKER:
+                # Wildcard is only allowed once
+                if joker_pos is None:
+                   joker_pos = len(result)
+                else:
+                   raise Exception("Illegal syntax for IP address")
+            elif part == ipv4_addr: # FIXME: Make sure IPv4 can only be last part
+                # FIXME: inet_aton allows IPv4 addresses with less than 4 octets 
+                result += socket.inet_aton(ipv4_addr)
+            else:
+                # Each part must be 16bit. Add missing zeroes before decoding. 
+                try:
+                    result += part.rjust(4, "0").decode("hex")
+                except TypeError:
+                    raise Exception("Illegal syntax for IP address")
+                    
+        # If there's a wildcard, fill up with zeros to reach 128bit (16 bytes) 
+        if JOKER in addr:
+            result = (result[:joker_pos] + "\x00" * (16 - len(result))
+                      + result[joker_pos:])
+    
+        if len(result) != 16:
+            raise Exception("Illegal syntax for IP address")
+        return result 
+    else:
+        raise Exception("Address family not supported")
+
+
+def inet_ntop(af, addr):
+    """Convert an IP address from binary form into text represenation"""
+    if af == socket.AF_INET:
+        return inet_ntoa(addr)
+    elif af == socket.AF_INET6:
+        # IPv6 addresses have 128bits (16 bytes)
+        if len(addr) != 16:
+            raise Exception("Illegal syntax for IP address")
+        parts = []
+        for left in [0, 2, 4, 6, 8, 10, 12, 14]:
+            try: 
+                value = struct.unpack("!H", addr[left:left+2])[0]
+                hexstr = hex(value)[2:]
+            except TypeError:
+                raise Exception("Illegal syntax for IP address")
+            parts.append(hexstr.lstrip("0").lower())
+        result = ":".join(parts)
+        while ":::" in result:
+            result = result.replace(":::", "::")
+        # Leaving out leading and trailing zeros is only allowed with ::
+        if result.endswith(":") and not result.endswith("::"):
+            result = result + "0"
+        if result.startswith(":") and not result.startswith("::"):
+            result = "0" + result
+        return result
+    else:
+        raise Exception("Address family not supported yet")        
diff --git a/scapy/route.py b/scapy/route.py
index 0ebf37656e6a7ee8f2f98f40a05afa63df1b1d61..d63176c0be56f43b8299910a2083b95343fa5931 100644
--- a/scapy/route.py
+++ b/scapy/route.py
@@ -112,6 +112,8 @@ class Route:
 
 
     def route(self,dest,verbose=None):
+        if type(dest) is list and dest:
+            dest = dest[0]
         if dest in self.cache:
             return self.cache[dest]
         if verbose is None:
diff --git a/scapy/route6.py b/scapy/route6.py
index 3cdf9ab2ffde271bdd021bca249581c3a746fb45..953339905820363c3ba8014a42162fedaaf0fae4 100644
--- a/scapy/route6.py
+++ b/scapy/route6.py
@@ -72,7 +72,7 @@ class Route6:
             # replace that unique address by the list of all addresses
             lifaddr = in6_getifaddr()             
             devaddrs = filter(lambda x: x[2] == dev, lifaddr)
-            ifaddr = construct_source_candidate_set(prefix, plen, devaddrs)
+            ifaddr = construct_source_candidate_set(prefix, plen, devaddrs, LOOPBACK_NAME)
 
         return (prefix, plen, gw, dev, ifaddr)
 
@@ -217,8 +217,8 @@ class Route6:
             warning("No route found for IPv6 destination %s (no default route?)" % dst)
             return (LOOPBACK_NAME, "::", "::") # XXX Linux specific
 
-        pathes.sort()
-        pathes.reverse()
+        # Sort with longest prefix first
+        pathes.sort(reverse=True)
 
         best_plen = pathes[0][0]
         pathes = filter(lambda x: x[0] == best_plen, pathes)
diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py
index 09c91881bb540f7bbb0cae643a320623ec24ecd5..b72d96541f7c487b05d66a0187ba8272910b781f 100644
--- a/scapy/sendrecv.py
+++ b/scapy/sendrecv.py
@@ -6,10 +6,10 @@
 import cPickle,os,sys,time,subprocess
 from select import select
 from data import *
-from arch import *
+import arch
 from config import conf
 from packet import Gen
-from utils import warning,get_temp_file
+from utils import warning,get_temp_file,PcapReader
 import plist
 from error import log_runtime,log_interactive
 from base_classes import SetGen
@@ -31,7 +31,7 @@ class debug:
 
 
 
-def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, multi=0):
+def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0, multi=0):
     if not isinstance(pkt, Gen):
         pkt = SetGen(pkt)
         
@@ -117,7 +117,7 @@ def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, m
                                 if remaintime <= 0:
                                     break
                             r = None
-                            if FREEBSD or DARWIN:
+                            if arch.FREEBSD or arch.DARWIN:
                                 inp, out, err = select(inmask,[],[], 0.05)
                                 if len(inp) == 0 or pks in inp:
                                     r = pks.nonblock_recv()
@@ -200,10 +200,10 @@ def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, m
     
     if verbose:
         print "\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans)
-    return plist.SndRcvList(ans),plist.PacketList(remain,"Unanswered"),debug.recv
+    return plist.SndRcvList(ans),plist.PacketList(remain,"Unanswered")
 
 
-def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, *args, **kargs):
+def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, realtime=None, *args, **kargs):
     if type(x) is str:
         x = Raw(load=x)
     if not isinstance(x, Gen):
@@ -215,9 +215,18 @@ def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, *args, **kargs):
         loop = -count
     elif not loop:
         loop=-1
+    dt0 = None
     try:
         while loop:
             for p in x:
+                if realtime:
+                    ct = time.time()
+                    if dt0:
+                        st = dt0+p.time-ct
+                        if st > 0:
+                            time.sleep(st)
+                    else:
+                        dt0 = ct-p.time 
                 s.send(p)
                 n += 1
                 if verbose:
@@ -232,18 +241,18 @@ def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, *args, **kargs):
         print "\nSent %i packets." % n
         
 @conf.commands.register
-def send(x, inter=0, loop=0, count=None, verbose=None, *args, **kargs):
+def send(x, inter=0, loop=0, count=None, verbose=None, realtime=None, *args, **kargs):
     """Send packets at layer 3
 send(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None"""
-    __gen_send(conf.L3socket(*args, **kargs), x, inter=inter, loop=loop, count=count,verbose=verbose)
+    __gen_send(conf.L3socket(*args, **kargs), x, inter=inter, loop=loop, count=count,verbose=verbose, realtime=realtime)
 
 @conf.commands.register
-def sendp(x, inter=0, loop=0, iface=None, iface_hint=None, count=None, verbose=None, *args, **kargs):
+def sendp(x, inter=0, loop=0, iface=None, iface_hint=None, count=None, verbose=None, realtime=None, *args, **kargs):
     """Send packets at layer 2
 sendp(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None"""
     if iface is None and iface_hint is not None:
         iface = conf.route.route(iface_hint)[0]
-    __gen_send(conf.L2socket(iface=iface, *args, **kargs), x, inter=inter, loop=loop, count=count, verbose=verbose)
+    __gen_send(conf.L2socket(iface=iface, *args, **kargs), x, inter=inter, loop=loop, count=count, verbose=verbose, realtime=realtime)
 
 @conf.commands.register
 def sendpfast(x, pps=None, mbps=None, realtime=None, loop=0, iface=None):
@@ -298,7 +307,7 @@ iface:    listen answers only on the given interface"""
     if not kargs.has_key("timeout"):
         kargs["timeout"] = -1
     s = conf.L3socket(filter=filter, iface=iface, nofilter=nofilter)
-    a,b,c=sndrcv(s,x,*args,**kargs)
+    a,b=sndrcv(s,x,*args,**kargs)
     s.close()
     return a,b
 
@@ -316,7 +325,7 @@ iface:    listen answers only on the given interface"""
     if not kargs.has_key("timeout"):
         kargs["timeout"] = -1
     s=conf.L3socket(filter=filter, nofilter=nofilter, iface=iface)
-    a,b,c=sndrcv(s,x,*args,**kargs)
+    a,b=sndrcv(s,x,*args,**kargs)
     s.close()
     if len(a) > 0:
         return a[0][1]
@@ -339,7 +348,7 @@ iface:    work only on the given interface"""
     if iface is None and iface_hint is not None:
         iface = conf.route.route(iface_hint)[0]
     s = conf.L2socket(iface=iface, filter=filter, nofilter=nofilter, type=type)
-    a,b,c=sndrcv(s ,x,*args,**kargs)
+    a,b=sndrcv(s ,x,*args,**kargs)
     s.close()
     return a,b
 
@@ -513,7 +522,8 @@ iface:    listen answers only on the given interface"""
 
 
 @conf.commands.register
-def sniff(count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, *arg, **karg):
+def sniff(count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None,
+          opened_socket=None, stop_filter=None, *arg, **karg):
     """Sniff packets
 sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets
 
@@ -528,15 +538,22 @@ lfilter: python function applied to each packet to determine
 offline: pcap file to read packets from, instead of sniffing them
 timeout: stop sniffing after a given time (default: None)
 L2socket: use the provided L2socket
+opened_socket: provide an object ready to use .recv() on
+stop_filter: python function applied to each packet to determine
+             if we have to stop the capture after this packet
+             ex: stop_filter = lambda x: x.haslayer(TCP)
     """
     c = 0
-
-    if offline is None:
-        if L2socket is None:
-            L2socket = conf.L2listen
-        s = L2socket(type=ETH_P_ALL, *arg, **karg)
+    
+    if opened_socket is not None:
+        s = opened_socket
     else:
-        s = PcapReader(offline)
+        if offline is None:
+            if L2socket is None:
+                L2socket = conf.L2listen
+            s = L2socket(type=ETH_P_ALL, *arg, **karg)
+        else:
+            s = PcapReader(offline)
 
     lst = []
     if timeout is not None:
@@ -562,11 +579,14 @@ L2socket: use the provided L2socket
                     r = prn(p)
                     if r is not None:
                         print r
+                if stop_filter and stop_filter(p):
+                    break
                 if count > 0 and c >= count:
                     break
         except KeyboardInterrupt:
             break
-    s.close()
+    if opened_socket is None:
+        s.close()
     return plist.PacketList(lst,"Sniffed")
 
 @conf.commands.register
diff --git a/scapy/supersocket.py b/scapy/supersocket.py
index e3e49c0613669b5aa3b795add9a9c9c42ea56012..9adf573323294ead65b7ad1b90c7b372639cac03 100644
--- a/scapy/supersocket.py
+++ b/scapy/supersocket.py
@@ -27,7 +27,7 @@ class SuperSocket:
         sx = str(x)
         x.sent_time = time.time()
         return self.outs.send(sx)
-    def recv(self, x):
+    def recv(self, x=MTU):
         return conf.raw_layer(self.ins.recv(x))
     def fileno(self):
         return self.ins.fileno()
@@ -40,10 +40,16 @@ class SuperSocket:
                 self.outs.close()
         if self.ins and self.ins.fileno() != -1:
             self.ins.close()
-    def bind_in(self, addr):
-        self.ins.bind(addr)
-    def bind_out(self, addr):
-        self.outs.bind(addr)
+    def sr(self, *args, **kargs):
+        return sendrecv.sndrcv(self, *args, **kargs)
+    def sr1(self, *args, **kargs):        
+        a,b = sendrecv.sndrcv(self, *args, **kargs)
+        if len(a) > 0:
+            return a[0][1]
+        else:
+            return None
+    def sniff(self, *args, **kargs):
+        return sendrecv.sniff(opened_socket=self, *args, **kargs)
 
 class L3RawSocket(SuperSocket):
     desc = "Layer 3 using Raw sockets (PF_INET/SOCK_RAW)"
@@ -51,7 +57,7 @@ class L3RawSocket(SuperSocket):
         self.outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
         self.outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1)
         self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type))
-    def recv(self, x):
+    def recv(self, x=MTU):
         return Ether(self.ins.recv(x)).payload
     def send(self, x):
         try:
@@ -95,3 +101,5 @@ class StreamSocket(SimpleSocket):
 
 if conf.L3socket is None:
     conf.L3socket = L3RawSocket
+
+import sendrecv
diff --git a/scapy/themes.py b/scapy/themes.py
index 432571424428c3bee7172bf214f6c1a49fb2a328..866de0e151e5dc5cf6e670c9ffcd91926741f158 100644
--- a/scapy/themes.py
+++ b/scapy/themes.py
@@ -3,7 +3,6 @@
 ## Copyright (C) Philippe Biondi <phil@secdev.org>
 ## This program is published under a GPLv2 license
 
-import config
 
 ##################
 ## Color themes ##
@@ -271,3 +270,5 @@ class ColorPrompt:
         except:
             return self.__prompt
 
+
+import config
diff --git a/scapy/utils.py b/scapy/utils.py
index 960dfb71f4128f2cfd8864b7425086a72366c5dd..06f451e07cfa7d500c1684078b126165aae24425 100644
--- a/scapy/utils.py
+++ b/scapy/utils.py
@@ -7,6 +7,7 @@ import os,sys,socket,types
 import random,time
 import gzip,zlib,cPickle
 import re,struct,array
+import subprocess
 
 import warnings
 warnings.filterwarnings("ignore","tempnam",RuntimeWarning, __name__)
@@ -16,6 +17,7 @@ from data import MTU
 from error import log_runtime,log_loading,log_interactive
 from base_classes import BasePacketList
 
+WINDOWS=sys.platform.startswith("win32")
 
 ###########
 ## Tools ##
@@ -260,6 +262,7 @@ try:
     inet_ntop = socket.inet_ntop
     inet_pton = socket.inet_pton
 except AttributeError:
+    from scapy.pton_ntop import *
     log_loading.info("inet_ntop/pton functions not found. Python IPv6 support not present")
 
 
@@ -275,7 +278,7 @@ def ltoa(x):
 def itom(x):
     return (0xffffffff00000000L>>x)&0xffffffffL
 
-def do_graph(graph,prog=None,format="svg",target=None, type=None,string=None,options=None):
+def do_graph(graph,prog=None,format=None,target=None,type=None,string=None,options=None):
     """do_graph(graph, prog=conf.prog.dot, format="svg",
          target="| conf.prog.display", options=None, [string=1]):
     string: if not None, simply return the graph string
@@ -285,20 +288,43 @@ def do_graph(graph,prog=None,format="svg",target=None, type=None,string=None,opt
     prog: which graphviz program to use
     options: options to be passed to prog"""
         
-
+    if format is None:
+        if WINDOWS:
+            format = "png" # use common format to make sure a viewer is installed
+        else:
+            format = "svg"
     if string:
         return graph
     if type is not None:
         format=type
     if prog is None:
         prog = conf.prog.dot
+    start_viewer=False
     if target is None:
-        target = "| %s" % conf.prog.display
+        if WINDOWS:
+            tempfile = os.tempnam("", "scapy") + "." + format
+            target = "> %s" % tempfile
+            start_viewer = True
+        else:
+            target = "| %s" % conf.prog.display
     if format is not None:
         format = "-T %s" % format
     w,r = os.popen2("%s %s %s %s" % (prog,options or "", format or "", target))
     w.write(graph)
     w.close()
+    if start_viewer:
+        # Workaround for file not found error: We wait until tempfile is written.
+        waiting_start = time.time()
+        while not os.path.exists(tempfile):
+            time.sleep(0.1)
+            if time.time() - waiting_start > 3:
+                warning("Temporary file '%s' could not be written. Graphic will not be displayed." % tempfile)
+                break
+        else:  
+            if conf.prog.display == conf.prog._default:
+                os.startfile(tempfile)
+            else:
+                subprocess.Popen([conf.prog.display, tempfile])
 
 _TEX_TR = {
     "{":"{\\tt\\char123}",
@@ -484,7 +510,7 @@ class RawPcapReader:
         return pkt
 
 
-    def read_packet(self):
+    def read_packet(self, size=MTU):
         """return a single packet read from the file
         
         returns None when no more packets are available
@@ -493,7 +519,7 @@ class RawPcapReader:
         if len(hdr) < 16:
             return None
         sec,usec,caplen,wirelen = struct.unpack(self.endian+"IIII", hdr)
-        s = self.f.read(caplen)
+        s = self.f.read(caplen)[:MTU]
         return s,(sec,usec,wirelen) # caplen = len(s)
 
 
@@ -519,10 +545,10 @@ class RawPcapReader:
             res.append(p)
         return res
 
-    def recv(self, size):
+    def recv(self, size=MTU):
         """ Emulate a socket
         """
-        return self.read_packet()[0]
+        return self.read_packet(size)[0]
 
     def fileno(self):
         return self.f.fileno()
@@ -540,8 +566,8 @@ class PcapReader(RawPcapReader):
         except KeyError:
             warning("PcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype,self.linktype))
             self.LLcls = conf.raw_layer
-    def read_packet(self):
-        rp = RawPcapReader.read_packet(self)
+    def read_packet(self, size=MTU):
+        rp = RawPcapReader.read_packet(self,size)
         if rp is None:
             return None
         s,(sec,usec,wirelen) = rp
@@ -560,8 +586,8 @@ class PcapReader(RawPcapReader):
         res = RawPcapReader.read_all(self, count)
         import plist
         return plist.PacketList(res,name = os.path.basename(self.filename))
-    def recv(self, size):
-        return self.read_packet()
+    def recv(self, size=MTU):
+        return self.read_packet(size)
         
 
 
@@ -635,7 +661,7 @@ class RawPcapWriter:
             if sec is None:
                 sec = it
             if usec is None:
-                usec = int((t-it)*1000000)
+                usec = int(round((t-it)*1000000))
         self.f.write(struct.pack(self.endian+"IIII", sec, usec, caplen, wirelen))
         self.f.write(packet)
         if self.gz and self.sync:
@@ -660,7 +686,7 @@ class PcapWriter(RawPcapWriter):
 
     def _write_packet(self, packet):        
         sec = int(packet.time)
-        usec = int((packet.time-sec)*1000000)
+        usec = int(round((packet.time-sec)*1000000))
         s = str(packet)
         caplen = len(s)
         RawPcapWriter._write_packet(self, s, sec, usec, caplen, caplen)
@@ -691,14 +717,14 @@ def wireshark(pktlist):
     """Run wireshark on a list of packets"""
     f = get_temp_file()
     wrpcap(f, pktlist)
-    os.spawnlp(os.P_NOWAIT, conf.prog.wireshark, conf.prog.wireshark, "-r", f)
+    subprocess.Popen([conf.prog.wireshark, "-r", f])
 
 @conf.commands.register
 def hexedit(x):
     x = str(x)
     f = get_temp_file()
     open(f,"w").write(x)
-    os.spawnlp(os.P_WAIT, conf.prog.hexedit, conf.prog.hexedit, f)
+    subprocess.call([conf.prog.hexedit, f])
     x = open(f).read()
     os.unlink(f)
     return x
diff --git a/scapy/utils6.py b/scapy/utils6.py
index 4f9b096734d6c48a59d0b8a96ade79ba37ae3841..eab813b35019a6576812f9cc60dddea42736d06f 100644
--- a/scapy/utils6.py
+++ b/scapy/utils6.py
@@ -24,9 +24,23 @@ def construct_source_candidate_set(addr, plen, laddr, loname):
     will then be performed to select the best source address associated
     with some specific destination that uses this prefix.
     """
+    def cset_sort(x,y):
+        x_global = 0
+        if in6_isgladdr(x):
+            x_global = 1
+        y_global = 0
+        if in6_isgladdr(y):
+            y_global = 1
+        res = y_global - x_global
+        if res != 0 or y_global != 1:
+            return res
+        # two global addresses: if one is native, it wins.
+        if not in6_isaddr6to4(x):
+            return -1;
+        return -res
 
     cset = []
-    if in6_isgladdr(addr):
+    if in6_isgladdr(addr) or in6_isuladdr(addr):
 	cset = filter(lambda x: x[1] == IPV6_ADDR_GLOBAL, laddr)
     elif in6_islladdr(addr):
 	cset = filter(lambda x: x[1] == IPV6_ADDR_LINKLOCAL, laddr)
@@ -44,6 +58,7 @@ def construct_source_candidate_set(addr, plen, laddr, loname):
     elif addr == '::' and plen == 0:
 	cset = filter(lambda x: x[1] == IPV6_ADDR_GLOBAL, laddr)
     cset = map(lambda x: x[0], cset)
+    cset.sort(cmp=cset_sort) # Sort with global addresses first
     return cset            
 
 def get_source_addr_from_candidate_set(dst, candidate_set):
@@ -52,21 +67,79 @@ def get_source_addr_from_candidate_set(dst, candidate_set):
     algorithm defined in section 5 of RFC 3484. The format is very different
     from that described in the document because it operates on a set 
     of candidate source address for some specific route.
-    
-    Rationale behind the implementation is to be able to make the right 
-    choice for a 6to4 destination when both a 6to4 address and a IPv6 native
-    address are available for that interface.
     """
+
+    def scope_cmp(a, b):
+        """
+        Given two addresses, returns -1, 0 or 1 based on comparison of
+        their scope
+        """
+        scope_mapper = {IPV6_ADDR_GLOBAL: 4,
+                        IPV6_ADDR_SITELOCAL: 3,
+                        IPV6_ADDR_LINKLOCAL: 2,
+                        IPV6_ADDR_LOOPBACK: 1}
+        sa = in6_getscope(a)
+        if sa == -1:
+            sa = IPV6_ADDR_LOOPBACK
+        sb = in6_getscope(b)
+        if sb == -1:
+            sb = IPV6_ADDR_LOOPBACK
+
+        sa = scope_mapper[sa]
+        sb = scope_mapper[sb]
+
+        if sa == sb:
+            return 0
+        if sa > sb:
+            return 1
+        return -1
+
+    def rfc3484_cmp(source_a, source_b):
+        """
+        The function implements a limited version of the rules from Source
+        Address selection algorithm defined section of RFC 3484.
+        """
+
+        # Rule 1: Prefer same address
+        if source_a == dst:
+            return 1
+        if source_b == dst:
+            return 1
+
+        # Rule 2: Prefer appropriate scope
+        tmp = scope_cmp(source_a, source_b)
+        if tmp == -1:
+            if scope_cmp(source_a, dst) == -1:
+                return 1
+            else:
+                return -1
+        elif tmp == 1:
+            if scope_cmp(source_b, dst) == -1:
+                return 1
+            else:
+                return -1
+
+        # Rule 3: cannot be easily implemented
+        # Rule 4: cannot be easily implemented
+        # Rule 5: does not make sense here
+        # Rule 6: cannot be implemented
+        # Rule 7: cannot be implemented
+        
+        # Rule 8: Longest prefix match
+        tmp1 = in6_get_common_plen(source_a, dst)
+        tmp2 = in6_get_common_plen(source_b, dst)
+        if tmp1 > tmp2:
+            return 1
+        elif tmp2 > tmp1:
+            return -1
+        return 0
     
-    if len(candidate_set) == 0:
+    if not candidate_set:
 	# Should not happen
 	return None
-    
-    if in6_isaddr6to4(dst):
-	tmp = filter(lambda x: in6_isaddr6to4(x), candidate_set)
-	if len(tmp) != 0:
-	    return tmp[0]
 
+    candidate_set.sort(cmp=rfc3484_cmp, reverse=True)
+    
     return candidate_set[0]
 
 
@@ -600,7 +673,7 @@ def in6_isuladdr(str):
     Returns True if provided address in printable format belongs to
     Unique local address space (fc00::/7).
     """
-    return in6_isincluded(str, 'fc::', 7)
+    return in6_isincluded(str, 'fc00::', 7)
 
 # TODO : we should see the status of Unique Local addresses against
 #        global address space.
@@ -667,21 +740,48 @@ def in6_isaddrllallservers(str):
     return (inet_pton(socket.AF_INET6, "ff02::2") ==
             inet_pton(socket.AF_INET6, str))
 
-
 def in6_getscope(addr):
     """
     Returns the scope of the address.
     """
-    if in6_isgladdr(addr):
+    if in6_isgladdr(addr) or in6_isuladdr(addr):
         scope = IPV6_ADDR_GLOBAL
     elif in6_islladdr(addr):
         scope = IPV6_ADDR_LINKLOCAL
     elif in6_issladdr(addr):
         scope = IPV6_ADDR_SITELOCAL
     elif in6_ismaddr(addr):
-        scope = IPV6_ADDR_MULTICAST
+        if in6_ismgladdr(addr):
+            scope = IPV6_ADDR_GLOBAL
+        elif in6_ismlladdr(addr):
+            scope = IPV6_ADDR_LINKLOCAL
+        elif in6_ismsladdr(addr):
+            scope = IPV6_ADDR_SITELOCAL
+        elif in6_ismnladdr(addr):
+            scope = IPV6_ADDR_LOOPBACK
+        else:
+            scope = -1
     elif addr == '::1':
         scope = IPV6_ADDR_LOOPBACK
     else:
         scope = -1
     return scope
+
+def in6_get_common_plen(a, b):
+    """
+    Return common prefix length of IPv6 addresses a and b.
+    """
+    def matching_bits(byte1, byte2):
+        for i in range(8):
+            cur_mask = 0x80 >> i
+            if (byte1 & cur_mask) != (byte2 & cur_mask):
+                return i
+        return 8
+        
+    tmpA = inet_pton(socket.AF_INET6, a)
+    tmpB = inet_pton(socket.AF_INET6, b)
+    for i in range(16):
+        mbits = matching_bits(ord(tmpA[i]), ord(tmpB[i]))
+        if mbits != 8:
+            return 8*i + mbits
+    return 128
diff --git a/scapy/volatile.py b/scapy/volatile.py
index 7f4d78fd4c70a22a24086a45f17bab9ae0f0c7e0..de2945935e6f5f87e2fbf611664c652c9bec2532 100644
--- a/scapy/volatile.py
+++ b/scapy/volatile.py
@@ -12,7 +12,7 @@ from utils import corrupt_bits,corrupt_bytes
 ####################
 
 
-class RandomSequence:
+class RandomEnumeration:
     """iterate through a sequence in random order.
        When all the values have been drawn, if forever=1, the drawing is done again.
        If renewkeys=0, the draw will be in the same order, guaranteeing that the same
@@ -42,10 +42,11 @@ class RandomSequence:
     def next(self):
         while True:
             if self.turns == 0 or (self.i == 0 and self.renewkeys):
+                self.cnt_key = self.rnd.randint(0,2**self.n-1)
                 self.sbox = [self.rnd.randint(0,self.fsmask) for k in xrange(self.sbox_size)]
             self.turns += 1
             while self.i < 2**self.n:
-                ct = self.i
+                ct = self.i^self.cnt_key
                 self.i += 1
                 for k in range(self.rounds): # Unbalanced Feistel Network
                     lsb = ct & self.fsmask
@@ -66,6 +67,13 @@ class VolatileValue:
     def __getattr__(self, attr):
         if attr == "__setstate__":
             raise AttributeError(attr)
+        elif attr == "__cmp__":
+            x = self._fix()
+            def cmp2(y,x=x):
+                if type(x) != type(y):
+                    return -1
+                return x.__cmp__(y)
+            return cmp2
         return getattr(self._fix(),attr)
     def _fix(self):
         return None
@@ -105,10 +113,10 @@ class RandNumExpo(RandField):
     def _fix(self):
         return self.base+int(round(random.expovariate(self.lambd)))
 
-class RandDraw(RandNum):
+class RandEnum(RandNum):
     """Instances evaluate to integer sampling without replacement from the given interval"""
     def __init__(self, min, max):
-        self.seq = RandomSequence(min,max)
+        self.seq = RandomEnumeration(min,max)
     def _fix(self):
         return self.seq.next()
 
@@ -144,37 +152,37 @@ class RandSLong(RandNum):
     def __init__(self):
         RandNum.__init__(self, -2L**63, 2L**63-1)
 
-class RandDrawByte(RandDraw):
+class RandEnumByte(RandEnum):
     def __init__(self):
-        RandDraw.__init__(self, 0, 2L**8-1)
+        RandEnum.__init__(self, 0, 2L**8-1)
 
-class RandDrawSByte(RandDraw):
+class RandEnumSByte(RandEnum):
     def __init__(self):
-        RandDraw.__init__(self, -2L**7, 2L**7-1)
+        RandEnum.__init__(self, -2L**7, 2L**7-1)
 
-class RandDrawShort(RandDraw):
+class RandEnumShort(RandEnum):
     def __init__(self):
-        RandDraw.__init__(self, 0, 2L**16-1)
+        RandEnum.__init__(self, 0, 2L**16-1)
 
-class RandDrawSShort(RandDraw):
+class RandEnumSShort(RandEnum):
     def __init__(self):
-        RandDraw.__init__(self, -2L**15, 2L**15-1)
+        RandEnum.__init__(self, -2L**15, 2L**15-1)
 
-class RandDrawInt(RandDraw):
+class RandEnumInt(RandEnum):
     def __init__(self):
-        RandDraw.__init__(self, 0, 2L**32-1)
+        RandEnum.__init__(self, 0, 2L**32-1)
 
-class RandDrawSInt(RandDraw):
+class RandEnumSInt(RandEnum):
     def __init__(self):
-        RandDraw.__init__(self, -2L**31, 2L**31-1)
+        RandEnum.__init__(self, -2L**31, 2L**31-1)
 
-class RandDrawLong(RandDraw):
+class RandEnumLong(RandEnum):
     def __init__(self):
-        RandDraw.__init__(self, 0, 2L**64-1)
+        RandEnum.__init__(self, 0, 2L**64-1)
 
-class RandDrawSLong(RandDraw):
+class RandEnumSLong(RandEnum):
     def __init__(self):
-        RandDraw.__init__(self, -2L**63, 2L**63-1)
+        RandEnum.__init__(self, -2L**63, 2L**63-1)
 
 class RandChoice(RandField):
     def __init__(self, *args):
@@ -226,13 +234,58 @@ class RandMAC(RandString):
                 v = RandByte()
             elif "-" in template[i]:
                 x,y = template[i].split("-")
-                v = RandSeq(int(x,16), int(y,16))
+                v = RandNum(int(x,16), int(y,16))
             else:
                 v = int(template[i],16)
             self.mac += (v,)
     def _fix(self):
         return "%02x:%02x:%02x:%02x:%02x:%02x" % self.mac
     
+class RandIP6(RandString):
+    def __init__(self, ip6template="**"):
+        self.tmpl = ip6template
+        self.sp = self.tmpl.split(":")
+        for i,v in enumerate(self.sp):
+            if not v or v == "**":
+                continue
+            if "-" in v:
+                a,b = v.split("-")
+            elif v == "*":
+                a=b=""
+            else:
+                a=b=v
+
+            if not a:
+                a = "0"
+            if not b:
+                b = "ffff"
+            if a==b:
+                self.sp[i] = int(a,16)
+            else:
+                self.sp[i] = RandNum(int(a,16), int(b,16))
+        self.variable = "" in self.sp
+        self.multi = self.sp.count("**")
+    def _fix(self):
+        done = 0
+        nbm = self.multi
+        ip = []
+        for i,n in enumerate(self.sp):
+            if n == "**":
+                nbm -= 1
+                remain = 8-(len(self.sp)-i-1)-len(ip)+nbm
+                if "" in self.sp:
+                    remain += 1
+                if nbm or self.variable:
+                    remain = random.randint(0,remain)
+                for j in range(remain):
+                    ip.append("%04x" % random.randint(0,65535))
+            elif not n:
+                ip.append("")
+            else:
+                ip.append("%04x" % n)
+        if len(ip) == 9:
+            ip.remove("")
+        return ":".join(ip)
 
 class RandOID(RandString):
     def __init__(self, fmt=None, depth=RandNumExpo(0.1), idnum=RandNumExpo(0.01)):
@@ -267,8 +320,6 @@ class RandOID(RandString):
             return ".".join(oid)
             
 
-from pprint import pprint
-        
 class RandRegExp(RandField):
     def __init__(self, regexp, lambda_=0.3,):
         self._regexp = regexp
diff --git a/setup.py b/setup.py
index 4f8a9b8cb14c149e160fe60cee3f023d3b5128bf..4c1982481e0749f2768997fd5c18f621f39c5de6 100755
--- a/setup.py
+++ b/setup.py
@@ -34,12 +34,16 @@ def make_ezipfile(base_name, base_dir, verbose=0, dry_run=0):
 
 archive_util.ARCHIVE_FORMATS["ezip"] = (make_ezipfile,[],'Executable ZIP file')
 
+SCRIPTS = ['bin/scapy','bin/UTscapy']
+# On Windows we also need additional batch files to run the above scripts 
+if os.name == "nt":
+  SCRIPTS += ['bin/scapy.bat','bin/UTscapy.bat']
 
 setup(
     name = 'scapy',
-    version = '2.0.1-dev', 
-    packages=['scapy','scapy/arch', 'scapy/layers','scapy/asn1','scapy/tools','scapy/modules'],
-    scripts = ['bin/scapy','bin/UTscapy'],
+    version = '2.1.0-dev',
+    packages=['scapy','scapy/arch', 'scapy/arch/windows', 'scapy/layers','scapy/asn1','scapy/tools','scapy/modules', 'scapy/crypto'],
+    scripts = SCRIPTS,
     data_files = [('share/man/man1', ["doc/scapy.1.gz"])],
 
     # Metadata
diff --git a/test/import_tester b/test/import_tester
new file mode 100644
index 0000000000000000000000000000000000000000..eebaba60135a9681186d726fd80b7ddd2b0a371a
--- /dev/null
+++ b/test/import_tester
@@ -0,0 +1,3 @@
+#! /bin/bash
+cd "$(dirname $0)/.."
+find scapy -name '*.py' | sed -e 's#/#.#g' -e 's/\(\.__init__\)\?\.py$//'  | while read a; do echo "######### $a"; python -c "import $a"; done
diff --git a/test/regression.uts b/test/regression.uts
index f53dc440418bef9f784e41856947e8e6b65d8e94..f5b5f732c653cc38a5a8ec8d98223c8a435efd68 100644
--- a/test/regression.uts
+++ b/test/regression.uts
@@ -895,7 +895,128 @@ class ATMT6(Automaton):
 a=ATMT6()
 a.run()
 assert( _ == 'Mercury' )
- 
+
+a.restart()
+a.run()
+assert( _ == 'Mercury' )
+
+= Automaton test io event
+~ automaton
+
+class ATMT7(Automaton):
+    @ATMT.state(initial=1)
+    def BEGIN(self):
+        self.res = "S"
+    @ATMT.ioevent(BEGIN, name="tst")
+    def tr1(self, fd):
+        self.res += fd.recv()
+        raise self.NEXT_STATE()
+    @ATMT.state()
+    def NEXT_STATE(self):
+        self.oi.tst.send("ur")
+    @ATMT.ioevent(NEXT_STATE, name="tst")
+    def tr2(self, fd):
+        self.res += fd.recv()
+        raise self.END()
+    @ATMT.state(final=1)
+    def END(self):
+        self.res += "n"
+        return self.res
+
+a=ATMT7()
+a.run(wait=False)
+a.io.tst.send("at")
+a.io.tst.recv()
+a.io.tst.send(_)
+a.run()
+assert( _ == "Saturn" )
+
+a.restart()
+a.run(wait=False)
+a.io.tst.send("at")
+a.io.tst.recv()
+a.io.tst.send(_)
+a.run()
+assert( _ == "Saturn" )
+
+= Automaton test io event from external fd
+~ automaton
+class ATMT8(Automaton):
+    @ATMT.state(initial=1)
+    def BEGIN(self):
+        self.res = "U"
+    @ATMT.ioevent(BEGIN, name="extfd")
+    def tr1(self, fd):
+        self.res += fd.read(2)
+        raise self.NEXT_STATE()
+    @ATMT.state()
+    def NEXT_STATE(self):
+        pass
+    @ATMT.ioevent(NEXT_STATE, name="extfd")
+    def tr2(self, fd):
+        self.res += fd.read(2)
+        raise self.END()
+    @ATMT.state(final=1)
+    def END(self):
+        self.res += "s"
+        return self.res
+
+r,w = os.pipe()
+
+a=ATMT8(external_fd={"extfd":r})
+a.run(wait=False)
+os.write(w,"ra")
+os.write(w,"nu")
+a.run()
+assert( _ == "Uranus" )
+
+a.restart()
+a.run(wait=False)
+os.write(w,"ra")
+os.write(w,"nu")
+a.run()
+assert( _ == "Uranus" )
+
+= Automaton test interception_points, and restart
+~ automaton
+class ATMT9(Automaton):
+    def my_send(self, x):
+        self.io.loop.send(x)
+    @ATMT.state(initial=1)
+    def BEGIN(self):
+        self.res = "V"
+        self.send(Raw("ENU"))
+    @ATMT.ioevent(BEGIN, name="loop")
+    def received_sth(self, fd):
+        self.res += fd.recv().load
+        raise self.END()
+    @ATMT.state(final=1)
+    def END(self):
+        self.res += "s"
+        return self.res
+
+a=ATMT9(debug=5)
+a.run()
+assert( _ == "VENUs" )
+
+a.restart()
+a.run()
+assert( _ == "VENUs" )
+
+a.restart()
+a.BEGIN.intercepts()
+while True:
+    try:
+        x = a.run()
+    except Automaton.InterceptionPoint,p:
+        a.accept_packet(Raw(p.packet.load.lower()), wait=False)
+    else:
+        break
+
+x
+assert( _ == "Venus" )
+
+
 
 + Test IP options
 
diff --git a/test/run_tests.bat b/test/run_tests.bat
new file mode 100644
index 0000000000000000000000000000000000000000..1aa3b29d01f3ee8c6b9438a9678a6c4f16e2122f
--- /dev/null
+++ b/test/run_tests.bat
@@ -0,0 +1,8 @@
+@echo off
+set MYDIR=%cd%\..
+set PYTHONPATH=%MYDIR%
+if [%1]==[] (
+  python %MYDIR%\scapy\tools\UTscapy.py -t regression.uts -f html -o scapy_regression_test_%DATE%.html
+) else (
+  python %MYDIR%\scapy\tools\UTscapy.py %1 %2 %3 %4 %5 %6 %7 %8 %9
+)