1 package Thrasher
::Plugin
::Socks
;
3 # This file is based on IO::Socket::Socks, but is modified
4 # to work with the interface of Thrasher::EventLoop. It should
5 # work with any conformant implementation of the EventLoop.
7 # Only reading and writing a single proxied socket works. Serving
8 # stuff will be removed, eventually.
10 # This file is used by the ProxyFileTransfer plugin, but I just don't
11 # care to go down another layer of hierarchy in the package name.
12 # Besides, other plugins could use this, someday.
14 # In theory, we should also convert this so that the process of
15 # connecting to the socket doesn't block, but uses the event loop
16 # callback system for everything. However, this is unlikely to be a
17 # problem until you have incredibly high load. In my experience, a
18 # file transfer is a rare thing. You want it to work when you want to
19 # move a file, but you don't move files anywhere near as often as you
22 ##############################################################################
24 # This library is free software; you can redistribute it and/or
25 # modify it under the terms of the GNU Library General Public
26 # License as published by the Free Software Foundation; either
27 # version 2 of the License, or (at your option) any later version.
29 # This library is distributed in the hope that it will be useful,
30 # but WITHOUT ANY WARRANTY; without even the implied warranty of
31 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
32 # Library General Public License for more details.
34 # You should have received a copy of the GNU Library General Public
35 # License along with this library; if not, write to the
36 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
37 # Boston, MA 02111-1307, USA.
39 # Copyright (C) 2003 Ryan Eatmon
41 ##############################################################################
46 use vars
qw(@ISA @EXPORT $VERSION %CODES );
48 @ISA = qw(Exporter IO::Socket::INET);
49 @EXPORT = qw( $SOCKS_ERROR );
51 use POSIX qw(F_SETFL O_NONBLOCK);
52 use Thrasher
::Log
qw(log);
57 use constant SOCKS5_VER
=> 5;
59 use constant ADDR_IPV4
=> 1;
60 use constant ADDR_DOMAINNAME
=> 3;
61 use constant ADDR_IPV6
=> 4;
63 use constant CMD_CONNECT
=> 1;
64 #use constant CMD_BIND => 2;
65 #use constant CMD_UDPASSOC => 3;
67 use constant AUTHMECH_ANON
=> 0;
68 #use constant AUTHMECH_GSSAPI => 1;
69 use constant AUTHMECH_USERPASS
=> 2;
70 use constant AUTHMECH_INVALID
=> 255;
72 $CODES{AUTHMECH
}->[AUTHMECH_INVALID
] = "No valid auth mechanisms";
74 use constant AUTHREPLY_SUCCESS
=> 0;
75 use constant AUTHREPLY_FAILURE
=> 1;
77 $CODES{AUTHREPLY
}->[AUTHREPLY_FAILURE
] = "Failed to authenticate";
79 use constant REPLY_SUCCESS
=> 0;
80 use constant REPLY_GENERAL_FAILURE
=> 1;
81 use constant REPLY_CONN_NOT_ALLOWED
=> 2;
82 use constant REPLY_NETWORK_UNREACHABLE
=> 3;
83 use constant REPLY_HOST_UNREACHABLE
=> 4;
84 use constant REPLY_CONN_REFUSED
=> 5;
85 use constant REPLY_TTL_EXPIRED
=> 6;
86 use constant REPLY_CMD_NOT_SUPPORTED
=> 7;
87 use constant REPLY_ADDR_NOT_SUPPORTED
=> 8;
89 $CODES{REPLY
}->[REPLY_SUCCESS
] = "Success";
90 $CODES{REPLY
}->[REPLY_GENERAL_FAILURE
] = "General failure";
91 $CODES{REPLY
}->[REPLY_CONN_NOT_ALLOWED
] = "Not allowed";
92 $CODES{REPLY
}->[REPLY_NETWORK_UNREACHABLE
] = "Network unreachable";
93 $CODES{REPLY
}->[REPLY_HOST_UNREACHABLE
] = "Host unreachable";
94 $CODES{REPLY
}->[REPLY_CONN_REFUSED
] = "Connection refused";
95 $CODES{REPLY
}->[REPLY_TTL_EXPIRED
] = "TTL expired";
96 $CODES{REPLY
}->[REPLY_CMD_NOT_SUPPORTED
] = "Command not supported";
97 $CODES{REPLY
}->[REPLY_ADDR_NOT_SUPPORTED
] = "Address not supported";
99 # To retain basic compatibility with the previous interface, we
100 # explicitly set which arguments we care about and which will
101 # go to the raw socket.
102 my %SOCKS_ARGUMENTS = map { $_ => 1 }
103 qw(ProxyAddr ProxyPort ConnectAddr ConnectPort AuthType
104 RequireAuth UserAuth Username Password Debug Listen
105 LocalAddr LocalPort PeerAddr PeerPort EventLoop
106 SuccessCallback FailureCallback);
108 my @REQUIRED_ARGS = qw(ProxyAddr ProxyPort EventLoop SuccessCallback
111 # Note this removes the automatic connect
112 # EventLoop is a reference to a Thrasher::EventLoop object of some
114 # SuccessCallback will be called with the next chunk of data, when
115 # we receive some, or undef if the socket is over normally
116 # FailureCallback will be called when something fails, including
117 # a failure to log in to the proxy or a lost connection.
124 $self->{socket_args
} = {Proto
=> 'tcp',
125 Type
=> SOCK_STREAM
};
131 if ($SOCKS_ARGUMENTS{$key}) {
132 $self->{$key} = $value;
134 $self->{socket_args
}->{$key} = $value;
138 $self->{AuthType
} ||= 'none';
139 if ($self->{AuthType
} ne 'none' &&
140 (!$self->{Username
} || !$self->{Password
})) {
141 croak
"An AuthTyype other than none requires a Username "
145 $self->{AuthMethods
} = [!$self->{RequireAuth
},
147 exists($self->{Listen
}) ?
148 defined($self->{UserAuth
}) :
149 $self->{AuthType
} eq 'userpass'
152 $self->{COMMAND
} = undef;
154 if (exists($self->{Listen
})) {
155 $self->{socket_args
}->{LocalAddr
} = $self->{ProxyAddr
};
156 $self->{socket_args
}->{LocalPort
} = $self->{ProxyPort
};
158 $self->{socket_args
}->{PeerAddr
} = $self->{ProxyAddr
};
159 $self->{socket_args
}->{PeerPort
} = $self->{ProxyPort
};
162 for my $required (@REQUIRED_ARGS) {
163 if (!$self->{$required}) {
164 croak
"You must provide $required to a Socks connection.";
168 if (ref($self->{FailureCallback
}) ne 'CODE' ||
169 ref($self->{SuccessCallback
}) ne 'CODE') {
170 croak
"FailureCallback and SuccessCallback both need to be "
174 $self->{socket} = new IO
::Socket
::INET
(%{$self->{socket_args
}});
176 if (!$self->{socket}) {
177 log("Failed to connect to XMPP proxy.");
181 # Setting Blocking => 0 doesn't seem to work as well as this does.
182 fcntl($self->{socket}, F_SETFL
, O_NONBLOCK
);
191 print "Socks: Connecting\n";
193 croak
("Undefined Socks object passed to connect.")
194 unless defined($self);
196 my $completed_connect = sub {
197 print "Calling completed connect callback\n";
199 if (defined($self->{ConnectAddr
}) &&
200 defined($self->{ConnectPort
}))
202 print "Proceding to connect socket.\n";
203 $self->connect_to_socket();
207 my $auth_mech = $self->_socks5_connect($completed_connect);
211 ###############################################################################
213 # _socks5_connect - Send the opening handsake, and process the reply.
215 ###############################################################################
219 my $after_connect_closure = shift;
221 #--------------------------------------------------------------------------
222 # Send the auth mechanisms
223 #--------------------------------------------------------------------------
225 $connect{version
} = SOCKS5_VER
;
227 foreach my $method (0..scalar(@
{$self->{AuthMethods
}}))
229 push(@methods,$method)
230 if ($self->{AuthMethods
}->[$method] == 1);
232 $connect{num_methods
} = $#methods + 1;
233 $connect{methods
} = \
@methods;
235 $self->_debug_connect("Send",\
%connect);
237 $self->_socks_send($connect{version
});
238 $self->_socks_send($connect{num_methods
});
239 foreach my $method (@
{$connect{methods
}})
241 $self->_socks_send($method);
243 $self->_socks_flush();
245 my $handle_reply = sub {
246 print "Socks: Made it to handle_reply.\n";
249 $connect_reply{version
} = $self->_socks_read();
251 $connect_reply{auth_method
} = $self->_socks_read();
254 $self->_debug_connect_reply("Recv",\
%connect_reply);
256 if ($connect_reply{auth_method
} == AUTHMECH_INVALID
)
258 log("Could not complete connection to proxy.");
259 $self->{socket}->close;
260 $self->{FailureCallback
}->();
264 if ($connect_reply{auth_method
} != AUTHMECH_ANON
)
266 die "Authentication not converted yet.";
267 return unless $self->_socks5_connect_auth();
270 print "About to run after_connect_closure\n";
271 $after_connect_closure->();
273 # We'll never run *this* callback again on this socket.
277 print "Adding handle reply watcher\n";
278 $self->{EventLoop
}->add_fd_watch
279 ($self->{socket}->fileno,
280 $Thrasher::EventLoop
::IN
,
285 ###############################################################################
287 # _socks5_connect_auth - Send and receive a SOCKS5 auth handshake
289 ###############################################################################
290 sub _socks5_connect_auth
294 #--------------------------------------------------------------------------
296 #--------------------------------------------------------------------------
299 $auth{user_length
} = length($self->{Username
});
300 $auth{user
} = $self->{Username
};
301 $auth{pass_length
} = length($self->{Password
});
302 $auth{pass
} = $self->{Password
};
304 $self->_debug_auth("Send",\
%auth);
306 $self->_socks_send($auth{version
});
307 $self->_socks_send($auth{user_length
});
308 $self->_socks_send_raw($auth{user
});
309 $self->_socks_send($auth{pass_length
});
310 $self->_socks_send_raw($auth{pass
});
313 #--------------------------------------------------------------------------
315 #--------------------------------------------------------------------------
317 $auth_reply{version
} = $self->_socks_read();
318 $auth_reply{status
} = $self->_socks_read();
320 $self->_debug_auth_reply("Recv",\
%auth_reply);
322 if ($auth_reply{status
} != AUTHREPLY_SUCCESS
)
324 $SOCKS_ERROR = "Authentication failed with SOCKS5 proxy.";
332 sub connect_to_socket
335 my $command = CMD_CONNECT
;
337 print "Socks: Connecting to socket.\n";
339 #--------------------------------------------------------------------------
341 #--------------------------------------------------------------------------
343 $command{version
} = SOCKS5_VER
;
344 $command{command
} = $command;
345 $command{reserved
} = 0;
346 $command{atype
} = ADDR_DOMAINNAME
;
347 $command{host_length
} = length($self->{ConnectAddr
});
348 $command{host
} = $self->{ConnectAddr
};
349 $command{port
} = $self->{ConnectPort
};
351 $self->_debug_command("Send",\
%command);
353 $self->_socks_send($command{version
});
354 $self->_socks_send($command{command
});
355 $self->_socks_send($command{reserved
});
356 $self->_socks_send($command{atype
});
357 $self->_socks_send($command{host_length
});
358 $self->_socks_send_raw($command{host
});
359 $self->_socks_send_raw(pack("n",$command{port
}));
360 $self->_socks_flush();
362 my $handle_connect_reply = sub {
363 print "Socks: Handling connect reply.\n";
365 $command_reply{version
} = $self->_socks_read();
366 $command_reply{status
} = $self->_socks_read();
368 if ($command_reply{status
} == REPLY_SUCCESS
)
370 $command_reply{reserved
} = $self->_socks_read();
371 $command_reply{atype
} = $self->_socks_read();
373 if ($command_reply{atype
} == ADDR_DOMAINNAME
)
375 $command_reply{host_length
} = $self->_socks_read();
376 $command_reply{host
} = $self->_socks_read_raw($command_reply{host_length
});
378 elsif ($command_reply{atype
} == ADDR_IPV4
)
380 $command_reply{host
} = unpack("N",$self->_socks_read_raw(4));
383 $command_reply{port
} = unpack("n",$self->_socks_read_raw(2));
386 $self->_debug_command_reply("Recv",\
%command_reply);
388 if ($command_reply{status
} != REPLY_SUCCESS
)
390 $self->{FailureCallback
}->();
394 print "Socks: Final handler installed.\n";
395 # If we got here, must be successful.
397 # We need to make sure that one side of the file transfer
398 # doesn't run way ahead of the other, and to do that,
399 # ProxyFileTransfer.pm needs full control of when the stream
400 # is actually read, so the OS correctly rate-limits the TCP
402 my $data_reader = sub {
404 my ($eof, $error, $data);
405 my $result = sysread($self->{socket}, $data, $chunk_sz);
406 if (! defined($result)) {
409 elsif ($result == 0) {
412 return ($eof, $error, $data);
414 $self->{SuccessCallback
}->($data_reader);
419 print "Socks: Preparing connection reply watch.\n";
420 $self->{EventLoop
}->add_fd_watch
421 ($self->{socket}->fileno,
422 $Thrasher::EventLoop
::IN
,
423 $handle_connect_reply);
427 my ($self, $directions, $callback) = @_;
429 my $callback_wrapped = sub {
430 my $ret = $callback->();
432 # Unset watch_id to reflect that this FD is (about to be) removed.
433 $self->{'watch_id'} = 0;
438 $self->{'watch_id'} = $self->{EventLoop
}->add_fd_watch(
439 $self->{socket}->fileno(),
445 sub remove_fd_watch
{
448 if ($self->{'watch_id'}) {
449 $self->{EventLoop
}->remove_fd_watch($self->{'watch_id'});
450 $self->{'watch_id'} = undef;
457 # Remove FD watch if present and close underlying socket.
458 $self->remove_fd_watch();
459 if ($self->{'socket'}) {
460 $self->{'socket'}->close();
464 ###############################################################################
465 #+-----------------------------------------------------------------------------
467 #+-----------------------------------------------------------------------------
468 ###############################################################################
470 ###############################################################################
472 # accept - When we are accepting new connections, we need to do the SOCKS
473 # handshaking before we return a usable socket.
475 ###############################################################################
480 croak
("Undefined IO::Socket::Socks object passed to accept.")
481 unless defined($self);
483 my $client = $self->SUPER::accept(@_);
487 $SOCKS_ERROR = "Proxy accept new client failed.";
491 my $authmech = $self->_socks5_accept($client);
492 return unless defined($authmech);
494 if ($authmech == AUTHMECH_USERPASS
)
496 return unless $self->_socks5_accept_auth($client);
499 return unless $self->_socks5_accept_command($client);
505 ###############################################################################
507 # _socks5_accept - Wait for an opening handsake, and reply.
509 ###############################################################################
515 #--------------------------------------------------------------------------
516 # Read the auth mechanisms
517 #--------------------------------------------------------------------------
519 $accept{version
} = $client->_socks_read();
520 $accept{num_methods
} = $client->_socks_read();
521 $accept{methods
} = [];
522 foreach (0..($accept{num_methods
}-1))
524 push(@
{$accept{methods
}},$client->_socks_read());
527 $self->_debug_connect("Recv",\
%accept);
529 if ($accept{num_methods
} == 0)
531 $SOCKS_ERROR = "No auth methods sent.";
537 foreach my $method (@
{$accept{methods
}})
539 if ($self->{AuthMethods
}->[$method] == 1)
546 if (!defined($authmech))
548 $authmech = AUTHMECH_INVALID
;
551 #--------------------------------------------------------------------------
553 #--------------------------------------------------------------------------
555 $accept_reply{version
} = SOCKS5_VER
;
556 $accept_reply{auth_method
} = AUTHMECH_INVALID
;
557 $accept_reply{auth_method
} = $authmech if defined($authmech);
559 $client->_socks_send($accept_reply{version
});
560 $client->_socks_send($accept_reply{auth_method
});
561 $client->_socks_flush;
563 $self->_debug_connect_reply("Send",\
%accept_reply);
565 if ($authmech == AUTHMECH_INVALID
)
567 $SOCKS_ERROR = "No available auth methods.";
575 ###############################################################################
577 # _socks5_accept_auth - Send and receive a SOCKS5 auth handshake
579 ###############################################################################
580 sub _socks5_accept_auth
585 #--------------------------------------------------------------------------
587 #--------------------------------------------------------------------------
589 $auth{version
} = $client->_socks_read();
590 $auth{user_length
} = $client->_socks_read();
591 $auth{user
} = $client->_socks_read_raw($auth{user_length
});
592 $auth{pass_length
} = $client->_socks_read();
593 $auth{pass
} = $client->_socks_read_raw($auth{pass_length
});
595 $self->_debug_auth("Recv",\
%auth);
598 if (defined($self->{UserAuth
}))
600 $status = &{$self->{UserAuth
}}($auth{user
},$auth{pass
});
603 #--------------------------------------------------------------------------
605 #--------------------------------------------------------------------------
607 $auth_reply{version
} = 1;
608 $auth_reply{status
} = AUTHREPLY_SUCCESS
;
609 $auth_reply{status
} = AUTHREPLY_FAILURE
if !$status;
611 $client->_socks_send($auth_reply{version
});
612 $client->_socks_send($auth_reply{status
});
613 $client->_socks_flush;
615 $self->_debug_auth_reply("Send",\
%auth_reply);
617 if ($auth_reply{status
} != AUTHREPLY_SUCCESS
)
619 $SOCKS_ERROR = "Authentication failed with SOCKS5 proxy.";
627 ###############################################################################
629 # _socks5_acccept_command - Process a SOCKS5 command request. Since this is
630 # a library and not a server, we cannot process the
631 # command. Let the parent program handle that.
633 ###############################################################################
634 sub _socks5_accept_command
639 #--------------------------------------------------------------------------
641 #--------------------------------------------------------------------------
643 $command{version
} = $client->_socks_read();
644 $command{command
} = $client->_socks_read();
645 $command{reserved
} = $client->_socks_read();
646 $command{atype
} = $client->_socks_read();
648 if ($command{atype
} == ADDR_DOMAINNAME
)
650 $command{host_length
} = $client->_socks_read();
651 $command{host
} = $client->_socks_read_raw($command{host_length
});
653 elsif ($command{atype
} == ADDR_IPV4
)
655 $command{host
} = unpack("N",$client->_socks_read_raw(4));
659 $client->_socks_accept_command_reply(REPLY_ADDR_NOT_SUPPORTED
);
660 $SOCKS_ERROR = $CODES{REPLY
}->[REPLY_ADDR_NOT_SUPPORTED
];
664 $command{port
} = unpack("n",$client->_socks_read_raw(2));
666 $self->_debug_command("Recv",\
%command);
668 ${*$client}->{SOCKS
}->{COMMAND
} = [$command{command
},$command{host
},$command{port
}];
674 ###############################################################################
676 # _socks5_acccept_command_reply - Answer a SOCKS5 command request. Since this
677 # is a library and not a server, we cannot
678 # process the command. Let the parent program
681 ###############################################################################
682 sub _socks5_accept_command_reply
689 if (!defined($reply) || !defined($host) || !defined($port))
691 croak
("You must provide a reply, host, and port on the command reply.");
694 #--------------------------------------------------------------------------
696 #--------------------------------------------------------------------------
698 $command_reply{version
} = SOCKS5_VER
;
699 $command_reply{status
} = $reply;
700 $command_reply{reserved
} = 0;
701 $command_reply{atype
} = ADDR_DOMAINNAME
;
702 $command_reply{host_length
} = length($host);
703 $command_reply{host
} = $host;
704 $command_reply{port
} = $port;
706 $self->_debug_command_reply("Send",\
%command_reply);
708 $self->_socks_send($command_reply{version
});
709 $self->_socks_send($command_reply{status
});
710 $self->_socks_send($command_reply{reserved
});
711 $self->_socks_send($command_reply{atype
});
712 $self->_socks_send($command_reply{host_length
});
713 $self->_socks_send_raw($command_reply{host
});
714 $self->_socks_send_raw(pack("n",$command_reply{port
}));
719 ###############################################################################
721 # command_reply - public reply wrapper to the client.
723 ###############################################################################
727 $self->_socks5_accept_command_reply(@_);
734 ###############################################################################
735 #+-----------------------------------------------------------------------------
737 #+-----------------------------------------------------------------------------
738 ###############################################################################
740 ###############################################################################
742 # _socks_read - send over the socket after packing according to the rules.
744 ###############################################################################
750 $data = pack("C",$data);
751 $self->_socks_send_raw($data);
755 ###############################################################################
757 # _socks_send_raw - send raw data across the socket.
759 ###############################################################################
765 push @
{$self->{data
}}, $data;
770 my $data = $self->{data
};
773 $data = join '', @
$data;
775 $self->{socket}->syswrite($data,length($data));
779 ###############################################################################
781 # _socks_read - read from the socket, and then unpack according to the rules.
783 ###############################################################################
788 $length = 1 unless defined($length);
790 my $data = $self->_socks_read_raw($length);
791 $data = unpack("C",$data);
796 ###############################################################################
798 # _socks_read_raw - read raw bytes off of the socket
800 ###############################################################################
805 $length = 1 unless defined($length);
808 sysread($self->{socket}, $data, $length);
815 ###############################################################################
816 #+-----------------------------------------------------------------------------
818 #+-----------------------------------------------------------------------------
819 ###############################################################################
827 return unless $self->{Debug
};
829 print "$tag: +------+------+-","-"x
(4*$connect->{num_methods
}),"-+\n";
830 print "$tag: | Vers | Auth |";
831 if ($connect->{num_methods
} > 0)
833 print " Meth "," "x
(4*($connect->{num_methods
}-1)),"|\n";
835 print "$tag: +------+------+-","-"x
(4*$connect->{num_methods
}),"-+\n";
838 printf("\\%02X",$connect->{version
});
840 printf("\\%02X",$connect->{num_methods
});
842 if ($connect->{num_methods
} > 0)
844 foreach my $method (@
{$connect->{methods
}})
846 printf("\\%02X ",$method);
852 print "$tag: +------+------+-","-"x
(4*$connect->{num_methods
}),"-+\n";
857 sub _debug_connect_reply
861 my $connect_reply = shift;
863 return unless $self->{Debug
};
865 print "$tag: +------+------+\n";
866 print "$tag: | Vers | Auth |\n";
867 print "$tag: +------+------+\n";
870 printf("\\%02X",$connect_reply->{version
});
872 printf("\\%02X",$connect_reply->{auth_method
});
875 print "$tag: +------+------+\n";
886 return unless $self->{Debug
};
888 print "$tag: +------+------+------","-"x
($auth->{user_length
}-4),"+------+-----","-"x
($auth->{pass_length
}-4),"-+\n";
889 print "$tag: | Vers | UsrL | User "," "x
($auth->{user_length
}-4),"| PasL | Pass"," "x
($auth->{pass_length
}-4)," |\n";
890 print "$tag: +------+------+------","-"x
($auth->{user_length
}-4),"+------+-----","-"x
($auth->{pass_length
}-4),"-+\n";
893 printf("\\%02X",$auth->{version
});
895 printf("\\%02d",$auth->{user_length
});
897 print $auth->{user
}," "x
(4-$auth->{user_length
});
899 printf("\\%02d",$auth->{pass_length
});
901 print $auth->{pass
}," "x
(4-$auth->{pass_length
});
904 print "$tag: +------+------+------","-"x
($auth->{user_length
}-4),"+------+-----","-"x
($auth->{pass_length
}-4),"-+\n";
909 sub _debug_auth_reply
913 my $auth_reply = shift;
915 return unless $self->{Debug
};
917 print "$tag: +------+------+\n";
918 print "$tag: | Vers | Stat |\n";
919 print "$tag: +------+------+\n";
922 printf("\\%02X",$auth_reply->{version
});
924 printf("\\%02X",$auth_reply->{status
});
927 print "$tag: +------+------+\n";
938 return unless $self->{Debug
};
940 print "$tag: +------+------+------+------+-------","-"x
$command->{host_length
},"-+-------+\n";
941 print "$tag: | Vers | Comm | Resv | ATyp | Host "," "x
$command->{host_length
}," | Port |\n";
942 print "$tag: +------+------+------+------+-------","-"x
$command->{host_length
},"-+-------+\n";
945 printf("\\%02X",$command->{version
});
947 printf("\\%02X",$command->{command
});
949 printf("\\%02X",$command->{reserved
});
951 printf("\\%02X",$command->{atype
});
953 printf("\\%02d",$command->{host_length
});
955 print $command->{host
};
957 printf("%-5d",$command->{port
});
960 print "$tag: +------+------+------+------+-------","-"x
$command->{host_length
},"-+-------+\n";
965 sub _debug_command_reply
969 my $command_reply = shift;
971 return unless $self->{Debug
};
973 print "$tag: +------+------+";
974 print "------+------+-------","-"x
$command_reply->{host_length
},"-+-------+"
975 if ($command_reply->{status
} == 0);
978 print "$tag: | Vers | Stat |";
979 print " Resv | ATyp | Host "," "x
$command_reply->{host_length
}," | Port |"
980 if ($command_reply->{status
} == 0);
983 print "$tag: +------+------+";
984 print "------+------+-------","-"x
$command_reply->{host_length
},"-+-------+"
985 if ($command_reply->{status
} == 0);
990 printf("\\%02X",$command_reply->{version
});
992 printf("\\%02X",$command_reply->{status
});
993 if ($command_reply->{status
} == 0)
996 printf("\\%02X",$command_reply->{reserved
});
998 printf("\\%02X",$command_reply->{atype
});
1000 printf("\\%02d",$command_reply->{host_length
});
1002 print $command_reply->{host
};
1004 printf("%-5d",$command_reply->{port
});
1012 print "$tag: +------+------+";
1013 print "------+------+-------","-"x
$command_reply->{host_length
},"-+-------+"
1014 if ($command_reply->{status
} == 0);