File Coverage

blib/lib/Protocol/SOCKS/Client.pm
Criterion Covered Total %
statement 54 57 94.7
branch 8 14 57.1
condition 3 8 37.5
subroutine 13 14 92.8
pod 8 8 100.0
total 86 101 85.1


line stmt bran cond sub pod time code
1             package Protocol::SOCKS::Client;
2             $Protocol::SOCKS::Client::VERSION = '0.003';
3 1     1   25312 use strict;
  1         2  
  1         32  
4 1     1   4 use warnings;
  1         2  
  1         26  
5              
6 1     1   732 use parent qw(Protocol::SOCKS);
  1         270  
  1         4  
7              
8             =head1 NAME
9              
10             Protocol::SOCKS::Client - client support for SOCKS protocol
11              
12             =head1 VERSION
13              
14             Version 0.003
15              
16             =head1 DESCRIPTION
17              
18             This provides an abstraction for dealing with the client side of the SOCKS protocol.
19              
20             =cut
21              
22 1     1   47 use Future;
  1         2  
  1         22  
23 1     1   5 use Socket qw(inet_pton inet_ntop inet_ntoa AF_INET AF_INET6);
  1         2  
  1         55  
24              
25 1     1   5 use Protocol::SOCKS::Constants qw(:all);
  1         2  
  1         616  
26              
27             =head1 METHODS
28              
29             =cut
30              
31             =head2 completion
32              
33             Returns the completion future.
34              
35             =cut
36              
37 0   0 0 1 0 sub completion { $_[0]->{completion} ||= $_[0]->new_future }
38              
39             =head2 auth
40              
41             Returns the auth Future.
42              
43             =cut
44              
45 6   66 6 1 2386 sub auth { $_[0]->{auth} ||= $_[0]->new_future }
46              
47             =head2 auth_methods
48              
49             Returns the list of auth methods we can handle.
50              
51             =cut
52              
53             sub auth_methods {
54 1     1 1 2 my $self = shift;
55 1   50     1 @{ $self->{auth_methods} ||= [ AUTH_NONE ] }
  1         19  
56             }
57              
58             =head2 init_packet
59              
60             Initial client packet.
61              
62             =cut
63              
64             sub init_packet {
65 1     1 1 3 my $self = shift;
66 1         3 my @methods = (0);
67 1         5 pack 'C1C/C*', $self->version, $self->auth_methods;
68             }
69              
70             =head2 on_read
71              
72             Handler for reading data from the server.
73              
74             =cut
75              
76             sub on_read {
77 2     2 1 1254 my ($self, $buf) = @_;
78 2 100       7 if(!$self->auth->is_ready) {
79 1 50       12 return unless length($$buf) >= 2;
80 1         9 my ($version, $method) = unpack 'C1C1', substr $$buf, 0, 2, '';
81 1 50       5 die "Unexpected version" unless $version == $self->version;
82 1 50       17 if($method == 0xFF) {
83 0         0 $self->auth->fail($method);
84             } else {
85 1         4 $self->auth->done($method);
86             }
87 1         46 return;
88             } else {
89             # warn "non-auth, have " . length($$buf) . "bytes";
90 1 50       11 return unless my ($host, $port) = $self->parse_reply($buf);
91              
92 1         1 my $f = shift @{$self->{awaiting_reply}};
  1         4  
93 1         5 $f->done($host, $port);
94             }
95             }
96              
97             =head2 init
98              
99             Startup - writes the initial packet to the server.
100              
101             =cut
102              
103             sub init {
104 1     1 1 476 my $self = shift;
105 1         7 $self->write($self->init_packet);
106             }
107              
108             =head2 connect
109              
110             Issues a connection request.
111              
112             =cut
113              
114             sub connect {
115 1     1 1 468 my ($self, $atype, $addr, $port) = @_;
116 1         5 my $f = $self->new_future;
117 1         20 my $opaque_addr = $self->pack_address($atype, $addr);
118 1         3 push @{$self->{awaiting_reply}}, $f;
  1         4  
119 1         6 $self->write(
120             pack(
121             'C1C1C1',
122             $self->version,
123             0x01,
124             0x00,
125             ) . $opaque_addr . pack('n1', $port)
126             );
127 1         641 $f;
128             }
129              
130             =head2 parse_reply
131              
132             Parse a server reply.
133              
134             =cut
135              
136             sub parse_reply {
137 1     1 1 3 my ($self, $buffref) = @_;
138 1 50       8 return unless length $$buffref >= 4;
139 1         7 my ($version, $status, $reserved, $atype) = unpack 'C1C1C1C1', substr $$buffref, 0, 4;
140 1 50       4 if($status != 0) {
141             # warn $Protocol::SOCKS::REPLY_CODE{$status};
142 0         0 return;
143             }
144              
145 1         3 substr $$buffref, 0, 3, '';
146 1         15 my $addr = $self->extract_address($buffref);
147 1         4 my $port = unpack 'n1', substr $$buffref, 0, 2, '';
148             # warn "Addr $addr, port $port\n";
149 1         6 return $addr, $port;
150             }
151              
152             1;
153              
154             __END__