AnyEvent-SSH2
view release on metacpan or search on metacpan
lib/AnyEvent/SSH2.pm view on Meta::CPAN
# $Id: SSH2.pm,v 1.47 2009/01/26 01:50:38 turnstep Exp $
package AnyEvent::SSH2;
use strict;
use AE;
use AnyEvent::Handle;
use Net::SSH::Perl::Kex;
use Net::SSH::Perl::ChannelMgr;
use Net::SSH::Perl::Packet;
use Net::SSH::Perl::Buffer;
use Net::SSH::Perl::Constants qw( :protocol :msg2 :compat :hosts :channels :proposal :kex
CHAN_INPUT_CLOSED CHAN_INPUT_WAIT_DRAIN );
use Net::SSH::Perl::Cipher;
use Net::SSH::Perl::AuthMgr;
use Net::SSH::Perl::Comp;
use Net::SSH::Perl::Util qw(:hosts);
use Scalar::Util qw(blessed weaken);
use Carp qw( croak );
use base qw( Net::SSH::Perl );
our $VERSION = '0.04';
use Errno qw( EAGAIN EWOULDBLOCK );
use vars qw( $VERSION $CONFIG $HOSTNAME @PROPOSAL );
use vars qw( @PROPOSAL );
@PROPOSAL = (
KEX_DEFAULT_KEX,
KEX_DEFAULT_PK_ALG,
KEX_DEFAULT_ENCRYPT,
KEX_DEFAULT_ENCRYPT,
KEX_DEFAULT_MAC,
KEX_DEFAULT_MAC,
KEX_DEFAULT_COMP,
KEX_DEFAULT_COMP,
KEX_DEFAULT_LANG,
KEX_DEFAULT_LANG,
);
$CONFIG = {};
BEGIN {
use Net::SSH::Perl::Packet;
no warnings qw(redefine);
*Net::SSH::Perl::Packet::send_ssh2 = sub {
my $pack = shift;
my $buffer = shift || $pack->{data};
my $ssh = $pack->{ssh};
my $kex = $ssh->kex;
my($ciph, $mac, $comp);
if ($kex) {
$ciph = $kex->send_cipher;
$mac = $kex->send_mac;
$comp = $kex->send_comp;
}
my $block_size = 8;
if ($comp && $comp->enabled) {
my $compressed = $comp->compress($buffer->bytes);
$buffer->empty;
$buffer->append($compressed);
}
my $len = $buffer->length + 4 + 1;
my $padlen = $block_size - ($len % $block_size);
$padlen += $block_size if $padlen < 4;
my $junk = $ciph ? (join '', map chr rand 255, 1..$padlen) : ("\0" x $padlen);
$buffer->append($junk);
my $packet_len = $buffer->length + 1;
$buffer->bytes(0, 0, pack("N", $packet_len) . pack("c", $padlen));
lib/AnyEvent/SSH2.pm view on Meta::CPAN
});
}
if (my $r = $h->{stderr}) {
$channel->register_handler("_extended_buffer",
$r->{code}, @{ $r->{extra} });
}
else {
$channel->register_handler("_extended_buffer", sub {
$stderr .= $_[1]->bytes;
});
}
$ssh->debug("Entering interactive session.");
$channel->{cb} = sub {
$cb->($ssh, $stdout, $stderr);
}
}
sub break_client_loop { $_[0]->{ek_client_loopcl_quit_pending} = 1 }
sub restore_client_loop { $_[0]->{_cl_quit_pending} = 0 }
sub _quit_pending { $_[0]->{_cl_quit_pending} }
sub client_loop {
my $ssh = shift;
return unless scalar @{$ssh->{events}{cmd}} > 0;
$ssh->emit('cmd');
$ssh->{_cl_quit_pending} = 0;
# åææé¢é
my $cmgr = $ssh->channel_mgr;
# å¤çæ¯ä¸ªé¢éçäºä»¶
my $h = $cmgr->handlers;
$ssh->event_loop($cmgr, $h);
}
sub event_loop {
my ($ssh, $cmgr, $h, $cb) = @_;
return $ssh->client_loop if $ssh->_quit_pending;
while (my $packet = Net::SSH::Perl::Packet->read_poll($ssh)) {
if (my $code = $h->{ $packet->type }) {
$code->($cmgr, $packet);
}
else {
$ssh->debug("Warning: ignore packet type " . $packet->type);
}
}
return $ssh->client_loop if $ssh->_quit_pending;
$cmgr->process_output_packets;
# 妿å¤çå®äº. å
³æææçè¿æ¥
# 乿以å¨è¿è¿è¡è¿ä¸ªæä½æ¯å 为主 channel ä¹éè¦æä½
for my $c (@{ $cmgr->{channels} }) {
next unless defined $c;
if ($c->{wfd} &&
$c->{extended}->length == 0 &&
$c->{output}->length == 0 &&
$c->{ostate} == CHAN_OUTPUT_WAIT_DRAIN ) {
$c->obuf_empty;
}
# ä¸é¢ obuf_empty ä¼ç» ostate åæ CHAN_OUTPUT_CLOSED
# ä¸é¢è¿ä¸ªå°±ä¼åå
³éç»è¿ç¨
if ($c->delete_if_full_closed) {
defined $c->{cb} ? $c->{cb}->() : '';
$cmgr->remove($c->{id});
}
}
my $oc = grep { defined } @{ $cmgr->{channels} };
return $ssh->client_loop unless $oc > 1;
my $cv = AE::cv sub {
my $result = shift->recv;
delete $ssh->{watcher};
$ssh->event_loop($cmgr, $h, $cb);
};
# è¿æ¯å¤çé¢éä¸çè¾åº, 客æ·ç«¯çè¾å
¥
for my $c (@{ $cmgr->{channels} }) {
next unless defined $c;
my $id = $c->{id};
if ($c->{rfd} && $c->{istate} == CHAN_INPUT_OPEN &&
$c->{remote_window} > 0 &&
$c->{input}->length < $c->{remote_window}) {
$ssh->{watcher}{$id}{rfd} = AE::io $c->{rfd}, 0, sub {
# 顺åºè®°å½ - é¢é - rfd
my $buf;
sysread $c->{rfd}, $buf, 8192;
($buf) = $buf =~ /(.*)/s;
$c->send_data($buf);
$cv->send('rfd');
delete $ssh->{watcher}{$id}{rfd}
};
}
# ç»å
容è¾åº
if (defined $c->{wfd} &&
$c->{ostate} == CHAN_OUTPUT_OPEN ||
$c->{ostate} == CHAN_OUTPUT_WAIT_DRAIN) {
if ($c->{output} and $c->{output}->length > 0) {
$ssh->{watcher}{$id}{wfd} = AE::io $c->{wfd}, 1, sub {
if (my $r = $c->{handlers}{"_output_buffer"}) {
$r->{code}->( $c, $c->{output}, @{ $r->{extra} } );
}
$c->{local_consumed} += $c->{output}->length;
$c->{output}->empty;
$cv->send('wfd');
delete $ssh->{watcher}{$id}{wfd}
}
}
}
if ($c->{efd} && $c->{extended}->length > 0) {
my $c->{watcher}{$id}{efd} = AE::io $c->{efd}, 1, sub {
if (my $r = $c->{handlers}{"_extended_buffer"}) {
$r->{code}->( $c, $c->{extended}, @{ $r->{extra} } );
}
$c->{local_consumed} += $c->{extended}->length;
$c->{extended}->empty;
$cv->send('efd');
delete $ssh->{watcher}{$id}{efd}
};
}
# åè¿ç¨
$c->check_window;
if ($c->delete_if_full_closed) {
defined $c->{cb} ? $c->{cb}->() : '';
$cmgr->remove($c->{id});
}
}
# è¿æ¯ä¸»è¿æ¥ç奿
my $handle = $ssh->{session}{sock};
$handle->push_read( chunk => 4 => sub {
my ($handle, $buf) = @_;
if (!length($buf)) {
croak "Connection failed: $!\n";
}
$ssh->break_client_loop if length($buf) == 0;
($buf) = $buf =~ /(.*)/s; ## Untaint data. Anything allowed.
$ssh->incoming_data->append($buf);
$cv->send('main');
});
}
sub channel_mgr {
my $ssh = shift;
unless (defined $ssh->{channel_mgr}) {
$ssh->{channel_mgr} = Net::SSH::Perl::ChannelMgr->new($ssh);
}
$ssh->{channel_mgr};
}
sub _read_version {
my $ssh = shift;
my $line = shift;;
my $len = length $line;
( run in 0.521 second using v1.01-cache-2.11-cpan-13bb782fe5a )