Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/layers/inet.py: 34%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1426 statements  

1# SPDX-License-Identifier: GPL-2.0-only 

2# This file is part of Scapy 

3# See https://scapy.net/ for more information 

4# Copyright (C) Philippe Biondi <phil@secdev.org> 

5 

6""" 

7IPv4 (Internet Protocol v4). 

8""" 

9 

10import time 

11import struct 

12import re 

13import random 

14import select 

15import socket 

16from collections import defaultdict 

17 

18from scapy.utils import checksum, do_graph, graphviz_escape, incremental_label, \ 

19 linehexdump, strxor, whois, colgen 

20from scapy.ansmachine import AnsweringMachine 

21from scapy.base_classes import Gen, Net, _ScopedIP 

22from scapy.consts import OPENBSD 

23from scapy.data import ( 

24 ETH_P_IP, 

25 ETH_P_ALL, 

26 DLT_RAW, 

27 DLT_RAW_ALT, 

28 DLT_IPV4, 

29 IP_PROTOS, 

30 TCP_SERVICES, 

31 UDP_SERVICES, 

32) 

33from scapy.layers.l2 import ( 

34 CookedLinux, 

35 Dot3, 

36 Ether, 

37 GRE, 

38 Loopback, 

39 SNAP, 

40 arpcachepoison, 

41 getmacbyip, 

42) 

43from scapy.compat import raw, chb, bytes_encode, Optional 

44from scapy.config import conf 

45from scapy.fields import ( 

46 BitEnumField, 

47 BitField, 

48 ByteEnumField, 

49 ByteField, 

50 ConditionalField, 

51 DestField, 

52 Emph, 

53 FieldLenField, 

54 FieldListField, 

55 FlagsField, 

56 IPField, 

57 IP6Field, 

58 IntField, 

59 MayEnd, 

60 MultiEnumField, 

61 MultipleTypeField, 

62 PacketField, 

63 PacketListField, 

64 ShortEnumField, 

65 ShortField, 

66 SourceIPField, 

67 StrField, 

68 StrFixedLenField, 

69 StrLenField, 

70 TrailerField, 

71 XByteField, 

72 XShortField, 

73) 

74from scapy.packet import Packet, bind_layers, bind_bottom_up, NoPayload 

75from scapy.volatile import RandShort, RandInt, RandBin, RandNum, VolatileValue 

76from scapy.sendrecv import sr, sr1 

77from scapy.plist import _PacketList, PacketList, SndRcvList 

78from scapy.automaton import Automaton, ATMT 

79from scapy.error import log_runtime, warning 

80from scapy.pton_ntop import inet_pton 

81 

82import scapy.as_resolvers 

83 

84#################### 

85# IP Tools class # 

86#################### 

87 

88 

89class IPTools(object): 

90 """Add more powers to a class with an "src" attribute.""" 

91 __slots__ = [] 

92 

93 def whois(self): 

94 """whois the source and print the output""" 

95 print(whois(self.src).decode("utf8", "ignore")) 

96 

97 def _ttl(self): 

98 """Returns ttl or hlim, depending on the IP version""" 

99 return self.hlim if isinstance(self, scapy.layers.inet6.IPv6) else self.ttl # noqa: E501 

100 

101 def ottl(self): 

102 t = sorted([32, 64, 128, 255] + [self._ttl()]) 

103 return t[t.index(self._ttl()) + 1] 

104 

105 def hops(self): 

106 return self.ottl() - self._ttl() 

107 

108 

109_ip_options_names = {0: "end_of_list", 

110 1: "nop", 

111 2: "security", 

112 3: "loose_source_route", 

113 4: "timestamp", 

114 5: "extended_security", 

115 6: "commercial_security", 

116 7: "record_route", 

117 8: "stream_id", 

118 9: "strict_source_route", 

119 10: "experimental_measurement", 

120 11: "mtu_probe", 

121 12: "mtu_reply", 

122 13: "flow_control", 

123 14: "access_control", 

124 15: "encode", 

125 16: "imi_traffic_descriptor", 

126 17: "extended_IP", 

127 18: "traceroute", 

128 19: "address_extension", 

129 20: "router_alert", 

130 21: "selective_directed_broadcast_mode", 

131 23: "dynamic_packet_state", 

132 24: "upstream_multicast_packet", 

133 25: "quick_start", 

134 30: "rfc4727_experiment", 

135 } 

136 

137 

138class _IPOption_HDR(Packet): 

139 fields_desc = [BitField("copy_flag", 0, 1), 

140 BitEnumField("optclass", 0, 2, {0: "control", 2: "debug"}), 

141 BitEnumField("option", 0, 5, _ip_options_names)] 

142 

143 

144class IPOption(Packet): 

145 name = "IP Option" 

146 fields_desc = [_IPOption_HDR, 

147 FieldLenField("length", None, fmt="B", # Only option 0 and 1 have no length and value # noqa: E501 

148 length_of="value", adjust=lambda pkt, l:l + 2), # noqa: E501 

149 StrLenField("value", "", length_from=lambda pkt:pkt.length - 2)] # noqa: E501 

150 

151 def extract_padding(self, p): 

152 return b"", p 

153 

154 registered_ip_options = {} 

155 

156 @classmethod 

157 def register_variant(cls): 

158 cls.registered_ip_options[cls.option.default] = cls 

159 

160 @classmethod 

161 def dispatch_hook(cls, pkt=None, *args, **kargs): 

162 if pkt: 

163 opt = pkt[0] & 0x1f 

164 if opt in cls.registered_ip_options: 

165 return cls.registered_ip_options[opt] 

166 return cls 

167 

168 

169class IPOption_EOL(IPOption): 

170 name = "IP Option End of Options List" 

171 option = 0 

172 fields_desc = [_IPOption_HDR] 

173 

174 

175class IPOption_NOP(IPOption): 

176 name = "IP Option No Operation" 

177 option = 1 

178 fields_desc = [_IPOption_HDR] 

179 

180 

181class IPOption_Security(IPOption): 

182 name = "IP Option Security" 

183 copy_flag = 1 

184 option = 2 

185 fields_desc = [_IPOption_HDR, 

186 ByteField("length", 11), 

187 ShortField("security", 0), 

188 ShortField("compartment", 0), 

189 ShortField("handling_restrictions", 0), 

190 StrFixedLenField("transmission_control_code", "xxx", 3), 

191 ] 

192 

193 

194class IPOption_RR(IPOption): 

195 name = "IP Option Record Route" 

196 option = 7 

197 fields_desc = [_IPOption_HDR, 

198 FieldLenField("length", None, fmt="B", 

199 length_of="routers", adjust=lambda pkt, l:l + 3), # noqa: E501 

200 ByteField("pointer", 4), # 4 is first IP 

201 FieldListField("routers", [], IPField("", "0.0.0.0"), 

202 length_from=lambda pkt:pkt.length - 3) 

203 ] 

204 

205 def get_current_router(self): 

