Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- class SshTunnel
- {
- public $localPort;
- protected $sshCommand;
- protected $verifyCommand;
- protected $lsofCommand;
- protected static $assignPort = 2049;
- protected $sshProcess;
- public $sshUsername;
- public $sshHost;
- public $sshPort;
- public $localAddress;
- public $bindHost;
- public $bindPort;
- public $identityFile;
- public $waitMs;
- public $tries;
- public $sshOptions;
- public $autoConnect;
- public $autoDisconnect;
- public $sshPath;
- public $lsofPath;
- public $ncPath;
- public function __construct(
- $sshUsername,
- $sshHost,
- $sshPort = 22,
- $localAddress = '127.0.0.1',
- $localPort = 0,
- $bindHost = '127.0.0.1',
- $bindPort = 3306,
- $identityFile = '',
- $waitMs = 1000000,
- $tries = 10,
- $sshOptions = ['-q'],
- $autoConnect = true,
- $autoDisconnect = true,
- $sshPath = 'ssh',
- $lsofPath = 'lsof',
- $ncPath = 'nc'
- ) {
- $this->sshUsername = $sshUsername;
- $this->sshHost = $sshHost;
- $this->sshPort = $sshPort;
- $this->localAddress = $localAddress;
- $this->localPort = $localPort;
- $this->bindHost = $bindHost;
- $this->bindPort = $bindPort;
- $this->identityFile = $identityFile;
- $this->waitMs = $waitMs;
- $this->tries = $tries;
- $this->sshOptions = $sshOptions;
- $this->autoConnect = $autoConnect;
- $this->autoDisconnect = $autoDisconnect;
- $this->sshPath = $sshPath;
- $this->lsofPath = $lsofPath;
- $this->ncPath = $ncPath;
- if (!empty($lsofPath)) {
- $this->lsofCommand = sprintf(
- '%s -P -n -i :%%d',
- escapeshellcmd($lsofPath)
- );
- }
- $this->localPort = $localPort;
- if (empty($this->localPort)) {
- do {
- $this->localPort = self::$assignPort++;
- } while ($this->localPort <= 65535 and false === $this->isLocalPortAvailable());
- }
- if (!empty($identityFile)) {
- $sshOptions = array_merge(
- $sshOptions,
- [
- '-i',
- $identityFile
- ]
- );
- }
- $this->sshCommand = array_merge(
- [
- $sshPath
- ],
- $sshOptions,
- [
- '-N',
- '-L',
- $this->localPort . ':' . $bindHost . ':' . $bindPort,
- '-p',
- $sshPort,
- $sshUsername . '@' . $sshHost
- ]
- );
- if (!empty($ncPath)) {
- $this->verifyCommand = sprintf(
- '%s -vz %s %d',
- escapeshellcmd($ncPath),
- escapeshellarg($localAddress),
- $this->localPort
- );
- }
- if ($autoConnect) {
- $this->connect();
- }
- }
- /**
- * Destructor. Disconnects SSH Tunnel.
- */
- public function __destruct()
- {
- if ($this->autoDisconnect) {
- $this->disconnect();
- }
- }
- /**
- * Check if the local port is available.
- * @return ?bool Null when there is no method to verify.
- * @throws \ErrorException
- */
- public function isLocalPortAvailable()
- {
- if (empty($this->lsofCommand)) {
- return null;
- }
- $exitCode = $this->runCommand(
- sprintf($this->lsofCommand, $this->localPort),
- [1 => ['file', '/dev/null', 'w']]
- );
- return (1 === $exitCode);
- }
- /**
- * Connect SSH tunnel.
- * @return bool
- * @throws \ErrorException
- */
- public function connect()
- {
- // Verify first. If there is already a working tunnel that's OK.
- if (true === $this->verifyTunnel()) {
- return true;
- }
- if (false === $this->isLocalPortAvailable()) {
- throw new ErrorException(
- sprintf(
- "Local port %d is not available.\nVerified with: %s",
- $this->localPort,
- sprintf($this->lsofCommand, $this->localPort)
- )
- );
- }
- $this->sshProcess = $this->openProcess($this->sshCommand);
- // Ensure we wait long enough for it to actually connect.
- usleep($this->waitMs);
- for ($i = 0; $i < $this->tries; $i++) {
- if (false !== $this->verifyTunnel()) {
- return true;
- }
- // Wait a bit until next iteration
- usleep($this->waitMs);
- }
- throw new ErrorException(
- sprintf(
- "SSH tunnel is not working.\nCreated with: %s\nVerified with: %s",
- implode(' ', $this->sshCommand),
- $this->verifyCommand
- )
- );
- }
- /**
- * Disconnect SSH tunnel.
- * @return bool True if successful.
- */
- public function disconnect()
- {
- if (!empty($this->sshProcess['proc'])) {
- $this->closeProcess($this->sshProcess, true);
- return true;
- }
- return false;
- }
- /**
- * Verifies whether the tunnel is active or not.
- * @return ?bool Null when there is no method to verify.
- * @throws \ErrorException
- */
- public function verifyTunnel()
- {
- if (empty($this->verifyCommand)) {
- return null;
- }
- return (0 === $this->runCommand($this->verifyCommand, [2 => ['file', '/dev/null', 'w']]));
- }
- /**
- * @param string|array<string> $command
- * @param array<resource|array<string>> $descriptorSpec
- * @return array<resource|array<resource>>
- * @throws \ErrorException
- */
- protected function openProcess(
- $command,
- $descriptorSpec = []
- ) {
- if (is_array($command)) {
- $command = implode(' ', $command);
- }
- $result = [
- 'pipes' => []
- ];
- $result['proc'] = proc_open(
- $command,
- $descriptorSpec,
- $result['pipes']
- );
- if (!is_resource($result['proc'])) {
- throw new ErrorException(sprintf("Error executing command: %s", $command));
- }
- $procStatus = proc_get_status($result['proc']);
- if (!$procStatus['running'] || !$procStatus['pid']) {
- throw new ErrorException(sprintf("Process is not running. Command: %s", $command));
- }
- return $result;
- }
- /**
- * Close a process opened by openProcess()
- * @param array<resource|array<resource>> $process
- * @param bool $kill
- * @return int
- */
- protected function closeProcess( $process, $kill = false)
- {
- foreach ($process['pipes'] as $pipe) {
- fclose($pipe);
- }
- if ($kill) {
- proc_terminate($process['proc']);
- proc_terminate($process['proc'], 9);
- }
- return proc_close($process['proc']);
- }
- /**
- * Runs a command, returns exit code.
- * @param string $command
- * @param array<resource|array<string>> $descriptorSpec
- * @return int 0 means Success.
- * @throws \ErrorException
- */
- protected function runCommand(
- $command,
- $descriptorSpec = []
- ) {
- return $this->closeProcess($this->openProcess($command, $descriptorSpec));
- }
- }
- $tunnel = new SshTunnel(
- 'fly',
- '103.209.159.190',
- 22,
- '127.0.0.1',
- 0,
- '127.0.0.1',
- 3306,
- __DIR__ . '/../../pdbsshkey'
- );
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement