123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- """
- Transmission Control Protocol (TCP)
- RFC 675 - Specification of Internet Transmission Control Program, December 1974 Version
- RFC 793 - TCP v4
- RFC 1122 - includes some error corrections for TCP
- RFC 1323 - TCP-Extensions
- RFC 1379 - Extending TCP for Transactions—Concepts
- RFC 1948 - Defending Against Sequence Number Attacks
- RFC 2018 - TCP Selective Acknowledgment Options
- RFC 4614 - A Roadmap for TCP Specification Documents
- RFC 5681 - TCP Congestion Control
- RFC 6298 - Computing TCP's Retransmission Timer
- RFC 6824 - TCP Extensions for Multipath Operation with Multiple Addresses
- """
- from pypacker import pypacker, triggerlist, checksum
- import logging
- import struct
- # avoid unneeded references for performance reasons
- pack = struct.pack
- unpack = struct.unpack
- logger = logging.getLogger("pypacker")
- # TCP control flags
- TH_FIN = 0x01 # end of data
- TH_SYN = 0x02 # synchronize sequence numbers
- TH_RST = 0x04 # reset connection
- TH_PUSH = 0x08 # push
- TH_ACK = 0x10 # acknowledgment number set
- TH_URG = 0x20 # urgent pointer set
- TH_ECE = 0x40 # ECN echo, RFC 3168
- TH_CWR = 0x80 # congestion window reduced
- TCP_PORT_MAX = 65535 # maximum port
- TCP_WIN_MAX = 65535 # maximum (unscaled) window
- # TCP Options (opt_type) - http://www.iana.org/assignments/tcp-parameters
- TCP_OPT_EOL = 0 # end of option list
- TCP_OPT_NOP = 1 # no operation
- TCP_OPT_MSS = 2 # maximum segment size
- TCP_OPT_WSCALE = 3 # window scale factor, RFC 1072
- TCP_OPT_SACKOK = 4 # SACK permitted, RFC 2018
- TCP_OPT_SACK = 5 # SACK, RFC 2018
- TCP_OPT_ECHO = 6 # echo (obsolete), RFC 1072
- TCP_OPT_ECHOREPLY = 7 # echo reply (obsolete), RFC 1072
- TCP_OPT_TIMESTAMP = 8 # timestamp, RFC 1323
- TCP_OPT_POCONN = 9 # partial order conn, RFC 1693
- TCP_OPT_POSVC = 10 # partial order service, RFC 1693
- TCP_OPT_CC = 11 # connection count, RFC 1644
- TCP_OPT_CCNEW = 12 # CC.NEW, RFC 1644
- TCP_OPT_CCECHO = 13 # CC.ECHO, RFC 1644
- TCP_OPT_ALTSUM = 14 # alt checksum request, RFC 1146
- TCP_OPT_ALTSUMDATA = 15 # alt checksum data, RFC 1146
- TCP_OPT_SKEETER = 16 # Skeeter
- TCP_OPT_BUBBA = 17 # Bubba
- TCP_OPT_TRAILSUM = 18 # trailer checksum
- TCP_OPT_MD5 = 19 # MD5 signature, RFC 2385
- TCP_OPT_SCPS = 20 # SCPS capabilities
- TCP_OPT_SNACK = 21 # selective negative acks
- TCP_OPT_REC = 22 # record boundaries
- TCP_OPT_CORRUPT = 23 # corruption experienced
- TCP_OPT_SNAP = 24 # SNAP
- TCP_OPT_TCPCOMP = 26 # TCP compression filter
- TCP_OPT_MAX = 27
- class TCPOptSingle(pypacker.Packet):
- __hdr__ = (
- ("type", "B", 0),
- )
- class TCPOptMulti(pypacker.Packet):
- """
- len = total length (header + data)
- """
- __hdr__ = (
- ("type", "B", 0),
- ("len", "B", 2)
- )
- def bin(self, update_auto_fields=True):
- if update_auto_fields:
- self.len = len(self)
- return pypacker.Packet.bin(self, update_auto_fields=update_auto_fields)
- class TCP(pypacker.Packet):
- __hdr__ = (
- ("sport", "H", 0xdead),
- ("dport", "H", 0),
- ("seq", "I", 0xdeadbeef),
- ("ack", "I", 0),
- ("off_x2", "B", ((5 << 4) | 0)), # 10*4 Byte
- ("flags", "B", TH_SYN), # acces via (obj.flags & TH_XYZ)
- ("win", "H", TCP_WIN_MAX),
- ("sum", "H", 0),
- ("urp", "H", 0),
- ("opts", None, triggerlist.TriggerList)
- )
- # 4 bits | 4 bits
- # offset | reserved
- # offset * 4 = header length
- def __get_off(self):
- return self.off_x2 >> 4
- def __set_off(self, value):
- self.off_x2 = (value << 4) | (self.off_x2 & 0xf)
- off = property(__get_off, __set_off)
- def bin(self, update_auto_fields=True):
- if update_auto_fields:
- """
- TCP-checksum needs to be updated on one of the following:
- - this layer itself or any upper layer changed
- - changes to the IP-pseudoheader
- There is no update on user-set checksums.
- """
- update = True
- # update header length. NOTE: needs to be a multiple of 4 Bytes.
- # options length need to be multiple of 4 Bytes
- if self._header_changed:
- self.off = int(self.header_len / 4) & 0xf
- try:
- # changes to IP-layer, don't mind if this isn't IP
- if not self._lower_layer._header_changed:
- # pseudoheader didn't change, further check for changes in layers
- update = self._changed()
- # logger.debug("lower layer found!")
- except AttributeError:
- # assume not an IP packet: we can't calculate the checksum
- # logger.debug("no lower layer found!")
- update = False
- if update:
- # logger.debug(">>> updating checksum")
- self._calc_sum()
- return pypacker.Packet.bin(self, update_auto_fields=update_auto_fields)
- def _dissect(self, buf):
- # update dynamic header parts. buf: 1010???? -clear reserved-> 1010 -> *4
- ol = ((buf[12] >> 4) << 2) - 20 # dataoffset - TCP-standard length
- if ol < 0:
- raise Exception("invalid header length")
- elif ol > 0:
- # parse options, add offset-length to standard-length
- opts_bytes = buf[20: 20 + ol]
- self._init_triggerlist("opts", opts_bytes, self.__parse_opts)
- ports = [unpack(">H", buf[0:2])[0], unpack(">H", buf[2:4])[0]]
- try:
- # source or destination port should match
- # logger.debug("TCP handler: %r" % self._handler[TCP.__name__])
- htype = [x for x in ports if x in self._handler[TCP.__name__]][0]
- # logger.debug("TCP: trying to set handler, type: %d = %s" % (type, self._handler[TCP.__name__][type]))
- self._init_handler(htype, buf[20 + ol:])
- except:
- # no type found
- pass
- return 20 + ol
- __TCP_OPT_SINGLE = set([TCP_OPT_EOL, TCP_OPT_NOP])
- @staticmethod
- def __parse_opts(buf):
- """Parse TCP options using buf and return them as List."""
- optlist = []
- i = 0
- while i < len(buf):
- # logger.debug("got TCP-option type %s" % buf[i])
- if buf[i] in TCP.__TCP_OPT_SINGLE:
- p = TCPOptSingle(type=buf[i])
- i += 1
- else:
- olen = buf[i + 1]
- # p = TCPOptMulti(type=buf[i], len=olen, body_bytes=buf[i + 2: i + olen])
- p = TCPOptMulti(buf[i: i + olen])
- i += olen # typefield + lenfield + data-len
- optlist.append(p)
- # logger.debug("tcp: parseopts finished, length: %d" % len(optlist))
- return optlist
- def _calc_sum(self):
- """Recalculate the TCP-checksum This won't reset changed state."""
- # TCP and underwriting are freaky bitches: we need the IP pseudoheader to calculate their checksum.
- try:
- # we need src/dst for checksum-calculation
- src, dst = self._lower_layer.src, self._lower_layer.dst
- self.sum = 0
- # logger.debug("TCP sum recalc: IP=%d / %s / %s" % (len(src), src, dst))
- tcp_bin = self.header_bytes + self.body_bytes
- # IP-pseudoheader, check if version 4 or 6
- if len(src) == 4:
- s = pack(">4s4sxBH", src, dst, 6, len(tcp_bin)) # 6 = TCP
- else:
- s = pack(">16s16sxBH", src, dst, 6, len(tcp_bin)) # 6 = TCP
- # Get checksum of concatenated pseudoheader+TCP packet
- # logger.debug("pseudoheader: %r" % s)
- # logger.debug("tcp_bin: %r" % tcp_bin)
- # assign via non-shadowed variable to trigger re-packing
- self.sum = checksum.in_cksum(s + tcp_bin)
- # logger.debug(">>> new checksum: %0X" % self._sum)
- except Exception:
- # not an IP packet as lower layer (src, dst not present) or invalid src/dst
- # logger.debug("could not calculate checksum: %r" % e)
- pass
- def direction(self, other):
- # logger.debug("checking direction: %s<->%s" % (self, other))
- if self.sport == other.sport and self.dport == other.dport:
- # consider packet to itself: can be DIR_REV
- return pypacker.Packet.DIR_SAME | pypacker.Packet.DIR_REV
- elif self.sport == other.dport and self.dport == other.sport:
- return pypacker.Packet.DIR_REV
- else:
- return pypacker.Packet.DIR_UNKNOWN
- def reverse_address(self):
- self.sport, self.dport = self.dport, self.sport
- TCP_PROTO_TELNET = 23
- TCP_PROTO_TPKT = 102
- TCP_PROTO_PMAP = 111
- TCP_PROTO_BGP = 179
- TCP_PROTO_SSL = 443
- TCP_PROTO_HTTP = (80, 8008, 8080)
- TCP_PROTO_RTP = (5004, 5005)
- TCP_PROTO_SIP = (5060, 5061)
- # load handler
- from pypacker.layer567 import bgp, http, rtp, sip, telnet, tpkt, pmap
- from pypacker.layer4 import ssl
- pypacker.Packet.load_handler(TCP,
- {
- TCP_PROTO_BGP: bgp.BGP,
- TCP_PROTO_TELNET: telnet.Telnet,
- TCP_PROTO_TPKT: tpkt.TPKT,
- TCP_PROTO_PMAP: pmap.Pmap,
- TCP_PROTO_HTTP: http.HTTP,
- TCP_PROTO_SSL: ssl.SSL,
- TCP_PROTO_RTP: rtp.RTP,
- TCP_PROTO_SIP: sip.SIP
- }
- )
|