File Coverage

blib/lib/Protocol/SOCKS.pm
Criterion Covered Total %
statement 44 49 89.8
branch 15 22 68.1
condition 3 4 75.0
subroutine 14 15 93.3
pod 9 9 100.0
total 85 99 85.8


line stmt bran cond sub pod time code
1             package Protocol::SOCKS;
2             # ABSTRACT: abstract SOCKS protocol support
3 3     3   26554 use strict;
  3         8  
  3         90  
4 3     3   16 use warnings;
  3         5  
  3         115  
5              
6             our $VERSION = '0.003';
7              
8             =head1 NAME
9              
10             Protocol::SOCKS - abstract support for the SOCKS5 network protocol
11              
12             =head1 VERSION
13              
14             Version 0.003
15              
16             =head1 DESCRIPTION
17              
18             =cut
19              
20 3     3   5059 use Future;
  3         44259  
  3         283  
21 3     3   12154 use Socket qw(inet_pton inet_ntop inet_ntoa AF_INET AF_INET6);
  3         12965  
  3         796  
22              
23 3     3   1806 use Protocol::SOCKS::Constants qw(:all);
  3         8  
  3         2404  
24              
25             our %REPLY_CODE = (
26             0x00 => 'succeeded',
27             0x01 => 'general SOCKS server failure',
28             0x02 => 'connection not allowed by ruleset',
29             0x03 => 'Network unreachable',
30             0x04 => 'Host unreachable',
31             0x05 => 'Connection refused',
32             0x06 => 'TTL expired',
33             0x07 => 'Command not supported',
34             0x08 => 'Address type not supported',
35             );
36              
37             =head1 METHODS
38              
39             =cut
40              
41             =head2 new
42              
43             Instantiates this protocol object.
44              
45             =cut
46              
47 4     4 1 604 sub new { my $class = shift; bless { @_ }, $class }
  4         22  
48              
49             =head2 version
50              
51             Our protocol version. Usually 5.
52              
53             =cut
54              
55 7   50 7 1 55 sub version { shift->{version} ||= 5 }
56              
57             =head2 write
58              
59             Called when we want to write data. Requires a writer to be configured.
60              
61             =cut
62              
63 4     4 1 25 sub write { $_[0]->{writer}->($_[1]) }
64              
65             =head2 new_future
66              
67             Instantiates a new L using the provided factory, or calls through to L->new.
68              
69             =cut
70              
71 4   100 4 1 24 sub new_future { (shift->{future_factory} ||= sub { Future->new })->() }
  4     4   38  
72              
73             =head2 pack_fqdn
74              
75             Packs a fully-qualified domain into a data structure.
76              
77             =cut
78              
79             sub pack_fqdn {
80 3     3 1 1225 my $self = shift;
81 3         11 $self->pack_address(ATYPE_FQDN, @_)
82             }
83              
84             =head2 pack_ipv4
85              
86             Packs an IPv4 address into a data structure.
87              
88             =cut
89              
90             sub pack_ipv4 {
91 3     3 1 1254 my $self = shift;
92 3         8 $self->pack_address(ATYPE_IPV4, @_)
93             }
94              
95             =head2 pack_ipv6
96              
97             Packs an IPv6 address into a data structure.
98              
99             =cut
100              
101             sub pack_ipv6 {
102 0     0 1 0 my $self = shift;
103 0         0 $self->pack_address(ATYPE_IPV6, @_)
104             }
105              
106             =head2 pack_address
107              
108             Packs an address of the given type into a data structure.
109              
110             =cut
111              
112             sub pack_address {
113 7     7 1 36 my ($self, $type, $addr) = @_;
114 7 100       26 if($type == ATYPE_IPV4) {
    50          
    50          
115 4         58 return pack('C1', $type) . inet_pton(AF_INET, $addr);
116             } elsif($type == ATYPE_IPV6) {
117 0         0 return pack('C1', $type) . inet_pton(AF_INET6, $addr);
118             } elsif($type == ATYPE_FQDN) {
119 3         35 return pack('C1C/a*', $type, $addr);
120             } else {
121 0         0 die sprintf 'unknown address type 0x%02x', $type;
122             }
123             }
124              
125             =head2 extract_address
126              
127             Extracts address information from a scalar ref.
128              
129             =cut
130              
131             sub extract_address {
132 10     10 1 1324 my ($self, $buf) = @_;
133 10 50       34 return undef unless length($$buf) > 1;
134              
135 10         42 my ($type) = unpack 'C1', substr $$buf, 0, 1;
136 10 100       43 if($type == ATYPE_IPV4) {
    100          
    50          
137 5 50       21 return undef unless length($$buf) >= (1 + 4);
138 5         25 (undef, my $ip) = unpack 'C1A4', substr $$buf, 0, 1 + 4, '';
139 5 100       22 return '' unless $ip;
140 4         44 return inet_ntoa($ip);
141             } elsif($type == ATYPE_IPV6) {
142 1 50       5 return undef unless length($$buf) >= (1 + 16);
143 1         6 (undef, my $ip) = unpack 'C1A16', substr $$buf, 0, 1 + 16, '';
144 1         22 return inet_ntop(AF_INET6, $ip);
145             } elsif($type == ATYPE_FQDN) {
146 4         10 my ($len) = unpack 'C1', substr $$buf, 1, 1;
147 4 50       12 return undef unless length($$buf) >= (1 + 1 + $len);
148 4         21 (undef, my $host) = unpack 'C1C/a*', substr $$buf, 0, 1 + 1 + $len, '';
149 4         18 return $host;
150             } else {
151 0           die sprintf 'unknown address type 0x%02x', $type;
152             }
153             }
154              
155             1;
156              
157             __END__