phpseclib scp->get(remote_file_path [, $localFile]) ;返回 1 个额外的 (NULL) 字符

问题描述 投票:0回答:0

似乎 phpseclib 的 scp get 函数在文件末尾返回了一个额外的 NULL,这不是原来的。

我写了一个脚本来演示这个问题。此脚本将使用 phpseclib scp->put() 将文件作为 /tmp/somefile 放入 user@localhost。然后它将使用 scp->get() 返回该文件。 (scp->get() 在有本地文件名和没有本地文件名的情况下都运行,以演示问题可能出在哪里。)代码如下:

#!/usr/bin/env php
<?php

set_include_path(get_include_path() . PATH_SEPARATOR . '/www/cgi-bin/phpseclib') ;
require_once('Crypt/RSA.php');
require_once('Net/SSH2.php');
require_once('Net/SCP.php');

function executeSSHCommand($ssh, $command) {
    $result = $ssh->exec($command) ;
    printf("    cmd: %s (%s)\n", $command, ($result === false ? 'ERROR' : 'OK') ) ;
    if (is_string($result) && strlen($result) > 0) {
        $responseLines = explode("\n", $result) ;
        foreach ($responseLines as $line) {
            if (strlen(trim($line)) > 0)
                printf("        > %s", $line ) ;
            }
        }        
    }

function toHumanPerms($perms) {
    $rtnVal = '' ;
    $workPerms = $perms ;
    foreach ([ 'o', 'g', 'u' ] as $type) {
        $thisPerms = ($workPerms & 0x07) ;
        $workPerms >>= 3 ;
        $r = ($thisPerms & 0x04) ? 'r' : '' ;
        $w = ($thisPerms & 0x02) ? 'w' : '' ;
        $x = ($thisPerms & 0x01) ? 'x' : '' ;
        if (strlen($rtnVal) > 0) $rtnVal .= ',' ;
        $rtnVal .= $type . '=' . $r . $w . $x ;
        }
    return $rtnVal ;
    }

$home = getenv('HOME') ;
$system = 'localhost' ;
$user = getenv('LOGNAME') ;
$hostString = $user . '@' . $system ;

$key = new Crypt_RSA() ;
if ( ! $key->loadKey(file_get_contents($home . DIRECTORY_SEPARATOR . '.ssh/id_rsa'))) {
    die("Could not load private key.\n") ;
    }
$ssh = new Net_SSH2($system) ;
if ($ssh) {
    if ($ssh->login($user, $key)) {
        $scp = new Net_SCP($ssh) ;
        $localFile = (count($argv) > 1 ? $argv[1] : $home . DIRECTORY_SEPARATOR . '.bashrc' ) ;   // If not specified in $argv then copy /etc/hosts
        $remoteFile = '/tmp/' . preg_replace('%[/]%','-',$localFile) ; // For example /tmp/-etc-hosts
        $copyBack = $localFile . '-copiedBack' ;
        $fileSize = filesize($localFile) ;
        $success = $scp->put($remoteFile , $localFile, NET_SCP_LOCAL_FILE) ;
        printf("    put: %s -> %s: %s\n", $localFile, $remoteFile, ($success === true ? 'OK' : '*ERROR*') ) ;
        if ($success) {
            $permsString = toHumanPerms(fileperms($localFile)) ;
            $command = 'chmod ' . $permsString . ' ' . escapeshellarg($remoteFile) ;
            executeSSHCommand($ssh, $command) ;
            $fileModTime = date('YmdHi.s', filemtime($localFile)) ;
            $command = sprintf('touch -t %s %s', $fileModTime, escapeshellarg($remoteFile)) ;
            executeSSHCommand($ssh, $command) ; // Execute but do not write to terminal.  (Write to log, though)
            $remoteData = $scp->get($remoteFile) ;           // copy file to local variable
            $success = $scp->get($remoteFile, $copyBack) ;   // copy file to filesystem
            printf("    get: %s -> %s: %s\n\n", $remoteFile, $copyBack, $success === true ? 'OK' : '*ERROR*' ) ;
            printf("Original file size was %d, returned data size is %d, returned file size is %d\n", $fileSize, strlen($remoteData), filesize($copyBack)) ;
            }
        }
    else {
        printf("**** Unable to authenticate user %s at host %s. ****\n", $user, $system ) ;
        }
    }
else {
    printf("**** Unable to connect to server %s. ****\n", $system) ;
    }

如果传递参数,则该参数将用作源文件名。目标文件名将是 /tmp/ 加上源文件名,但斜杠将被破折号代替。例如:

testscp /path/to/file

目标文件名将是 /tmp/-path-to-file

如果没有传递文件名,那么将使用 $HOME/.bashrc(和 /tmp/-home-username-.bashrc)。

当我使用任何常规文件(文本、二进制文件等)执行此操作时,get(..., $filename) 返回的文件和 get(...) 返回的字符串比原始文件长一个字节,在每种情况下我都尝试过,除了一个:原始文件名是空的。空文件的情况下工作正常,但在所有其他情况下,似乎在 get() 的最后抛出了一个额外的 NULL 字符。

put($filename, $targetname, , NET_SCP_LOCAL_FILE) 写入的文件在任何情况下都是原始文件的正确副本(通过 diff 证明)。

我无法确定造成这种情况的原因。由于它似乎一直是错误的,我会假设这个问题是众所周知的,或者我做的不正确。希望是后者。

很容易在 scp->get() 的用法之后进行操作,如果存在则从末尾删除 null ...但可能存在 null 应该存在的情况(其他情况下不存在额外的 NULL发送)。所以那不好。

读过这篇文章的人是否知道我可以做些什么来解决这个问题,或者如果是这样的话报告问题的方法?

谢谢。

更新:目前,我修改了 phpseclib/Net/SCP.php 如下:

在函数 get() 中,就在这两行之后(我的副本中的第 284 和 285 行):

while ($size < $info['size']) {
    $data = $this->_receive();

我添加了以下内容:

    // Following added by Lovelady, 6-May-2023 because scp->get() always returned an extra byte for non-zero get.
    $expected = $info['size'] - $size ;         // Lovelady
    if (strlen($data) == $expected + 1)         // Lovelady (If exactly 1 byte too many...)
        $data = substr($data, 0, $expected) ;   // Lovelady
    // Above added by Lovelady, 6-May-2023 because scp->get() always returned an extra byte for non-empty file.

在某些时候,除非其他人这样做,否则我可能会回去尝试找出为什么 $this->receive() 最后返回额外的 NULL。

我认为我目前的答案不太理想,但相对安全,因为例程已经知道它应该接收多少字节,并且正好超过了 1.

php phpseclib
© www.soinside.com 2019 - 2024. All rights reserved.