-
-
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: If the above helped you in any way, then follow me on Twitter or send me some coins:
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. Sign up with Coinbase and instantly get $10 in BTC. I also accept old-school PayPal.
If you have no crypto, follow me at least on Twitter.