File Coverage

blib/lib/NetPacket/TCP.pm
Criterion Covered Total %
statement 107 135 79.2
branch 15 20 75.0
condition n/a
subroutine 16 18 88.8
pod 4 5 80.0
total 142 178 79.7


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