Skip to content

Instantly share code, notes, and snippets.

@magicdude4eva
Created April 14, 2019 10:54
Show Gist options
  • Save magicdude4eva/5a8d14fe7c08262408e0356953a617cb to your computer and use it in GitHub Desktop.
Save magicdude4eva/5a8d14fe7c08262408e0356953a617cb to your computer and use it in GitHub Desktop.
Huawei B618s-22d - reboot script and retrieval of other info
<?php
// Config
$routerAddress = '192.168.8.1';
$username="admin";
$password="password";
// Code
$router = new HuaweiB618($routerAddress);
if ($router->login($username, $password)==FALSE) { echo $router->LastError. "!\n"; die(); }
// activate one, or more, functions that you want to try
if ($router->reboot()==FALSE) echo $router->LastError. "!\n"; die();
//if ($ret=$router->DeviceInformation()) echo $ret."\n"; else { echo $router->LastError. "!\n"; die(); }
//if ($ret=$router->CurrentPlmn()) echo $ret."\n"; else { echo $router->LastError. "!\n"; die(); }
//if ($ret=$router->TrafficStatistics()) echo $ret."\n"; else { echo $router->LastError. "!\n"; die(); }
//if ($ret=$router->Status()) echo $ret."\n"; else { echo $router->LastError. "!\n"; die(); }
//if ($router->SendSms("+43670XXXXXX", "SMS sent via php class")==FALSE) { echo $router->LastError. "!\n"; die(); }
if ($router->logout() == FALSE) { echo $router->LastError. "!\n"; die(); }
echo 'OK';
/*
Done on 14th April 2019 for the Huawei B618s-22d
DeviceName: B618s-22d
HardwareVersion: WL1B610FM
SoftwareVersion: 11.196.01.00.965
To run on PI you need to install: sudo apt-get install php-curl php-xml
*/
class HuaweiB618
{
public $url = "";
public $LastError = "";
private $ch = NULL;
private $SessionID = "";
private $TokInfo = array ("");
private $ContLen = 0;
private $options = array(
CURLOPT_RETURNTRANSFER => true, // return web page
CURLOPT_HEADER => false, // NO return headers in addition to content
CURLOPT_FOLLOWLOCATION => true, // follow redirects
CURLOPT_CONNECTTIMEOUT => 5, // timeout on connect
CURLOPT_TIMEOUT => 5, // timeout on response
CURLOPT_MAXREDIRS => 1, // stop after 1 redirect
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
);
private $firstNonce = ""; // example: "f0ce57a3413f75627f2fb0748f6c0edfd7ca88f6f9538fe2a3dc0e03bb01f5c0" // MUST change at every login !
public function __destruct ( ) { if ($this->ch) curl_close( $this->ch ); }
public function __construct($URL="192.168.8.1") { $this->url=$URL; }
/********************************************
* Generic POST *
********************************************/
private function POST($post, $Url, $tok="")
{
if ($tok=="") $tok=$this->TokInfo[0];
curl_setopt($this->ch, CURLOPT_URL, $this->url . $Url);
curl_setopt($this->ch, CURLOPT_POST, 1);
curl_setopt($this->ch, CURLOPT_POSTFIELDS,$post);
curl_setopt($this->ch, CURLOPT_COOKIE, $this->SessionID);
curl_setopt($this->ch, CURLOPT_HTTPHEADER, array( "__RequestVerificationToken: ".$tok,
"Connection: keep-alive"));
return curl_exec( $this->ch );
}
/********************************************
* Generic GET *
********************************************/
private function GET($fun)
{
curl_setopt($this->ch, CURLOPT_URL, $this->url . $fun);
curl_setopt($this->ch, CURLOPT_COOKIE, $this->SessionID);
curl_setopt($this->ch, CURLOPT_POST, 0);
if (!($body = curl_exec( $this->ch ))) return FALSE;
return substr($body, -$this->ContLen);
}
/********************************************
* Error Error Error Error Error *
********************************************/
private function ERROR($f)
{
$this->LastError = $f ." error - ". curl_error($this->ch) . " ( ". curl_errno($this->ch)." )";
return FALSE;
}
/********************************************
* GET /api/webserver/SesTokInfo *
* Recovery <TokInfo> and <SessionID> *
********************************************/
private function SesTokInfo()
{
if (!($body = $this->GET("/api/webserver/SesTokInfo"))) return $this->ERROR(__METHOD__);
$xmlP=simplexml_load_string($body);
$this->SessionID =$xmlP->SesInfo;
$this->TokInfo[0] =$xmlP->TokInfo;
if (!$this->SessionID || !$this->TokInfo[0]) return $this->ERROR (__METHOD__ ."->NAK");
return TRUE;
}
/********************************************
* GET /api/user/state-login *
********************************************/
private function StateLogin()
{
if (!($ret=$this->GET("/api/user/state-login"))) return $this->ERROR(__METHOD__);
return $ret;
}
/********************************************
* GET /api/monitoring/status *
********************************************/
public function Status()
{
if (!($ret=$this->GET("/api/monitoring/status"))) return $this->ERROR(__METHOD__);
return $ret;
}
/********************************************************************************
* login with SCRAM ( Salted Challenge Response Authentication Mechanism ) *
* *
* Warning: the session expires after 5 minutes from login *
********************************************************************************/
public function login($user, $password)
{
$this->ch = curl_init();
$ch=$this->ch;
curl_setopt_array( $ch, $this->options );
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($CH,$header) {
if (substr($header,0,27) == '__RequestVerificationToken:') $this->TokInfo = explode("#", trim(substr($header,27))); else
if (substr($header,0,11) == 'Set-Cookie:') $this->SessionID= trim(substr($header,11)); else
if (substr($header,0,15) == 'Content-Length:') $this->ContLen =(int)trim(substr($header,15));
return strlen($header);
});
if (!$this->SesTokInfo()) return FALSE;
/*------------------------------------------+
| Now I create a new firstNonce |
+------------------------------------------*/
$ctx = hash_init('sha256');
hash_update($ctx, time().date("-m-d H:i:s")); $this->firstNonce=hash_final($ctx);
/*------------------------------------------+
| POST /api/user/challenge_login |
| This POST changes the TokInfo ! |
+------------------------------------------*/
$post="<?xml version='1.0' encoding='UTF-8'?>".
"<request>".
"<username>".$user."</username>".
"<firstnonce>".$this->firstNonce."</firstnonce>".
"<mode>1</mode>".
"</request>";
if (!($body = $this->POST($post, "/api/user/challenge_login"))) return $this->ERROR(__METHOD__ . "->challenge_login");
$body_content = substr($body, -$this->ContLen);
$xmlP = simplexml_load_string($body_content);
$salt = $xmlP->salt;
$servernonce = $xmlP->servernonce;
$iter = (int) $xmlP->iterations; // always = 100
/*------------------------------------------+
| Calculating the clientproof |
+------------------------------------------*/
$authMsg = $this->firstNonce . "," . $servernonce . "," . $servernonce;
$ctx = hash_init('sha256');
$saltPassword = hash_pbkdf2('sha256', $password, hex2bin($salt), $iter, 0, TRUE);
$clientKey = hash_hmac ('sha256', $saltPassword, "Client Key", TRUE);
hash_update($ctx, $clientKey);
$storedkey = hash_final ($ctx, TRUE);
$signature = hash_hmac ('sha256', $storedkey, $authMsg, TRUE);
for($i = 0; $i < strlen($clientKey); $i++) $clientKey[$i] = $clientKey[$i] ^ $signature[$i];
$clientproof=bin2hex($clientKey);
/*------------------------------------------+
| POST api/user/authentication_login |
| This POST changes the SessionID cookie |
+------------------------------------------*/
$post="<?xml version='1.0' encoding='UTF-8'?>".
"<request>".
"<clientproof>" . $clientproof . "</clientproof>".
"<finalnonce>" . $servernonce . "</finalnonce>".
"</request>";
if (!($body = $this->POST($post, "/api/user/authentication_login"))) return $this->ERROR(__METHOD__ . "->authentication_login");
/*------------------------------------------+
| GET /api/user/state-login |
+------------------------------------------*/
if (($body = $this->StateLogin())==FALSE) return FALSE;
$xmlP=simplexml_load_string($body);
if ($xmlP->State != "0" || $xmlP->Username != $user) return $this->ERROR(__METHOD__ ."->StateLogin NAK");
return TRUE;
}
/********************************************
* GET /api/device/information *
********************************************/
public function DeviceInformation ()
{
if (!($ret=$this->GET("/api/device/information"))) return $this->ERROR(__METHOD__);
return $ret;
}
/********************************************
* GET /api/net/current-plmn *
********************************************/
public function CurrentPlmn ()
{
if (!($ret=$this->GET("/api/net/current-plmn"))) return $this->ERROR(__METHOD__);
return $ret;
}
/********************************************
* GET /api/monitoring/traffic-statistics *
********************************************/
public function TrafficStatistics ()
{
if (!($ret=$this->GET("/api/monitoring/traffic-statistics"))) return $this->ERROR(__METHOD__);
return $ret;
}
/********************************************
* POST /api/sms/send-sms *
********************************************/
public function SendSms ($to, $testo)
{
$post= "<?xml version='1.0' encoding='UTF-8'?>\n".
"<request>\n".
"<Index>-1</Index>\n".
"<Phones>\n".
"<Phone>".$to."</Phone>\n".
"</Phones>\n".
"<Sca></Sca>\n".
"<Content>".$testo."</Content>\n".
"<Length>".strlen($testo)."</Length>\n".
"<Reserved>1</Reserved>\n".
"<Date>".date("Y-m-d H:i:s")."</Date>\n".
"</request>\n";
if (!($body = $this->POST($post, "/api/sms/send-sms"))) return $this->ERROR(__METHOD__);
if (!strpos($body, "<response>OK</response>")) return $this->ERROR(__METHOD__ ."->NAK");
return substr($body, -$this->ContLen);
}
/********************************************
* POST /api/device/control reboot *
********************************************/
public function reboot ($rr="1")
{
$post= "<?xml version='1.0' encoding='UTF-8'?>\n".
"<request>\n".
"<Control>".$rr."</Control>\n". // 1=Reboot | 2=Reset
"</request>\n";
if (!($body = $this->POST($post, "/api/device/control"))) return $this->ERROR(__METHOD__);
if (!strpos($body, "<response>OK</response>")) return $this->ERROR(__METHOD__ ."->NAK");
curl_close( $this->ch ); $this->ch=NULL;
return substr($body, -$this->ContLen);
}
/********************************************
* POST /api/user/logout *
********************************************/
public function logout ()
{
$post= "<?xml version='1.0' encoding='UTF-8'?>\n".
"<request>\n".
"<Logout>1</Logout>\n".
"</request>\n";
if (!($body = $this->POST($post, "/api/user/logout"))) return $this->ERROR(__METHOD__);
if (!strpos($body, "<response>OK</response>")) return $this->ERROR(__METHOD__ .":NAK");
curl_close( $this->ch ); $this->ch=NULL;
return TRUE;
}
}
?>
@MulMic
Copy link

