| 1 | #!/usr/bin/env python |
|---|
| 2 | # -*- coding: utf-8 -*- |
|---|
| 3 | |
|---|
| 4 | # Copyright (C) 2008 Adriano Monteiro Marques. |
|---|
| 5 | # |
|---|
| 6 | # Author: Bartosz SKOWRON <getxsick at gmail dot com> |
|---|
| 7 | # |
|---|
| 8 | # This library is free software; you can redistribute it and/or modify |
|---|
| 9 | # it under the terms of the GNU Lesser General Public License as published |
|---|
| 10 | # by the Free Software Foundation; either version 2.1 of the License, or |
|---|
| 11 | # (at your option) any later version. |
|---|
| 12 | # |
|---|
| 13 | # This library is distributed in the hope that it will be useful, but |
|---|
| 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
|---|
| 15 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public |
|---|
| 16 | # License for more details. |
|---|
| 17 | # |
|---|
| 18 | # You should have received a copy of the GNU Lesser General Public License |
|---|
| 19 | # along with this library; if not, write to the Free Software Foundation, |
|---|
| 20 | # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 21 | |
|---|
| 22 | import umpa.protocols._consts as const |
|---|
| 23 | |
|---|
| 24 | from umpa.protocols import * |
|---|
| 25 | from umpa.protocols._layer4 import * |
|---|
| 26 | from umpa.utils import net |
|---|
| 27 | from umpa.utils import bits |
|---|
| 28 | |
|---|
| 29 | class HSequenceNumber(IntField): |
|---|
| 30 | """The sequence number of the first data octet in this segment (except |
|---|
| 31 | when SYN is present). |
|---|
| 32 | |
|---|
| 33 | See RFC 793 for more. |
|---|
| 34 | """ |
|---|
| 35 | bits = 32 |
|---|
| 36 | auto = True |
|---|
| 37 | def _generate_value(self): |
|---|
| 38 | # TODO: implemention real auto-filling here ;) |
|---|
| 39 | # otherwise we can simple return 0 |
|---|
| 40 | return 0 |
|---|
| 41 | |
|---|
| 42 | class HAcknowledgmentNumber(IntField): |
|---|
| 43 | """If the ACK control bit is set this field contains the value of the |
|---|
| 44 | next sequence number the sender of the segment is expecting to receive. |
|---|
| 45 | |
|---|
| 46 | See RFC 793 for more. |
|---|
| 47 | """ |
|---|
| 48 | bits = 32 |
|---|
| 49 | auto = True |
|---|
| 50 | def _generate_value(self): |
|---|
| 51 | # TODO: implemention real auto-filling here ;) |
|---|
| 52 | # otherwise we can simple return 0 |
|---|
| 53 | return 1 |
|---|
| 54 | |
|---|
| 55 | class HDataOffset(SpecialIntField): |
|---|
| 56 | """The number of 32 bit words in the TCP Header. This indicates where |
|---|
| 57 | the data begins. |
|---|
| 58 | |
|---|
| 59 | See RFC 793 for more. |
|---|
| 60 | """ |
|---|
| 61 | bits = 4 |
|---|
| 62 | auto = True |
|---|
| 63 | def _generate_value(self): |
|---|
| 64 | # returns in 32-bits units |
|---|
| 65 | return 5 + self._tmp_value / 32 # 5 is a minimum value |
|---|
| 66 | |
|---|
| 67 | class HReserved(IntField): |
|---|
| 68 | """Reserved for future use. |
|---|
| 69 | |
|---|
| 70 | See RFC 793 for more. |
|---|
| 71 | """ |
|---|
| 72 | bits = 6 |
|---|
| 73 | auto = True |
|---|
| 74 | def _generate_value(self): |
|---|
| 75 | return 0 |
|---|
| 76 | |
|---|
| 77 | class HWindow(IntField): |
|---|
| 78 | """The number of data octets beginning with the one indicated in the |
|---|
| 79 | acknowledgment field which the sender of this segment is willing to accept. |
|---|
| 80 | |
|---|
| 81 | See RFC 793 for more. |
|---|
| 82 | """ |
|---|
| 83 | bits = 16 |
|---|
| 84 | auto = True |
|---|
| 85 | def _generate_value(self): |
|---|
| 86 | # TODO: implemention real auto-filling here ;) |
|---|
| 87 | # otherwise we can simple return 0 |
|---|
| 88 | return 512 |
|---|
| 89 | |
|---|
| 90 | class HUrgentPointer(IntField): |
|---|
| 91 | """This field communicates the current value of the urgent pointer as a |
|---|
| 92 | positive offset from the sequence number in this segment. |
|---|
| 93 | |
|---|
| 94 | See RFC 793 for more. |
|---|
| 95 | """ |
|---|
| 96 | bits = 16 |
|---|
| 97 | auto = True |
|---|
| 98 | def _generate_value(self): |
|---|
| 99 | # TODO: implemention real auto-filling here ;) |
|---|
| 100 | # otherwise we can simple return 0 |
|---|
| 101 | return 0 |
|---|
| 102 | |
|---|
| 103 | class TCP(Protocol): |
|---|
| 104 | """This is Transmission Control Protocol. |
|---|
| 105 | It the most common protocol in the Internet on fourth layer |
|---|
| 106 | of the OSI model. |
|---|
| 107 | """ |
|---|
| 108 | layer = 4 # layer of the OSI |
|---|
| 109 | protocol_id = const.PROTOCOL_TCP |
|---|
| 110 | |
|---|
| 111 | _ordered_fields = ('source_port', 'destination_port', '_sequence_number', |
|---|
| 112 | '_acknowledgment_number', '_data_offset', '_reserved', |
|---|
| 113 | 'control_bits', '_window', '_checksum', '_urgent_pointer', |
|---|
| 114 | 'options', '_padding',) |
|---|
| 115 | |
|---|
| 116 | def __init__(self, **kw): |
|---|
| 117 | control_bits = ('urg', 'ack', 'psh', 'rst', 'syn', 'fin') |
|---|
| 118 | control_bits_predefined = dict.fromkeys(control_bits, 0) |
|---|
| 119 | |
|---|
| 120 | fields_list = [ PortField("Source Port", 0), |
|---|
| 121 | PortField("Destination Port", 0), |
|---|
| 122 | HSequenceNumber("Sequence Number"), |
|---|
| 123 | HAcknowledgmentNumber("Acknowledgment Number"), |
|---|
| 124 | HDataOffset("DataOffset"), HReserved("Reserved", 0), |
|---|
| 125 | Flags("Control Bits", control_bits, |
|---|
| 126 | **control_bits_predefined), |
|---|
| 127 | HWindow("Window"), Layer4ChecksumField("Checksum"), |
|---|
| 128 | HUrgentPointer("Urgent Pointer"), Flags("Options", ()), |
|---|
| 129 | PaddingField("Padding") ] |
|---|
| 130 | |
|---|
| 131 | # we call super.__init__ after prepared necessary data |
|---|
| 132 | super(TCP, self).__init__(fields_list, **kw) |
|---|
| 133 | |
|---|
| 134 | # set __doc__ for fields - it's important if you want to get hints |
|---|
| 135 | # in some frontends. E.g. Umit Project provides one... |
|---|
| 136 | self._get_field('source_port').set_doc("The source port number. \ |
|---|
| 137 | See RFC 793 for more.") |
|---|
| 138 | self._get_field('destination_port').set_doc("The destination port \ |
|---|
| 139 | number. See RFC 793 for more.") |
|---|
| 140 | self._get_field('control_bits').set_doc("URG, ACK, PSH, RST, SYN, FIN \ |
|---|
| 141 | flags. See RFC 793 for more.") |
|---|
| 142 | self._get_field('options').set_doc("Options may occupy space at the \ |
|---|
| 143 | end of the TCP header and are a multiple of 8 bits in length. See RFC 793 \ |
|---|
| 144 | for more.") |
|---|
| 145 | self._get_field('_padding').set_doc("The TCP header padding is used \ |
|---|
| 146 | to ensure that the TCP header ends and data begins on a 32 bit boundary. \ |
|---|
| 147 | See RFC 793 for more.") |
|---|
| 148 | |
|---|
| 149 | def _raw(self, protocol_container, protocol_bits): |
|---|
| 150 | bit = 0 |
|---|
| 151 | raw_value = 0 |
|---|
| 152 | |
|---|
| 153 | # Padding |
|---|
| 154 | self._get_field('_padding')._tmp_value = \ |
|---|
| 155 | self._get_field('options').bits |
|---|
| 156 | |
|---|
| 157 | # Data Offset |
|---|
| 158 | self._get_field('_data_offset')._tmp_value = \ |
|---|
| 159 | self._get_field('options').bits + self._get_field('_padding').bits |
|---|
| 160 | |
|---|
| 161 | # so we make a big number with bits of every fields of the protocol |
|---|
| 162 | for field in reversed(self._ordered_fields): |
|---|
| 163 | x = self._get_field(field).fillout() |
|---|
| 164 | raw_value |= x << bit |
|---|
| 165 | bit += self._get_field(field).bits |
|---|
| 166 | |
|---|
| 167 | # rev_offset it the offset from the right side |
|---|
| 168 | cksum_rev_offset = bit - self.get_offset('_checksum') - \ |
|---|
| 169 | self._get_field('_checksum').bits |
|---|
| 170 | # checking if user not defined his own value of checksum |
|---|
| 171 | if bits.get_bits(raw_value, self._get_field('_checksum').bits, |
|---|
| 172 | cksum_rev_offset, rev_offset=True) == 0: |
|---|
| 173 | cksum = 0 |
|---|
| 174 | offset = 0 |
|---|
| 175 | # TODO: payload is off. it should works but it's odd, we generate |
|---|
| 176 | # bits by calling get_raw for payload. completely but. |
|---|
| 177 | # also because of some new suggestions about look after of payload |
|---|
| 178 | # very very soon, should be reorgnized payload issue |
|---|
| 179 | # and this issue also |
|---|
| 180 | # |
|---|
| 181 | # Payload |
|---|
| 182 | #it = iter(protocol_container) |
|---|
| 183 | #for proto in it: |
|---|
| 184 | # if proto is self: |
|---|
| 185 | # break |
|---|
| 186 | #try: |
|---|
| 187 | # proto = it.next() |
|---|
| 188 | # payload = proto._get_raw(protocol_container, protocol_bits) |
|---|
| 189 | #except StopIteration: |
|---|
| 190 | # payload = 0 |
|---|
| 191 | |
|---|
| 192 | #cksum = payload |
|---|
| 193 | #offset = protocol_bits |
|---|
| 194 | |
|---|
| 195 | # TCP Header |
|---|
| 196 | cksum |= raw_value << offset |
|---|
| 197 | offset += bit |
|---|
| 198 | |
|---|
| 199 | # Pseudo Header |
|---|
| 200 | # |
|---|
| 201 | # TCP header length...converted to bits unit |
|---|
| 202 | total_length = self._get_field('_data_offset').fillout()*32 |
|---|
| 203 | # add payload |
|---|
| 204 | total_length += protocol_bits |
|---|
| 205 | # conversion to bytes unit |
|---|
| 206 | total_length /= 8 |
|---|
| 207 | |
|---|
| 208 | # create pseudo header object |
|---|
| 209 | pheader = PseudoHeader(self.protocol_id, total_length) |
|---|
| 210 | # generate raw value of it |
|---|
| 211 | pheader_bits = pheader._get_raw(protocol_container, |
|---|
| 212 | protocol_bits)[0] |
|---|
| 213 | # added pseudo header bits to cksum value |
|---|
| 214 | cksum |= pheader_bits << offset |
|---|
| 215 | |
|---|
| 216 | # finally, calcute and apply checksum |
|---|
| 217 | raw_cksum = net.in_cksum(cksum) |
|---|
| 218 | raw_value |= raw_cksum << cksum_rev_offset |
|---|
| 219 | |
|---|
| 220 | return raw_value, bit |
|---|
| 221 | |
|---|
| 222 | protocols = [ TCP, ] |
|---|