2015-06-22 10 views
6

Mam problem, który mnie zaskoczył.PHP Telnet/SSH dynamiczny login

Próbuję zautomatyzować logowanie interfejsu CLI do routera i uruchomić niektóre polecenia uzyskane przez stronę sieci Web. Jednak nie wiem, czy router ma włączone telnet lub SSH (może być jeden, drugi lub oba) i mam listę możliwych kombinacji nazwy użytkownika/hasła, które muszę spróbować uzyskać dostęp.
Och, i nie mogę zmienić ani typu protokołu, ani referencji na urządzeniu, więc to naprawdę nie jest opcja.

Udało mi się dowiedzieć, jak zalogować się do routera za pomocą znanego protokołu i poświadczeń logowania i uruchomić niezbędne polecenia (zawarte poniżej), ale nie wiem, czy powinienem użyć bloku if/else do pracy poprzez decyzje telnet/ssh, lub czy zmiana switcha może być lepsza? Czy użycie Expect wewnątrz PHP byłoby prostszym sposobem na przejście?

function tunnelRun($commands,$user,$pass, $yubi){ 
    $cpeIP = "1.2.3.4"; 
    $commands_explode = explode("\n", $commands); 

    $screenOut = ""; 

    $ssh = new Net_SSH2('router_jumphost'); 
    if (!$ssh->login($user, $pass . $yubi)) { 
     exit('Login Failed'); 
    } 


    $ssh->setTimeout(2); 
    $ssh->write("ssh -l username $cpeIP\n"); 
    $ssh->read("assword:"); 
    $ssh->write("password\n"); 
    $ssh->read("#"); 
    $ssh->write("\n"); 
    $cpePrompt = $ssh->read('/.*[#|>]/', NET_SSH2_READ_REGEX); 
    $cpePrompt = str_replace("\n", '', trim($cpePrompt)); 
    $ssh->write("config t\n"); 


    foreach ($commands_explode as $i) { 
     $ssh->write("$i\n"); // note the "\n" 
     $ssh->setTimeout(2); 
     $screenOut .= $ssh->read(); 

    } 
    $ssh->write("end\n"); 
    $ssh->read($cpePrompt); 
    $ssh->write("exit\n"); 
    echo "Router Update completed! Results below:<br><br>"; 

    echo "<div id=\"text_out\"><textarea style=\" border:none; width: 700px;\" rows=\"20\">".$screenOut."</textarea></div>"; 

Update:

Roztwór I udał się z pętli, gdy przełącznik /. Poszedłbym na trasę Expect, ale ciągle napotykałem problemy związane z włączeniem modułu Expect do PHP na moim serwerze (Windows box). Gdybym używał serwera Unix/Linux Expect byłby najprostszym sposobem na osiągnięcie tego . Zrobiłem to na razie w działającym demo, więc nadal brakuje instrukcji dotyczących przypadków, a obsługa błędów wciąż musi zostać rozwiązana, ale podstawowy pomysł istnieje. Nadal chcę przesuwać instrukcje preg_match dookoła nieco więcej, aby dopasować je na górze pętli while (aby nie spamować całej sekcji case z różnymi liniami preg_match), ale to może okazać się więcej pracy niż ja chcę na razie. Mam nadzieję, że to pomoże komuś innemu próbować zrobić to samo!

<?php 
include('Net/SSH2.php'); 
define('NET_SSH2_LOGGING', NET_SSH2_LOG_COMPLEX); 
ini_set('display_errors', 1); 

$conn = new Net_SSH2('somewhere.outthere.com'); 
if (!$conn->login($user, $pass . $yubi)) { 
    exit('Login Failed'); 
} 

$prompt = "Testing#"; 

$conn->setTimeout(2); 
$conn->write("PS1=\"$prompt\""); 
$conn->read(); 
$conn->write("\n"); 
$screenOut = $conn->read(); 

//echo "$screenOut is set on the shell<br><br>"; 
echo $login_db[3][0]. " ". $login_db[3][1]; 

$logged_in = false; 
$status = "SSH"; 
$status_prev = ""; 
$login_counter = 0; 
while (!$logged_in && $login_counter <=3) { 
    switch ($status) { 

     case "Telnet": 
      break; 
     case "SSH": 
      $conn->write("\n"); 
      $conn->write("ssh -l " . $login_db[$login_counter][0] . " $cpeIP\n"); 
      $status_prev = $status; 
      $status = $conn->read('/\n([.*])$/', NET_SSH2_READ_REGEX); 
      break; 
     case (preg_match('/Permission denied.*/', $status) ? true : false): 
      $conn->write(chr(3)); //Sends Ctrl+C 
      $status = $conn->read(); 
      if (strstr($status, "Testing#")) { 
       $status = "SSH"; 
       $login_counter++; 
       break; 
      } else { 
       break 2; 
      } 
     case (preg_match('/[pP]assword:/', $status) ? true : false): 

      $conn->write($login_db[$login_counter][1] . "\n"); 
      $status_prev = $status; 
      $status = $conn->read('/\n([.*])$/', NET_SSH2_READ_REGEX); 
      break; 
     case (preg_match('/yes\/no/', $status) ? true : false): 
      $conn->write("yes\n"); 
      $status_prev = $status; 
      $status = $conn->read('/\n([.*])$/', NET_SSH2_READ_REGEX); 
      break; 
     case (preg_match('/(^[a-zA-Z0-9_]+[#]$)|(>)/', $status,$matches) ? true : false): 


      $conn->write("show version\n"); 
      $status = $conn->read(">"); 
      if(preg_match('/ADTRAN|Adtran|Cisco/', $status)? true:false){ 
       $logged_in = true; 
       break; 
      } 

     default: 
      echo "<br>Something done messed up! Exiting"; 
      break 2; 

    } 
    //echo "<pre>" . $conn->getLog() . "</pre>"; 
} 
if ($logged_in === true) { 
    echo "<br> Made it out of the While loop cleanly"; 
} else { 
    echo "<br> Made it out of the While loop, but not cleanly"; 
} 
echo "<pre>" . $conn->getLog() . "</pre>"; 

$conn->disconnect(); 
echo "disconnected cleanly"; 
} 
?> 
+0

