使用Perl的Compress :: Zlib无法扩充RFC1950 zlib压缩流

时间:2016-10-10 12:51:17

标签: perl zlib

RFC1950指定了使用zlib压缩数据流的协议。

我试图编写一个可以解压缩传入数据的简单telnet客户端。但是,我没有尝试任何工作。下面的脚本使用Compress :: Zlib-> inflate,但是失败了。

我还尝试过IO :: Uncompress :: Inflate,IO :: Uncompress :: AnyInflate和Compress :: Raw :: Zlib。由于它们都不起作用,我猜测我错过了一些关键步骤。

编辑:以下代码现在可以完美运行。谢谢,全部。

#!/usr/bin/perl --

use strict;
use warnings;
use Compress::Zlib;
use Gtk2 '-init';

# -----------------------------------------------------------------------------
# Try to decompress a zlib stream using Compress::Zlib.
# This script works as a bare-bones telnet client. 
# During telnet option negotiation, we tell the server to use a zlib compression 
#   stream (RFC1950).
# Once option negotiation is complete, everything it sends us should be 
#   compressed.
# The connection is managed by Net::Telnet, whose ->_fillbuf function has been
#   modified so that incoming text can be decompressed (inflated) before being
#   displayed in the terminal window
# All calls to Compress::Zlib->inflate now succeeds
# -----------------------------------------------------------------------------

# Compress::Zlib object
our ($ZLIB_OBJ, $ZLIB_STATUS, $STREAM_FLAG, $TELNET_OBJ);

# Monkey-patch Net::Telnet so this test file contains only the Net::Telnet 
#   function we want to modify (following http://perlmonks.org/?node_id=1173735)
require Net::Telnet;

{
    no warnings 'redefine';
    *Net::Telnet::_fillbuf = sub {

        # Modified ->_fillBuf. The modified section is clearly marked. Also 
        #   removed some logging code we don't need

        my ($self, $s, $endtime) = @_;
        my (
        $msg,
        $nfound,
        $nread,
        $pushback_len,
        $read_pos,
        $ready,
        $timed_out,
        $timeout,
        $unparsed_pos,
        );

        ## If error from last read not yet reported then do it now.
        if ($s->{pending_errormsg}) {
        $msg = $s->{pending_errormsg};
        $s->{pending_errormsg} = "";
        return $self->error($msg);
        }

        return unless $s->{opened};

        while (1) {
        ## Maximum buffer size exceeded?
        return $self->error("maximum input buffer length exceeded: ",
                    $s->{maxbufsize}, " bytes")
            unless length($s->{buf}) <= $s->{maxbufsize};

        ## Determine how long to wait for input ready.
#        ($timed_out, $timeout) = &_timeout_interval($endtime);
        ($timed_out, $timeout) = &Net::Telnet::_timeout_interval($endtime);
        if ($timed_out) {
            $s->{timedout} = 1;
            return $self->error("read timed-out");
        }

        ## Wait for input ready.
        $nfound = select $ready=$s->{fdmask}, "", "", $timeout;

        ## Append to buffer any partially processed telnet or CR sequence.
        $pushback_len = length $s->{pushback_buf};
        if ($pushback_len) {
            $s->{buf} .= $s->{pushback_buf};
            $s->{pushback_buf} = "";
        }

        ## Read the waiting data.
        $read_pos = length $s->{buf};
        $unparsed_pos = $read_pos - $pushback_len;
        $nread = sysread $self, $s->{buf}, $s->{blksize}, $read_pos;

        ### Modified section ##################################################
        if ($nread && $s->{opts}{86}{remote_enabled}) {

            my ($buff, $posn, $previous, $nout, $status);

            $buff = $s->{buf};

            # We're expecting telnet option negotiation IAC SB MCCP IAC SE,
            #   followed by chr(120), which marks the start of the zlib stream
            if (! $STREAM_FLAG) {

                $posn = index($buff, chr(120));
                if ($posn > -1) {

                    # Ignore everything before the zlib stream
                    $buff = substr($buff, $posn);
                    # IAC... received
                    $STREAM_FLAG = 1;
                }

            } elsif ($pushback_len) {

                # If any partially processed telnet or CR sequence was appended 
                #   to the buffer, we mustn't try to inflate that portion
                $previous = substr($buff, 0, $pushback_len);
                $buff = substr($buff, $pushback_len);
            }

            if ($buff && $STREAM_FLAG) {

                # zlib stream has started. Decompress stuff
                ($nout, $status) = $ZLIB_OBJ->inflate($buff);

                # Respond to stream end or inflation errors
                if ($status == Z_STREAM_END) {

                    print "*TEST* End of zlib stream\n";
                    # (Don't inflate anything after this point)
                    $STREAM_FLAG = 0;
                    # Append anything after the end of the data stream
                    if (defined $previous) {
                        $s->{buf} = $previous . $nout . $buff;
                    } else {
                        $s->{buf} = $nout . $buff;
                    }

                    $nread = length $s->{buf};

                } elsif ($status != Z_OK) {

                    print "*TEST* Error inflating: errnum: $status\n";
                    if ($ZLIB_OBJ->msg()) {
                        print "*TEST*   msg: " . $ZLIB_OBJ->msg() . "\n";
                    } else {
                        print "*TEST*   msg: <none>\n";
                    }

                } else {

                    # Inflation successful!
                    if (defined $previous) {
                        $s->{buf} = $previous . $nout;
                    } else {
                        $s->{buf} = $nout;
                    }

                    $nread = length $s->{buf};
                }
            }
        }    
        #######################################################################

        ## Handle eof.
        if ($nread == 0) {  # eof read
            $s->{opened} = '';
            return;
        }

        ## Process any telnet commands in the data stream.
        if ($s->{telnet_mode} and index($s->{buf},"\377",$unparsed_pos) > -1) {
#            &_interpret_tcmd($self, $s, $unparsed_pos);
            &Net::Telnet::_interpret_tcmd($self, $s, $unparsed_pos);
        }

        ## Process any carriage-return sequences in the data stream.
#        &_interpret_cr($s, $unparsed_pos);
        &Net::Telnet::_interpret_cr($s, $unparsed_pos);

        ## Read again if all chars read were consumed as telnet cmds.
        next if $unparsed_pos >= length $s->{buf};

        ## Save the last line read.
#        &_save_lastline($s);
        &Net::Telnet::_save_lastline($s);

        ## We've successfully read some data into the buffer.
        last;
        } # end while(1)

        1;

    } # end sub _fillbuf
}   # end of monkey patch

# Connect to a random MUD that uses zlib compression, implemented using the 
#   MCCP protocol (RFC1950)
$TELNET_OBJ = Net::Telnet->new();
$TELNET_OBJ->open(
    Host        => 'iberiamud.mooo.com',
    Port        => 5900,
);

# Telnet option negotiation - accept zlib compression (must specify a callback 
#   subroutine)
$TELNET_OBJ->option_callback(sub {

    my ($obj, $option, $isRemote, $isEnabled, $wasEnabled, $bufPosn) = @_;

    print "MCCP enabled!\n";
    return 1;
});

$TELNET_OBJ->option_accept(Will => 86);

# Initiate Compress::Zlib
($ZLIB_OBJ, $ZLIB_STATUS) = inflateInit();
if (! defined $ZLIB_OBJ) {

    print "->inflateInit failed with error code: $ZLIB_STATUS\n";
} 

# Use a standard Glib::Timeout to check the connection for incoming data, and to 
#   display it in the user's terminal window
my $id = Glib::Timeout->add(100, sub {

    my $receive = $TELNET_OBJ->get(
        Errmode => sub {},                 
        Timeout => 0,
    );

    if (defined $receive && $receive =~ m/connect/) {

        # Send a few invalid logins, to generate some compressed text for 
        #   Compress::Zlib to inflate
        my @invalidList = (
            'connect testing testing', 
            'connect elvis presley',
            'connect samson delilah',
        );

        foreach my $cmd (@invalidList) {

            $TELNET_OBJ->print($cmd);
        }
    }

    if ($receive) {

        print $receive;
    }    

    return 1;
});

## Use a Gtk2 main loop because 'while (1) {}' doesn't work
Gtk2->main();

0 个答案:

没有答案