-
-
Save magicdude4eva/5a8d14fe7c08262408e0356953a617cb to your computer and use it in GitHub Desktop.
<?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; | |
} | |
} | |
?> |
thank you so much.
work fine with Huawei B311As-853
Hardware versionWL2B311M
Software version10.0.1.1(H187SP11C00)
when will expire SessionID and firstNonce after set?
Great script! Thank you very much!
Stupid question but what does "2=Reset" instead of 1 in function reboot do? Is that a factory reset?
I'm looking for a way to (re)connect LTE without a hardware reboot on a B528.
Hello, I don't know how to use the script?
How can it be done?
thanks, work fine on B818-263
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
Donations are always welcome
🍺 Please support me: Although all my software is free, it is always appreciated if you can support my efforts on Github with a contribution via Paypal - this allows me to write cool projects like this in my personal time and hopefully help you or your business.
Go to Curve.com to add your Crypto.com card to ApplePay and signup to Crypto.com for a staking and free Crypto debit card.
Use Binance Exchange to trade #altcoins. I also accept old-school PayPal.