Search code examples
phpsshstreamblocking

Sending input to the blocking stream of PHP ssh


I have similar situation as the following question SSH2 change a user password

The only difference is my action is "scp", I want to send ssh command through PHP to send the files to remote machine using scp command (I know php itself has method to achieve this, but for some reason, I skip that method).

Code is as follows



//run specified cmd with stream reading and writing
    function runCMDAdvance($cmd){
        if (strpos($cmd,'scp') !== false) {
                        //reply machine when ask for password in file transfer
            $confirmPass = $this->password."\n".$this->password."\n";
            if (!($stream = ssh2_exec($this->conn, $cmd )))
            {
                echo "fail: unable to execute command\n";
                fclose($stream);
                return ERROR_SSH_EXEC_COMM;
            }

            fwrite($stream,$confirmPass); //>>Actually I want to pass the password information by writing to the stream, but turn out that it does not work
            stream_set_blocking($stream, true);
        }
    }

When I try to write the password information after the stream set blocking, the php execution code will stuck. (I guess the reason will be the remote machine is waiting for the password information but php does not able to give them, so it just keep waiting and jam the php running thread.)

I have read through the above question but can't seem to get the right direction to achieve this, is there any ideas on this??

Again, thanks all for the help!

Referring to neubert's answer, using phpseclib still not work...

//$cmd is $cmd =  'scp '.$scpPath.' root@'.$ip.':/root';
function runCMDAdvance($cmd, $tVal){

        if (strstr($cmd,"scp")!==FALSE){
            if((include('Net/SSH2.php')) && (include('Net/SCP.php'))){
                //set ssh timeout value
                $this->setTimeoutVar($tVal);

                $ssh = new Net_SSH2($this->host);
                if (!$ssh->login($this->username, $this->password)){
                    echo "Bad Login";
                    return false;
                }
                $ssh->write($cmd."\n");
                $ssh->read(' password:');
                $ssh->write($this->password."\n");
            }
        }
        else{
            echo "Wrong command used, please check your code";
            return false;
        }
    }

My solution to deal with SSH read and write stream

//run any specified cmd using ssh
    //parameter:
    //cmd>>running command
    //tVal>>command run timeout
    //return:terminal stdout
    function runCMDAdvance($sshIP, $cmd, $tVal,$tarIP){
        $ssh = new Net_SSH2($sshIP);
        if (!$ssh->login($this->username, $this->password)){
            echo "Bad Login";
            return false;
        }
        //set timeout for running ssh command, should be done after the ssh object has been initialized
        $ssh->setTimeout($tVal);

        //scp action detected
        if (strstr($cmd,"scp")!==FALSE){
            //if this is the first time to do the ssh, connect with "yes" for answering
            if ($ssh->write($cmd)){
                //store the output of the text message given by the router
                $ssh_stdout = $ssh->read();
                //echo $ssh_stdout."|||";
                if (strstr($ssh_stdout,'trusted ')!==FALSE){
                    $ssh->write("y\n");
                    //and ask password
                    $ssh->read(' :password');
                }
                //deal with known host situation
                elseif (strstr($ssh_stdout,'ssh-keygen ')!==FALSE){
                    $ssh->write('ssh-keygen -f '.'/root/.ssh/known_hosts -R '.$tarIP."\n");
                    $ssh->write($cmd);
                    $ssh->read(' :password');
                    $ssh->write($this->password."\n");
                    return true;
                }
                //deal with still connecting situation
                elseif (strstr($ssh_stdout,'Are you sure you want to continue connecting')!==FALSE){
                    $ssh->write("yes\n");
                    $ssh->read(' :password');
                    $ssh->write($this->password."\n");
                    return true;
                }
                $ssh->write($this->password."\n");
                $returnRead = $ssh->read();
                $ssh->disconnect();
                return true;
            }//end of inner inner if
        }//end of scp if (deal with scp)
        else{//if not scp action,just normal action,simply execute the command
            //exec will not return anything if the command is not run successfully
            $res = $ssh->exec($cmd);
            $ssh->disconnect();
            return $res;
        }
    }

Solution

  • Here's how you could do it with phpseclib, a pure PHP SSH2 implementation:

    <?php
    include('Net/SSH2.php');
    
    define('NET_SSH2_LOGGING', 3);
    
    $ssh = new Net_SSH2('www.website1.com');
    $ssh->login('username', 'password');
    
    $ssh->write("ssh username@www.website2.com\n");
    $ssh->read(' password:');
    $ssh->write("password\n");
    
    $ssh->setTimeout(0.5);
    echo $ssh->read();