206 return self.routers[self.pointer // 4 - 1] 

207 

208 

209class IPOption_LSRR(IPOption_RR): 

210 name = "IP Option Loose Source and Record Route" 

211 copy_flag = 1 

212 option = 3 

213 

214 

215class IPOption_SSRR(IPOption_RR): 

216 name = "IP Option Strict Source and Record Route" 

217 copy_flag = 1 

218 option = 9 

219 

220 

221class IPOption_Stream_Id(IPOption): 

222 name = "IP Option Stream ID" 

223 copy_flag = 1 

224 option = 8 

225 fields_desc = [_IPOption_HDR, 

226 ByteField("length", 4), 

227 ShortField("security", 0), ] 

228 

229 

230class IPOption_MTU_Probe(IPOption): 

231 name = "IP Option MTU Probe" 

232 option = 11 

233 fields_desc = [_IPOption_HDR, 

234 ByteField("length", 4), 

235 ShortField("mtu", 0), ] 

236 

237 

238class IPOption_MTU_Reply(IPOption_MTU_Probe): 

239 name = "IP Option MTU Reply" 

240 option = 12 

241 

242 

243class IPOption_Traceroute(IPOption): 

244 name = "IP Option Traceroute" 

245 option = 18 

246 fields_desc = [_IPOption_HDR, 

247 ByteField("length", 12), 

248 ShortField("id", 0), 

249 ShortField("outbound_hops", 0), 

250 ShortField("return_hops", 0), 

251 IPField("originator_ip", "0.0.0.0")] 

252 

253 

254class IPOption_Timestamp(IPOption): 

255 name = "IP Option Timestamp" 

256 optclass = 2 

257 option = 4 

258 fields_desc = [_IPOption_HDR, 

259 ByteField("length", None), 

260 ByteField("pointer", 9), 

261 BitField("oflw", 0, 4), 

262 BitEnumField("flg", 1, 4, 

263 {0: "timestamp_only", 

264 1: "timestamp_and_ip_addr", 

265 3: "prespecified_ip_addr"}), 

266 ConditionalField(IPField("internet_address", "0.0.0.0"), 

267 lambda pkt: pkt.flg != 0), 

268 IntField('timestamp', 0)] 

269 

270 def post_build(self, p, pay): 

271 if self.length is None: 

272 p = p[:1] + struct.pack("!B", len(p)) + p[2:] 

273 return p + pay 

274 

275 

276class IPOption_Address_Extension(IPOption): 

277 name = "IP Option Address Extension" 

278 copy_flag = 1 

279 option = 19 

280 fields_desc = [_IPOption_HDR, 

281 ByteField("length", 10), 

282 IPField("src_ext", "0.0.0.0"), 

283 IPField("dst_ext", "0.0.0.0")] 

284 

285 

286class IPOption_Router_Alert(IPOption): 

287 name = "IP Option Router Alert" 

288 copy_flag = 1 

289 option = 20 

290 fields_desc = [_IPOption_HDR, 

291 ByteField("length", 4), 

292 ShortEnumField("alert", 0, {0: "router_shall_examine_packet"}), ] # noqa: E501 

293 

294 

295class IPOption_SDBM(IPOption): 

296 name = "IP Option Selective Directed Broadcast Mode" 

297 copy_flag = 1 

298 option = 21 

299 fields_desc = [_IPOption_HDR, 

300 FieldLenField("length", None, fmt="B", 

301 length_of="addresses", adjust=lambda pkt, l:l + 2), # noqa: E501 

302 FieldListField("addresses", [], IPField("", "0.0.0.0"), 

303 length_from=lambda pkt:pkt.length - 2) 

304 ] 

305 

306 

307TCPOptions = ( 

308 {0: ("EOL", None), 

309 1: ("NOP", None), 

310 2: ("MSS", "!H"), 

311 3: ("WScale", "!B"), 

312 4: ("SAckOK", None), 

313 5: ("SAck", "!"), 

314 8: ("Timestamp", "!II"), 

315 14: ("AltChkSum", "!BH"), 

316 15: ("AltChkSumOpt", None), 

317 19: ("MD5", "16s"), 

318 25: ("Mood", "!p"), 

319 28: ("UTO", "!H"), 

320 29: ("AO", None), 

321 34: ("TFO", "!II"), 

322 # RFC 3692 

323 # 253: ("Experiment", "!HHHH"), 

324 # 254: ("Experiment", "!HHHH"), 

325 }, 

326 {"EOL": 0, 

327 "NOP": 1, 

328 "MSS": 2, 

329 "WScale": 3, 

330 "SAckOK": 4, 

331 "SAck": 5, 

332 "Timestamp": 8, 

333 "AltChkSum": 14, 

334 "AltChkSumOpt": 15, 

335 "MD5": 19, 

336 "Mood": 25, 

337 "UTO": 28, 

338 "AO": 29, 

339 "TFO": 34, 

340 }) 

341 

342 

343class TCPAOValue(Packet): 

344 """Value of TCP-AO option""" 

345 fields_desc = [ 

346 ByteField("keyid", None), 

347 ByteField("rnextkeyid", None), 

348 StrLenField("mac", "", length_from=lambda p:len(p.original) - 2), 

349 ] 

350 

351 

352def get_tcpao(tcphdr): 

353 # type: (TCP) -> Optional[TCPAOValue] 

354 """Get the TCP-AO option from the header""" 

355 for optid, optval in tcphdr.options: 

356 if optid == 'AO': 

357 return optval 

358 return None 

359 

360 

361class RandTCPOptions(VolatileValue): 

362 def __init__(self, size=None): 

363 if size is None: 

364 size = RandNum(1, 5) 

365 self.size = size 

366 

367 def _fix(self): 

368 # Pseudo-Random amount of options 

369 # Random ("NAME", fmt) 

370 rand_patterns = [ 

371 random.choice(list( 

372 (opt, fmt) for opt, fmt in TCPOptions[0].values() 

373 if opt != 'EOL' 

374 )) 

375 for _ in range(self.size) 

376 ] 

377 rand_vals = [] 

378 for oname, fmt in rand_patterns: 

379 if fmt is None: 

380 rand_vals.append((oname, b'')) 

381 else: 

382 # Process the fmt arguments 1 by 1 

383 structs = re.findall(r"!?([bBhHiIlLqQfdpP]|\d+[spx])", fmt) 

384 rval = [] 

385 for stru in structs: 

386 stru = "!" + stru 

387 if "s" in stru or "p" in stru: # str / chr 

388 v = bytes(RandBin(struct.calcsize(stru))) 

389 else: # int 

390 _size = struct.calcsize(stru) 

391 v = random.randint(0, 2 ** (8 * _size) - 1) 

392 rval.append(v) 

393 rand_vals.append((oname, tuple(rval))) 

394 return rand_vals 

395 

396 def __bytes__(self): 

397 return TCPOptionsField.i2m(None, None, self._fix()) 

398 

399 

400class TCPOptionsField(StrField): 

401 islist = 1 

402 

403 def getfield(self, pkt, s): 

404 opsz = (pkt.dataofs - 5) * 4 

405 if opsz < 0: 

406 log_runtime.info( 

407 "bad dataofs (%i). Assuming dataofs=5", pkt.dataofs 

408 ) 

409 opsz = 0 

410 return s[opsz:], self.m2i(pkt, s[:opsz]) 

411 

412 def m2i(self, pkt, x): 

413 opt = [] 

414 while x: 

415 onum = x[0] 

416 if onum == 0: 

417 opt.append(("EOL", None)) 

418 break 

419 if onum == 1: 

420 opt.append(("NOP", None)) 

421 x = x[1:] 

422 continue 

423 try: 

424 olen = x[1] 

425 except IndexError: 

426 olen = 0 

427 if olen < 2: 

428 log_runtime.info( 

429 "Malformed TCP option (announced length is %i)", olen 

430 ) 

431 olen = 2 

432 oval = x[2:olen] 

433 if onum in TCPOptions[0]: 

434 oname, ofmt = TCPOptions[0][onum] 

435 if onum == 5: # SAck 

436 ofmt += "%iI" % (len(oval) // 4) 

437 if onum == 29: # AO 

438 oval = TCPAOValue(oval) 

439 if ofmt and struct.calcsize(ofmt) == len(oval): 

440 oval = struct.unpack(ofmt, oval) 

441 if len(oval) == 1: 

442 oval = oval[0] 

443 opt.append((oname, oval)) 

444 else: 

445 opt.append((onum, oval)) 

446 x = x[olen:] 

447 return opt 

448 

449 def i2h(self, pkt, x): 

450 if not x: 

451 return [] 

452 return x 

453 

454 def i2m(self, pkt, x): 

455 opt = b"" 

456 for oname, oval in x: 

457 # We check for a (0, b'') or (1, b'') option first 

458 oname = {0: "EOL", 1: "NOP"}.get(oname, oname) 

459 if isinstance(oname, str): 

460 if oname == "NOP": 

461 opt += b"\x01" 

462 continue 

463 elif oname == "EOL": 

464 opt += b"\x00" 

465 continue 

466 elif oname in TCPOptions[1]: 

467 onum = TCPOptions[1][oname] 

468 ofmt = TCPOptions[0][onum][1] 

469 if onum == 5: # SAck 

470 ofmt += "%iI" % len(oval) 

471 _test_isinstance = not isinstance(oval, (bytes, str)) 

472 if ofmt is not None and (_test_isinstance or "s" in ofmt): 

473 if not isinstance(oval, tuple): 

474 oval = (oval,) 

475 oval = struct.pack(ofmt, *oval) 

476 if onum == 29: # AO 

477 oval = bytes(oval) 

478 else: 

479 warning("Option [%s] unknown. Skipped.", oname) 

480 continue 

481 else: 

482 onum = oname 

483 if not isinstance(onum, int): 

484 warning("Invalid option number [%i]" % onum) 

485 continue 

486 if not isinstance(oval, (bytes, str)): 

487 warning("Option [%i] is not bytes." % onum) 

488 continue 

489 if isinstance(oval, str): 

490 oval = bytes_encode(oval) 

491 opt += chb(onum) + chb(2 + len(oval)) + oval 

492 return opt + b"\x00" * (3 - ((len(opt) + 3) % 4)) # Padding 

493 

494 def randval(self): 

495 return RandTCPOptions() 

496 

497 

498class ICMPTimeStampField(IntField): 

499 re_hmsm = re.compile("([0-2]?[0-9])[Hh:](([0-5]?[0-9])([Mm:]([0-5]?[0-9])([sS:.]([0-9]{0,3}))?)?)?$") # noqa: E501 

500 

501 def i2repr(self, pkt, val): 

502 if val is None: 

503 return "--" 

504 else: 

505 sec, milli = divmod(val, 1000) 

506 min, sec = divmod(sec, 60) 

507 hour, min = divmod(min, 60) 

508 return "%d:%d:%d.%d" % (hour, min, sec, int(milli)) 

509 

510 def any2i(self, pkt, val): 

511 if isinstance(val, str): 

512 hmsms = self.re_hmsm.match(val) 

513 if hmsms: 

514 h, _, m, _, s, _, ms = hmsms.groups() 

515 ms = int(((ms or "") + "000")[:3]) 

516 val = ((int(h) * 60 + int(m or 0)) * 60 + int(s or 0)) * 1000 + ms # noqa: E501 

517 else: 

518 val = 0 

519 elif val is None: 

520 val = int((time.time() % (24 * 60 * 60)) * 1000) 

521 return val 

522 

523 

524class DestIPField(IPField, DestField): 

525 bindings = {} 

526 

527 def __init__(self, name, default): 

528 IPField.__init__(self, name, None) 

529 DestField.__init__(self, name, default) 

530 

531 def i2m(self, pkt, x): 

532 if x is None: 

533 x = self.dst_from_pkt(pkt) 

534 return IPField.i2m(self, pkt, x) 

535 

536 def i2h(self, pkt, x): 

537 if x is None: 

538 x = self.dst_from_pkt(pkt) 

539 return IPField.i2h(self, pkt, x) 

540 

541 

542class IP(Packet, IPTools): 

543 name = "IP" 

544 fields_desc = [BitField("version", 4, 4), 

545 BitField("ihl", None, 4), 

546 XByteField("tos", 0), 

547 ShortField("len", None), 

548 ShortField("id", 1), 

549 FlagsField("flags", 0, 3, ["MF", "DF", "evil"]), 

550 BitField("frag", 0, 13), 

551 ByteField("ttl", 64), 

552 ByteEnumField("proto", 0, IP_PROTOS), 

553 XShortField("chksum", None), 

554 # IPField("src", "127.0.0.1"), 

555 Emph(SourceIPField("src")), 

556 Emph(DestIPField("dst", "127.0.0.1")), 

557 PacketListField("options", [], IPOption, length_from=lambda p:p.ihl * 4 - 20)] # noqa: E501 

558 

559 def post_build(self, p, pay): 

560 ihl = self.ihl 

561 p += b"\0" * ((-len(p)) % 4) # pad IP options if needed 

562 if ihl is None: 

563 ihl = len(p) // 4 

564 p = chb(((self.version & 0xf) << 4) | ihl & 0x0f) + p[1:] 

565 if self.len is None: 

566 tmp_len = len(p) + len(pay) 

567 p = p[:2] + struct.pack("!H", tmp_len) + p[4:] 

568 if self.chksum is None: 

569 ck = checksum(p) 

570 p = p[:10] + chb(ck >> 8) + chb(ck & 0xff) + p[12:] 

571 return p + pay 

572 

573 def extract_padding(self, s): 

574 tmp_len = self.len - (self.ihl << 2) 

575 if tmp_len < 0: 

576 return s, b"" 

577 return s[:tmp_len], s[tmp_len:] 

578 

579 def route(self): 

580 dst = self.dst 

581 scope = None 

582 if isinstance(dst, (Net, _ScopedIP)): 

583 scope = dst.scope 

584 if isinstance(dst, (Gen, list)): 

585 dst = next(iter(dst)) 

586 if conf.route is None: 

587 # unused import, only to initialize conf.route 

588 import scapy.route # noqa: F401 

589 if not isinstance(dst, (str, bytes, int)): 

590 dst = str(dst) 

591 return conf.route.route(dst, dev=scope) 

592 

593 def hashret(self): 

594 if ((self.proto == socket.IPPROTO_ICMP) and 

595 (isinstance(self.payload, ICMP)) and 

596 (self.payload.type in [3, 4, 5, 11, 12])): 

597 return self.payload.payload.hashret() 

598 if not conf.checkIPinIP and self.proto in [4, 41]: # IP, IPv6 

599 return self.payload.hashret() 

600 if self.dst == "224.0.0.251": # mDNS 

601 return struct.pack("B", self.proto) + self.payload.hashret() 

602 if conf.checkIPsrc and conf.checkIPaddr: 

603 return (strxor(inet_pton(socket.AF_INET, self.src), 

604 inet_pton(socket.AF_INET, self.dst)) + 

605 struct.pack("B", self.proto) + self.payload.hashret()) 

606 return struct.pack("B", self.proto) + self.payload.hashret() 

607 

608 def answers(self, other): 

609 if not conf.checkIPinIP: # skip IP in IP and IPv6 in IP 

610 if self.proto in [4, 41]: 

611 return self.payload.answers(other) 

612 if isinstance(other, IP) and other.proto in [4, 41]: 

613 return self.answers(other.payload) 

614 if conf.ipv6_enabled \ 

615 and isinstance(other, scapy.layers.inet6.IPv6) \ 

616 and other.nh in [4, 41]: 

617 return self.answers(other.payload) 

618 if not isinstance(other, IP): 

619 return 0 

620 if conf.checkIPaddr: 

621 if other.dst == "224.0.0.251" and self.dst == "224.0.0.251": # mDNS # noqa: E501 

622 return self.payload.answers(other.payload) 

623 elif (self.dst != other.src): 

624 return 0 

625 if ((self.proto == socket.IPPROTO_ICMP) and 

626 (isinstance(self.payload, ICMP)) and 

627 (self.payload.type in [3, 4, 5, 11, 12])): 

628 # ICMP error message 

629 return self.payload.payload.answers(other) 

630 

631 else: 

632 if ((conf.checkIPaddr and (self.src != other.dst)) or 

633 (self.proto != other.proto)): 

634 return 0 

635 return self.payload.answers(other.payload) 

636 

637 def mysummary(self): 

638 s = self.sprintf("%IP.src% > %IP.dst% %IP.proto%") 

639 if self.frag: 

640 s += " frag:%i" % self.frag 

641 return s 

642 

643 def fragment(self, fragsize=1480): 

644 """Fragment IP datagrams""" 

645 return fragment(self, fragsize=fragsize) 

646 

647 

648def in4_pseudoheader(proto, u, plen): 

649 # type: (int, IP, int) -> bytes 

650 """IPv4 Pseudo Header as defined in RFC793 as bytes 

651 

652 :param proto: value of upper layer protocol 

653 :param u: IP layer instance 

654 :param plen: the length of the upper layer and payload 

655 """ 

656 u = u.copy() 

657 if u.len is not None: 

658 if u.ihl is None: 

659 olen = sum(len(x) for x in u.options) 

660 ihl = 5 + olen // 4 + (1 if olen % 4 else 0) 

661 else: 

662 ihl = u.ihl 

663 ln = max(u.len - 4 * ihl, 0) 

664 else: 

665 ln = plen 

666 

667 # Filter out IPOption_LSRR and IPOption_SSRR 

668 sr_options = [opt for opt in u.options if isinstance(opt, IPOption_LSRR) or 

669 isinstance(opt, IPOption_SSRR)] 

670 len_sr_options = len(sr_options) 

671 if len_sr_options == 1 and len(sr_options[0].routers): 

672 # The checksum must be computed using the final 

673 # destination address 

674 u.dst = sr_options[0].routers[-1] 

675 elif len_sr_options > 1: 

676 message = "Found %d Source Routing Options! " 

677 message += "Falling back to IP.dst for checksum computation." 

678 warning(message, len_sr_options) 

679 

680 return struct.pack("!4s4sHH", 

681 inet_pton(socket.AF_INET, u.src), 

682 inet_pton(socket.AF_INET, u.dst), 

683 proto, 

684 ln) 

685 

686 

687def in4_chksum(proto, u, p): 

688 # type: (int, IP, bytes) -> int 

689 """IPv4 Pseudo Header checksum as defined in RFC793 

690 

691 :param proto: value of upper layer protocol 

692 :param u: upper layer instance 

693 :param p: the payload of the upper layer provided as a string 

694 """ 

695 if not isinstance(u, IP): 

696 warning("No IP underlayer to compute checksum. Leaving null.") 

697 return 0 

698 psdhdr = in4_pseudoheader(proto, u, len(p)) 

699 return checksum(psdhdr + p) 

700 

701 

702def _is_ipv6_layer(p): 

703 # type: (Packet) -> bytes 

704 return (isinstance(p, scapy.layers.inet6.IPv6) or 

705 isinstance(p, scapy.layers.inet6._IPv6ExtHdr)) 

706 

707 

708def tcp_pseudoheader(tcp): 

709 # type: (TCP) -> bytes 

710 """Pseudoheader of a TCP packet as bytes 

711 

712 Requires underlayer to be either IP or IPv6 

713 """ 

714 if isinstance(tcp.underlayer, IP): 

715 plen = len(bytes(tcp)) 

716 return in4_pseudoheader(socket.IPPROTO_TCP, tcp.underlayer, plen) 

717 elif conf.ipv6_enabled and _is_ipv6_layer(tcp.underlayer): 

718 plen = len(bytes(tcp)) 

719 return raw(scapy.layers.inet6.in6_pseudoheader( 

720 socket.IPPROTO_TCP, tcp.underlayer, plen)) 

721 else: 

722 raise ValueError("TCP packet does not have IP or IPv6 underlayer") 

723 

724 

725def calc_tcp_md5_hash(tcp, key): 

726 # type: (TCP, bytes) -> bytes 

727 """Calculate TCP-MD5 hash from packet and return a 16-byte string""" 

728 import hashlib 

729 

730 h = hashlib.md5() # nosec 

731 tcp_bytes = bytes(tcp) 

732 doff = tcp_bytes[12] >> 4 

733 h.update(tcp_pseudoheader(tcp)) 

734 h.update(tcp_bytes[:16]) 

735 h.update(b"\x00\x00") 

736 h.update(tcp_bytes[18:20]) 

737 h.update(tcp_bytes[doff << 2:]) 

738 h.update(key) 

739 

740 return h.digest() 

741 

742 

743def sign_tcp_md5(tcp, key): 

744 # type: (TCP, bytes) -> None 

745 """Append TCP-MD5 signature to tcp packet""" 

746 tcp.options = tcp.options + [('MD5', b'')] 

747 sig = calc_tcp_md5_hash(tcp, key) 

748 tcp.options[-1] = ('MD5', sig) 

749 

750 

751class TCP(Packet): 

752 name = "TCP" 

753 fields_desc = [ShortEnumField("sport", 20, TCP_SERVICES), 

754 ShortEnumField("dport", 80, TCP_SERVICES), 

755 IntField("seq", 0), 

756 IntField("ack", 0), 

757 BitField("dataofs", None, 4), 

758 BitField("reserved", 0, 3), 

759 FlagsField("flags", 0x2, 9, "FSRPAUECN"), 

760 ShortField("window", 8192), 

761 XShortField("chksum", None), 

762 ShortField("urgptr", 0), 

763 TCPOptionsField("options", "")] 

764 

765 def post_build(self, p, pay): 

766 p += pay 

767 dataofs = self.dataofs 

768 if dataofs is None: 

769 opt_len = len(self.get_field("options").i2m(self, self.options)) 

770 dataofs = 5 + ((opt_len + 3) // 4) 

771 dataofs = (dataofs << 4) | p[12] & 0x0f 

772 p = p[:12] + chb(dataofs & 0xff) + p[13:] 

773 if self.chksum is None: 

774 if isinstance(self.underlayer, IP): 

775 ck = in4_chksum(socket.IPPROTO_TCP, self.underlayer, p) 

776 p = p[:16] + struct.pack("!H", ck) + p[18:] 

777 elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr): # noqa: E501 

778 ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_TCP, self.underlayer, p) # noqa: E501 

779 p = p[:16] + struct.pack("!H", ck) + p[18:] 

780 else: 

781 log_runtime.info( 

782 "No IP underlayer to compute checksum. Leaving null." 

783 ) 

784 return p 

785 

786 def hashret(self): 

787 if conf.checkIPsrc: 

788 return struct.pack("H", self.sport ^ self.dport) + self.payload.hashret() # noqa: E501 

789 else: 

790 return self.payload.hashret() 

791 

792 def answers(self, other): 

793 if not isinstance(other, TCP): 

794 return 0 

795 # RST packets don't get answers 

796 if other.flags.R: 

797 return 0 

798 # We do not support the four-way handshakes with the SYN+ACK 

799 # answer split in two packets (one ACK and one SYN): in that 

800 # case the ACK will be seen as an answer, but not the SYN. 

801 if self.flags.S: 

802 # SYN packets without ACK are not answers 

803 if not self.flags.A: 

804 return 0 

805 # SYN+ACK packets answer SYN packets 

806 if not other.flags.S: 

807 return 0 

808 if conf.checkIPsrc: 

809 if not ((self.sport == other.dport) and 

810 (self.dport == other.sport)): 

811 return 0 

812 # Do not check ack value for SYN packets without ACK 

813 if not (other.flags.S and not other.flags.A) \ 

814 and abs(other.ack - self.seq) > 2: 

815 return 0 

816 # Do not check ack value for RST packets without ACK 

817 if self.flags.R and not self.flags.A: 

818 return 1 

819 if abs(other.seq - self.ack) > 2 + len(other.payload): 

820 return 0 

821 return 1 

822 

823 def mysummary(self): 

824 if isinstance(self.underlayer, IP): 

825 return self.underlayer.sprintf("TCP %IP.src%:%TCP.sport% > %IP.dst%:%TCP.dport% %TCP.flags%") # noqa: E501 

826 elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6): # noqa: E501 

