tcp.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. """
  2. Transmission Control Protocol (TCP)
  3. RFC 675 - Specification of Internet Transmission Control Program, December 1974 Version
  4. RFC 793 - TCP v4
  5. RFC 1122 - includes some error corrections for TCP
  6. RFC 1323 - TCP-Extensions
  7. RFC 1379 - Extending TCP for Transactions—Concepts
  8. RFC 1948 - Defending Against Sequence Number Attacks
  9. RFC 2018 - TCP Selective Acknowledgment Options
  10. RFC 4614 - A Roadmap for TCP Specification Documents
  11. RFC 5681 - TCP Congestion Control
  12. RFC 6298 - Computing TCP's Retransmission Timer
  13. RFC 6824 - TCP Extensions for Multipath Operation with Multiple Addresses
  14. """
  15. from pypacker import pypacker, triggerlist, checksum
  16. import logging
  17. import struct
  18. # avoid unneeded references for performance reasons
  19. pack = struct.pack
  20. unpack = struct.unpack
  21. logger = logging.getLogger("pypacker")
  22. # TCP control flags
  23. TH_FIN = 0x01 # end of data
  24. TH_SYN = 0x02 # synchronize sequence numbers
  25. TH_RST = 0x04 # reset connection
  26. TH_PUSH = 0x08 # push
  27. TH_ACK = 0x10 # acknowledgment number set
  28. TH_URG = 0x20 # urgent pointer set
  29. TH_ECE = 0x40 # ECN echo, RFC 3168
  30. TH_CWR = 0x80 # congestion window reduced
  31. TCP_PORT_MAX = 65535 # maximum port
  32. TCP_WIN_MAX = 65535 # maximum (unscaled) window
  33. # TCP Options (opt_type) - http://www.iana.org/assignments/tcp-parameters
  34. TCP_OPT_EOL = 0 # end of option list
  35. TCP_OPT_NOP = 1 # no operation
  36. TCP_OPT_MSS = 2 # maximum segment size
  37. TCP_OPT_WSCALE = 3 # window scale factor, RFC 1072
  38. TCP_OPT_SACKOK = 4 # SACK permitted, RFC 2018
  39. TCP_OPT_SACK = 5 # SACK, RFC 2018
  40. TCP_OPT_ECHO = 6 # echo (obsolete), RFC 1072
  41. TCP_OPT_ECHOREPLY = 7 # echo reply (obsolete), RFC 1072
  42. TCP_OPT_TIMESTAMP = 8 # timestamp, RFC 1323
  43. TCP_OPT_POCONN = 9 # partial order conn, RFC 1693
  44. TCP_OPT_POSVC = 10 # partial order service, RFC 1693
  45. TCP_OPT_CC = 11 # connection count, RFC 1644
  46. TCP_OPT_CCNEW = 12 # CC.NEW, RFC 1644
  47. TCP_OPT_CCECHO = 13 # CC.ECHO, RFC 1644
  48. TCP_OPT_ALTSUM = 14 # alt checksum request, RFC 1146
  49. TCP_OPT_ALTSUMDATA = 15 # alt checksum data, RFC 1146
  50. TCP_OPT_SKEETER = 16 # Skeeter
  51. TCP_OPT_BUBBA = 17 # Bubba
  52. TCP_OPT_TRAILSUM = 18 # trailer checksum
  53. TCP_OPT_MD5 = 19 # MD5 signature, RFC 2385
  54. TCP_OPT_SCPS = 20 # SCPS capabilities
  55. TCP_OPT_SNACK = 21 # selective negative acks
  56. TCP_OPT_REC = 22 # record boundaries
  57. TCP_OPT_CORRUPT = 23 # corruption experienced
  58. TCP_OPT_SNAP = 24 # SNAP
  59. TCP_OPT_TCPCOMP = 26 # TCP compression filter
  60. TCP_OPT_MAX = 27
  61. class TCPOptSingle(pypacker.Packet):
  62. __hdr__ = (
  63. ("type", "B", 0),
  64. )
  65. class TCPOptMulti(pypacker.Packet):
  66. """
  67. len = total length (header + data)
  68. """
  69. __hdr__ = (
  70. ("type", "B", 0),
  71. ("len", "B", 2)
  72. )
  73. def bin(self, update_auto_fields=True):
  74. if update_auto_fields:
  75. self.len = len(self)
  76. return pypacker.Packet.bin(self, update_auto_fields=update_auto_fields)
  77. class TCP(pypacker.Packet):
  78. __hdr__ = (
  79. ("sport", "H", 0xdead),
  80. ("dport", "H", 0),
  81. ("seq", "I", 0xdeadbeef),
  82. ("ack", "I", 0),
  83. ("off_x2", "B", ((5 << 4) | 0)), # 10*4 Byte
  84. ("flags", "B", TH_SYN), # acces via (obj.flags & TH_XYZ)
  85. ("win", "H", TCP_WIN_MAX),
  86. ("sum", "H", 0),
  87. ("urp", "H", 0),
  88. ("opts", None, triggerlist.TriggerList)
  89. )
  90. # 4 bits | 4 bits
  91. # offset | reserved
  92. # offset * 4 = header length
  93. def __get_off(self):
  94. return self.off_x2 >> 4
  95. def __set_off(self, value):
  96. self.off_x2 = (value << 4) | (self.off_x2 & 0xf)
  97. off = property(__get_off, __set_off)
  98. def bin(self, update_auto_fields=True):
  99. if update_auto_fields:
  100. """
  101. TCP-checksum needs to be updated on one of the following:
  102. - this layer itself or any upper layer changed
  103. - changes to the IP-pseudoheader
  104. There is no update on user-set checksums.
  105. """
  106. update = True
  107. # update header length. NOTE: needs to be a multiple of 4 Bytes.
  108. # options length need to be multiple of 4 Bytes
  109. if self._header_changed:
  110. self.off = int(self.header_len / 4) & 0xf
  111. try:
  112. # changes to IP-layer, don't mind if this isn't IP
  113. if not self._lower_layer._header_changed:
  114. # pseudoheader didn't change, further check for changes in layers
  115. update = self._changed()
  116. # logger.debug("lower layer found!")
  117. except AttributeError:
  118. # assume not an IP packet: we can't calculate the checksum
  119. # logger.debug("no lower layer found!")
  120. update = False
  121. if update:
  122. # logger.debug(">>> updating checksum")
  123. self._calc_sum()
  124. return pypacker.Packet.bin(self, update_auto_fields=update_auto_fields)
  125. def _dissect(self, buf):
  126. # update dynamic header parts. buf: 1010???? -clear reserved-> 1010 -> *4
  127. ol = ((buf[12] >> 4) << 2) - 20 # dataoffset - TCP-standard length
  128. if ol < 0:
  129. raise Exception("invalid header length")
  130. elif ol > 0:
  131. # parse options, add offset-length to standard-length
  132. opts_bytes = buf[20: 20 + ol]
  133. self._init_triggerlist("opts", opts_bytes, self.__parse_opts)
  134. ports = [unpack(">H", buf[0:2])[0], unpack(">H", buf[2:4])[0]]
  135. try:
  136. # source or destination port should match
  137. # logger.debug("TCP handler: %r" % self._handler[TCP.__name__])
  138. htype = [x for x in ports if x in self._handler[TCP.__name__]][0]
  139. # logger.debug("TCP: trying to set handler, type: %d = %s" % (type, self._handler[TCP.__name__][type]))
  140. self._init_handler(htype, buf[20 + ol:])
  141. except:
  142. # no type found
  143. pass
  144. return 20 + ol
  145. __TCP_OPT_SINGLE = set([TCP_OPT_EOL, TCP_OPT_NOP])
  146. @staticmethod
  147. def __parse_opts(buf):
  148. """Parse TCP options using buf and return them as List."""
  149. optlist = []
  150. i = 0
  151. while i < len(buf):
  152. # logger.debug("got TCP-option type %s" % buf[i])
  153. if buf[i] in TCP.__TCP_OPT_SINGLE:
  154. p = TCPOptSingle(type=buf[i])
  155. i += 1
  156. else:
  157. olen = buf[i + 1]
  158. # p = TCPOptMulti(type=buf[i], len=olen, body_bytes=buf[i + 2: i + olen])
  159. p = TCPOptMulti(buf[i: i + olen])
  160. i += olen # typefield + lenfield + data-len
  161. optlist.append(p)
  162. # logger.debug("tcp: parseopts finished, length: %d" % len(optlist))
  163. return optlist
  164. def _calc_sum(self):
  165. """Recalculate the TCP-checksum This won't reset changed state."""
  166. # TCP and underwriting are freaky bitches: we need the IP pseudoheader to calculate their checksum.
  167. try:
  168. # we need src/dst for checksum-calculation
  169. src, dst = self._lower_layer.src, self._lower_layer.dst
  170. self.sum = 0
  171. # logger.debug("TCP sum recalc: IP=%d / %s / %s" % (len(src), src, dst))
  172. tcp_bin = self.header_bytes + self.body_bytes
  173. # IP-pseudoheader, check if version 4 or 6
  174. if len(src) == 4:
  175. s = pack(">4s4sxBH", src, dst, 6, len(tcp_bin)) # 6 = TCP
  176. else:
  177. s = pack(">16s16sxBH", src, dst, 6, len(tcp_bin)) # 6 = TCP
  178. # Get checksum of concatenated pseudoheader+TCP packet
  179. # logger.debug("pseudoheader: %r" % s)
  180. # logger.debug("tcp_bin: %r" % tcp_bin)
  181. # assign via non-shadowed variable to trigger re-packing
  182. self.sum = checksum.in_cksum(s + tcp_bin)
  183. # logger.debug(">>> new checksum: %0X" % self._sum)
  184. except Exception:
  185. # not an IP packet as lower layer (src, dst not present) or invalid src/dst
  186. # logger.debug("could not calculate checksum: %r" % e)
  187. pass
  188. def direction(self, other):
  189. # logger.debug("checking direction: %s<->%s" % (self, other))
  190. if self.sport == other.sport and self.dport == other.dport:
  191. # consider packet to itself: can be DIR_REV
  192. return pypacker.Packet.DIR_SAME | pypacker.Packet.DIR_REV
  193. elif self.sport == other.dport and self.dport == other.sport:
  194. return pypacker.Packet.DIR_REV
  195. else:
  196. return pypacker.Packet.DIR_UNKNOWN
  197. def reverse_address(self):
  198. self.sport, self.dport = self.dport, self.sport
  199. TCP_PROTO_TELNET = 23
  200. TCP_PROTO_TPKT = 102
  201. TCP_PROTO_PMAP = 111
  202. TCP_PROTO_BGP = 179
  203. TCP_PROTO_SSL = 443
  204. TCP_PROTO_HTTP = (80, 8008, 8080)
  205. TCP_PROTO_RTP = (5004, 5005)
  206. TCP_PROTO_SIP = (5060, 5061)
  207. # load handler
  208. from pypacker.layer567 import bgp, http, rtp, sip, telnet, tpkt, pmap
  209. from pypacker.layer4 import ssl
  210. pypacker.Packet.load_handler(TCP,
  211. {
  212. TCP_PROTO_BGP: bgp.BGP,
  213. TCP_PROTO_TELNET: telnet.Telnet,
  214. TCP_PROTO_TPKT: tpkt.TPKT,
  215. TCP_PROTO_PMAP: pmap.Pmap,
  216. TCP_PROTO_HTTP: http.HTTP,
  217. TCP_PROTO_SSL: ssl.SSL,
  218. TCP_PROTO_RTP: rtp.RTP,
  219. TCP_PROTO_SIP: sip.SIP
  220. }
  221. )