Merge pull request #1065 from gpotter2/tls-cont

TLS continuous data
diff --git a/README.md b/README.md
index 557b0f3..8f4963c 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@
 using default values that work.
 
 It can easily handle most classical tasks like scanning, tracerouting, probing,
-unit tests, attacks or network discovery (it can replace `hping`, 85% of `nmap̀`,
+unit tests, attacks or network discovery (it can replace `hping`, 85% of `nmap`,
 `arpspoof`, `arp-sk`, `arping`, `tcpdump`, `wireshark`, `p0f`, etc.). It also
 performs very well at a lot of other specific tasks that most other tools can't
 handle, like sending invalid frames, injecting your own 802.11 frames, combining
@@ -66,7 +66,7 @@
 
 p = IP(dst="github.com")/TCP()
 r = sr1(p)
-print r.summary()
+print(r.summary())
 ```
 
 Then, launch the script with:
diff --git a/scapy/arch/bpf/core.py b/scapy/arch/bpf/core.py
index 0340fa6..040067b 100644
--- a/scapy/arch/bpf/core.py
+++ b/scapy/arch/bpf/core.py
@@ -173,7 +173,7 @@
 
             # Check if the interface can be used
             try:
-                fcntl.ioctl(fd, BIOCSETIF, struct.pack("16s16x", ifname))
+                fcntl.ioctl(fd, BIOCSETIF, struct.pack("16s16x", ifname.encode()))
                 interfaces.append((ifname, int(ifname[-1])))
             except IOError as err:
                 pass
diff --git a/scapy/arch/bpf/supersocket.py b/scapy/arch/bpf/supersocket.py
index 34c8f15..7d04e18 100644
--- a/scapy/arch/bpf/supersocket.py
+++ b/scapy/arch/bpf/supersocket.py
@@ -66,7 +66,7 @@
 
         # Assign the network interface to the BPF handle
         try:
-            fcntl.ioctl(self.ins, BIOCSETIF, struct.pack("16s16x", self.iface))
+            fcntl.ioctl(self.ins, BIOCSETIF, struct.pack("16s16x", self.iface.encode()))
         except IOError:
             raise Scapy_Exception("BIOCSETIF failed on %s" % self.iface)
         self.assigned_interface = self.iface
@@ -324,7 +324,7 @@
         # Assign the network interface to the BPF handle
         if self.assigned_interface != iff:
             try:
-                fcntl.ioctl(self.outs, BIOCSETIF, struct.pack("16s16x", iff))
+                fcntl.ioctl(self.outs, BIOCSETIF, struct.pack("16s16x", iff.encode()))
             except IOError:
                 raise Scapy_Exception("BIOCSETIF failed on %s" % iff)
             self.assigned_interface = iff
diff --git a/scapy/layers/tls/automaton.py b/scapy/layers/tls/automaton.py
index c9f2329..886b1b2 100644
--- a/scapy/layers/tls/automaton.py
+++ b/scapy/layers/tls/automaton.py
@@ -130,6 +130,7 @@
                 else:
                     self.remain_in += tmp
             except:
+                self.vprint("Could not join host ! Retrying...")
                 retry -= 1
 
         if len(self.remain_in) < 2 or len(self.remain_in) != grablen:
diff --git a/scapy/layers/tls/automaton_cli.py b/scapy/layers/tls/automaton_cli.py
index d58a6b2..bda49df 100644
--- a/scapy/layers/tls/automaton_cli.py
+++ b/scapy/layers/tls/automaton_cli.py
@@ -20,6 +20,7 @@
 from __future__ import print_function
 import socket
 
+from scapy.pton_ntop import inet_pton
 from scapy.utils import randstring
 from scapy.automaton import ATMT
 from scapy.layers.tls.automaton import _TLSAutomaton
@@ -70,9 +71,9 @@
         self.remote_name = None
         try:
             if ':' in server:
-                socket.inet_pton(socket.AF_INET6, server)
+                inet_pton(socket.AF_INET6, server)
             else:
-                socket.inet_pton(socket.AF_INET, server)
+                inet_pton(socket.AF_INET, server)
         except:
             self.remote_name = socket.getfqdn(server)
             if self.remote_name != server:
diff --git a/scapy/layers/tls/automaton_srv.py b/scapy/layers/tls/automaton_srv.py
index 9f76df4..1d37aa4 100644
--- a/scapy/layers/tls/automaton_srv.py
+++ b/scapy/layers/tls/automaton_srv.py
@@ -19,6 +19,7 @@
 from __future__ import print_function
 import socket
 
+from scapy.pton_ntop import inet_pton
 from scapy.utils import randstring, repr_hex
 from scapy.automaton import ATMT
 from scapy.layers.tls.automaton import _TLSAutomaton
@@ -69,9 +70,9 @@
                                                    **kargs)
         try:
             if ':' in server:
-                socket.inet_pton(socket.AF_INET6, server)
+                inet_pton(socket.AF_INET6, server)
             else:
-                socket.inet_pton(socket.AF_INET, server)
+                inet_pton(socket.AF_INET, server)
             tmp = socket.getaddrinfo(server, sport)
         except:
             tmp = socket.getaddrinfo(socket.getfqdn(server), sport)
diff --git a/scapy/layers/tls/cert.py b/scapy/layers/tls/cert.py
index 735e63a..acd771a 100644
--- a/scapy/layers/tls/cert.py
+++ b/scapy/layers/tls/cert.py
@@ -279,9 +279,9 @@
     def import_from_tuple(self, tup):
         # this is rarely used
         e, m, mLen = tup
-        if isinstance(m, str):
+        if isinstance(m, bytes):
             m = pkcs_os2ip(m)
-        if isinstance(e, str):
+        if isinstance(e, bytes):
             e = pkcs_os2ip(e)
         self.fill_and_store(modulus=m, pubExp=e)
         self.pem = self.pubkey.public_bytes(
@@ -512,7 +512,6 @@
 
     @crypto_validator
     def import_from_asn1pkt(self, privkey):
-        print(privkey)
         self.key = serialization.load_der_private_key(raw(privkey), None,
                                                   backend=default_backend())
         self.pubkey = self.key.public_key()
diff --git a/scapy/layers/tls/crypto/cipher_block.py b/scapy/layers/tls/crypto/cipher_block.py
index bd2b04f..ab463cc 100644
--- a/scapy/layers/tls/crypto/cipher_block.py
+++ b/scapy/layers/tls/crypto/cipher_block.py
@@ -51,7 +51,7 @@
             else:
                 l = self.key_len
             key = b"\0" * l
-        if iv is None or iv == "":
+        if not iv:
             self.ready["iv"] = False
             iv = b"\0" * self.block_size
 
diff --git a/scapy/layers/tls/crypto/groups.py b/scapy/layers/tls/crypto/groups.py
index 9cef2a3..f0a9ca3 100644
--- a/scapy/layers/tls/crypto/groups.py
+++ b/scapy/layers/tls/crypto/groups.py
@@ -68,8 +68,8 @@
         return the_class
 
 
-class _FFDHParams(object):
-    __metaclass__ = _FFDHParamsMetaclass
+class _FFDHParams(six.with_metaclass(_FFDHParamsMetaclass)):
+    pass
 
 
 class modp768(_FFDHParams):
diff --git a/scapy/layers/tls/handshake.py b/scapy/layers/tls/handshake.py
index 4a4226a..95cd914 100644
--- a/scapy/layers/tls/handshake.py
+++ b/scapy/layers/tls/handshake.py
@@ -608,7 +608,7 @@
     def dispatch_hook(cls, _pkt=None, *args, **kargs):
         if _pkt:
             tls_session = kargs.get("tls_session", None)
-            if tls_session and tls_session.tls_version >= 0x0304:
+            if tls_session and (tls_session.tls_version or 0) >= 0x0304:
                 return TLS13Certificate
         return TLSCertificate
 
diff --git a/scapy/layers/tls/keyexchange.py b/scapy/layers/tls/keyexchange.py
index 386a9bb..64ed6be 100644
--- a/scapy/layers/tls/keyexchange.py
+++ b/scapy/layers/tls/keyexchange.py
@@ -120,7 +120,7 @@
         s = pkt.tls_session
         if s.tls_version and s.tls_version < 0x0300:
             if len(s.client_certs) > 0:
-                sig_len = s.client_certs[0].pubKey.pubkey.key_size / 8
+                sig_len = s.client_certs[0].pubKey.pubkey.key_size // 8
             else:
                 warning("No client certificate provided. "
                         "We're making a wild guess about the signature size.")
@@ -219,7 +219,7 @@
         i = self.m2i(pkt, s)
         if i is None:
             return s, None
-        remain = ""
+        remain = b""
         if conf.padding_layer in i:
             r = i[conf.padding_layer]
             del(r.underlayer.payload)
@@ -307,12 +307,12 @@
         default_params = _ffdh_groups['modp2048'][0].parameter_numbers()
         default_mLen = _ffdh_groups['modp2048'][1]
 
-        if self.dh_p is "":
-            self.dh_p = pkcs_i2osp(default_params.p, default_mLen/8)
+        if not self.dh_p:
+            self.dh_p = pkcs_i2osp(default_params.p, default_mLen//8)
         if self.dh_plen is None:
             self.dh_plen = len(self.dh_p)
 
-        if self.dh_g is "":
+        if not self.dh_g:
             self.dh_g = pkcs_i2osp(default_params.g, 1)
         if self.dh_glen is None:
             self.dh_glen = 1
@@ -321,11 +321,11 @@
         g = pkcs_os2ip(self.dh_g)
         real_params = dh.DHParameterNumbers(p, g).parameters(default_backend())
 
-        if self.dh_Ys is "":
+        if not self.dh_Ys:
             s.server_kx_privkey = real_params.generate_private_key()
             pubkey = s.server_kx_privkey.public_key()
             y = pubkey.public_numbers().y
-            self.dh_Ys = pkcs_i2osp(y, pubkey.key_size/8)
+            self.dh_Ys = pkcs_i2osp(y, pubkey.key_size//8)
         # else, we assume that the user wrote the server_kx_privkey by himself
         if self.dh_Yslen is None:
             self.dh_Yslen = len(self.dh_Ys)
@@ -642,13 +642,13 @@
         self.tls_session.server_tmp_rsa_key = k
         pubNum = k.pubkey.public_numbers()
 
-        if self.rsamod is "":
-            self.rsamod = pkcs_i2osp(pubNum.n, k.pubkey.key_size/8)
+        if not self.rsamod:
+            self.rsamod = pkcs_i2osp(pubNum.n, k.pubkey.key_size//8)
         if self.rsamodlen is None:
             self.rsamodlen = len(self.rsamod)
 
         rsaexplen = math.ceil(math.log(pubNum.e)/math.log(2)/8.)
-        if self.rsaexp is "":
+        if not self.rsaexp:
             self.rsaexp = pkcs_i2osp(pubNum.e, rsaexplen)
         if self.rsaexplen is None:
             self.rsaexplen = len(self.rsaexp)
@@ -722,7 +722,7 @@
         s.client_kx_privkey = params.generate_private_key()
         pubkey = s.client_kx_privkey.public_key()
         y = pubkey.public_numbers().y
-        self.dh_Yc = pkcs_i2osp(y, pubkey.key_size/8)
+        self.dh_Yc = pkcs_i2osp(y, pubkey.key_size//8)
 
         if s.client_kx_privkey and s.server_kx_pubkey:
             pms = s.client_kx_privkey.exchange(s.server_kx_pubkey)
@@ -730,7 +730,7 @@
             s.compute_ms_and_derive_keys()
 
     def post_build(self, pkt, pay):
-        if self.dh_Yc == "":
+        if not self.dh_Yc:
             try:
                 self.fill_missing()
             except ImportError:
@@ -782,8 +782,8 @@
         x = pubkey.public_numbers().x
         y = pubkey.public_numbers().y
         self.ecdh_Yc = (b"\x04" +
-                        pkcs_i2osp(x, params.key_size/8) +
-                        pkcs_i2osp(y, params.key_size/8))
+                        pkcs_i2osp(x, params.key_size//8) +
+                        pkcs_i2osp(y, params.key_size//8))
 
         if s.client_kx_privkey and s.server_kx_pubkey:
             pms = s.client_kx_privkey.exchange(ec.ECDH(), s.server_kx_pubkey)
@@ -791,7 +791,7 @@
             s.compute_ms_and_derive_keys()
 
     def post_build(self, pkt, pay):
-        if self.ecdh_Yc == "":
+        if not self.ecdh_Yc:
             try:
                 self.fill_missing()
             except ImportError:
diff --git a/scapy/layers/tls/keyexchange_tls13.py b/scapy/layers/tls/keyexchange_tls13.py
index 2bbdc51..09af443 100644
--- a/scapy/layers/tls/keyexchange_tls13.py
+++ b/scapy/layers/tls/keyexchange_tls13.py
@@ -84,7 +84,7 @@
         if self.group is None:
             self.group = 23     # secp256r1
 
-        if self.key_exchange == "":
+        if not self.key_exchange:
             try:
                 self.create_privkey()
             except ImportError:
diff --git a/scapy/layers/tls/record.py b/scapy/layers/tls/record.py
index 6f88fcf..f2d5024 100644
--- a/scapy/layers/tls/record.py
+++ b/scapy/layers/tls/record.py
@@ -99,7 +99,7 @@
                 return cls(m, tls_session=pkt.tls_session)
             except:
                 if conf.debug_dissector:
-                    traceback.print_exc()
+                    raise
                 return Raw(m)
 
     def getfield(self, pkt, s):
@@ -357,7 +357,7 @@
             if version > 0x300:
                 h = alg.digest(read_seq_num + hdr + msg)
             elif version == 0x300:
-                h = alg.digest_sslv3(read_seq_num + hdr[0] + hdr[3:5] + msg)
+                h = alg.digest_sslv3(read_seq_num + hdr[:1] + hdr[3:5] + msg)
             else:
                 raise Exception("Unrecognized version.")
         except HMACError:
diff --git a/scapy/modules/p0f.py b/scapy/modules/p0f.py
index bc312a1..aa1f2e9 100644
--- a/scapy/modules/p0f.py
+++ b/scapy/modules/p0f.py
@@ -22,6 +22,7 @@
 from scapy.error import warning, Scapy_Exception, log_runtime
 from scapy.volatile import RandInt, RandByte, RandChoice, RandNum, RandShort, RandString
 from scapy.sendrecv import sniff
+from scapy.modules import six
 from scapy.modules.six.moves import map, range
 if conf.route is None:
     # unused import, only to initialize conf.route
@@ -359,10 +360,7 @@
     
     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()
@@ -386,7 +384,15 @@
     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
+    # Take the options already set as "hints" to use in the new packet if we
+    # can. MSS, WScale and Timestamp can all be wildcarded in a signature, so
+    # we'll use the already-set values if they're valid integers.
+    orig_opts = dict(pkt.payload.options)
+    int_only = lambda val: val if isinstance(val, six.integer_types) else None
+    mss_hint = int_only(orig_opts.get('MSS'))
+    wscale_hint = int_only(orig_opts.get('WScale'))
+    ts_hint = [int_only(o) for o in orig_opts.get('Timestamp', (None, None))]
+
     options = []
     if pers[4] != '.':
         for opt in pers[4].split(','):
@@ -394,42 +400,73 @@
                 # MSS might have a maximum size because of window size
                 # specification
                 if pers[0][0] == 'S':
-                    maxmss = (2**16-1) / int(pers[0][1:])
+                    maxmss = (2**16-1) // int(pers[0][1:])
                 else:
                     maxmss = (2**16-1)
+                # disregard hint if out of range
+                if mss_hint and not 0 <= mss_hint <= maxmss:
+                    mss_hint = None
                 # 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)))
+                    if mss_hint is not None:
+                        options.append(('MSS', mss_hint))
+                    else:
+                        options.append(('MSS', random.randint(1, maxmss)))
                 elif opt[1] == '%':
                     coef = int(opt[2:])
-                    options.append(('MSS', coef*random.randint(1,maxmss/coef)))
+                    if mss_hint is not None and mss_hint % coef == 0:
+                        options.append(('MSS', mss_hint))
+                    else:
+                        options.append((
+                            'MSS', coef*random.randint(1, maxmss//coef)))
                 else:
                     options.append(('MSS', int(opt[1:])))
             elif opt[0] == 'W':
+                if wscale_hint and not 0 <= wscale_hint < 2**8:
+                    wscale_hint = None
                 if opt[1:] == '*':
-                    options.append(('WScale', RandByte()))
+                    if wscale_hint is not None:
+                        options.append(('WScale', wscale_hint))
+                    else:
+                        options.append(('WScale', RandByte()))
                 elif opt[1] == '%':
                     coef = int(opt[2:])
-                    options.append(('WScale', coef*RandNum(min=1,
-                                                           max=(2**8-1)/coef)))
+                    if wscale_hint is not None and wscale_hint % coef == 0:
+                        options.append(('WScale', wscale_hint))
+                    else:
+                        options.append((
+                            'WScale', coef*RandNum(min=1, max=(2**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]:
+                # Determine first timestamp.
+                if uptime is not None:
+                    ts_a = uptime
+                elif ts_hint[0] and 0 < ts_hint[0] < 2**32:
+                    # Note: if first ts is 0, p0f registers it as "T0" not "T",
+                    # hence we don't want to use the hint if it was 0.
+                    ts_a = ts_hint[0]
+                else:
+                    ts_a = random.randint(120, 100*60*60*24*365)
+                # Determine second timestamp.
+                if 'T' not in pers[5]:
+                    ts_b = 0
+                elif ts_hint[1] and 0 < ts_hint[1] < 2**32:
+                    ts_b = ts_hint[1]
+                else:
                     # 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)))
+                    ts_b = random.randint(1, 2**32-1)
+                options.append(('Timestamp', (ts_a, ts_b)))
             elif opt == 'S':
                 options.append(('SAckOK', ''))
             elif opt == 'N':
@@ -457,7 +494,7 @@
         pkt.payload.window = int(pers[0])
     elif pers[0][0] == '%':
         coef = int(pers[0][1:])
-        pkt.payload.window = coef * RandNum(min=1,max=(2**16-1)/coef)
+        pkt.payload.window = coef * RandNum(min=1, max=(2**16-1)//coef)
     elif pers[0][0] == 'T':
         pkt.payload.window = mtu * int(pers[0][1:])
     elif pers[0][0] == 'S':
@@ -465,7 +502,7 @@
         mss = [x for x in options if x[0] == 'MSS']
         if not mss:
             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:])
+        pkt.payload.window = mss[0][1] * int(pers[0][1:])
     else:
         raise Scapy_Exception('Unhandled window size specification')
     
@@ -487,7 +524,7 @@
                 if db == p0fo_kdb:
                     pkt.payload.flags |= 0x20 # U
                 else:
-                    pkt.payload.flags |= RandChoice(8, 32, 40) #P / U / PU
+                    pkt.payload.flags |= random.choice([8, 32, 40])  # P/U/PU
             elif qq == 'D' and db != p0fo_kdb:
                 pkt /= conf.raw_layer(load=RandString(random.randint(1, 10))) # XXX p0fo.fp
             elif qq == 'Q': pkt.payload.seq = pkt.payload.ack
diff --git a/scapy/volatile.py b/scapy/volatile.py
index 8b86213..0e963db 100644
--- a/scapy/volatile.py
+++ b/scapy/volatile.py
@@ -121,6 +121,8 @@
         return other - self._fix()
     def __mul__(self, other):
         return self._fix() * other
+    def __rmul__(self, other):
+        return other * self._fix()
     def __floordiv__(self, other):
         return self._fix() / other
     __div__ = __floordiv__
diff --git a/test/mock_windows.uts b/test/mock_windows.uts
index b016294..81abb6b 100644
--- a/test/mock_windows.uts
+++ b/test/mock_windows.uts
@@ -206,21 +206,23 @@
 from scapy.config import conf
 
 ps_ip = get_ip_from_name(conf.iface.name)
-ps_ip
 ps_if_list = get_windows_if_list()
-ps_if_list
 ps_read_routes = read_routes()
-ps_read_routes
 
 # Turn on VBS mode
 conf.prog.powershell = None
 
 = Test get_ip_from_name with VBS
+ps_ip
+
 assert get_ip_from_name(conf.iface.name) == ps_ip
 
 = Test get_windows_if_list with VBS
+ps_if_list
 
 def is_in_if_list(i, list):
+    if not i["mac"]:
+        return True
     for j in list:
         if j["guid"] == i["guid"] and j["name"] == i["name"]:
             return True
@@ -229,20 +231,23 @@
 vbs_if_list = get_windows_if_list()
 vbs_if_list
 _correct = True
-for i in ps_if_list:
-    if not is_in_if_list(i, vbs_if_list):
+for i in vbs_if_list:
+    if not is_in_if_list(i, ps_if_list):
         _correct = False
         break
 
 assert _correct
 
 = Test read_routes with VBS
+ps_read_routes
 
 def is_in_route_list(i, list):
+    # Ignore all empty IP or macs
+    if i[4] == '':
+        return True
+    if i[3].mac == '' or i[3].guid == '' or i[3].ip == '':
+        return True
     for j in list:
-        #Ignore all empty IP
-        if j[4] == '' or i[4] == '':
-            return True
         if j[2] == i[2] and j[4] == i[4] and j[3].guid == i[3].guid:
             return True
     return False
@@ -250,8 +255,8 @@
 vbs_read_routes = read_routes()
 vbs_if_list
 _correct = True
-for i in ps_read_routes:
-    if not is_in_route_list(i, vbs_read_routes):
+for i in vbs_read_routes:
+    if not is_in_route_list(i, ps_read_routes):
         _correct = False
         break
 
diff --git a/test/p0f.uts b/test/p0f.uts
new file mode 100644
index 0000000..04f415b
--- /dev/null
+++ b/test/p0f.uts
@@ -0,0 +1,62 @@
+% Tests for Scapy's p0f module.
+
+~ p0f
+
+
+############
+############
++ Basic p0f module tests
+
+= Module loading
+load_module('p0f')
+
+
+############
+############
++ Tests for p0f_impersonate
+
+# XXX: a lot of pieces of p0f_impersonate don't have tests yet.
+
+= Impersonate when window size must be multiple of some integer
+sig = ('%467', 64, 1, 60, 'M*,W*', '.', 'Phony Sys', '1.0')
+pkt = p0f_impersonate(IP()/TCP(), signature=sig)
+assert pkt.payload.window % 467 == 0
+
+= Handle unusual flags ("F") quirk
+sig = ('1024', 64, 0, 60, 'W*', 'F', 'Phony Sys', '1.0')
+pkt = p0f_impersonate(IP()/TCP(), signature=sig)
+assert (pkt.payload.flags & 40) in (8, 32, 40)
+
+= Use valid option values from original packet
+sig = ('S4', 64, 1, 60, 'M*,W*,T', '.', 'Phony Sys', '1.0')
+opts = [('MSS', 1400), ('WScale', 3), ('Timestamp', (97256, 0))]
+pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig)
+assert pkt.payload.options == opts
+
+= Use valid option values when multiples required
+sig = ('S4', 64, 1, 60, 'M%37,W%19', '.', 'Phony Sys', '1.0')
+opts = [('MSS', 37*15), ('WScale', 19*12)]
+pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig)
+assert pkt.payload.options == opts
+
+= Discard non-multiple option values when multiples required
+sig = ('S4', 64, 1, 60, 'M%37,W%19', '.', 'Phony Sys', '1.0')
+opts = [('MSS', 37*15 + 1), ('WScale', 19*12 + 1)]
+pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig)
+assert pkt.payload.options[0][1] % 37 == 0
+assert pkt.payload.options[1][1] % 19 == 0
+
+= Discard bad timestamp values
+sig = ('S4', 64, 1, 60, 'M*,T', '.', 'Phony Sys', '1.0')
+opts = [('Timestamp', (0, 1000))]
+pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig)
+# since option is "T" and not "T0":
+assert pkt.payload.options[1][1][0] > 0
+# since T quirk is not present:
+assert pkt.payload.options[1][1][1] == 0
+
+= Discard 2nd timestamp of 0 if "T" quirk is present
+sig = ('S4', 64, 1, 60, 'M*,T', 'T', 'Phony Sys', '1.0')
+opts = [('Timestamp', (54321, 0))]
+pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig)
+assert pkt.payload.options[1][1][1] > 0
diff --git a/test/tls/tests_tls_netaccess.uts b/test/tls/tests_tls_netaccess.uts
index 63424ca..30ea346 100644
--- a/test/tls/tests_tls_netaccess.uts
+++ b/test/tls/tests_tls_netaccess.uts
@@ -9,7 +9,7 @@
 ### DISCLAIMER: Those tests are slow ###
 
 = Load server util functions
-~ open_ssl_client
+~ open_ssl_client crypto
 
 from __future__ import print_function
 
@@ -64,28 +64,29 @@
 
 
 = Testing TLS server with TLS 1.0 and TLS_RSA_WITH_RC4_128_SHA
-~ open_ssl_client FIXME_py3
+~ open_ssl_client crypto
 
 test_tls_server("RC4-SHA", "-tls1")
 
 = Testing TLS server with TLS 1.1 and TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
-~ open_ssl_client FIXME_py3
+~ open_ssl_client crypto
 
 test_tls_server("EDH-RSA-DES-CBC3-SHA", "-tls1_1")
 
 = Testing TLS server with TLS 1.2 and TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
-~ open_ssl_client FIXME_py3
+~ open_ssl_client crypto
 
 test_tls_server("DHE-RSA-AES128-SHA256", "-tls1_2")
 
 = Testing TLS server with TLS 1.2 and TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
-~ open_ssl_client FIXME_py3
+~ open_ssl_client crypto
 
 test_tls_server("ECDHE-RSA-AES256-GCM-SHA384", "-tls1_2")
 
 + TLS client automaton tests
 
 = Load client utils functions
+~ crypto
 
 import sys, os, threading
 
@@ -112,32 +113,32 @@
         assert False
 
 = Testing TLS server and client with SSLv2 and SSL_CK_DES_192_EDE3_CBC_WITH_MD5
-~ FIXME_py3
+~ crypto
 
 perform_tls_client_test("0700c0", "0002")
 
 = Testing TLS client with SSLv3 and TLS_RSA_EXPORT_WITH_RC4_40_MD5
-~ FIXME_py3
+~ crypto
 
 perform_tls_client_test("0003", "0300")
 
 = Testing TLS client with TLS 1.0 and TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
-~ FIXME_py3
+~ crypto
 
 perform_tls_client_test("0088", "0301")
 
 = Testing TLS client with TLS 1.1 and TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
-~ FIXME_py3
+~ crypto
 
 perform_tls_client_test("c013", "0302")
 
 = Testing TLS client with TLS 1.2 and TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
-~ FIXME_py3
+~ crypto
 
 perform_tls_client_test("009e", "0303")
 
 = Testing TLS client with TLS 1.2 and TLS_ECDH_anon_WITH_RC4_128_SHA
-~ FIXME_py3
+~ crypto
 
 perform_tls_client_test("c016", "0303")