'' '' '' else' bloki i 'switch' -case" bloki są praktycznie takie same. –

Odpowiedz

3

Jeśli instrukcje mogą spowodować, że twój kod stanie się nieczytelny.
W takim przypadku proponuję użyć bloków obudów,
, ponieważ obudowa przełącznika pozwoli ci napisać wyraźniejszy kod i pozwoli ci skuteczniej wychwycić wyjątkowe wartości.

Korzystanie spodziewać w PHP jest prosty:

<?php> 
ini_set("expect.loguser", "Off"); 

$stream = fopen("expect://ssh [email protected] uptime", "r"); 

$cases = array (
    array (0 => "password:", 1 => PASSWORD) 
); 

switch (expect_expectl ($stream, $cases)) { 
    case PASSWORD: 
     fwrite ($stream, "password\n"); 
     break; 
    default: 
     die ("Error was occurred while connecting to the remote host!\n"); 
} 

while ($line = fgets($stream)) { 
     print $line; 
} 
fclose ($stream); 

?> 
+0

Powyższy kod wygląda bardzo elegancko, ale piszesz strumień, który otworzyłeś jako przeczytany. Czytanie i pisanie wymaga na pewno proc_open do wywoływania środowiska wykonawczego? – symcbean

+0

Rzeczywiście. proc_open - Wykonuje polecenie i otwiera wskaźniki plików dla wejścia/wyjścia (pobrane z: http://php.net/manual/en/function.proc-open.php) –

0

Istnieją pewne komplikacje przy użyciu file_wrapper spodziewać. Gdyby to był ja, po prostu wybrałbym proste połączenie z gniazdem dla telnetu i sondy dla podpowiedzi (z limitem czasu), jeśli połączenie ssh nie powiedzie się.

Przy swobodnej inspekcji, telnet client here wydaje się być sensownie napisany - i przy odrobinie zmiany nazwy może zapewnić ten sam interfejs co rozszerzenie klienta ssh2 (oprócz bitu połączenia).