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'});
456 # Remove FD watch if present and close underlying socket.
457 $self->remove_fd_watch();
458 if ($self->{'socket'}) {
459 $self->{'socket'}->close();
463 ###############################################################################
464 #+-----------------------------------------------------------------------------
466 #+-----------------------------------------------------------------------------
467 ###############################################################################
469 ###############################################################################
471 # accept - When we are accepting new connections, we need to do the SOCKS
472 # handshaking before we return a usable socket.
474 ###############################################################################
479 croak
("Undefined IO::Socket::Socks object passed to accept.")
480 unless defined($self);
482 my $client = $self->SUPER::accept(@_);
486 $SOCKS_ERROR = "Proxy accept new client failed.";
490 my $authmech = $self->_socks5_accept($client);
491 return unless defined($authmech);
493 if ($authmech == AUTHMECH_USERPASS
)
495 return unless $self->_socks5_accept_auth($client);
498 return unless $self->_socks5_accept_command($client);
504 ###############################################################################
506 # _socks5_accept - Wait for an opening handsake, and reply.
508 ###############################################################################
514 #--------------------------------------------------------------------------
515 # Read the auth mechanisms
516 #--------------------------------------------------------------------------
518 $accept{version
} = $client->_socks_read();
519 $accept{num_methods
} = $client->_socks_read();
520 $accept{methods
} = [];
521 foreach (0..($accept{num_methods
}-1))
523 push(@
{$accept{methods
}},$client->_socks_read());
526 $self->_debug_connect("Recv",\
%accept);
528 if ($accept{num_methods
} == 0)
530 $SOCKS_ERROR = "No auth methods sent.";
536 foreach my $method (@
{$accept{methods
}})
538 if ($self->{AuthMethods
}->[$method] == 1)
545 if (!defined($authmech))
547 $authmech = AUTHMECH_INVALID
;
550 #--------------------------------------------------------------------------
552 #--------------------------------------------------------------------------
554 $accept_reply{version
} = SOCKS5_VER
;
555 $accept_reply{auth_method
} = AUTHMECH_INVALID
;
556 $accept_reply{auth_method
} = $authmech if defined($authmech);
558 $client->_socks_send($accept_reply{version
});
559 $client->_socks_send($accept_reply{auth_method
});
560 $client->_socks_flush;
562 $self->_debug_connect_reply("Send",\
%accept_reply);
564 if ($authmech == AUTHMECH_INVALID
)
566 $SOCKS_ERROR = "No available auth methods.";
574 ###############################################################################
576 # _socks5_accept_auth - Send and receive a SOCKS5 auth handshake
578 ###############################################################################
579 sub _socks5_accept_auth
584 #--------------------------------------------------------------------------
586 #--------------------------------------------------------------------------
588 $auth{version
} = $client->_socks_read();
589 $auth{user_length
} = $client->_socks_read();
590 $auth{user
} = $client->_socks_read_raw($auth{user_length
});
591 $auth{pass_length
} = $client->_socks_read();
592 $auth{pass
} = $client->_socks_read_raw($auth{pass_length
});
594 $self->_debug_auth("Recv",\
%auth);
597 if (defined($self->{UserAuth
}))
599 $status = &{$self->{UserAuth
}}($auth{user
},$auth{pass
});
602 #--------------------------------------------------------------------------
604 #--------------------------------------------------------------------------
606 $auth_reply{version
} = 1;
607 $auth_reply{status
} = AUTHREPLY_SUCCESS
;
608 $auth_reply{status
} = AUTHREPLY_FAILURE
if !$status;
610 $client->_socks_send($auth_reply{version
});
611 $client->_socks_send($auth_reply{status
});
612 $client->_socks_flush;
614 $self->_debug_auth_reply("Send",\
%auth_reply);
616 if ($auth_reply{status
} != AUTHREPLY_SUCCESS
)
618 $SOCKS_ERROR = "Authentication failed with SOCKS5 proxy.";
626 ###############################################################################
628 # _socks5_acccept_command - Process a SOCKS5 command request. Since this is
629 # a library and not a server, we cannot process the
630 # command. Let the parent program handle that.
632 ###############################################################################
633 sub _socks5_accept_command
638 #--------------------------------------------------------------------------
640 #--------------------------------------------------------------------------
642 $command{version
} = $client->_socks_read();
643 $command{command
} = $client->_socks_read();
644 $command{reserved
} = $client->_socks_read();
645 $command{atype
} = $client->_socks_read();
647 if ($command{atype
} == ADDR_DOMAINNAME
)
649 $command{host_length
} = $client->_socks_read();
650 $command{host
} = $client->_socks_read_raw($command{host_length
});
652 elsif ($command{atype
} == ADDR_IPV4
)
654 $command{host
} = unpack("N",$client->_socks_read_raw(4));
658 $client->_socks_accept_command_reply(REPLY_ADDR_NOT_SUPPORTED
);
659 $SOCKS_ERROR = $CODES{REPLY
}->[REPLY_ADDR_NOT_SUPPORTED
];
663 $command{port
} = unpack("n",$client->_socks_read_raw(2));
665 $self->_debug_command("Recv",\
%command);
667 ${*$client}->{SOCKS
}->{COMMAND
} = [$command{command
},$command{host
},$command{port
}];
673 ###############################################################################
675 # _socks5_acccept_command_reply - Answer a SOCKS5 command request. Since this
676 # is a library and not a server, we cannot
677 # process the command. Let the parent program
680 ###############################################################################
681 sub _socks5_accept_command_reply
688 if (!defined($reply) || !defined($host) || !defined($port))
690 croak
("You must provide a reply, host, and port on the command reply.");
693 #--------------------------------------------------------------------------
695 #--------------------------------------------------------------------------
697 $command_reply{version
} = SOCKS5_VER
;
698 $command_reply{status
} = $reply;
699 $command_reply{reserved
} = 0;
700 $command_reply{atype
} = ADDR_DOMAINNAME
;
701 $command_reply{host_length
} = length($host);
702 $command_reply{host
} = $host;
703 $command_reply{port
} = $port;
705 $self->_debug_command_reply("Send",\
%command_reply);
707 $self->_socks_send($command_reply{version
});
708 $self->_socks_send($command_reply{status
});
709 $self->_socks_send($command_reply{reserved
});
710 $self->_socks_send($command_reply{atype
});
711 $self->_socks_send($command_reply{host_length
});
712 $self->_socks_send_raw($command_reply{host
});
713 $self->_socks_send_raw(pack("n",$command_reply{port
}));
718 ###############################################################################
720 # command_reply - public reply wrapper to the client.
722 ###############################################################################
726 $self->_socks5_accept_command_reply(@_);
733 ###############################################################################
734 #+-----------------------------------------------------------------------------
736 #+-----------------------------------------------------------------------------
737 ###############################################################################
739 ###############################################################################
741 # _socks_read - send over the socket after packing according to the rules.
743 ###############################################################################
749 $data = pack("C",$data);
750 $self->_socks_send_raw($data);
754 ###############################################################################
756 # _socks_send_raw - send raw data across the socket.
758 ###############################################################################
764 push @
{$self->{data
}}, $data;
769 my $data = $self->{data
};
772 $data = join '', @
$data;
774 $self->{socket}->syswrite($data,length($data));
778 ###############################################################################
780 # _socks_read - read from the socket, and then unpack according to the rules.
782 ###############################################################################
787 $length = 1 unless defined($length);
789 my $data = $self->_socks_read_raw($length);
790 $data = unpack("C",$data);
795 ###############################################################################
797 # _socks_read_raw - read raw bytes off of the socket
799 ###############################################################################
804 $length = 1 unless defined($length);
807 sysread($self->{socket}, $data, $length);
814 ###############################################################################
815 #+-----------------------------------------------------------------------------
817 #+-----------------------------------------------------------------------------
818 ###############################################################################
826 return unless $self->{Debug
};
828 print "$tag: +------+------+-","-"x
(4*$connect->{num_methods
}),"-+\n";
829 print "$tag: | Vers | Auth |";
830 if ($connect->{num_methods
} > 0)
832 print " Meth "," "x
(4*($connect->{num_methods
}-1)),"|\n";
834 print "$tag: +------+------+-","-"x
(4*$connect->{num_methods
}),"-+\n";
837 printf("\\%02X",$connect->{version
});
839 printf("\\%02X",$connect->{num_methods
});
841 if ($connect->{num_methods
} > 0)
843 foreach my $method (@
{$connect->{methods
}})
845 printf("\\%02X ",$method);
851 print "$tag: +------+------+-","-"x
(4*$connect->{num_methods
}),"-+\n";
856 sub _debug_connect_reply
860 my $connect_reply = shift;
862 return unless $self->{Debug
};
864 print "$tag: +------+------+\n";
865 print "$tag: | Vers | Auth |\n";
866 print "$tag: +------+------+\n";
869 printf("\\%02X",$connect_reply->{version
});
871 printf("\\%02X",$connect_reply->{auth_method
});
874 print "$tag: +------+------+\n";
885 return unless $self->{Debug
};
887 print "$tag: +------+------+------","-"x
($auth->{user_length
}-4),"+------+-----","-"x
($auth->{pass_length
}-4),"-+\n";
888 print "$tag: | Vers | UsrL | User "," "x
($auth->{user_length
}-4),"| PasL | Pass"," "x
($auth->{pass_length
}-4)," |\n";
889 print "$tag: +------+------+------","-"x
($auth->{user_length
}-4),"+------+-----","-"x
($auth->{pass_length
}-4),"-+\n";
892 printf("\\%02X",$auth->{version
});
894 printf("\\%02d",$auth->{user_length
});
896 print $auth->{user
}," "x
(4-$auth->{user_length
});
898 printf("\\%02d",$auth->{pass_length
});
900 print $auth->{pass
}," "x
(4-$auth->{pass_length
});
903 print "$tag: +------+------+------","-"x
($auth->{user_length
}-4),"+------+-----","-"x
($auth->{pass_length
}-4),"-+\n";
908 sub _debug_auth_reply
912 my $auth_reply = shift;
914 return unless $self->{Debug
};
916 print "$tag: +------+------+\n";
917 print "$tag: | Vers | Stat |\n";
918 print "$tag: +------+------+\n";
921 printf("\\%02X",$auth_reply->{version
});
923 printf("\\%02X",$auth_reply->{status
});
926 print "$tag: +------+------+\n";
937 return unless $self->{Debug
};
939 print "$tag: +------+------+------+------+-------","-"x
$command->{host_length
},"-+-------+\n";
940 print "$tag: | Vers | Comm | Resv | ATyp | Host "," "x
$command->{host_length
}," | Port |\n";
941 print "$tag: +------+------+------+------+-------","-"x
$command->{host_length
},"-+-------+\n";
944 printf("\\%02X",$command->{version
});
946 printf("\\%02X",$command->{command
});
948 printf("\\%02X",$command->{reserved
});
950 printf("\\%02X",$command->{atype
});
952 printf("\\%02d",$command->{host_length
});
954 print $command->{host
};
956 printf("%-5d",$command->{port
});
959 print "$tag: +------+------+------+------+-------","-"x
$command->{host_length
},"-+-------+\n";
964 sub _debug_command_reply
968 my $command_reply = shift;
970 return unless $self->{Debug
};
972 print "$tag: +------+------+";
973 print "------+------+-------","-"x
$command_reply->{host_length
},"-+-------+"
974 if ($command_reply->{status
} == 0);
977 print "$tag: | Vers | Stat |";
978 print " Resv | ATyp | Host "," "x
$command_reply->{host_length
}," | Port |"
979 if ($command_reply->{status
} == 0);
982 print "$tag: +------+------+";
983 print "------+------+-------","-"x
$command_reply->{host_length
},"-+-------+"
984 if ($command_reply->{status
} == 0);
989 printf("\\%02X",$command_reply->{version
});
991 printf("\\%02X",$command_reply->{status
});
992 if ($command_reply->{status
} == 0)
995 printf("\\%02X",$command_reply->{reserved
});
997 printf("\\%02X",$command_reply->{atype
});
999 printf("\\%02d",$command_reply->{host_length
});
1001 print $command_reply->{host
};
1003 printf("%-5d",$command_reply->{port
});
1011 print "$tag: +------+------+";
1012 print "------+------+-------","-"x
$command_reply->{host_length
},"-+-------+"
1013 if ($command_reply->{status
} == 0);