-
-
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; | |
| } | |
| } | |
| ?> |
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
thanks, work fine on B818-263