827 return self.underlayer.sprintf("TCP %IPv6.src%:%TCP.sport% > %IPv6.dst%:%TCP.dport% %TCP.flags%") # noqa: E501 

828 else: 

829 return self.sprintf("TCP %TCP.sport% > %TCP.dport% %TCP.flags%") 

830 

831 

832class UDP(Packet): 

833 name = "UDP" 

834 fields_desc = [ShortEnumField("sport", 53, UDP_SERVICES), 

835 ShortEnumField("dport", 53, UDP_SERVICES), 

836 ShortField("len", None), 

837 XShortField("chksum", None), ] 

838 

839 def post_build(self, p, pay): 

840 p += pay 

841 tmp_len = self.len 

842 if tmp_len is None: 

843 tmp_len = len(p) 

844 p = p[:4] + struct.pack("!H", tmp_len) + p[6:] 

845 if self.chksum is None: 

846 if isinstance(self.underlayer, IP): 

847 ck = in4_chksum(socket.IPPROTO_UDP, self.underlayer, p) 

848 # According to RFC768 if the result checksum is 0, it should be set to 0xFFFF # noqa: E501 

849 if ck == 0: 

850 ck = 0xFFFF 

851 p = p[:6] + struct.pack("!H", ck) + p[8:] 

852 elif isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr): # noqa: E501 

853 ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_UDP, self.underlayer, p) # noqa: E501 

854 # According to RFC2460 if the result checksum is 0, it should be set to 0xFFFF # noqa: E501 

855 if ck == 0: 

856 ck = 0xFFFF 

857 p = p[:6] + struct.pack("!H", ck) + p[8:] 

858 else: 

859 log_runtime.info( 

860 "No IP underlayer to compute checksum. Leaving null." 

861 ) 

862 return p 

863 

864 def extract_padding(self, s): 

865 tmp_len = self.len - 8 

866 return s[:tmp_len], s[tmp_len:] 

867 

868 def hashret(self): 

869 return self.payload.hashret() 

870 

871 def answers(self, other): 

872 if not isinstance(other, UDP): 

873 return 0 

874 if conf.checkIPsrc: 

875 if self.dport != other.sport: 

876 return 0 

877 return self.payload.answers(other.payload) 

878 

879 def mysummary(self): 

880 if isinstance(self.underlayer, IP): 

881 return self.underlayer.sprintf("UDP %IP.src%:%UDP.sport% > %IP.dst%:%UDP.dport%") # noqa: E501 

882 elif isinstance(self.underlayer, scapy.layers.inet6.IPv6): 

883 return self.underlayer.sprintf("UDP %IPv6.src%:%UDP.sport% > %IPv6.dst%:%UDP.dport%") # noqa: E501 

884 else: 

885 return self.sprintf("UDP %UDP.sport% > %UDP.dport%") 

886 

887 

888# RFC 4884 ICMP extensions 

889_ICMP_classnums = { 

890 # https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-ext-classes 

891 1: "MPLS", 

892 2: "Interface Information", 

893 3: "Interface Identification", 

894 4: "Extended Information", 

895} 

896 

897 

898class ICMPExtension_Object(Packet): 

899 name = "ICMP Extension Object" 

900 show_indent = 0 

901 fields_desc = [ 

902 ShortField("len", None), 

903 ByteEnumField("classnum", 0, _ICMP_classnums), 

904 ByteField("classtype", 0), 

905 ] 

906 

907 def post_build(self, p, pay): 

908 if self.len is None: 

909 tmp_len = len(p) + len(pay) 

910 p = struct.pack("!H", tmp_len) + p[2:] 

911 return p + pay 

912 

913 registered_icmp_exts = {} 

914 

915 @classmethod 

916 def register_variant(cls): 

917 cls.registered_icmp_exts[cls.classnum.default] = cls 

918 

919 @classmethod 

920 def dispatch_hook(cls, _pkt=None, *args, **kargs): 

921 if _pkt and len(_pkt) >= 4: 

922 classnum = _pkt[2] 

923 if classnum in cls.registered_icmp_exts: 

924 return cls.registered_icmp_exts[classnum] 

925 return cls 

926 

927 

928class ICMPExtension_InterfaceInformation(ICMPExtension_Object): 

929 name = "ICMP Extension Object - Interface Information Object (RFC5837)" 

930 

931 fields_desc = [ 

932 ShortField("len", None), 

933 ByteEnumField("classnum", 2, _ICMP_classnums), 

934 BitField("classtype", 0, 2), 

935 BitField("reserved", 0, 2), 

936 BitField("has_ifindex", 0, 1), 

937 BitField("has_ipaddr", 0, 1), 

938 BitField("has_ifname", 0, 1), 

939 BitField("has_mtu", 0, 1), 

940 ConditionalField(IntField("ifindex", None), lambda pkt: pkt.has_ifindex == 1), 

941 ConditionalField(ShortField("afi", None), lambda pkt: pkt.has_ipaddr == 1), 

942 ConditionalField(ShortField("reserved2", 0), lambda pkt: pkt.has_ipaddr == 1), 

943 ConditionalField(IPField("ip4", None), lambda pkt: pkt.afi == 1), 

944 ConditionalField(IP6Field("ip6", None), lambda pkt: pkt.afi == 2), 

945 ConditionalField( 

946 FieldLenField("ifname_len", None, fmt="B", length_of="ifname"), 

947 lambda pkt: pkt.has_ifname == 1, 

948 ), 

949 ConditionalField( 

950 StrLenField("ifname", None, length_from=lambda pkt: pkt.ifname_len), 

951 lambda pkt: pkt.has_ifname == 1, 

952 ), 

953 ConditionalField(IntField("mtu", None), lambda pkt: pkt.has_mtu == 1), 

954 ] 

955 

956 def self_build(self, **kwargs): 

957 if self.afi is None: 

958 if self.ip4 is not None: 

959 self.afi = 1 

960 elif self.ip6 is not None: 

961 self.afi = 2 

962 return ICMPExtension_Object.self_build(self, **kwargs) 

963 

964 

965class ICMPExtension_Header(Packet): 

966 r""" 

967 ICMP Extension per RFC4884. 

968 

969 Example:: 

970 

971 pkt = IP(dst="127.0.0.1", src="127.0.0.1") / ICMP( 

972 type="time-exceeded", 

973 code="ttl-zero-during-transit", 

974 ext=ICMPExtension_Header() / ICMPExtension_InterfaceInformation( 

975 has_ifindex=1, 

976 has_ipaddr=1, 

977 has_ifname=1, 

978 ip4="10.10.10.10", 

979 ifname="hey", 

980 ) 

981 ) / IPerror(src="12.4.4.4", dst="12.1.1.1") / \ 

982 UDPerror(sport=42315, dport=33440) / \ 

983 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 

984 """ 

985 

986 name = "ICMP Extension Header (RFC4884)" 

987 show_indent = 0 

988 fields_desc = [ 

989 BitField("version", 2, 4), 

990 BitField("reserved", 0, 12), 

991 XShortField("chksum", None), 

992 ] 

993 

994 _min_ieo_len = len(ICMPExtension_Object()) 

995 

996 def post_build(self, p, pay): 

997 p += pay 

998 if self.chksum is None: 

999 ck = checksum(p) 

1000 p = p[:2] + chb(ck >> 8) + chb(ck & 0xFF) + p[4:] 

1001 return p 

1002 

1003 def guess_payload_class(self, payload): 

1004 if len(payload) < self._min_ieo_len: 

1005 return Packet.guess_payload_class(self, payload) 

1006 return ICMPExtension_Object 

1007 

1008 

1009class _ICMPExtensionField(TrailerField): 

1010 # We use a TrailerField for building only. Dissection is normal. 

1011 

1012 def __init__(self): 

1013 super(_ICMPExtensionField, self).__init__( 

1014 PacketField( 

1015 "ext", 

1016 None, 

1017 ICMPExtension_Header, 

1018 ), 

1019 ) 

1020 

1021 def getfield(self, pkt, s): 

1022 # RFC4884 section 5.2 says if the ICMP packet length 

1023 # is >144 then ICMP extensions start at byte 137. 

1024 if len(pkt.original) < 144: 

1025 return s, None 

1026 offset = 136 + len(s) - len(pkt.original) 

1027 data = s[offset:] 

1028 # Validate checksum 

1029 if checksum(data) == data[3:5]: 

1030 return s, None # failed 

1031 # Dissect 

1032 return s[:offset], ICMPExtension_Header(data) 

1033 

1034 def addfield(self, pkt, s, val): 

1035 if val is None: 

1036 return s 

1037 data = bytes(val) 

1038 # Calc how much padding we need, not how much we deserve 

1039 pad = 136 - len(pkt.payload) - len(s) 

1040 if pad < 0: 

1041 warning("ICMPExtension_Header is after the 136th octet of ICMP.") 

1042 return data 

1043 return super(_ICMPExtensionField, self).addfield(pkt, s, b"\x00" * pad + data) 

1044 

1045 

1046class _ICMPExtensionPadField(TrailerField): 

1047 def __init__(self): 

1048 super(_ICMPExtensionPadField, self).__init__( 

1049 StrFixedLenField("extpad", "", length=0) 

1050 ) 

1051 

1052 def i2repr(self, pkt, s): 

1053 if s and s == b"\x00" * len(s): 

1054 return "b'' (%s octets)" % len(s) 

1055 return self.fld.i2repr(pkt, s) 

1056 

1057 

1058def _ICMP_extpad_post_dissection(self, pkt): 

1059 # If we have padding, put it in 'extpad' for re-build 

1060 if pkt.ext: 

1061 pad = pkt.lastlayer() 

1062 if isinstance(pad, conf.padding_layer): 

1063 pad.underlayer.remove_payload() 

1064 pkt.extpad = pad.load 

1065 

1066 

1067icmptypes = {0: "echo-reply", 

1068 3: "dest-unreach", 

1069 4: "source-quench", 

1070 5: "redirect", 

1071 8: "echo-request", 

1072 9: "router-advertisement", 

1073 10: "router-solicitation", 

1074 11: "time-exceeded", 

1075 12: "parameter-problem", 

1076 13: "timestamp-request", 

1077 14: "timestamp-reply", 

1078 15: "information-request", 

1079 16: "information-response", 

1080 17: "address-mask-request", 

1081 18: "address-mask-reply", 

1082 30: "traceroute", 

1083 31: "datagram-conversion-error", 

1084 32: "mobile-host-redirect", 

1085 33: "ipv6-where-are-you", 

1086 34: "ipv6-i-am-here", 

1087 35: "mobile-registration-request", 

1088 36: "mobile-registration-reply", 

1089 37: "domain-name-request", 

1090 38: "domain-name-reply", 

1091 39: "skip", 

1092 40: "photuris"} 

1093 

1094 

1095icmpcodes = {3: {0: "network-unreachable", 

1096 1: "host-unreachable", 

1097 2: "protocol-unreachable", 

1098 3: "port-unreachable", 

1099 4: "fragmentation-needed", 

1100 5: "source-route-failed", 

1101 6: "network-unknown", 

1102 7: "host-unknown", 

1103 9: "network-prohibited", 

1104 10: "host-prohibited", 

1105 11: "TOS-network-unreachable", 

1106 12: "TOS-host-unreachable", 

1107 13: "communication-prohibited", 

1108 14: "host-precedence-violation", 

1109 15: "precedence-cutoff", }, 

1110 5: {0: "network-redirect", 

1111 1: "host-redirect", 

1112 2: "TOS-network-redirect", 

1113 3: "TOS-host-redirect", }, 

1114 11: {0: "ttl-zero-during-transit", 

1115 1: "ttl-zero-during-reassembly", }, 

1116 12: {0: "ip-header-bad", 

1117 1: "required-option-missing", }, 

1118 40: {0: "bad-spi", 

1119 1: "authentication-failed", 

1120 2: "decompression-failed", 

1121 3: "decryption-failed", 

1122 4: "need-authentification", 

1123 5: "need-authorization", }, } 

1124 

1125 

1126_icmp_answers = [ 

1127 (8, 0), 

1128 (13, 14), 

1129 (15, 16), 

1130 (17, 18), 

1131 (33, 34), 

1132 (35, 36), 

1133 (37, 38), 

1134] 

1135 

1136icmp_id_seq_types = [0, 8, 13, 14, 15, 16, 17, 18, 37, 38] 

1137 

1138 

1139class ICMP(Packet): 

1140 name = "ICMP" 

1141 fields_desc = [ 

1142 ByteEnumField("type", 8, icmptypes), 

1143 MultiEnumField("code", 0, icmpcodes, 

1144 depends_on=lambda pkt:pkt.type, fmt="B"), 

1145 XShortField("chksum", None), 

1146 ConditionalField( 

1147 XShortField("id", 0), 

1148 lambda pkt: pkt.type in icmp_id_seq_types 

1149 ), 

1150 ConditionalField( 

1151 XShortField("seq", 0), 

1152 lambda pkt: pkt.type in icmp_id_seq_types 

1153 ), 

1154 ConditionalField( 

1155 # Timestamp only (RFC792) 

1156 ICMPTimeStampField("ts_ori", None), 

1157 lambda pkt: pkt.type in [13, 14] 

1158 ), 

1159 ConditionalField( 

1160 # Timestamp only (RFC792) 

1161 ICMPTimeStampField("ts_rx", None), 

1162 lambda pkt: pkt.type in [13, 14] 

1163 ), 

1164 ConditionalField( 

1165 # Timestamp only (RFC792) 

1166 ICMPTimeStampField("ts_tx", None), 

1167 lambda pkt: pkt.type in [13, 14] 

1168 ), 

1169 ConditionalField( 

1170 # Redirect only (RFC792) 

1171 IPField("gw", "0.0.0.0"), 

1172 lambda pkt: pkt.type == 5 

1173 ), 

1174 ConditionalField( 

1175 # Parameter problem only (RFC792) 

1176 ByteField("ptr", 0), 

1177 lambda pkt: pkt.type == 12 

1178 ), 

1179 ConditionalField( 

1180 ByteField("reserved", 0), 

1181 lambda pkt: pkt.type in [3, 11] 

1182 ), 

1183 ConditionalField( 

1184 ByteField("length", 0), 

1185 lambda pkt: pkt.type in [3, 11, 12] 

1186 ), 

1187 ConditionalField( 

1188 IPField("addr_mask", "0.0.0.0"), 

1189 lambda pkt: pkt.type in [17, 18] 

1190 ), 

1191 ConditionalField( 

1192 ShortField("nexthopmtu", 0), 

1193 lambda pkt: pkt.type == 3 

1194 ), 

1195 MultipleTypeField( 

1196 [ 

1197 (ShortField("unused", 0), 

1198 lambda pkt:pkt.type in [11, 12]), 

1199 (IntField("unused", 0), 

1200 lambda pkt:pkt.type not in [0, 3, 5, 8, 11, 12, 

1201 13, 14, 15, 16, 17, 

1202 18]) 

1203 ], 

1204 StrFixedLenField("unused", "", length=0), 

1205 ), 

1206 # RFC4884 ICMP extension 

1207 ConditionalField( 

1208 _ICMPExtensionPadField(), 

1209 lambda pkt: pkt.type in [3, 11, 12], 

1210 ), 

1211 ConditionalField( 

1212 _ICMPExtensionField(), 

1213 lambda pkt: pkt.type in [3, 11, 12], 

1214 ), 

1215 ] 

1216 

1217 # To handle extpad 

1218 post_dissection = _ICMP_extpad_post_dissection 

1219 

1220 def post_build(self, p, pay): 

1221 p += pay 

1222 if self.chksum is None: 

1223 ck = checksum(p) 

1224 p = p[:2] + chb(ck >> 8) + chb(ck & 0xff) + p[4:] 

1225 return p 

1226 

1227 def hashret(self): 

1228 if self.type in icmp_id_seq_types: 

1229 return struct.pack("HH", self.id, self.seq) + self.payload.hashret() # noqa: E501 

1230 return self.payload.hashret() 

1231 

1232 def answers(self, other): 

1233 if not isinstance(other, ICMP): 

1234 return 0 

1235 if ((other.type, self.type) in _icmp_answers and 

1236 self.id == other.id and 

1237 self.seq == other.seq): 

1238 return 1 

1239 return 0 

1240 

1241 def guess_payload_class(self, payload): 

1242 if self.type in [3, 4, 5, 11, 12]: 

1243 return IPerror 

1244 else: 

1245 return None 

1246 

1247 def mysummary(self): 

1248 extra = "" 

1249 if self.ext: 

1250 extra = self.ext.payload.sprintf(" ext:%classnum%") 

1251 if isinstance(self.underlayer, IP): 

1252 return self.underlayer.sprintf( 

1253 "ICMP %IP.src% > %IP.dst% %ICMP.type% %ICMP.code%" 

1254 ) + extra 

1255 else: 

1256 return self.sprintf("ICMP %ICMP.type% %ICMP.code%") + extra 

1257 

1258 

1259# IP / TCP / UDP error packets 

1260 

1261class IPerror(IP): 

1262 name = "IP in ICMP" 

1263 

1264 def answers(self, other): 

1265 if not isinstance(other, IP): 

1266 return 0 

1267 

1268 # Check if IP addresses match 

1269 test_IPsrc = not conf.checkIPsrc or self.src == other.src 

1270 test_IPdst = self.dst == other.dst 

1271 

1272 # Check if IP ids match 

1273 test_IPid = not conf.checkIPID or self.id == other.id 

1274 test_IPid |= conf.checkIPID and self.id == socket.htons(other.id) 

1275 

1276 # Check if IP protocols match 

1277 test_IPproto = self.proto == other.proto 

1278 

1279 if not (test_IPsrc and test_IPdst and test_IPid and test_IPproto): 

1280 return 0 

1281 

1282 return self.payload.answers(other.payload) 

1283 

1284 def mysummary(self): 

1285 return Packet.mysummary(self) 

1286 

1287 

1288class TCPerror(TCP): 

1289 name = "TCP in ICMP" 

1290 fields_desc = ( 

1291 TCP.fields_desc[:2] + 

1292 # MayEnd after the 8 first octets. 

1293 [MayEnd(TCP.fields_desc[2])] + 

1294 TCP.fields_desc[3:] 

1295 ) 

1296 

1297 def answers(self, other): 

1298 if not isinstance(other, TCP): 

1299 return 0 

1300 if conf.checkIPsrc: 

1301 if not ((self.sport == other.sport) and 

1302 (self.dport == other.dport)): 

1303 return 0 

1304 if conf.check_TCPerror_seqack: 

1305 if self.seq is not None: 

1306 if self.seq != other.seq: 

1307 return 0 

1308 if self.ack is not None: 

1309 if self.ack != other.ack: 

1310 return 0 

1311 return 1 

1312 

1313 def mysummary(self): 

1314 return Packet.mysummary(self) 

1315 

1316 

1317class UDPerror(UDP): 

1318 name = "UDP in ICMP" 

1319 

1320 def answers(self, other): 

1321 if not isinstance(other, UDP): 

1322 return 0 

1323 if conf.checkIPsrc: 

1324 if not ((self.sport == other.sport) and 

1325 (self.dport == other.dport)): 

1326 return 0 

1327 return 1 

1328 

1329 def mysummary(self): 

1330 return Packet.mysummary(self) 

1331 

1332 

1333class ICMPerror(ICMP): 

1334 name = "ICMP in ICMP" 

1335 

1336 def answers(self, other): 

1337 if not isinstance(other, ICMP): 

1338 return 0 

1339 if not ((self.type == other.type) and 

1340 (self.code == other.code)): 

1341 return 0 

1342 if self.code in [0, 8, 13, 14, 17, 18]: 

1343 if (self.id == other.id and 

1344 self.seq == other.seq): 

1345 return 1 

1346 else: 

1347 return 0 

1348 else: 

1349 return 1 

1350 

1351 def mysummary(self): 

1352 return Packet.mysummary(self) 

1353 

1354 

1355bind_layers(Ether, IP, type=2048) 

1356bind_layers(CookedLinux, IP, proto=2048) 

1357bind_layers(GRE, IP, proto=2048) 

1358bind_layers(SNAP, IP, code=2048) 

1359bind_bottom_up(Loopback, IP, type=0) 

1360bind_layers(Loopback, IP, type=socket.AF_INET) 

1361bind_layers(IPerror, IPerror, frag=0, proto=4) 

1362bind_layers(IPerror, ICMPerror, frag=0, proto=1) 

1363bind_layers(IPerror, TCPerror, frag=0, proto=6) 

1364bind_layers(IPerror, UDPerror, frag=0, proto=17) 

1365bind_layers(IP, IP, frag=0, proto=4) 

1366bind_layers(IP, ICMP, frag=0, proto=1) 

1367bind_layers(IP, TCP, frag=0, proto=6) 

1368bind_layers(IP, UDP, frag=0, proto=17) 

1369bind_layers(IP, GRE, frag=0, proto=47) 

1370bind_layers(UDP, GRE, dport=4754) 

1371 

1372conf.l2types.register(DLT_RAW, IP) 

1373conf.l2types.register_num2layer(DLT_RAW_ALT, IP) 

1374conf.l2types.register(DLT_IPV4, IP) 

1375if OPENBSD: 

1376 conf.l2types.register_num2layer(228, IP) 

1377 

1378conf.l3types.register(ETH_P_IP, IP) 

1379conf.l3types.register_num2layer(ETH_P_ALL, IP) 

1380 

1381 

1382def inet_register_l3(l2, l3): 

1383 """ 

1384 Resolves the default L2 destination address when IP is used. 

1385 """ 

1386 return getmacbyip(l3.dst) 

1387 

1388 

1389conf.neighbor.register_l3(Ether, IP, inet_register_l3) 

1390conf.neighbor.register_l3(Dot3, IP, inet_register_l3) 

1391 

1392 

1393################### 

1394# Fragmentation # 

1395################### 

1396 

1397@conf.commands.register 

1398def fragment(pkt, fragsize=1480): 

1399 """Fragment a big IP datagram""" 

1400 if fragsize < 8: 

1401 warning("fragsize cannot be lower than 8") 

1402 fragsize = max(fragsize, 8) 

1403 lastfragsz = fragsize 

1404 fragsize -= fragsize % 8 

1405 lst = [] 

1406 for p in pkt: 

1407 s = raw(p[IP].payload) 

1408 nb = (len(s) - lastfragsz + fragsize - 1) // fragsize + 1 

1409 for i in range(nb): 

1410 q = p.copy() 

1411 del q[IP].payload 

1412 del q[IP].chksum 

1413 del q[IP].len 

1414 if i != nb - 1: 

1415 q[IP].flags |= 1 

1416 fragend = (i + 1) * fragsize 

1417 else: 

1418 fragend = i * fragsize + lastfragsz 

1419 q[IP].frag += i * fragsize // 8 

1420 r = conf.raw_layer(load=s[i * fragsize:fragend]) 

1421 r.overload_fields = p[IP].payload.overload_fields.copy() 

1422 q.add_payload(r) 

1423 lst.append(q) 

1424 return lst 

1425 

1426 

1427@conf.commands.register 

1428def overlap_frag(p, overlap, fragsize=8, overlap_fragsize=None): 

1429 """Build overlapping fragments to bypass NIPS 

1430 

1431p: the original packet 

1432overlap: the overlapping data 

1433fragsize: the fragment size of the packet 

1434overlap_fragsize: the fragment size of the overlapping packet""" 

1435 

1436 if overlap_fragsize is None: 

1437 overlap_fragsize = fragsize 

1438 q = p.copy() 

1439 del q[IP].payload 

1440 q[IP].add_payload(overlap) 

1441 

1442 qfrag = fragment(q, overlap_fragsize) 

1443 qfrag[-1][IP].flags |= 1 

1444 return qfrag + fragment(p, fragsize) 

1445 

1446 

1447class BadFragments(ValueError): 

1448 def __init__(self, *args, **kwargs): 

1449 self.frags = kwargs.pop("frags", None) 

1450 super(BadFragments, self).__init__(*args, **kwargs) 

1451 

1452 

1453def _defrag_iter_and_check_offsets(frags): 

1454 """ 

1455 Internal generator used in _defrag_ip_pkt 

1456 """ 

1457 offset = 0 

1458 for pkt, o, length in frags: 

1459 if offset != o: 

1460 if offset > o: 

1461 op = ">" 

1462 else: 

1463 op = "<" 

1464 warning("Fragment overlap (%i %s %i) on %r" % (offset, op, o, pkt)) 

1465 raise BadFragments 

1466 offset += length 

1467 yield bytes(pkt[IP].payload) 

1468 

1469 

1470def _defrag_ip_pkt(pkt, frags): 

1471 """ 

1472 Defragment a single IP packet. 

1473 

1474 :param pkt: the new pkt 

1475 :param frags: a defaultdict(list) used for storage 

1476 :return: a tuple (fragmented, defragmented_value) 

1477 """ 

1478 ip = pkt[IP] 

1479 if pkt.frag != 0 or ip.flags.MF: 

1480 # fragmented ! 

1481 uid = (ip.id, ip.src, ip.dst, ip.proto) 

1482 if ip.len is None or ip.ihl is None: 

1483 fraglen = len(ip.payload) 

1484 else: 

1485 fraglen = ip.len - (ip.ihl << 2) 

1486 # (pkt, frag offset, frag len) 

1487 frags[uid].append((pkt, ip.frag << 3, fraglen)) 

1488 if not ip.flags.MF: # no more fragments = last fragment 

1489 curfrags = sorted(frags[uid], key=lambda x: x[1]) # sort by offset 

1490 try: 

1491 data = b"".join(_defrag_iter_and_check_offsets(curfrags)) 

1492 except ValueError: 

1493 # bad fragment 

1494 badfrags = frags[uid] 

1495 del frags[uid] 

1496 raise BadFragments(frags=badfrags) 

1497 # re-build initial packet without fragmentation 

1498 p = curfrags[0][0].copy() 

1499 pay_class = p[IP].payload.__class__ 

1500 p[IP].flags.MF = False 

1501 p[IP].remove_payload() 

1502 p[IP].len = None 

1503 p[IP].chksum = None 

1504 # append defragmented payload 

1505 p /= pay_class(data) 

1506 # cleanup 

1507 del frags[uid] 

1508 return True, p 

1509 return True, None 

1510 return False, pkt 

1511 

1512 

1513def _defrag_logic(plist, complete=False): 

1514 """ 

1515 Internal function used to defragment a list of packets. 

1516 It contains the logic behind the defrag() and defragment() functions 

1517 """ 

1518 frags = defaultdict(list) 

1519 final = [] 

1520 notfrag = [] 

1521 badfrag = [] 

1522 # Defrag 

1523 for i, pkt in enumerate(plist): 

1524 if IP not in pkt: 

1525 # no IP layer 

1526 if complete: 

1527 final.append(pkt) 

1528 continue 

1529 try: 

1530 fragmented, defragmented_value = _defrag_ip_pkt( 

1531 pkt, 

1532 frags, 

1533 ) 

1534 except BadFragments as ex: 

1535 if complete: 

1536 final.extend(ex.frags) 

1537 else: 

1538 badfrag.extend(ex.frags) 

1539 continue 

1540 if complete and defragmented_value: 

1541 final.append(defragmented_value) 

1542 elif defragmented_value: 

1543 if fragmented: 

1544 final.append(defragmented_value) 

1545 else: 

1546 notfrag.append(defragmented_value) 

1547 # Return 

1548 if complete: 

1549 if hasattr(plist, "listname"): 

1550 name = "Defragmented %s" % plist.listname 

1551 else: 

1552 name = "Defragmented" 

1553 return PacketList(final, name=name) 

1554 else: 

1555 return PacketList(notfrag), PacketList(final), PacketList(badfrag) 

1556 

1557 

1558@conf.commands.register 

1559def defrag(plist): 

1560 """defrag(plist) -> ([not fragmented], [defragmented], 

1561 [ [bad fragments], [bad fragments], ... ])""" 

1562 return _defrag_logic(plist, complete=False) 

1563 

1564 

1565@conf.commands.register 

1566def defragment(plist): 

1567 """defragment(plist) -> plist defragmented as much as possible """ 

1568 return _defrag_logic(plist, complete=True) 

1569 

1570 

1571# Add timeskew_graph() method to PacketList 

1572def _packetlist_timeskew_graph(self, ip, **kargs): 

1573 """Tries to graph the timeskew between the timestamps and real time for a given ip""" # noqa: E501 

1574 # Defer imports of matplotlib until its needed 

1575 # because it has a heavy dep chain 

1576 from scapy.libs.matplot import ( 

1577 plt, 

1578 MATPLOTLIB_INLINED, 

1579 MATPLOTLIB_DEFAULT_PLOT_KARGS 

1580 ) 

1581 

1582 # Filter TCP segments which source address is 'ip' 

1583 tmp = (self._elt2pkt(x) for x in self.res) 

1584 b = (x for x in tmp if IP in x and x[IP].src == ip and TCP in x) 

1585 

1586 # Build a list of tuples (creation_time, replied_timestamp) 

1587 c = [] 

1588 tsf = ICMPTimeStampField("", None) 

1589 for p in b: 

1590 opts = p.getlayer(TCP).options 

1591 for o in opts: 

1592 if o[0] == "Timestamp": 

1593 c.append((p.time, tsf.any2i("", o[1][0]))) 

1594 

1595 # Stop if the list is empty 

1596 if not c: 

1597 warning("No timestamps found in packet list") 

1598 return [] 

1599 

1600 # Prepare the data that will be plotted 

1601 first_creation_time = c[0][0] 

1602 first_replied_timestamp = c[0][1] 

1603 

1604 def _wrap_data(ts_tuple, wrap_seconds=2000): 

1605 """Wrap the list of tuples.""" 

1606 

1607 ct, rt = ts_tuple # (creation_time, replied_timestamp) 

1608 X = ct % wrap_seconds 

1609 Y = ((ct - first_creation_time) - ((rt - first_replied_timestamp) / 1000.0)) # noqa: E501 

1610 

1611 return X, Y 

1612 

1613 data = [_wrap_data(e) for e in c] 

1614 

1615 # Mimic the default gnuplot output 

1616 if kargs == {}: 

1617 kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS 

1618 lines = plt.plot(data, **kargs) 

1619 

1620 # Call show() if matplotlib is not inlined 

1621 if not MATPLOTLIB_INLINED: 

1622 plt.show() 

1623 

1624 return lines 

1625 

1626 

1627_PacketList.timeskew_graph = _packetlist_timeskew_graph 

1628 

1629 

1630# Create a new packet list 

1631class TracerouteResult(SndRcvList): 

1632 __slots__ = ["graphdef", "graphpadding", "graphASres", "padding", "hloc", 

1633 "nloc"] 

1634 

1635 def __init__(self, res=None, name="Traceroute", stats=None): 

1636 SndRcvList.__init__(self, res, name, stats) 

1637 self.graphdef = None 

1638 self.graphASres = None 

1639 self.padding = 0 

1640 self.hloc = None 

1641 self.nloc = None 

1642 

1643 def show(self): 

1644 return self.make_table(lambda s, r: (s.sprintf("%IP.dst%:{TCP:tcp%ir,TCP.dport%}{UDP:udp%ir,UDP.dport%}{ICMP:ICMP}"), # noqa: E501 

1645 s.ttl, 

1646 r.sprintf("%-15s,IP.src% {TCP:%TCP.flags%}{ICMP:%ir,ICMP.type%}"))) # noqa: E501 

1647 

1648 def get_trace(self): 

1649 trace = {} 

1650 for s, r in self.res: 

1651 if IP not in s: 

1652 continue 

1653 d = s[IP].dst 

1654 if d not in trace: 

1655 trace[d] = {} 

1656 trace[d][s[IP].ttl] = r[IP].src, ICMP not in r 

1657 for k in trace.values(): 

1658 try: 

1659 m = min(x for x, y in k.items() if y[1]) 

1660 except ValueError: 

1661 continue 

1662 for li in list(k): # use list(): k is modified in the loop 

1663 if li > m: 

1664 del k[li] 

1665 return trace 

1666 

1667 def trace3D(self, join=True): 

1668 """Give a 3D representation of the traceroute. 

1669 right button: rotate the scene 

1670 middle button: zoom 

1671 shift-left button: move the scene 

1672 left button on a ball: toggle IP displaying 

1673 double-click button on a ball: scan ports 21,22,23,25,80 and 443 and display the result""" # noqa: E501 

1674 # When not ran from a notebook, vpython pooly closes itself 

1675 # using os._exit once finished. We pack it into a Process 

1676 import multiprocessing 

1677 p = multiprocessing.Process(target=self.trace3D_notebook) 

1678 p.start() 

1679 if join: 

1680 p.join() 

1681 

1682 def trace3D_notebook(self): 

1683 """Same than trace3D, used when ran from Jupyter notebooks""" 

1684 trace = self.get_trace() 

1685 import vpython 

1686 

1687 class IPsphere(vpython.sphere): 

1688 def __init__(self, ip, **kargs): 

1689 vpython.sphere.__init__(self, **kargs) 

1690 self.ip = ip 

1691 self.label = None 

1692 self.setlabel(self.ip) 

1693 self.last_clicked = None 

1694 self.full = False 

1695 self.savcolor = vpython.vec(*self.color.value) 

1696 

1697 def fullinfos(self): 

1698 self.full = True 

1699 self.color = vpython.vec(1, 0, 0) 

1700 a, b = sr(IP(dst=self.ip) / TCP(dport=[21, 22, 23, 25, 80, 443], flags="S"), timeout=2, verbose=0) # noqa: E501 

1701 if len(a) == 0: 

1702 txt = "%s:\nno results" % self.ip 

1703 else: 

1704 txt = "%s:\n" % self.ip 

1705 for s, r in a: 

1706 txt += r.sprintf("{TCP:%IP.src%:%TCP.sport% %TCP.flags%}{TCPerror:%IPerror.dst%:%TCPerror.dport% %IP.src% %ir,ICMP.type%}\n") # noqa: E501 

1707 self.setlabel(txt, visible=1) 

1708 

1709 def unfull(self): 

1710 self.color = self.savcolor 

1711 self.full = False 

1712 self.setlabel(self.ip) 

1713 

1714 def setlabel(self, txt, visible=None): 

1715 if self.label is not None: 

1716 if visible is None: 

1717 visible = self.label.visible 

1718 self.label.visible = 0 

1719 elif visible is None: 

1720 visible = 0 

1721 self.label = vpython.label(text=txt, pos=self.pos, space=self.radius, xoffset=10, yoffset=20, visible=visible) # noqa: E501 

1722 

1723 def check_double_click(self): 

1724 try: 

1725 if self.full or not self.label.visible: 

1726 return False 

1727 if self.last_clicked is not None: 

1728 return (time.time() - self.last_clicked) < 0.5 

1729 return False 

1730 finally: 

1731 self.last_clicked = time.time() 

1732 

1733 def action(self): 

1734 self.label.visible ^= 1 

1735 if self.full: 

1736 self.unfull() 

1737 

1738 vpython.scene = vpython.canvas() 

1739 vpython.scene.title = "<center><u><b>%s</b></u></center>" % self.listname # noqa: E501 

1740 vpython.scene.append_to_caption( 

1741 re.sub( 

1742 r'\%(.*)\%', 

1743 r'<span style="color: red">\1</span>', 

1744 re.sub( 

1745 r'\`(.*)\`', 

1746 r'<span style="color: #3399ff">\1</span>', 

1747 """<u><b>Commands:</b></u> 

1748%Click% to toggle information about a node. 

1749%Double click% to perform a quick web scan on this node. 

1750<u><b>Camera usage:</b></u> 

1751`Right button drag or Ctrl-drag` to rotate "camera" to view scene. 

1752`Shift-drag` to move the object around. 

1753`Middle button or Alt-drag` to drag up or down to zoom in or out. 

1754 On a two-button mouse, `middle is wheel or left + right`. 

1755Touch screen: pinch/extend to zoom, swipe or two-finger rotate.""" 

1756 ) 

1757 ) 

1758 ) 

1759 vpython.scene.exit = True 

1760 rings = {} 

1761 tr3d = {} 

1762 for i in trace: 

1763 tr = trace[i] 

1764 tr3d[i] = [] 

1765 for t in range(1, max(tr) + 1): 

1766 if t not in rings: 

1767 rings[t] = [] 

1768 if t in tr: 

1769 if tr[t] not in rings[t]: 

1770 rings[t].append(tr[t]) 

1771 tr3d[i].append(rings[t].index(tr[t])) 

1772 else: 

1773 rings[t].append(("unk", -1)) 

1774 tr3d[i].append(len(rings[t]) - 1) 

1775 

1776 for t in rings: 

1777 r = rings[t] 

1778 tmp_len = len(r) 

1779 for i in range(tmp_len): 

1780 if r[i][1] == -1: 

1781 col = vpython.vec(0.75, 0.75, 0.75) 

1782 elif r[i][1]: 

1783 col = vpython.color.green 

1784 else: 

1785 col = vpython.color.blue 

1786 

1787 s = IPsphere(pos=vpython.vec((tmp_len - 1) * vpython.cos(2 * i * vpython.pi / tmp_len), (tmp_len - 1) * vpython.sin(2 * i * vpython.pi / tmp_len), 2 * t), # noqa: E501 

1788 ip=r[i][0], 

1789 color=col) 

1790 for trlst in tr3d.values(): 

1791 if t <= len(trlst): 

1792 if trlst[t - 1] == i: 

1793 trlst[t - 1] = s 

1794 forecol = colgen(0.625, 0.4375, 0.25, 0.125) 

1795 for trlst in tr3d.values(): 

1796 col = vpython.vec(*next(forecol)) 

1797 start = vpython.vec(0, 0, 0) 

1798 for ip in trlst: 

1799 vpython.cylinder(pos=start, axis=ip.pos - start, color=col, radius=0.2) # noqa: E501 

1800 start = ip.pos 

1801 

1802 vpython.rate(50) 

1803 

1804 # Keys handling 

1805 # TODO: there is currently no way of closing vpython correctly 

1806 # https://github.com/BruceSherwood/vpython-jupyter/issues/36 

1807 # def keyboard_press(ev): 

1808 # k = ev.key 

1809 # if k == "esc" or k == "q": 

1810 # pass # TODO: close 

1811 # 

1812 # vpython.scene.bind('keydown', keyboard_press) 

1813 

1814 # Mouse handling 

1815 def mouse_click(ev): 

1816 if ev.press == "left": 

1817 o = vpython.scene.mouse.pick 

1818 if o and isinstance(o, IPsphere): 

1819 if o.check_double_click(): 

1820 if o.ip == "unk": 

1821 return 

1822 o.fullinfos() 

1823 else: 

1824 o.action() 

1825 

1826 vpython.scene.bind('mousedown', mouse_click) 

1827 

1828 def world_trace(self): 

1829 """Display traceroute results on a world map.""" 

1830 

1831 # Check that the geoip2 module can be imported 

1832 # Doc: http://geoip2.readthedocs.io/en/latest/ 

1833 from scapy.libs.matplot import plt, MATPLOTLIB, MATPLOTLIB_INLINED 

1834 

1835 try: 

1836 # GeoIP2 modules need to be imported as below 

1837 import geoip2.database 

1838 import geoip2.errors 

1839 except ImportError: 

1840 log_runtime.error( 

1841 "Cannot import geoip2. Won't be able to plot the world." 

1842 ) 

1843 return [] 

1844 # Check availability of database 

1845 if not conf.geoip_city: 

1846 log_runtime.error( 

1847 "Cannot import the geolite2 CITY database.\n" 

1848 "Download it from http://dev.maxmind.com/geoip/geoip2/geolite2/" # noqa: E501 

1849 " then set its path to conf.geoip_city" 

1850 ) 

1851 return [] 

1852 # Check availability of plotting devices 

1853 try: 

1854 import cartopy.crs as ccrs 

1855 except ImportError: 

1856 log_runtime.error( 

1857 "Cannot import cartopy.\n" 

1858 "More infos on http://scitools.org.uk/cartopy/docs/latest/installing.html" # noqa: E501 

1859 ) 

1860 return [] 

1861 if not MATPLOTLIB: 

1862 log_runtime.error( 

1863 "Matplotlib is not installed. Won't be able to plot the world." 

1864 ) 

1865 return [] 

1866 

1867 # Open & read the GeoListIP2 database 

1868 try: 

1869 db = geoip2.database.Reader(conf.geoip_city) 

1870 except Exception: 

1871 log_runtime.error( 

1872 "Cannot open geoip2 database at %s", 

1873 conf.geoip_city 

1874 ) 

1875 return [] 

1876 

1877 # Regroup results per trace 

1878 ips = {} 

1879 rt = {} 

1880 ports_done = {} 

1881 for s, r in self.res: 

1882 ips[r.src] = None 

1883 if s.haslayer(TCP) or s.haslayer(UDP): 

1884 trace_id = (s.src, s.dst, s.proto, s.dport) 

1885 elif s.haslayer(ICMP): 

1886 trace_id = (s.src, s.dst, s.proto, s.type) 

1887 else: 

1888 trace_id = (s.src, s.dst, s.proto, 0) 

1889 trace = rt.get(trace_id, {}) 

1890 if not r.haslayer(ICMP) or r.type != 11: 

1891 if trace_id in ports_done: 

1892 continue 

1893 ports_done[trace_id] = None 

1894 trace[s.ttl] = r.src 

1895 rt[trace_id] = trace 

1896 

1897 # Get the addresses locations 

1898 trt = {} 

1899 for trace_id in rt: 

1900 trace = rt[trace_id] 

1901 loctrace = [] 

1902 for i in range(max(trace)): 

1903 ip = trace.get(i, None) 

1904 if ip is None: 

1905 continue 

1906 # Fetch database 

1907 try: 

1908 sresult = db.city(ip) 

1909 except geoip2.errors.AddressNotFoundError: 

1910 continue 

1911 loctrace.append((sresult.location.longitude, sresult.location.latitude)) # noqa: E501 

1912 if loctrace: 

1913 trt[trace_id] = loctrace 

1914 

1915 # Load the map renderer 

1916 plt.figure(num='Scapy') 

1917 ax = plt.axes(projection=ccrs.PlateCarree()) 

1918 # Draw countries 

1919 ax.coastlines() 

1920 ax.stock_img() 

1921 # Set normal size 

1922 ax.set_global() 

1923 # Add title 

1924 plt.title("Scapy traceroute results") 

1925 

1926 from matplotlib.collections import LineCollection 

1927 from matplotlib import colors as mcolors 

1928 colors_cycle = iter(mcolors.BASE_COLORS) 

1929 lines = [] 

1930 

1931 # Split traceroute measurement 

1932 for key, trc in trt.items(): 

1933 # Get next color 

1934 color = next(colors_cycle) 

1935 # Gather mesurments data 

1936 data_lines = [(trc[i], trc[i + 1]) for i in range(len(trc) - 1)] 

1937 # Create line collection 

1938 line_col = LineCollection(data_lines, linewidths=2, 

1939 label=key[1], 

1940 color=color) 

1941 lines.append(line_col) 

1942 ax.add_collection(line_col) 

1943 # Create map points 

1944 lines.extend([ax.plot(*x, marker='.', color=color) for x in trc]) 

1945 

1946 # Generate legend 

1947 ax.legend() 

1948 

1949 # Call show() if matplotlib is not inlined 

1950 if not MATPLOTLIB_INLINED: 

1951 plt.show() 

1952 

1953 # Clean 

1954 ax.remove() 

1955 

1956 # Return the drawn lines 

1957 return lines 

1958 

1959 def make_graph(self, ASres=None, padding=0): 

1960 self.graphASres = ASres 

1961 self.graphpadding = padding 

1962 ips = {} 

1963 rt = {} 

1964 ports = {} 

1965 ports_done = {} 

1966 for s, r in self.res: 

1967 r = r.getlayer(IP) or (conf.ipv6_enabled and r[scapy.layers.inet6.IPv6]) or r # noqa: E501 

1968 s = s.getlayer(IP) or (conf.ipv6_enabled and s[scapy.layers.inet6.IPv6]) or s # noqa: E501 

1969 ips[r.src] = None 

1970 if TCP in s: 

1971 trace_id = (s.src, s.dst, 6, s.dport) 

1972 elif UDP in s: 

1973 trace_id = (s.src, s.dst, 17, s.dport) 

1974 elif ICMP in s: 

1975 trace_id = (s.src, s.dst, 1, s.type) 

1976 else: 

1977 trace_id = (s.src, s.dst, s.proto, 0) 

1978 trace = rt.get(trace_id, {}) 

1979 ttl = conf.ipv6_enabled and scapy.layers.inet6.IPv6 in s and s.hlim or s.ttl # noqa: E501 

1980 if not (ICMP in r and r[ICMP].type == 11) and not (conf.ipv6_enabled and scapy.layers.inet6.IPv6 in r and scapy.layers.inet6.ICMPv6TimeExceeded in r): # noqa: E501 

1981 if trace_id in ports_done: 

1982 continue 

1983 ports_done[trace_id] = None 

1984 p = ports.get(r.src, []) 

1985 if TCP in r: 

1986 p.append(r.sprintf("<T%ir,TCP.sport%> %TCP.sport% %TCP.flags%")) # noqa: E501 

1987 trace[ttl] = r.sprintf('"%r,src%":T%ir,TCP.sport%') 

1988 elif UDP in r: 

1989 p.append(r.sprintf("<U%ir,UDP.sport%> %UDP.sport%")) 

1990 trace[ttl] = r.sprintf('"%r,src%":U%ir,UDP.sport%') 

1991 elif ICMP in r: 

1992 p.append(r.sprintf("<I%ir,ICMP.type%> ICMP %ICMP.type%")) 

1993 trace[ttl] = r.sprintf('"%r,src%":I%ir,ICMP.type%') 

1994 else: 

1995 p.append(r.sprintf("{IP:<P%ir,proto%> IP %proto%}{IPv6:<P%ir,nh%> IPv6 %nh%}")) # noqa: E501 

1996 trace[ttl] = r.sprintf('"%r,src%":{IP:P%ir,proto%}{IPv6:P%ir,nh%}') # noqa: E501 

1997 ports[r.src] = p 

1998 else: 

1999 trace[ttl] = r.sprintf('"%r,src%"') 

2000 rt[trace_id] = trace 

2001 

2002 # Fill holes with unk%i nodes 

2003 unknown_label = incremental_label("unk%i") 

2004 blackholes = [] 

2005 bhip = {} 

2006 for rtk in rt: 

2007 trace = rt[rtk] 

2008 max_trace = max(trace) 

2009 for n in range(min(trace), max_trace): 

2010 if n not in trace: 

2011 trace[n] = next(unknown_label) 

2012 if rtk not in ports_done: 

2013 if rtk[2] == 1: # ICMP 

2014 bh = "%s %i/icmp" % (rtk[1], rtk[3]) 

2015 elif rtk[2] == 6: # TCP 

2016 bh = "%s %i/tcp" % (rtk[1], rtk[3]) 

2017 elif rtk[2] == 17: # UDP 

2018 bh = '%s %i/udp' % (rtk[1], rtk[3]) 

2019 else: 

2020 bh = '%s %i/proto' % (rtk[1], rtk[2]) 

2021 ips[bh] = None 

2022 bhip[rtk[1]] = bh 

2023 bh = '"%s"' % bh 

2024 trace[max_trace + 1] = bh 

2025 blackholes.append(bh) 

2026 

2027 # Find AS numbers 

2028 ASN_query_list = set(x.rsplit(" ", 1)[0] for x in ips) 

2029 if ASres is None: 

2030 ASNlist = [] 

2031 else: 

2032 ASNlist = ASres.resolve(*ASN_query_list) 

2033 

2034 ASNs = {} 

2035 ASDs = {} 

2036 for ip, asn, desc, in ASNlist: 

2037 if asn is None: 

2038 continue 

2039 iplist = ASNs.get(asn, []) 

2040 if ip in bhip: 

2041 if ip in ports: 

2042 iplist.append(ip) 

2043 iplist.append(bhip[ip]) 

2044 else: 

2045 iplist.append(ip) 

2046 ASNs[asn] = iplist 

2047 ASDs[asn] = desc 

2048 

2049 backcolorlist = colgen("60", "86", "ba", "ff") 

2050 forecolorlist = colgen("a0", "70", "40", "20") 

2051 

2052 s = "digraph trace {\n" 

2053 

2054 s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" 

2055 

2056 s += "\n#ASN clustering\n" 

2057 for asn in ASNs: 

2058 s += '\tsubgraph cluster_%s {\n' % asn 

2059 col = next(backcolorlist) 

2060 s += '\t\tcolor="#%s%s%s";' % col 

2061 s += '\t\tnode [fillcolor="#%s%s%s",style=filled];' % col 

2062 s += '\t\tfontsize = 10;' 

2063 s += '\t\tlabel = "%s\\n[%s]"\n' % ( 

2064 graphviz_escape(asn), 

2065 graphviz_escape(ASDs[asn]), 

2066 ) 

2067 for ip in ASNs[asn]: 

2068 

2069 s += '\t\t"%s";\n' % ip 

2070 s += "\t}\n" 

2071 

2072 s += "#endpoints\n" 

2073 for p in ports: 

2074 s += '\t"%s" [shape=record,color=black,fillcolor=green,style=filled,label="%s|%s"];\n' % (p, p, "|".join(ports[p])) # noqa: E501 

2075 

2076 s += "\n#Blackholes\n" 

2077 for bh in blackholes: 

2078 s += '\t%s [shape=octagon,color=black,fillcolor=red,style=filled];\n' % bh # noqa: E501 

2079 

2080 if padding: 

2081 s += "\n#Padding\n" 

2082 pad = {} 

2083 for snd, rcv in self.res: 

2084 if rcv.src not in ports and rcv.haslayer(conf.padding_layer): 

2085 p = rcv.getlayer(conf.padding_layer).load 

2086 if p != b"\x00" * len(p): 

2087 pad[rcv.src] = None 

2088 for rcv in pad: 

2089 s += '\t"%s" [shape=triangle,color=black,fillcolor=red,style=filled];\n' % rcv # noqa: E501 

2090 

2091 s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" 

2092 

2093 for rtk in rt: 

2094 s += "#---[%s\n" % repr(rtk) 

2095 s += '\t\tedge [color="#%s%s%s"];\n' % next(forecolorlist) 

2096 trace = rt[rtk] 

2097 maxtrace = max(trace) 

2098 for n in range(min(trace), maxtrace): 

2099 s += '\t%s ->\n' % trace[n] 

2100 s += '\t%s;\n' % trace[maxtrace] 

2101 

2102 s += "}\n" 

2103 self.graphdef = s 

2104 

2105 def graph(self, ASres=conf.AS_resolver, padding=0, **kargs): 

2106 """x.graph(ASres=conf.AS_resolver, other args): 

2107 ASres=None : no AS resolver => no clustering 

2108 ASres=AS_resolver() : default whois AS resolver (riswhois.ripe.net) 

2109 ASres=AS_resolver_cymru(): use whois.cymru.com whois database 

2110 ASres=AS_resolver(server="whois.ra.net") 

2111 type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option # noqa: E501 

2112 target: filename or redirect. Defaults pipe to Imagemagick's display program # noqa: E501 

2113 prog: which graphviz program to use""" 

2114 if (self.graphdef is None or 

2115 self.graphASres != ASres or 

2116 self.graphpadding != padding): 

2117 self.make_graph(ASres, padding) 

2118 

2119 return do_graph(self.graphdef, **kargs) 

2120 

2121 

2122@conf.commands.register 

2123def traceroute(target, dport=80, minttl=1, maxttl=30, sport=RandShort(), l4=None, filter=None, timeout=2, verbose=None, **kargs): # noqa: E501 

2124 """Instant TCP traceroute 

2125 

2126 :param target: hostnames or IP addresses 

2127 :param dport: TCP destination port (default is 80) 

2128 :param minttl: minimum TTL (default is 1) 

2129 :param maxttl: maximum TTL (default is 30) 

2130 :param sport: TCP source port (default is random) 

2131 :param l4: use a Scapy packet instead of TCP 

2132 :param filter: BPF filter applied to received packets 

2133 :param timeout: time to wait for answers (default is 2s) 

2134 :param verbose: detailed output 

2135 :return: an TracerouteResult, and a list of unanswered packets""" 

2136 if verbose is None: 

2137 verbose = conf.verb 

2138 if filter is None: 

2139 # we only consider ICMP error packets and TCP packets with at 

2140 # least the ACK flag set *and* either the SYN or the RST flag 

2141 # set 

2142 filter = "(icmp and (icmp[0]=3 or icmp[0]=4 or icmp[0]=5 or icmp[0]=11 or icmp[0]=12)) or (tcp and (tcp[13] & 0x16 > 0x10))" # noqa: E501 

2143 if l4 is None: 

2144 a, b = sr(IP(dst=target, id=RandShort(), ttl=(minttl, maxttl)) / TCP(seq=RandInt(), sport=sport, dport=dport), # noqa: E501 

2145 timeout=timeout, filter=filter, verbose=verbose, **kargs) 

2146 else: 

2147 # this should always work 

2148 filter = "ip" 

2149 a, b = sr(IP(dst=target, id=RandShort(), ttl=(minttl, maxttl)) / l4, 

2150 timeout=timeout, filter=filter, verbose=verbose, **kargs) 

2151 

2152 a = TracerouteResult(a.res) 

2153 if verbose: 

2154 a.show() 

2155 return a, b 

2156 

2157 

2158@conf.commands.register 

2159def traceroute_map(ips, **kargs): 

2160 """Util function to call traceroute on multiple targets, then 

2161 show the different paths on a map. 

2162 

2163 :param ips: a list of IPs on which traceroute will be called 

2164 :param kargs: (optional) kwargs, passed to traceroute 

2165 """ 

2166 kargs.setdefault("verbose", 0) 

2167 return traceroute(ips)[0].world_trace() 

2168 

2169############################# 

2170# Simple TCP client stack # 

2171############################# 

2172 

2173 

2174class TCP_client(Automaton): 

2175 """ 

2176 Creates a TCP Client Automaton. 

2177 This automaton will handle TCP 3-way handshake. 

2178 

2179 Usage: the easiest usage is to use it as a SuperSocket. 

2180 >>> a = TCP_client.tcplink(HTTP, "www.google.com", 80) 

2181 >>> a.send(HTTPRequest()) 

2182 >>> a.recv() 

2183 

2184 :param ip: the ip to connect to 

2185 :param port: 

2186 :param src: (optional) use another source IP 

2187 :param sport: (optional) the TCP source port (default: random) 

2188 :param seq: (optional) initial TCP sequence number (default: random) 

2189 """ 

2190 

2191 def parse_args(self, ip, port, srcip=None, sport=None, seq=None, ack=0, **kargs): 

2192 from scapy.sessions import TCPSession 

2193 self.dst = str(Net(ip)) 

2194 self.dport = port 

2195 self.sport = sport if sport is not None else random.randrange(0, 2**16) 

2196 self.l4 = IP(dst=ip, src=srcip) / TCP( 

2197 sport=self.sport, dport=self.dport, 

2198 flags=0, 

2199 seq=seq if seq is not None else random.randrange(0, 2**32), 

2200 ack=ack, 

2201 ) 

2202 self.src = self.l4.src 

2203 self.sack = self.l4[TCP].ack 

2204 self.rel_seq = None 

2205 self.rcvbuf = TCPSession() 

2206 bpf = "host %s and host %s and port %i and port %i" % (self.src, 

2207 self.dst, 

2208 self.sport, 

2209 self.dport) 

2210 Automaton.parse_args(self, filter=bpf, **kargs) 

2211 

2212 def _transmit_packet(self, pkt): 

2213 """Transmits a packet from TCPSession to the SuperSocket""" 

2214 self.oi.tcp.send(raw(pkt[TCP].payload)) 

2215 

2216 def master_filter(self, pkt): 

2217 return (IP in pkt and 

2218 pkt[IP].src == self.dst and 

2219 pkt[IP].dst == self.src and 

2220 TCP in pkt and 

2221 pkt[TCP].sport == self.dport and 

2222 pkt[TCP].dport == self.sport and 

2223 self.l4[TCP].seq >= pkt[TCP].ack and # XXX: seq/ack 2^32 wrap up # noqa: E501 

2224 ((self.l4[TCP].ack == 0) or (self.sack <= pkt[TCP].seq <= self.l4[TCP].ack + pkt[TCP].window))) # noqa: E501 

2225 

2226 @ATMT.state(initial=1) 

2227 def START(self): 

2228 pass 

2229 

2230 @ATMT.state() 

2231 def SYN_SENT(self): 

2232 pass 

2233 

2234 @ATMT.state() 

2235 def ESTABLISHED(self): 

2236 pass 

2237 

2238 @ATMT.state() 

2239 def LAST_ACK(self): 

2240 pass 

2241 

2242 @ATMT.state(final=1) 

2243 def CLOSED(self): 

2244 pass 

2245 

2246 @ATMT.state(stop=1) 

2247 def STOP(self): 

2248 pass 

2249 

2250 @ATMT.state() 

2251 def STOP_SENT_FIN_ACK(self): 

2252 pass 

2253 

2254 @ATMT.condition(START) 

2255 def connect(self): 

2256 raise self.SYN_SENT() 

2257 

2258 @ATMT.action(connect) 

2259 def send_syn(self): 

2260 self.l4[TCP].flags = "S" 

2261 self.send(self.l4) 

2262 self.l4[TCP].seq += 1 

2263 

2264 @ATMT.receive_condition(SYN_SENT) 

2265 def synack_received(self, pkt): 

2266 if pkt[TCP].flags.SA: 

2267 raise self.ESTABLISHED().action_parameters(pkt) 

2268 

2269 @ATMT.action(synack_received) 

2270 def send_ack_of_synack(self, pkt): 

2271 self.l4[TCP].ack = pkt[TCP].seq + 1 

2272 self.l4[TCP].flags = "A" 

2273 self.send(self.l4) 

2274 

2275 @ATMT.receive_condition(ESTABLISHED) 

2276 def incoming_data_received(self, pkt): 

2277 if not isinstance(pkt[TCP].payload, (NoPayload, conf.padding_layer)): 

2278 raise self.ESTABLISHED().action_parameters(pkt) 

2279 

2280 @ATMT.action(incoming_data_received) 

2281 def receive_data(self, pkt): 

2282 data = raw(pkt[TCP].payload) 

2283 if data and self.l4[TCP].ack == pkt[TCP].seq: 

2284 self.sack = self.l4[TCP].ack 

2285 self.l4[TCP].ack += len(data) 

2286 self.l4[TCP].flags = "A" 

2287 # Answer with an Ack 

2288 self.send(self.l4) 

2289 # Process data - will be sent to the SuperSocket through this 

2290 pkt = self.rcvbuf.process(pkt) 

2291 if pkt: 

2292 self._transmit_packet(pkt) 

2293 

2294 @ATMT.ioevent(ESTABLISHED, name="tcp", as_supersocket="tcplink") 

2295 def outgoing_data_received(self, fd): 

2296 raise self.ESTABLISHED().action_parameters(fd.recv()) 

2297 

2298 @ATMT.action(outgoing_data_received) 

2299 def send_data(self, d): 

2300 self.l4[TCP].flags = "PA" 

2301 self.send(self.l4 / d) 

2302 self.l4[TCP].seq += len(d) 

2303 

2304 @ATMT.receive_condition(ESTABLISHED) 

2305 def reset_received(self, pkt): 

2306 if pkt[TCP].flags.R: 

2307 raise self.CLOSED() 

2308 

2309 @ATMT.receive_condition(ESTABLISHED) 

2310 def fin_received(self, pkt): 

2311 if pkt[TCP].flags.F: 

2312 raise self.LAST_ACK().action_parameters(pkt) 

2313 

2314 @ATMT.action(fin_received) 

2315 def send_finack(self, pkt): 

2316 self.l4[TCP].flags = "FA" 

2317 self.l4[TCP].ack = pkt[TCP].seq + 1 

2318 self.send(self.l4) 

2319 self.l4[TCP].seq += 1 

2320 

2321 @ATMT.receive_condition(LAST_ACK) 

2322 def ack_of_fin_received(self, pkt): 

2323 if pkt[TCP].flags.A: 

2324 raise self.CLOSED() 

2325 

2326 @ATMT.condition(STOP) 

2327 def stop_requested(self): 

2328 raise self.STOP_SENT_FIN_ACK() 

2329 

2330 @ATMT.action(stop_requested) 

2331 def stop_send_finack(self): 

2332 self.l4[TCP].flags = "FA" 

2333 self.send(self.l4) 

2334 self.l4[TCP].seq += 1 

2335 

2336 @ATMT.receive_condition(STOP_SENT_FIN_ACK) 

2337 def stop_fin_received(self, pkt): 

2338 if pkt[TCP].flags.F: 

2339 raise self.CLOSED().action_parameters(pkt) 

2340 

2341 @ATMT.action(stop_fin_received) 

2342 def stop_send_ack(self, pkt): 

2343 self.l4[TCP].flags = "A" 

2344 self.l4[TCP].ack = pkt[TCP].seq + 1 

2345 self.send(self.l4) 

2346 

2347 @ATMT.timeout(SYN_SENT, 1) 

2348 def syn_ack_timeout(self): 

2349 raise self.CLOSED() 

2350 

2351 @ATMT.timeout(STOP_SENT_FIN_ACK, 1) 

2352 def stop_ack_timeout(self): 

2353 raise self.CLOSED() 

2354 

2355 

2356##################### 

2357# Reporting stuff # 

2358##################### 

2359 

2360 

2361@conf.commands.register 

2362def report_ports(target, ports): 

2363 """portscan a target and output a LaTeX table 

2364report_ports(target, ports) -> string""" 

2365 ans, unans = sr(IP(dst=target) / TCP(dport=ports), timeout=5) 

2366 rep = "\\begin{tabular}{|r|l|l|}\n\\hline\n" 

2367 for s, r in ans: 

2368 if not r.haslayer(ICMP): 

2369 if r.payload.flags == 0x12: 

2370 rep += r.sprintf("%TCP.sport% & open & SA \\\\\n") 

2371 rep += "\\hline\n" 

2372 for s, r in ans: 

2373 if r.haslayer(ICMP): 

2374 rep += r.sprintf("%TCPerror.dport% & closed & ICMP type %ICMP.type%/%ICMP.code% from %IP.src% \\\\\n") # noqa: E501 

2375 elif r.payload.flags != 0x12: 

2376 rep += r.sprintf("%TCP.sport% & closed & TCP %TCP.flags% \\\\\n") 

2377 rep += "\\hline\n" 

2378 for i in unans: 

2379 rep += i.sprintf("%TCP.dport% & ? & unanswered \\\\\n") 

2380 rep += "\\hline\n\\end{tabular}\n" 

2381 return rep 

2382 

2383 

2384@conf.commands.register 

2385def IPID_count(lst, funcID=lambda x: x[1].id, funcpres=lambda x: x[1].summary()): # noqa: E501 

2386 """Identify IP id values classes in a list of packets 

2387 

2388lst: a list of packets 

2389funcID: a function that returns IP id values 

2390funcpres: a function used to summarize packets""" 

2391 idlst = [funcID(e) for e in lst] 

2392 idlst.sort() 

2393 classes = [idlst[0]] 

2394 classes += [t[1] for t in zip(idlst[:-1], idlst[1:]) if abs(t[0] - t[1]) > 50] # noqa: E501 

2395 lst = [(funcID(x), funcpres(x)) for x in lst] 

2396 lst.sort() 

2397 print("Probably %i classes: %s" % (len(classes), classes)) 

2398 for id, pr in lst: 

2399 print("%5i" % id, pr) 

2400 

2401 

2402@conf.commands.register 

2403def fragleak(target, sport=123, dport=123, timeout=0.2, onlyasc=0, count=None): 

2404 load = "XXXXYYYYYYYYYY" 

2405 pkt = IP(dst=target, id=RandShort(), options=b"\x00" * 40, flags=1) 

2406 pkt /= UDP(sport=sport, dport=sport) / load 

2407 s = conf.L3socket() 

2408 intr = 0 

2409 found = {} 

2410 try: 

2411 while count is None or count: 

2412 if count is not None and isinstance(count, int): 

2413 count -= 1 

2414 try: 

2415 if not intr: 

2416 s.send(pkt) 

2417 sin = select.select([s], [], [], timeout)[0] 

2418 if not sin: 

2419 continue 

2420 ans = s.recv(1600) 

2421 if not isinstance(ans, IP): # TODO: IPv6 

2422 continue 

2423 if not isinstance(ans.payload, ICMP): 

2424 continue 

2425 if not isinstance(ans.payload.payload, IPerror): 

2426 continue 

2427 if ans.payload.payload.dst != target: 

2428 continue 

2429 if ans.src != target: 

2430 print("leak from", ans.src) 

2431 if not ans.haslayer(conf.padding_layer): 

2432 continue 

2433 leak = ans.getlayer(conf.padding_layer).load 

2434 if leak not in found: 

2435 found[leak] = None 

2436 linehexdump(leak, onlyasc=onlyasc) 

2437 except KeyboardInterrupt: 

2438 if intr: 

2439 raise 

2440 intr = 1 

2441 except KeyboardInterrupt: 

2442 pass 

2443 

2444 

2445@conf.commands.register 

2446def fragleak2(target, timeout=0.4, onlyasc=0, count=None): 

2447 found = {} 

2448 try: 

2449 while count is None or count: 

2450 if count is not None and isinstance(count, int): 

2451 count -= 1 

2452 

2453 pkt = IP(dst=target, options=b"\x00" * 40, proto=200) 

2454 pkt /= "XXXXYYYYYYYYYYYY" 

2455 p = sr1(pkt, timeout=timeout, verbose=0) 

2456 if not p: 

2457 continue 

2458 if conf.padding_layer in p: 

2459 leak = p[conf.padding_layer].load 

2460 if leak not in found: 

2461 found[leak] = None 

2462 linehexdump(leak, onlyasc=onlyasc) 

2463 except Exception: 

2464 pass 

2465 

2466 

2467@conf.commands.register 

2468class connect_from_ip: 

2469 """ 

2470 Open a TCP socket to a host:port while spoofing another IP. 

2471 

2472 :param host: the host to connect to 

2473 :param port: the port to connect to 

2474 :param srcip: the IP to spoof. the cache of the gateway will 

2475 be poisonned with this IP. 

2476 :param poison: (optional, default True) ARP poison the gateway (or next hop), 

2477 so that it answers us (only one packet). 

2478 :param timeout: (optional) the socket timeout. 

2479 

2480 Example - Connect to 192.168.0.1:80 spoofing 192.168.0.2:: 

2481 

2482 from scapy.layers.http import HTTP, HTTPRequest 

2483 client = connect_from_ip("192.168.0.1", 80, "192.168.0.2") 

2484 sock = SSLStreamSocket(client.sock, HTTP) 

2485 resp = sock.sr1(HTTP() / HTTPRequest(Path="/")) 

2486 

2487 Example - Connect to 192.168.0.1:443 with TLS wrapping spoofing 192.168.0.2:: 

2488 

2489 import ssl 

2490 from scapy.layers.http import HTTP, HTTPRequest 

2491 context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) 

2492 context.check_hostname = False 

2493 context.verify_mode = ssl.CERT_NONE 

2494 client = connect_from_ip("192.168.0.1", 443, "192.168.0.2") 

2495 sock = context.wrap_socket(client.sock) 

2496 sock = SSLStreamSocket(client.sock, HTTP) 

2497 resp = sock.sr1(HTTP() / HTTPRequest(Path="/")) 

2498 """ 

2499 

2500 def __init__(self, host, port, srcip, poison=True, timeout=1, debug=0): 

2501 host = str(Net(host)) 

2502 if poison: 

2503 # poison the next hop 

2504 gateway = conf.route.route(host)[2] 

2505 if gateway == "0.0.0.0": 

2506 # on lan 

2507 gateway = host 

2508 getmacbyip(gateway) # cache real gateway before poisoning 

2509 arpcachepoison(gateway, srcip, count=1, interval=0, verbose=0) 

2510 # create a socket pair 

2511 self._sock, self.sock = socket.socketpair() 

2512 self.sock.settimeout(timeout) 

2513 self.client = TCP_client( 

2514 host, port, 

2515 srcip=srcip, 

2516 external_fd={"tcp": self._sock}, 

2517 debug=debug, 

2518 ) 

2519 # start the TCP_client 

2520 self.client.runbg() 

2521 

2522 def close(self): 

2523 self.client.stop() 

2524 self.client.destroy() 

2525 self.sock.close() 

2526 self._sock.close() 

2527 

2528 

2529class ICMPEcho_am(AnsweringMachine): 

2530 """Responds to ICMP Echo-Requests (ping)""" 

2531 function_name = "icmpechod" 

2532 

2533 def is_request(self, req): 

2534 if req.haslayer(ICMP): 

2535 icmp_req = req.getlayer(ICMP) 

2536 if icmp_req.type == 8: # echo-request 

2537 return True 

2538 

2539 return False 

2540 

2541 def print_reply(self, req, reply): 

2542 print("Replying %s to %s" % (reply[IP].dst, req[IP].dst)) 

2543 

2544 def make_reply(self, req): 

2545 reply = req.copy() 

2546 reply[ICMP].type = 0 # echo-reply 

2547 # Force re-generation of the checksum 

2548 reply[ICMP].chksum = None 

2549 if req.haslayer(IP): 

2550 reply[IP].src, reply[IP].dst = req[IP].dst, req[IP].src 

2551 reply[IP].chksum = None 

2552 if req.haslayer(Ether): 

2553 reply[Ether].src, reply[Ether].dst = ( 

2554 None if req[Ether].dst == "ff:ff:ff:ff:ff:ff" else req[Ether].dst, 

2555 req[Ether].src, 

2556 ) 

2557 return reply 

2558 

2559 

2560conf.stats_classic_protocols += [TCP, UDP, ICMP] 

2561conf.stats_dot11_protocols += [TCP, UDP, ICMP] 

2562 

2563if conf.ipv6_enabled: 

2564 import scapy.layers.inet6