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