1## This file is part of Scapy
2## See http://www.secdev.org/projects/scapy for more informations
3## Copyright (C) Philippe Biondi <phil@secdev.org>
4## This program is published under a GPLv2 license
5
6"""
7General utility functions.
8"""
9
10from __future__ import absolute_import
11from __future__ import print_function
12import os, sys, socket, types
13import random, time
14import gzip, zlib
15import re, struct, array
16import subprocess
17import tempfile
18
19import warnings
20import scapy.modules.six as six
21from scapy.modules.six.moves import range
22warnings.filterwarnings("ignore","tempnam",RuntimeWarning, __name__)
23
24from scapy.config import conf
25from scapy.consts import DARWIN, WINDOWS
26from scapy.data import MTU
27from scapy.compat import *
28from scapy.error import log_runtime, log_loading, log_interactive, Scapy_Exception, warning
29from scapy.base_classes import BasePacketList
30
31###########
32## Tools ##
33###########
34
35def get_temp_file(keep=False, autoext=""):
36    """Create a temporary file and return its name. When keep is False,
37the file is deleted when scapy exits.
38
39    """
40    fname = tempfile.NamedTemporaryFile(prefix="scapy", suffix=autoext,
41                                        delete=False).name
42    if not keep:
43        conf.temp_files.append(fname)
44    return fname
45
46def sane_color(x):
47    r=""
48    for i in x:
49        j = orb(i)
50        if (j < 32) or (j >= 127):
51            r=r+conf.color_theme.not_printable(".")
52        else:
53            r=r+chr(j)
54    return r
55
56def sane(x):
57    r=""
58    for i in x:
59        j = orb(i)
60        if (j < 32) or (j >= 127):
61            r=r+"."
62        else:
63            r=r+chr(j)
64    return r
65
66@conf.commands.register
67def restart():
68    """Restarts scapy"""
69    if not conf.interactive or not os.path.isfile(sys.argv[0]):
70        raise OSError("Scapy was not started from console")
71    if WINDOWS:
72        os._exit(subprocess.call([sys.executable] + sys.argv))
73    os.execv(sys.executable, [sys.executable] + sys.argv)
74
75def lhex(x):
76    if type(x) in six.integer_types:
77        return hex(x)
78    elif isinstance(x, tuple):
79        return "(%s)" % ", ".join(map(lhex, x))
80    elif isinstance(x, list):
81        return "[%s]" % ", ".join(map(lhex, x))
82    else:
83        return x
84
85@conf.commands.register
86def hexdump(x, dump=False):
87    """ Build a tcpdump like hexadecimal view
88
89    :param x: a Packet
90    :param dump: define if the result must be printed or returned in a variable
91    :returns: a String only when dump=True
92    """
93    s = ""
94    x = raw(x)
95    l = len(x)
96    i = 0
97    while i < l:
98        s += "%04x  " % i
99        for j in range(16):
100            if i+j < l:
101                s += "%02X" % orb(x[i+j])
102            else:
103                s += "  "
104            if j%16 == 7:
105                s += ""
106        s += " "
107        s += sane_color(x[i:i+16])
108        i += 16
109        s += "\n"
110    # remove trailing \n
111    if s.endswith("\n"):
112        s = s[:-1]
113    if dump:
114        return s
115    else:
116        print(s)
117
118
119@conf.commands.register
120def linehexdump(x, onlyasc=0, onlyhex=0, dump=False):
121    """ Build an equivalent view of hexdump() on a single line
122
123    Note that setting both onlyasc and onlyhex to 1 results in a empty output
124
125    :param x: a Packet
126    :param onlyasc: 1 to display only the ascii view
127    :param onlyhex: 1 to display only the hexadecimal view
128    :param dump: print the view if False
129    :returns: a String only when dump=True
130    """
131    s = ""
132    x = raw(x)
133    l = len(x)
134    if not onlyasc:
135        for i in range(l):
136            s += "%02X" % orb(x[i])
137        if not onlyhex:  # separate asc & hex if both are displayed
138            s += " "
139    if not onlyhex:
140        s += sane_color(x)
141    if dump:
142        return s
143    else:
144        print(s)
145
146@conf.commands.register
147def chexdump(x, dump=False):
148    """ Build a per byte hexadecimal representation
149
150    Example:
151        >>> chexdump(IP())
152        0x45, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x7c, 0xe7, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01
153
154    :param x: a Packet
155    :param dump: print the view if False
156    :returns: a String only if dump=True
157    """
158    x = raw(x)
159    s = ", ".join("%#04x" % orb(x) for x in x)
160    if dump:
161        return s
162    else:
163        print(s)
164
165@conf.commands.register
166def hexstr(x, onlyasc=0, onlyhex=0):
167    s = []
168    if not onlyasc:
169        s.append(" ".join("%02x" % orb(b) for b in x))
170    if not onlyhex:
171        s.append(sane(x))
172    return "  ".join(s)
173
174def repr_hex(s):
175    """ Convert provided bitstring to a simple string of hex digits """
176    return "".join("%02x" % orb(x) for x in s)
177
178@conf.commands.register
179def hexdiff(x,y):
180    """Show differences between 2 binary strings"""
181    x=raw(x)[::-1]
182    y=raw(y)[::-1]
183    SUBST=1
184    INSERT=1
185    d = {(-1, -1): (0, (-1, -1))}
186    for j in range(len(y)):
187        d[-1,j] = d[-1,j-1][0]+INSERT, (-1,j-1)
188    for i in range(len(x)):
189        d[i,-1] = d[i-1,-1][0]+INSERT, (i-1,-1)
190
191    for j in range(len(y)):
192        for i in range(len(x)):
193            d[i,j] = min( ( d[i-1,j-1][0]+SUBST*(x[i] != y[j]), (i-1,j-1) ),
194                          ( d[i-1,j][0]+INSERT, (i-1,j) ),
195                          ( d[i,j-1][0]+INSERT, (i,j-1) ) )
196
197
198    backtrackx = []
199    backtracky = []
200    i=len(x)-1
201    j=len(y)-1
202    while not (i == j == -1):
203        i2,j2 = d[i,j][1]
204        backtrackx.append(x[i2+1:i+1])
205        backtracky.append(y[j2+1:j+1])
206        i,j = i2,j2
207
208
209
210    x = y = i = 0
211    colorize = { 0: lambda x:x,
212                -1: conf.color_theme.left,
213                 1: conf.color_theme.right }
214
215    dox=1
216    doy=0
217    l = len(backtrackx)
218    while i < l:
219        separate=0
220        linex = backtrackx[i:i+16]
221        liney = backtracky[i:i+16]
222        xx = sum(len(k) for k in linex)
223        yy = sum(len(k) for k in liney)
224        if dox and not xx:
225            dox = 0
226            doy = 1
227        if dox and linex == liney:
228            doy=1
229
230        if dox:
231            xd = y
232            j = 0
233            while not linex[j]:
234                j += 1
235                xd -= 1
236            print(colorize[doy-dox]("%04x" % xd), end=' ')
237            x += xx
238            line=linex
239        else:
240            print("    ", end=' ')
241        if doy:
242            yd = y
243            j = 0
244            while not liney[j]:
245                j += 1
246                yd -= 1
247            print(colorize[doy-dox]("%04x" % yd), end=' ')
248            y += yy
249            line=liney
250        else:
251            print("    ", end=' ')
252
253        print(" ", end=' ')
254
255        cl = ""
256        for j in range(16):
257            if i+j < l:
258                if line[j]:
259                    col = colorize[(linex[j]!=liney[j])*(doy-dox)]
260                    print(col("%02X" % orb(line[j])), end=' ')
261                    if linex[j]==liney[j]:
262                        cl += sane_color(line[j])
263                    else:
264                        cl += col(sane(line[j]))
265                else:
266                    print("  ", end=' ')
267                    cl += " "
268            else:
269                print("  ", end=' ')
270            if j == 7:
271                print("", end=' ')
272
273
274        print(" ",cl)
275
276        if doy or not yy:
277            doy=0
278            dox=1
279            i += 16
280        else:
281            if yy:
282                dox=0
283                doy=1
284            else:
285                i += 16
286
287if struct.pack("H",1) == b"\x00\x01": # big endian
288    def checksum(pkt):
289        if len(pkt) % 2 == 1:
290            pkt += b"\0"
291        s = sum(array.array("H", pkt))
292        s = (s >> 16) + (s & 0xffff)
293        s += s >> 16
294        s = ~s
295        return s & 0xffff
296else:
297    def checksum(pkt):
298        if len(pkt) % 2 == 1:
299            pkt += b"\0"
300        s = sum(array.array("H", pkt))
301        s = (s >> 16) + (s & 0xffff)
302        s += s >> 16
303        s = ~s
304        return (((s>>8)&0xff)|s<<8) & 0xffff
305
306
307def _fletcher16(charbuf):
308    # This is based on the GPLed C implementation in Zebra <http://www.zebra.org/>
309    c0 = c1 = 0
310    for char in charbuf:
311        c0 += orb(char)
312        c1 += c0
313
314    c0 %= 255
315    c1 %= 255
316    return (c0,c1)
317
318@conf.commands.register
319def fletcher16_checksum(binbuf):
320    """ Calculates Fletcher-16 checksum of the given buffer.
321
322        Note:
323        If the buffer contains the two checkbytes derived from the Fletcher-16 checksum
324        the result of this function has to be 0. Otherwise the buffer has been corrupted.
325    """
326    (c0,c1)= _fletcher16(binbuf)
327    return (c1 << 8) | c0
328
329
330@conf.commands.register
331def fletcher16_checkbytes(binbuf, offset):
332    """ Calculates the Fletcher-16 checkbytes returned as 2 byte binary-string.
333
334        Including the bytes into the buffer (at the position marked by offset) the
335        global Fletcher-16 checksum of the buffer will be 0. Thus it is easy to verify
336        the integrity of the buffer on the receiver side.
337
338        For details on the algorithm, see RFC 2328 chapter 12.1.7 and RFC 905 Annex B.
339    """
340
341    # This is based on the GPLed C implementation in Zebra <http://www.zebra.org/>
342    if len(binbuf) < offset:
343        raise Exception("Packet too short for checkbytes %d" % len(binbuf))
344
345    binbuf = binbuf[:offset] + b"\x00\x00" + binbuf[offset + 2:]
346    (c0,c1)= _fletcher16(binbuf)
347
348    x = ((len(binbuf) - offset - 1) * c0 - c1) % 255
349
350    if (x <= 0):
351        x += 255
352
353    y = 510 - c0 - x
354
355    if (y > 255):
356        y -= 255
357    return chb(x) + chb(y)
358
359
360def mac2str(mac):
361    return b"".join(chb(int(x, 16)) for x in mac.split(':'))
362
363def str2mac(s):
364    if isinstance(s, str):
365        return ("%02x:"*6)[:-1] % tuple(map(ord, s))
366    return ("%02x:"*6)[:-1] % tuple(s)
367
368def randstring(l):
369    """
370    Returns a random string of length l (l >= 0)
371    """
372    return b"".join(struct.pack('B', random.randint(0, 255)) for _ in range(l))
373
374def zerofree_randstring(l):
375    """
376    Returns a random string of length l (l >= 0) without zero in it.
377    """
378    return b"".join(struct.pack('B', random.randint(1, 255)) for _ in range(l))
379
380def strxor(s1, s2):
381    """
382    Returns the binary XOR of the 2 provided strings s1 and s2. s1 and s2
383    must be of same length.
384    """
385    return b"".join(map(lambda x,y:chb(orb(x)^orb(y)), s1, s2))
386
387def strand(s1, s2):
388    """
389    Returns the binary AND of the 2 provided strings s1 and s2. s1 and s2
390    must be of same length.
391    """
392    return b"".join(map(lambda x,y:chb(orb(x)&orb(y)), s1, s2))
393
394
395# Workaround bug 643005 : https://sourceforge.net/tracker/?func=detail&atid=105470&aid=643005&group_id=5470
396try:
397    socket.inet_aton("255.255.255.255")
398except socket.error:
399    def inet_aton(x):
400        if x == "255.255.255.255":
401            return b"\xff"*4
402        else:
403            return socket.inet_aton(x)
404else:
405    inet_aton = socket.inet_aton
406
407inet_ntoa = socket.inet_ntoa
408from scapy.pton_ntop import *
409
410
411def atol(x):
412    try:
413        ip = inet_aton(x)
414    except socket.error:
415        ip = inet_aton(socket.gethostbyname(x))
416    return struct.unpack("!I", ip)[0]
417def ltoa(x):
418    return inet_ntoa(struct.pack("!I", x&0xffffffff))
419
420def itom(x):
421    return (0xffffffff00000000>>x)&0xffffffff
422
423class ContextManagerSubprocess(object):
424    """
425    Context manager that eases checking for unknown command.
426
427    Example:
428    >>> with ContextManagerSubprocess("my custom message"):
429    >>>     subprocess.Popen(["unknown_command"])
430
431    """
432    def __init__(self, name, prog):
433        self.name = name
434        self.prog = prog
435
436    def __enter__(self):
437        pass
438
439    def __exit__(self, exc_type, exc_value, traceback):
440        if isinstance(exc_value, (OSError, TypeError)):
441            msg = "%s: executing %r failed" % (self.name, self.prog) if self.prog else "Could not execute %s, is it installed ?" % self.name
442            if not conf.interactive:
443                raise OSError(msg)
444            else:
445                log_runtime.error(msg, exc_info=True)
446                return True  # Suppress the exception
447
448class ContextManagerCaptureOutput(object):
449    """
450    Context manager that intercept the console's output.
451
452    Example:
453    >>> with ContextManagerCaptureOutput() as cmco:
454    ...     print("hey")
455    ...     assert cmco.get_output() == "hey"
456    """
457    def __init__(self):
458        self.result_export_object = ""
459        try:
460            import mock
461        except:
462            raise ImportError("The mock module needs to be installed !")
463    def __enter__(self):
464        import mock
465        def write(s, decorator=self):
466            decorator.result_export_object += s
467        mock_stdout = mock.Mock()
468        mock_stdout.write = write
469        self.bck_stdout = sys.stdout
470        sys.stdout = mock_stdout
471        return self
472    def __exit__(self, *exc):
473        sys.stdout = self.bck_stdout
474        return False
475    def get_output(self, eval_bytes=False):
476        if self.result_export_object.startswith("b'") and eval_bytes:
477            return plain_str(eval(self.result_export_object))
478        return self.result_export_object
479
480def do_graph(graph,prog=None,format=None,target=None,type=None,string=None,options=None):
481    """do_graph(graph, prog=conf.prog.dot, format="svg",
482         target="| conf.prog.display", options=None, [string=1]):
483    string: if not None, simply return the graph string
484    graph: GraphViz graph description
485    format: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option
486    target: filename or redirect. Defaults pipe to Imagemagick's display program
487    prog: which graphviz program to use
488    options: options to be passed to prog"""
489
490    if format is None:
491        if WINDOWS:
492            format = "png" # use common format to make sure a viewer is installed
493        else:
494            format = "svg"
495    if string:
496        return graph
497    if type is not None:
498        format=type
499    if prog is None:
500        prog = conf.prog.dot
501    start_viewer=False
502    if target is None:
503        if WINDOWS:
504            target = get_temp_file(autoext="."+format)
505            start_viewer = True
506        else:
507            with ContextManagerSubprocess("do_graph()", conf.prog.display):
508                target = subprocess.Popen([conf.prog.display],
509                                          stdin=subprocess.PIPE).stdin
510    if format is not None:
511        format = "-T%s" % format
512    if isinstance(target, str):
513        if target.startswith('|'):
514            target = subprocess.Popen(target[1:].lstrip(), shell=True,
515                                      stdin=subprocess.PIPE).stdin
516        elif target.startswith('>'):
517            target = open(target[1:].lstrip(), "wb")
518        else:
519            target = open(os.path.abspath(target), "wb")
520    proc = subprocess.Popen("\"%s\" %s %s" % (prog, options or "", format or ""),
521                            shell=True, stdin=subprocess.PIPE, stdout=target)
522    proc.stdin.write(raw(graph))
523    try:
524        target.close()
525    except:
526        pass
527    if start_viewer:
528        # Workaround for file not found error: We wait until tempfile is written.
529        waiting_start = time.time()
530        while not os.path.exists(target.name):
531            time.sleep(0.1)
532            if time.time() - waiting_start > 3:
533                warning("Temporary file '%s' could not be written. Graphic will not be displayed.", tempfile)
534                break
535        else:
536            if conf.prog.display == conf.prog._default:
537                os.startfile(target.name)
538            else:
539                with ContextManagerSubprocess("do_graph()", conf.prog.display):
540                    subprocess.Popen([conf.prog.display, target.name])
541
542_TEX_TR = {
543    "{":"{\\tt\\char123}",
544    "}":"{\\tt\\char125}",
545    "\\":"{\\tt\\char92}",
546    "^":"\\^{}",
547    "$":"\\$",
548    "#":"\\#",
549    "~":"\\~",
550    "_":"\\_",
551    "&":"\\&",
552    "%":"\\%",
553    "|":"{\\tt\\char124}",
554    "~":"{\\tt\\char126}",
555    "<":"{\\tt\\char60}",
556    ">":"{\\tt\\char62}",
557    }
558
559def tex_escape(x):
560    s = ""
561    for c in x:
562        s += _TEX_TR.get(c,c)
563    return s
564
565def colgen(*lstcol,**kargs):
566    """Returns a generator that mixes provided quantities forever
567    trans: a function to convert the three arguments into a color. lambda x,y,z:(x,y,z) by default"""
568    if len(lstcol) < 2:
569        lstcol *= 2
570    trans = kargs.get("trans", lambda x,y,z: (x,y,z))
571    while True:
572        for i in range(len(lstcol)):
573            for j in range(len(lstcol)):
574                for k in range(len(lstcol)):
575                    if i != j or j != k or k != i:
576                        yield trans(lstcol[(i+j)%len(lstcol)],lstcol[(j+k)%len(lstcol)],lstcol[(k+i)%len(lstcol)])
577
578def incremental_label(label="tag%05i", start=0):
579    while True:
580        yield label % start
581        start += 1
582
583def binrepr(val):
584    return bin(val)[2:]
585
586def long_converter(s):
587    return int(s.replace('\n', '').replace(' ', ''), 16)
588
589#########################
590#### Enum management ####
591#########################
592
593class EnumElement:
594    _value=None
595    def __init__(self, key, value):
596        self._key = key
597        self._value = value
598    def __repr__(self):
599        return "<%s %s[%r]>" % (self.__dict__.get("_name", self.__class__.__name__), self._key, self._value)
600    def __getattr__(self, attr):
601        return getattr(self._value, attr)
602    def __str__(self):
603        return self._key
604    def __bytes__(self):
605        return raw(self.__str__())
606    def __hash__(self):
607        return self._value
608    def __int__(self):
609        return int(self._value)
610    def __eq__(self, other):
611        return self._value == int(other)
612    def __neq__(self, other):
613        return not self.__eq__(other)
614
615
616class Enum_metaclass(type):
617    element_class = EnumElement
618    def __new__(cls, name, bases, dct):
619        rdict={}
620        for k,v in six.iteritems(dct):
621            if isinstance(v, int):
622                v = cls.element_class(k,v)
623                dct[k] = v
624                rdict[v] = k
625        dct["__rdict__"] = rdict
626        return super(Enum_metaclass, cls).__new__(cls, name, bases, dct)
627    def __getitem__(self, attr):
628        return self.__rdict__[attr]
629    def __contains__(self, val):
630        return val in self.__rdict__
631    def get(self, attr, val=None):
632        return self.__rdict__.get(attr, val)
633    def __repr__(self):
634        return "<%s>" % self.__dict__.get("name", self.__name__)
635
636
637
638###################
639## Object saving ##
640###################
641
642
643def export_object(obj):
644    print(bytes_base64(gzip.zlib.compress(six.moves.cPickle.dumps(obj, 2), 9)))
645
646def import_object(obj=None):
647    if obj is None:
648        obj = sys.stdin.read()
649    return six.moves.cPickle.loads(gzip.zlib.decompress(base64_bytes(obj.strip())))
650
651
652def save_object(fname, obj):
653    """Pickle a Python object"""
654
655    fd = gzip.open(fname, "wb")
656    six.moves.cPickle.dump(obj, fd)
657    fd.close()
658
659def load_object(fname):
660    """unpickle a Python object"""
661    return six.moves.cPickle.load(gzip.open(fname,"rb"))
662
663@conf.commands.register
664def corrupt_bytes(s, p=0.01, n=None):
665    """Corrupt a given percentage or number of bytes from a string"""
666    s = array.array("B",raw(s))
667    l = len(s)
668    if n is None:
669        n = max(1,int(l*p))
670    for i in random.sample(range(l), n):
671        s[i] = (s[i]+random.randint(1,255))%256
672    return s.tostring()
673
674@conf.commands.register
675def corrupt_bits(s, p=0.01, n=None):
676    """Flip a given percentage or number of bits from a string"""
677    s = array.array("B",raw(s))
678    l = len(s)*8
679    if n is None:
680        n = max(1,int(l*p))
681    for i in random.sample(range(l), n):
682        s[i // 8] ^= 1 << (i % 8)
683    return s.tostring()
684
685
686
687
688#############################
689## pcap capture file stuff ##
690#############################
691
692@conf.commands.register
693def wrpcap(filename, pkt, *args, **kargs):
694    """Write a list of packets to a pcap file
695
696filename: the name of the file to write packets to, or an open,
697          writable file-like object. The file descriptor will be
698          closed at the end of the call, so do not use an object you
699          do not want to close (e.g., running wrpcap(sys.stdout, [])
700          in interactive mode will crash Scapy).
701gz: set to 1 to save a gzipped capture
702linktype: force linktype value
703endianness: "<" or ">", force endianness
704sync: do not bufferize writes to the capture file
705
706    """
707    with PcapWriter(filename, *args, **kargs) as fdesc:
708        fdesc.write(pkt)
709
710@conf.commands.register
711def rdpcap(filename, count=-1):
712    """Read a pcap or pcapng file and return a packet list
713
714count: read only <count> packets
715
716    """
717    with PcapReader(filename) as fdesc:
718        return fdesc.read_all(count=count)
719
720
721class PcapReader_metaclass(type):
722    """Metaclass for (Raw)Pcap(Ng)Readers"""
723
724    def __new__(cls, name, bases, dct):
725        """The `alternative` class attribute is declared in the PcapNg
726        variant, and set here to the Pcap variant.
727
728        """
729        newcls = super(PcapReader_metaclass, cls).__new__(cls, name, bases, dct)
730        if 'alternative' in dct:
731            dct['alternative'].alternative = newcls
732        return newcls
733
734    def __call__(cls, filename):
735        """Creates a cls instance, use the `alternative` if that
736        fails.
737
738        """
739        i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__)
740        filename, fdesc, magic = cls.open(filename)
741        try:
742            i.__init__(filename, fdesc, magic)
743        except Scapy_Exception:
744            if "alternative" in cls.__dict__:
745                cls = cls.__dict__["alternative"]
746                i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__)
747                try:
748                    i.__init__(filename, fdesc, magic)
749                except Scapy_Exception:
750                    raise
751                    try:
752                        i.f.seek(-4, 1)
753                    except:
754                        pass
755                    raise Scapy_Exception("Not a supported capture file")
756
757        return i
758
759    @staticmethod
760    def open(filename):
761        """Open (if necessary) filename, and read the magic."""
762        if isinstance(filename, six.string_types):
763            try:
764                fdesc = gzip.open(filename,"rb")
765                magic = fdesc.read(4)
766            except IOError:
767                fdesc = open(filename, "rb")
768                magic = fdesc.read(4)
769        else:
770            fdesc = filename
771            filename = (fdesc.name
772                        if hasattr(fdesc, "name") else
773                        "No name")
774            magic = fdesc.read(4)
775        return filename, fdesc, magic
776
777
778class RawPcapReader(six.with_metaclass(PcapReader_metaclass)):
779    """A stateful pcap reader. Each packet is returned as a string"""
780    def __init__(self, filename, fdesc, magic):
781        self.filename = filename
782        self.f = fdesc
783        if magic == b"\xa1\xb2\xc3\xd4": # big endian
784            self.endian = ">"
785            self.nano = False
786        elif magic == b"\xd4\xc3\xb2\xa1": # little endian
787            self.endian = "<"
788            self.nano = False
789        elif magic == b"\xa1\xb2\x3c\x4d":  # big endian, nanosecond-precision
790            self.endian = ">"
791            self.nano = True
792        elif magic == b"\x4d\x3c\xb2\xa1":  # little endian, nanosecond-precision
793            self.endian = "<"
794            self.nano = True
795        else:
796            raise Scapy_Exception(
797                "Not a pcap capture file (bad magic: %r)" % magic
798            )
799        hdr = self.f.read(20)
800        if len(hdr)<20:
801            raise Scapy_Exception("Invalid pcap file (too short)")
802        vermaj, vermin, tz, sig, snaplen, linktype = struct.unpack(
803            self.endian + "HHIIII", hdr
804        )
805        self.linktype = linktype
806
807    def __iter__(self):
808        return self
809
810    def next(self):
811        """implement the iterator protocol on a set of packets in a pcap file"""
812        pkt = self.read_packet()
813        if pkt == None:
814            raise StopIteration
815        return pkt
816    __next__ = next
817
818
819    def read_packet(self, size=MTU):
820        """return a single packet read from the file
821
822        returns None when no more packets are available
823        """
824        hdr = self.f.read(16)
825        if len(hdr) < 16:
826            return None
827        sec,usec,caplen,wirelen = struct.unpack(self.endian+"IIII", hdr)
828        s = self.f.read(caplen)[:size]
829        return s,(sec,usec,wirelen) # caplen = len(s)
830
831
832    def dispatch(self, callback):
833        """call the specified callback routine for each packet read
834
835        This is just a convenience function for the main loop
836        that allows for easy launching of packet processing in a
837        thread.
838        """
839        for p in self:
840            callback(p)
841
842    def read_all(self,count=-1):
843        """return a list of all packets in the pcap file
844        """
845        res=[]
846        while count != 0:
847            count -= 1
848            p = self.read_packet()
849            if p is None:
850                break
851            res.append(p)
852        return res
853
854    def recv(self, size=MTU):
855        """ Emulate a socket
856        """
857        return self.read_packet(size=size)[0]
858
859    def fileno(self):
860        return self.f.fileno()
861
862    def close(self):
863        return self.f.close()
864
865    def __enter__(self):
866        return self
867
868    def __exit__(self, exc_type, exc_value, tracback):
869        self.close()
870
871
872class PcapReader(RawPcapReader):
873    def __init__(self, filename, fdesc, magic):
874        RawPcapReader.__init__(self, filename, fdesc, magic)
875        try:
876            self.LLcls = conf.l2types[self.linktype]
877        except KeyError:
878            warning("PcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype,self.linktype))
879            self.LLcls = conf.raw_layer
880    def read_packet(self, size=MTU):
881        rp = RawPcapReader.read_packet(self, size=size)
882        if rp is None:
883            return None
884        s,(sec,usec,wirelen) = rp
885
886        try:
887            p = self.LLcls(s)
888        except KeyboardInterrupt:
889            raise
890        except:
891            if conf.debug_dissector:
892                raise
893            p = conf.raw_layer(s)
894        p.time = sec + (0.000000001 if self.nano else 0.000001) * usec
895        return p
896    def read_all(self,count=-1):
897        res = RawPcapReader.read_all(self, count)
898        from scapy import plist
899        return plist.PacketList(res,name = os.path.basename(self.filename))
900    def recv(self, size=MTU):
901        return self.read_packet(size=size)
902
903
904class RawPcapNgReader(RawPcapReader):
905    """A stateful pcapng reader. Each packet is returned as a
906    string.
907
908    """
909
910    alternative = RawPcapReader
911
912    def __init__(self, filename, fdesc, magic):
913        self.filename = filename
914        self.f = fdesc
915        # A list of (linktype, snaplen, tsresol); will be populated by IDBs.
916        self.interfaces = []
917        self.blocktypes = {
918            1: self.read_block_idb,
919            2: self.read_block_pkt,
920            3: self.read_block_spb,
921            6: self.read_block_epb,
922        }
923        if magic != b"\x0a\x0d\x0d\x0a": # PcapNg:
924            raise Scapy_Exception(
925                "Not a pcapng capture file (bad magic: %r)" % magic
926            )
927        # see https://github.com/pcapng/pcapng
928        blocklen, magic = self.f.read(4), self.f.read(4)
929        if magic == b"\x1a\x2b\x3c\x4d":
930            self.endian = ">"
931        elif magic == b"\x4d\x3c\x2b\x1a":
932            self.endian = "<"
933        else:
934            raise Scapy_Exception("Not a pcapng capture file (bad magic)")
935        try:
936            self.f.seek(0)
937        except:
938            pass
939
940    def read_packet(self, size=MTU):
941        """Read blocks until it reaches either EOF or a packet, and
942        returns None or (packet, (linktype, sec, usec, wirelen)),
943        where packet is a string.
944
945        """
946        while True:
947            try:
948                blocktype, blocklen = struct.unpack(self.endian + "2I",
949                                                    self.f.read(8))
950            except struct.error:
951                return None
952            block = self.f.read(blocklen - 12)
953            if blocklen % 4:
954                pad = self.f.read(4 - (blocklen % 4))
955                warning("PcapNg: bad blocklen %d (MUST be a multiple of 4. "
956                        "Ignored padding %r" % (blocklen, pad))
957            try:
958                if (blocklen,) != struct.unpack(self.endian + 'I',
959                                                self.f.read(4)):
960                    warning("PcapNg: Invalid pcapng block (bad blocklen)")
961            except struct.error:
962                return None
963            res = self.blocktypes.get(blocktype,
964                                      lambda block, size: None)(block, size)
965            if res is not None:
966                return res
967
968    def read_block_idb(self, block, _):
969        """Interface Description Block"""
970        options = block[16:]
971        tsresol = 1000000
972        while len(options) >= 4:
973            code, length = struct.unpack(self.endian + "HH", options[:4])
974            # PCAP Next Generation (pcapng) Capture File Format
975            # 4.2. - Interface Description Block
976            # http://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.4.2
977            if code == 9 and length == 1 and len(options) >= 5:
978                tsresol = orb(options[4])
979                tsresol = (2 if tsresol & 128 else 10) ** (tsresol & 127)
980            if code == 0:
981                if length != 0:
982                    warning("PcapNg: invalid option length %d for end-of-option" % length)
983                break
984            if length % 4:
985                length += (4 - (length % 4))
986            options = options[4 + length:]
987        self.interfaces.append(struct.unpack(self.endian + "HxxI", block[:8])
988                               + (tsresol,))
989
990    def read_block_epb(self, block, size):
991        """Enhanced Packet Block"""
992        intid, tshigh, tslow, caplen, wirelen = struct.unpack(
993            self.endian + "5I",
994            block[:20],
995        )
996        return (block[20:20 + caplen][:size],
997                (self.interfaces[intid][0], self.interfaces[intid][2],
998                 tshigh, tslow, wirelen))
999
1000    def read_block_spb(self, block, size):
1001        """Simple Packet Block"""
1002        # "it MUST be assumed that all the Simple Packet Blocks have
1003        # been captured on the interface previously specified in the
1004        # first Interface Description Block."
1005        intid = 0
1006        wirelen, = struct.unpack(self.endian + "I", block[:4])
1007        caplen = min(wirelen, self.interfaces[intid][1])
1008        return (block[4:4 + caplen][:size],
1009                (self.interfaces[intid][0], self.interfaces[intid][2],
1010                 None, None, wirelen))
1011
1012    def read_block_pkt(self, block, size):
1013        """(Obsolete) Packet Block"""
1014        intid, drops, tshigh, tslow, caplen, wirelen = struct.unpack(
1015            self.endian + "HH4I",
1016            block[:20],
1017        )
1018        return (block[20:20 + caplen][:size],
1019                (self.interfaces[intid][0], self.interfaces[intid][2],
1020                 tshigh, tslow, wirelen))
1021
1022
1023class PcapNgReader(RawPcapNgReader):
1024
1025    alternative = PcapReader
1026
1027    def __init__(self, filename, fdesc, magic):
1028        RawPcapNgReader.__init__(self, filename, fdesc, magic)
1029
1030    def read_packet(self, size=MTU):
1031        rp = RawPcapNgReader.read_packet(self, size=size)
1032        if rp is None:
1033            return None
1034        s, (linktype, tsresol, tshigh, tslow, wirelen) = rp
1035        try:
1036            p = conf.l2types[linktype](s)
1037        except KeyboardInterrupt:
1038            raise
1039        except:
1040            if conf.debug_dissector:
1041                raise
1042            p = conf.raw_layer(s)
1043        if tshigh is not None:
1044            p.time = float((tshigh << 32) + tslow) / tsresol
1045        return p
1046    def read_all(self,count=-1):
1047        res = RawPcapNgReader.read_all(self, count)
1048        from scapy import plist
1049        return plist.PacketList(res, name=os.path.basename(self.filename))
1050    def recv(self, size=MTU):
1051        return self.read_packet()
1052
1053
1054class RawPcapWriter:
1055    """A stream PCAP writer with more control than wrpcap()"""
1056    def __init__(self, filename, linktype=None, gz=False, endianness="",
1057                 append=False, sync=False, nano=False):
1058        """
1059filename:   the name of the file to write packets to, or an open,
1060            writable file-like object.
1061linktype:   force linktype to a given value. If None, linktype is taken
1062            from the first writer packet
1063gz:         compress the capture on the fly
1064endianness: force an endianness (little:"<", big:">"). Default is native
1065append:     append packets to the capture file instead of truncating it
1066sync:       do not bufferize writes to the capture file
1067nano:       use nanosecond-precision (requires libpcap >= 1.5.0)
1068
1069        """
1070
1071        self.linktype = linktype
1072        self.header_present = 0
1073        self.append = append
1074        self.gz = gz
1075        self.endian = endianness
1076        self.sync = sync
1077        self.nano = nano
1078        bufsz=4096
1079        if sync:
1080            bufsz = 0
1081
1082        if isinstance(filename, six.string_types):
1083            self.filename = filename
1084            self.f = [open,gzip.open][gz](filename,append and "ab" or "wb", gz and 9 or bufsz)
1085        else:
1086            self.f = filename
1087            self.filename = (filename.name
1088                             if hasattr(filename, "name") else
1089                             "No name")
1090
1091    def fileno(self):
1092        return self.f.fileno()
1093
1094    def _write_header(self, pkt):
1095        self.header_present=1
1096
1097        if self.append:
1098            # Even if prone to race conditions, this seems to be
1099            # safest way to tell whether the header is already present
1100            # because we have to handle compressed streams that
1101            # are not as flexible as basic files
1102            g = [open,gzip.open][self.gz](self.filename,"rb")
1103            if g.read(16):
1104                return
1105
1106        self.f.write(struct.pack(self.endian+"IHHIIII", 0xa1b23c4d if self.nano else 0xa1b2c3d4,
1107                                 2, 4, 0, 0, MTU, self.linktype))
1108        self.f.flush()
1109
1110
1111    def write(self, pkt):
1112        """accepts either a single packet or a list of packets to be
1113        written to the dumpfile
1114
1115        """
1116        if isinstance(pkt, str):
1117            if not self.header_present:
1118                self._write_header(pkt)
1119            self._write_packet(pkt)
1120        else:
1121            pkt = pkt.__iter__()
1122            if not self.header_present:
1123                try:
1124                    p = next(pkt)
1125                except StopIteration:
1126                    self._write_header(b"")
1127                    return
1128                self._write_header(p)
1129                self._write_packet(p)
1130            for p in pkt:
1131                self._write_packet(p)
1132
1133    def _write_packet(self, packet, sec=None, usec=None, caplen=None, wirelen=None):
1134        """writes a single packet to the pcap file
1135        """
1136        if isinstance(packet, tuple):
1137            for pkt in packet:
1138                self._write_packet(pkt, sec=sec, usec=usec, caplen=caplen,
1139                                   wirelen=wirelen)
1140            return
1141        if caplen is None:
1142            caplen = len(packet)
1143        if wirelen is None:
1144            wirelen = caplen
1145        if sec is None or usec is None:
1146            t=time.time()
1147            it = int(t)
1148            if sec is None:
1149                sec = it
1150            if usec is None:
1151                usec = int(round((t - it) * (1000000000 if self.nano else 1000000)))
1152        self.f.write(struct.pack(self.endian+"IIII", sec, usec, caplen, wirelen))
1153        self.f.write(packet)
1154        if self.sync:
1155            self.f.flush()
1156
1157    def flush(self):
1158        return self.f.flush()
1159
1160    def close(self):
1161        return self.f.close()
1162
1163    def __enter__(self):
1164        return self
1165    def __exit__(self, exc_type, exc_value, tracback):
1166        self.flush()
1167        self.close()
1168
1169
1170class PcapWriter(RawPcapWriter):
1171    """A stream PCAP writer with more control than wrpcap()"""
1172    def _write_header(self, pkt):
1173        if isinstance(pkt, tuple) and pkt:
1174            pkt = pkt[0]
1175        if self.linktype == None:
1176            try:
1177                self.linktype = conf.l2types[pkt.__class__]
1178            except KeyError:
1179                warning("PcapWriter: unknown LL type for %s. Using type 1 (Ethernet)", pkt.__class__.__name__)
1180                self.linktype = 1
1181        RawPcapWriter._write_header(self, pkt)
1182
1183    def _write_packet(self, packet):
1184        if isinstance(packet, tuple):
1185            for pkt in packet:
1186                self._write_packet(pkt)
1187            return
1188        sec = int(packet.time)
1189        usec = int(round((packet.time - sec) * (1000000000 if self.nano else 1000000)))
1190        s = raw(packet)
1191        caplen = len(s)
1192        RawPcapWriter._write_packet(self, s, sec, usec, caplen, caplen)
1193
1194
1195re_extract_hexcap = re.compile("^((0x)?[0-9a-fA-F]{2,}[ :\t]{,3}|) *(([0-9a-fA-F]{2} {,2}){,16})")
1196
1197@conf.commands.register
1198def import_hexcap():
1199    p = ""
1200    try:
1201        while True:
1202            l = input().strip()
1203            try:
1204                p += re_extract_hexcap.match(l).groups()[2]
1205            except:
1206                warning("Parsing error during hexcap")
1207                continue
1208    except EOFError:
1209        pass
1210
1211    p = p.replace(" ","")
1212    return p.decode("hex")
1213
1214
1215
1216@conf.commands.register
1217def wireshark(pktlist):
1218    """Run wireshark on a list of packets"""
1219    f = get_temp_file()
1220    wrpcap(f, pktlist)
1221    with ContextManagerSubprocess("wireshark()", conf.prog.wireshark):
1222        subprocess.Popen([conf.prog.wireshark, "-r", f])
1223
1224@conf.commands.register
1225def tcpdump(pktlist, dump=False, getfd=False, args=None,
1226            prog=None, getproc=False, quiet=False):
1227    """Run tcpdump or tshark on a list of packets
1228
1229pktlist: a Packet instance, a PacketList instance or a list of Packet
1230         instances. Can also be a filename (as a string) or an open
1231         file-like object that must be a file format readable by
1232         tshark (Pcap, PcapNg, etc.)
1233
1234dump:    when set to True, returns a string instead of displaying it.
1235getfd:   when set to True, returns a file-like object to read data
1236         from tcpdump or tshark from.
1237getproc: when set to True, the subprocess.Popen object is returned
1238args:    arguments (as a list) to pass to tshark (example for tshark:
1239         args=["-T", "json"]). Defaults to ["-n"].
1240prog:    program to use (defaults to tcpdump, will work with tshark)
1241quiet:   when set to True, the process stderr is discarded
1242
1243Examples:
1244
1245>>> tcpdump([IP()/TCP(), IP()/UDP()])
1246reading from file -, link-type RAW (Raw IP)
124716:46:00.474515 IP 127.0.0.1.20 > 127.0.0.1.80: Flags [S], seq 0, win 8192, length 0
124816:46:00.475019 IP 127.0.0.1.53 > 127.0.0.1.53: [|domain]
1249
1250>>> tcpdump([IP()/TCP(), IP()/UDP()], prog=conf.prog.tshark)
1251  1   0.000000    127.0.0.1 -> 127.0.0.1    TCP 40 20->80 [SYN] Seq=0 Win=8192 Len=0
1252  2   0.000459    127.0.0.1 -> 127.0.0.1    UDP 28 53->53 Len=0
1253
1254To get a JSON representation of a tshark-parsed PacketList(), one can:
1255>>> import json, pprint
1256>>> json_data = json.load(tcpdump(IP(src="217.25.178.5", dst="45.33.32.156"),
1257...                               prog=conf.prog.tshark, args=["-T", "json"],
1258...                               getfd=True))
1259>>> pprint.pprint(json_data)
1260[{u'_index': u'packets-2016-12-23',
1261  u'_score': None,
1262  u'_source': {u'layers': {u'frame': {u'frame.cap_len': u'20',
1263                                      u'frame.encap_type': u'7',
1264[...]
1265                                      u'frame.time_relative': u'0.000000000'},
1266                           u'ip': {u'ip.addr': u'45.33.32.156',
1267                                   u'ip.checksum': u'0x0000a20d',
1268[...]
1269                                   u'ip.ttl': u'64',
1270                                   u'ip.version': u'4'},
1271                           u'raw': u'Raw packet data'}},
1272  u'_type': u'pcap_file'}]
1273>>> json_data[0]['_source']['layers']['ip']['ip.ttl']
1274u'64'
1275
1276    """
1277    getfd = getfd or getproc
1278    if prog is None:
1279        prog = [conf.prog.tcpdump]
1280    elif isinstance(prog, six.string_types):
1281        prog = [prog]
1282    _prog_name = "windump()" if WINDOWS else "tcpdump()"
1283    if pktlist is None:
1284        with ContextManagerSubprocess(_prog_name, prog[0]):
1285            proc = subprocess.Popen(
1286                prog + (args if args is not None else []),
1287                stdout=subprocess.PIPE if dump or getfd else None,
1288                stderr=open(os.devnull) if quiet else None,
1289            )
1290    elif isinstance(pktlist, six.string_types):
1291        with ContextManagerSubprocess(_prog_name, prog[0]):
1292            proc = subprocess.Popen(
1293                prog + ["-r", pktlist] + (args if args is not None else []),
1294                stdout=subprocess.PIPE if dump or getfd else None,
1295                stderr=open(os.devnull) if quiet else None,
1296            )
1297    elif DARWIN:
1298        # Tcpdump cannot read from stdin, see
1299        # <http://apple.stackexchange.com/questions/152682/>
1300        tmpfile = tempfile.NamedTemporaryFile(delete=False)
1301        try:
1302            tmpfile.writelines(iter(lambda: pktlist.read(1048576), b""))
1303        except AttributeError:
1304            wrpcap(tmpfile, pktlist)
1305        else:
1306            tmpfile.close()
1307        with ContextManagerSubprocess(_prog_name, prog[0]):
1308            proc = subprocess.Popen(
1309                prog + ["-r", tmpfile.name] + (args if args is not None else []),
1310                stdout=subprocess.PIPE if dump or getfd else None,
1311                stderr=open(os.devnull) if quiet else None,
1312            )
1313        conf.temp_files.append(tmpfile.name)
1314    else:
1315        with ContextManagerSubprocess(_prog_name, prog[0]):
1316            proc = subprocess.Popen(
1317                prog + ["-r", "-"] + (args if args is not None else []),
1318                stdin=subprocess.PIPE,
1319                stdout=subprocess.PIPE if dump or getfd else None,
1320                stderr=open(os.devnull) if quiet else None,
1321            )
1322        try:
1323            proc.stdin.writelines(iter(lambda: pktlist.read(1048576), b""))
1324        except AttributeError:
1325            wrpcap(proc.stdin, pktlist)
1326        else:
1327            proc.stdin.close()
1328    if dump:
1329        return b"".join(iter(lambda: proc.stdout.read(1048576), b""))
1330    if getproc:
1331        return proc
1332    if getfd:
1333        return proc.stdout
1334    proc.wait()
1335
1336@conf.commands.register
1337def hexedit(x):
1338    x = str(x)
1339    f = get_temp_file()
1340    open(f,"wb").write(x)
1341    with ContextManagerSubprocess("hexedit()", conf.prog.hexedit):
1342        subprocess.call([conf.prog.hexedit, f])
1343    x = open(f).read()
1344    os.unlink(f)
1345    return x
1346
1347def get_terminal_width():
1348    """Get terminal width if in a window"""
1349    if WINDOWS:
1350        from ctypes import windll, create_string_buffer
1351        # http://code.activestate.com/recipes/440694-determine-size-of-console-window-on-windows/
1352        h = windll.kernel32.GetStdHandle(-12)
1353        csbi = create_string_buffer(22)
1354        res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
1355        if res:
1356            import struct
1357            (bufx, bufy, curx, cury, wattr,
1358             left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
1359            sizex = right - left + 1
1360            #sizey = bottom - top + 1
1361            return sizex
1362        else:
1363            return None
1364    else:
1365        sizex = 0
1366        try:
1367            import struct, fcntl, termios
1368            s = struct.pack('HHHH', 0, 0, 0, 0)
1369            x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
1370            sizex = struct.unpack('HHHH', x)[1]
1371        except IOError:
1372            pass
1373        if not sizex:
1374            try:
1375                sizex = int(os.environ['COLUMNS'])
1376            except:
1377                pass
1378        if sizex:
1379            return sizex
1380        else:
1381            return None
1382
1383def pretty_list(rtlst, header, sortBy=0):
1384    """Pretty list to fit the terminal, and add header"""
1385    _l_header = len(header[0])
1386    _space = "  "
1387    # Sort correctly
1388    rtlst.sort(key=lambda x: x[sortBy])
1389    # Append tag
1390    rtlst = header + rtlst
1391    # Detect column's width
1392    colwidth = [max([len(y) for y in x]) for x in zip(*rtlst)]
1393    # Make text fit in box (if exist)
1394    # TODO: find a better and more precise way of doing this. That's currently working but very complicated
1395    width = get_terminal_width()
1396    if width:
1397        if sum(colwidth) > width:
1398            # Needs to be cropped
1399            _med = (width // _l_header) - (1 if WINDOWS else 0) # Windows has a fat window border
1400            # Crop biggest until size is correct
1401            for i in range(1, len(colwidth)): # Should use while, but this is safer
1402                if (sum(colwidth)+6) <= width:
1403                    break
1404                _max = max(colwidth)
1405                colwidth = [_med if x == _max else x for x in colwidth]
1406            def _crop(x, width):
1407                _r = x[:width]
1408                if _r != x:
1409                    _r = x[:width-3]
1410                    return _r + "..."
1411                return _r
1412            rtlst = [tuple([_crop(rtlst[j][i], colwidth[i]) for i in range(0, len(rtlst[j]))]) for j in range(0, len(rtlst))]
1413            # Recalculate column's width
1414            colwidth = [max([len(y) for y in x]) for x in zip(*rtlst)]
1415    fmt = _space.join(["%%-%ds"%x for x in colwidth])
1416    rt = "\n".join([fmt % x for x in rtlst])
1417    return rt
1418
1419def __make_table(yfmtfunc, fmtfunc, endline, data, fxyz, sortx=None, sorty=None, seplinefunc=None):
1420    vx = {}
1421    vy = {}
1422    vz = {}
1423    vxf = {}
1424    vyf = {}
1425    l = 0
1426    for e in data:
1427        xx, yy, zz = [str(s) for s in fxyz(e)]
1428        l = max(len(yy),l)
1429        vx[xx] = max(vx.get(xx,0), len(xx), len(zz))
1430        vy[yy] = None
1431        vz[(xx,yy)] = zz
1432
1433    vxk = list(vx)
1434    vyk = list(vy)
1435    if sortx:
1436        vxk.sort(key=sortx)
1437    else:
1438        try:
1439            vxk.sort(key=int)
1440        except:
1441            try:
1442                vxk.sort(key=atol)
1443            except:
1444                vxk.sort()
1445    if sorty:
1446        vyk.sort(key=sorty)
1447    else:
1448        try:
1449            vyk.sort(key=int)
1450        except:
1451            try:
1452                vyk.sort(key=atol)
1453            except:
1454                vyk.sort()
1455
1456
1457    if seplinefunc:
1458        sepline = seplinefunc(l, [vx[x] for x in vxk])
1459        print(sepline)
1460
1461    fmt = yfmtfunc(l)
1462    print(fmt % "", end=' ')
1463    for x in vxk:
1464        vxf[x] = fmtfunc(vx[x])
1465        print(vxf[x] % x, end=' ')
1466    print(endline)
1467    if seplinefunc:
1468        print(sepline)
1469    for y in vyk:
1470        print(fmt % y, end=' ')
1471        for x in vxk:
1472            print(vxf[x] % vz.get((x,y), "-"), end=' ')
1473        print(endline)
1474    if seplinefunc:
1475        print(sepline)
1476
1477def make_table(*args, **kargs):
1478    __make_table(lambda l:"%%-%is" % l, lambda l:"%%-%is" % l, "", *args, **kargs)
1479
1480def make_lined_table(*args, **kargs):
1481    __make_table(lambda l:"%%-%is |" % l, lambda l:"%%-%is |" % l, "",
1482                 seplinefunc=lambda a,x:"+".join('-'*(y+2) for y in [a-1]+x+[-2]),
1483                 *args, **kargs)
1484
1485def make_tex_table(*args, **kargs):
1486    __make_table(lambda l: "%s", lambda l: "& %s", "\\\\", seplinefunc=lambda a,x:"\\hline", *args, **kargs)
1487
1488###############################################
1489### WHOIS CLIENT (not available on windows) ###
1490###############################################
1491
1492def whois(ip_address):
1493    """Whois client for Python"""
1494    whois_ip = str(ip_address)
1495    try:
1496        query = socket.gethostbyname(whois_ip)
1497    except:
1498        query = whois_ip
1499    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1500    s.connect(("whois.ripe.net", 43))
1501    s.send(query.encode("utf8") + b"\r\n")
1502    answer = b""
1503    while True:
1504        d = s.recv(4096)
1505        answer += d
1506        if not d:
1507            break
1508    s.close()
1509    ignore_tag = b"remarks:"
1510    # ignore all lines starting with the ignore_tag
1511    lines = [ line for line in answer.split(b"\n") if not line or (line and not line.startswith(ignore_tag))]
1512    # remove empty lines at the bottom
1513    for i in range(1, len(lines)):
1514        if not lines[-i].strip():
1515            del lines[-i]
1516        else:
1517            break
1518    return b"\n".join(lines[3:])
1519