File Coverage

blib/lib/NetPacket/TCP.pm
Criterion Covered Total %
statement 111 139 79.8
branch 15 20 75.0
condition n/a
subroutine 17 19 89.4
pod 4 5 80.0
total 147 183 80.3


line stmt bran cond sub pod time code
1             #
2             # NetPacket::TCP - Decode and encode TCP (Transmission Control
3             # Protocol) packets.
4             #
5             # Encode and checksumming part, Stephanie Wehner, atrak@itsx.com
6              
7             package NetPacket::TCP;
8             BEGIN {
9 3     3   24911 $NetPacket::TCP::AUTHORITY = 'cpan:YANICK';
10             }
11             # ABSTRACT: Assemble and disassemble TCP (Transmission Control Protocol) packets.
12             $NetPacket::TCP::VERSION = '1.5.0';
13 3     3   25 use strict;
  3         4  
  3         116  
14 3     3   14 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  3         5  
  3         247  
15 3     3   576 use NetPacket;
  3         6  
  3         160  
16              
17             my $myclass;
18              
19             # TCP Flags
20              
21 3     3   18 use constant FIN => 0x01;
  3         6  
  3         202  
22 3     3   17 use constant SYN => 0x02;
  3         14  
  3         140  
23 3     3   15 use constant RST => 0x04;
  3         8  
  3         146  
24 3     3   17 use constant PSH => 0x08;
  3         7  
  3         142  
25 3     3   15 use constant ACK => 0x10;
  3         4  
  3         128  
26 3     3   16 use constant URG => 0x20;
  3         5  
  3         161  
27 3     3   21 use constant ECE => 0x40;
  3         6  
  3         139  
28 3     3   16 use constant CWR => 0x80;
  3         4  
  3         309  
29              
30             BEGIN {
31 3     3   61 @ISA = qw(Exporter NetPacket);
32              
33             # Items to export into callers namespace by default
34             # (move infrequently used names to @EXPORT_OK below)
35              
36 3         52 @EXPORT = qw(FIN SYN RST PSH ACK URG ECE CWR
37             );
38              
39             # Other items we are prepared to export if requested
40              
41 3         14 @EXPORT_OK = qw(tcp_strip
42             );
43              
44             # Tags:
45              
46 3         1442 %EXPORT_TAGS = (
47             ALL => [@EXPORT, @EXPORT_OK],
48             strip => [qw(tcp_strip)],
49             );
50              
51             }
52              
53             #
54             # Strip header from packet and return the data contained in it
55             #
56              
57             undef &tcp_strip;
58             *tcp_strip = \&strip;
59              
60             sub strip {
61 0     0 1 0 my ($pkt) = @_;
62              
63 0         0 my $tcp_obj = NetPacket::TCP->decode($pkt);
64 0         0 return $tcp_obj->{data};
65             }
66              
67             #
68             # Decode the packet
69             #
70              
71             sub decode {
72 3     3 1 24 my $class = shift;
73 3         6 my($pkt, $parent) = @_;
74 3         6 my $self = {};
75              
76             # Class fields
77              
78 3         6 $self->{_parent} = $parent;
79 3         5 $self->{_frame} = $pkt;
80              
81             # Decode TCP packet
82              
83 3 50       8 if (defined($pkt)) {
84 3         5 my $tmp;
85              
86 3         30 ($self->{src_port}, $self->{dest_port}, $self->{seqnum},
87             $self->{acknum}, $tmp, $self->{winsize}, $self->{cksum},
88             $self->{urg}, $self->{options}) =
89             unpack("nnNNnnnna*", $pkt);
90              
91             # Extract flags
92            
93 3         8 $self->{hlen} = ($tmp & 0xf000) >> 12;
94 3         6 $self->{reserved} = ($tmp & 0x0f00) >> 8;
95 3         7 $self->{flags} = $tmp & 0x00ff;
96            
97             # Decode variable length header and remaining data
98              
99 3         5 my $olen = $self->{hlen} - 5;
100 3 50       9 $olen = 0 if $olen < 0; # Check for bad hlen
101              
102             # Option length is number of 32 bit words
103              
104 3         5 $olen *= 4;
105              
106 3         18 ( $self->{options}, $self->{data} )
107             = unpack( 'a' . $olen . 'a*', $self->{options});
108             }
109              
110             # Return a blessed object
111              
112 3         8 bless($self, $class);
113 3         10 return $self;
114             }
115              
116             #
117             # Encode a packet
118             #
119              
120             sub encode {
121              
122 0     0 1 0 my $self = shift;
123 0         0 my ($ip) = @_;
124 0         0 my ($packet,$tmp);
125              
126             # First of all, fix the checksum
127 0         0 $self->checksum($ip);
128              
129 0         0 $tmp = $self->{hlen} << 12;
130 0         0 $tmp = $tmp | (0x0f00 & ($self->{reserved} << 8));
131 0         0 $tmp = $tmp | (0x00ff & $self->{flags});
132              
133             # Put the packet together
134 0         0 $packet = pack('n n N N n n n n a* a*',
135             $self->{src_port}, $self->{dest_port}, $self->{seqnum},
136             $self->{acknum}, $tmp, $self->{winsize}, $self->{cksum},
137             $self->{urg}, $self->{options},$self->{data});
138              
139              
140 0         0 return($packet);
141              
142             }
143              
144             #
145             # TCP Checksum
146             #
147              
148             sub checksum {
149              
150 2     2 0 33 my $self = shift;
151 2         5 my ($ip) = @_;
152 2         3 my ($packet,$zero,$tcplen,$tmp);
153 0         0 my ($src_ip, $dest_ip,$proto);
154              
155 2         4 $zero = 0;
156 2         122 $proto = 6;
157 2         19 $tcplen = ($self->{hlen} * 4)+ length($self->{data});
158              
159 3     3   60 no warnings qw/ uninitialized /;
  3         15  
  3         2123  
160 2         5 $tmp = $self->{hlen} << 12;
161 2         6 $tmp = $tmp | (0x0f00 & ($self->{reserved} << 8));
162 2         6 $tmp = $tmp | (0x00ff & $self->{flags});
163              
164             # Pack pseudo-header for tcp checksum
165              
166 2         306 $src_ip = gethostbyname($ip->{src_ip});
167 2         166 $dest_ip = gethostbyname($ip->{dest_ip});
168              
169 2         25 $packet = pack('a4a4nnnnNNnnnna*a*',
170             $src_ip,$dest_ip,$proto,$tcplen,
171             $self->{src_port}, $self->{dest_port}, $self->{seqnum},
172             $self->{acknum}, $tmp, $self->{winsize}, $zero,
173             $self->{urg}, $self->{options},$self->{data});
174              
175             # pad packet if odd-sized
176 2 100       11 $packet .= "\x00" if length( $packet ) % 2;
177              
178 2         12 $self->{cksum} = NetPacket::htons(NetPacket::in_cksum($packet));
179             }
180              
181             sub parse_tcp_options {
182             #
183             # dissect tcp options header. see:
184             # http://www.networksorcery.com/enp/protocol/tcp.htm#Options
185             #
186             # we create an byte array from the options header
187             # and iterate through that. If we find an option
188             # kind number we act accordingly (sometimes it has
189             # a fixed length, sometimes a variable one).
190             # once we've got the option stored, we shift the
191             # bytes we fetched away from the byte array and
192             # re-enter the loop.
193              
194 2     2 1 699 my $self = shift;
195              
196 2         6 my $opts = $self->{options};
197 2         16 my @bytes = split //, $opts;
198 2         5 my %options;
199             my $size;
200 10         18 ENTRY:
201             $size = $#bytes;
202 10         19 foreach my $byte (@bytes) {
203 8         16 my $kind = unpack('C', $byte);
204 8 100       34 if($kind == 2) {
    100          
    100          
    100          
    50          
    50          
205             # MSS.
206             # next byte is size, set to 4
207             # next 2 bytes are mss value 16 bit unsigned short
208 1         4 $options{mss} = unpack('n', $bytes[2] . $bytes[3]);
209 1         2 shift @bytes;
210 1         2 shift @bytes;
211 1         1 shift @bytes;
212 1         2 shift @bytes;
213 1         19 goto ENTRY;
214             }
215             elsif ($kind == 1) {
216             # a noop
217 3         4 shift @bytes;
218 3         56 goto ENTRY;
219             }
220             elsif ($kind == 3) {
221             # Windows Scale Factor
222             # next byte is size, set to 3
223             # next byte is shift count, 8 bit unsigned
224 1         3 $options{ws} = unpack('C', $bytes[2]);
225 1         2 shift @bytes;
226 1         1 shift @bytes;
227 1         2 shift @bytes;
228 1         14 goto ENTRY;
229             }
230             elsif ($kind == 4) {
231             # SACK Permitted
232             # next byte is length
233 1         4 $options{sack} = unpack('C', $bytes[1]);
234 1         2 shift @bytes;
235 1         2 shift @bytes;
236 1         16 goto ENTRY;
237             }
238             elsif ($kind == 5) {
239             # SACK Blocks
240             # next byte is length, 2 + (number of blocks * 8)
241             # in every block,
242             # former 4 bytes is SACK left edge, 32 bit unsigned int
243             # latter 4 bytes is SACK right edge, 32 bit unsigned int
244 0         0 my $block_num = (unpack('C', $bytes[1]) - 2) / 8;
245 0         0 shift @bytes;
246 0         0 shift @bytes;
247 0         0 my @sack_blocks;
248 0         0 for (1..$block_num) {
249 0         0 push @sack_blocks, [unpack('N', join '', @bytes[0..3]),
250             unpack('N', join '', @bytes[4..7])];
251 0         0 shift @bytes;
252 0         0 shift @bytes;
253 0         0 shift @bytes;
254 0         0 shift @bytes;
255 0         0 shift @bytes;
256 0         0 shift @bytes;
257 0         0 shift @bytes;
258 0         0 shift @bytes;
259             }
260 0         0 $options{sack_blocks} = \@sack_blocks;
261             }
262             elsif ($kind == 8) {
263             # timestamp
264             # next byte is length, set to 10
265             # next 4 byte is timestamp, 32 bit unsigned int
266             # next 4 byte is timestamp echo reply, 32 bit unsigned int
267 2         8 $options{ts} = unpack('N', join '', @bytes[2..5]);
268 2         7 $options{er} = unpack('N', join '', @bytes[6,7,8,9]);
269 2         3 shift @bytes;
270 2         3 shift @bytes;
271 2         2 shift @bytes;
272 2         4 shift @bytes;
273 2         3 shift @bytes;
274 2         3 shift @bytes;
275 2         2 shift @bytes;
276 2         3 shift @bytes;
277 2         3 shift @bytes;
278 2         2 shift @bytes;
279 2         31 goto ENTRY;
280             }
281             }
282 2 50       20 return wantarray ? %options : \%options;
283             }
284             #
285             # Module initialisation
286             #
287              
288             1;
289              
290             # autoloaded methods go after the END token (&& pod) below
291              
292             __END__