phpseclib scp->get()返回额外NULL字符问题及解决求助
phpseclib SCP->get() 函数返回内容末尾多出NULL字节的问题
问题现象
phpseclib的scp->get()函数存在异常:获取文件时,会在返回内容的末尾添加一个原文件中不存在的NULL字符。
复现测试
- 测试流程:编写脚本先通过
scp->put()将本地文件上传至localhost的/tmp目录,再分别以「返回字符串」「写入本地文件」两种方式调用scp->get()获取该文件 - 测试结果:
- 除空文件外,所有常规文件(文本、二进制等)的返回结果都比原文件多1字节(即额外的NULL)
scp->put()上传至远程的文件与原文件完全一致(经diff验证)
- 注意:直接去除末尾NULL的方案不可行,因为存在该NULL为文件合法内容的场景
测试脚本代码
#!/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\n", $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'); // 未通过参数指定则复制.bashrc $remoteFile = '/tmp/' . preg_replace('%[/]%','-',$localFile); // 示例:/tmp/-home-user-.bashrc $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); // 执行命令(日志记录,不输出到终端) $remoteData = $scp->get($remoteFile); // 将文件内容复制到本地变量 $success = $scp->get($remoteFile, $copyBack); // 将文件下载到本地文件系统 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); }
临时修复方案
修改phpseclib/Net/SCP.php的get()函数,在以下代码之后:
while ($size < $info['size']) { $data = $this->_receive();
添加以下代码:
// 2023年5月6日添加,解决scp->get()获取非空文件时总是多返回1字节的问题 $expected = $info['size'] - $size; if (strlen($data) == $expected + 1) // 如果接收的数据刚好比预期多1字节 $data = substr($data, 0, $expected);
诉求
希望找到该问题的根源及更优解决方案,或了解问题的正确上报方式
内容的提问来源于stack exchange,提问作者Dennis
相关产品推荐
相关产品推荐