MulMic commented Nov 13, 2023

Hi magicdude4eva,

thanks for providing this nice and straightforward example of making use of the Huawei ajax api.

Alas I don't have coins nor do I socialize via "social" media. I prefer personal contact face to face. So, alas, I can not pay you in the currency of your desire, but I can light a candle for you in a church of your preference ;-)

While I still strive to keep much of my tinkering in bash scripting, I learned quite a lot about PHP from you, I even installed a LAMP server on my Pi for that end.

I found, that PHP can, and probably should be used for commandline scripting more often.

I added a shebang to your script (#!/usr/bin/php) and set the file executable, so I can now simply invoke the script by typing ./Huawei.php.

or use it for piping by adding "$input = stream_get_contents(STDIN);" and evaluating the data for use in the script. (As pointed out at https://stackoverflow.com/questions/5891888/piping-data-into-command-line-php).

This enables me to use PHP functions in bash scripting, which may sound weird but I somehow like the odd solutions, just for the heck oft them.

For instance, adding "$message=stream_get_contents(STDIN);" to the script and replacing the message part of the send sms function with $message, I can then do "./huawei.php <<< 'hello world' to send an sms to the given phone number. Nifty, isn't it ?

Presently I am using a Huawei B818-263 (4G) as well as a Huawei H138-380 (5G) router, both can be controlled perfectly with the script.

MulMic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment