X-Git-Url: https://git.stricted.de/?p=GitHub%2FStricted%2Fspeedport-hybrid-php-api.git;a=blobdiff_plain;f=SpeedportHybrid.class.php;h=c135643cd5163d77b29c03fef5f52a4a9e0d8315;hp=9ff3a7f1a93ef059a975e73fbe10ebdb3ebb498e;hb=5b40b9c92d17334a29473c1c10558344659995d0;hpb=e8db2f0bff744403b2364e8de4ad1ec5afa1d99f diff --git a/SpeedportHybrid.class.php b/SpeedportHybrid.class.php index 9ff3a7f..c135643 100644 --- a/SpeedportHybrid.class.php +++ b/SpeedportHybrid.class.php @@ -1,33 +1,35 @@ - * @copyright 2015 Jan Altensen (Stricted) + * @copyright 2015-2016 Jan Altensen (Stricted) */ class SpeedportHybrid { - /** - * password-challenge - * @var string - */ - private $challenge = ''; + use Connection; + use CryptLib; + use Firewall; + use Login; + use Network; + use Phone; + use System; /** - * csrf_token - * @var string - */ - private $token = ''; - - /** - * hashed password - * @var string - */ - private $hash = ''; - - /** - * session cookie - * @var string + * class version + * @const string */ - private $session = ''; + const VERSION = '1.0.5'; /** * router url @@ -36,382 +38,37 @@ class SpeedportHybrid { private $url = ''; /** - * derivedk cookie - * @var string + * inititalize this class + * + * @param string $url */ - private $derivedk = ''; - public function __construct ($url = 'http://speedport.ip/') { $this->url = $url; + $this->checkRequirements(); } /** - * Requests the password-challenge from the router. + * check php requirements */ - private function getChallenge () { - $path = 'data/Login.json'; - $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'challengev' => 'null'); - $data = $this->sentRequest($path, $fields); - $data = json_decode($data['body'], true); - $data = $this->getValues($data); - - if (isset($data['challengev']) && !empty($data['challengev'])) { - $this->challenge = $data['challengev']; + private function checkRequirements () { + if (!extension_loaded('curl')) { + throw new Exception("The PHP Extension 'curl' is missing."); } - } - - /** - * login into the router with the given password - * - * @param string $password - * @return boolean - */ - public function login ($password) { - $this->getChallenge(); - - if (empty($this->challenge)) { - throw new Exception('unable to get the challenge from the router'); + else if (!extension_loaded('json')) { + throw new Exception("The PHP Extension 'json' is missing."); } - - $path = 'data/Login.json'; - $this->hash = hash('sha256', $this->challenge.':'.$password); - $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash); - $data = $this->sentRequest($path, $fields); - $json = json_decode($data['body'], true); - $json = $this->getValues($json); - if (isset($json['login']) && $json['login'] == 'success') { - if (isset($data['header']['Set-Cookie']) && !empty($data['header']['Set-Cookie'])) { - preg_match('/^.*(SessionID_R3=[a-z0-9]*).*/i', $data['header']['Set-Cookie'], $match); - if (isset($match[1]) && !empty($match[1])) { - $this->session = $match[1]; - } - else { - throw new Exception('unable to get the session cookie from the router'); - } - - // calculate derivedk - if (!function_exists("hash_pbkdf2")) { - require_once 'CryptLib/CryptLib.php'; - $pbkdf2 = new CryptLib\Key\Derivation\PBKDF\PBKDF2(array('hash' => 'sha1')); - $this->derivedk = bin2hex($pbkdf2->derive(hash('sha256', $password), substr($this->challenge, 0, 16), 1000, 32)); - $this->derivedk = substr($this->derivedk, 0, 32); - } - else { - $this->derivedk = hash_pbkdf2('sha1', hash('sha256', $password), substr($this->challenge, 0, 16), 1000, 32); - } - - // get the csrf_token - $this->token = $this->getToken(); - - if ($this->checkLogin() === true) { - return true; - } - } - } - - return false; - } - - /** - * check if we are logged in - * - * @return boolean - */ - public function checkLogin () { - $path = 'data/SecureStatus.json'; - $fields = array(); - $cookie = 'challengev='.$this->challenge.'; '.$this->session; - $data = $this->sentRequest($path, $fields, $cookie); - - if (empty($data['body'])) { - throw new Exception('unable to get SecureStatus data'); - } - - $json = json_decode($data['body'], true); - $json = $this->getValues($json); - - if ($json['loginstate'] != 1) { - return false; - } - - return true; - } - - /** - * logout - * - * @return array - */ - public function logout () { - if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method'); - - $path = 'data/Login.json'; - $fields = array('csrf_token' => $this->token, 'logout' => 'byby'); - $cookie = 'challengev='.$this->challenge.'; '.$this->session; - $data = $this->sentRequest($path, $fields, $cookie); - if ($this->checkLogin() === false) { - // reset challenge and session - $this->challenge = ''; - $this->session = ''; - $this->token = ""; - - $json = json_decode($data['body'], true); - - return $json; - } - else { - throw new Exception('logout failed'); - } - } - - /** - * reboot the router - * - * @return array - */ - public function reboot () { - if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method'); - - $path = 'data/Reboot.json'; - $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash, 'reboot_device' => 'true'); - $cookie = 'challengev='.$this->challenge.'; '.$this->session; - $data = $this->sentRequest($path, $fields, $cookie); - - $json = json_decode($data['body'], true); - - return $json; - } - - /** - * change dsl connection status - * - * @param string $status - */ - public function changeConnectionStatus ($status) { - if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method'); - - $path = 'data/Connect.json'; - - if ($status == 'online' || $status == 'offline') { - $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash, 'req_connect' => $status); - $cookie = 'challengev='.$this->challenge.'; '.$this->session; - $data = $this->sentRequest($path, $fields, $cookie); - - $json = json_decode($this->decrypt($data['body']), true); - - return $json; - } - else { - throw new Exception(); - } - } - - /** - * return the given json as array - * - * @param string $file - * @return array - */ - public function getData ($file) { - if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method'); - - $path = 'data/'.$file.'.json'; - $fields = array(); - $cookie = 'challengev='.$this->challenge.'; '.$this->session; - $data = $this->sentRequest($path, $fields, $cookie); - - if (empty($data['body'])) { - throw new Exception('unable to get '.$file.' data'); - } - - $json = json_decode($data['body'], true); - - return $json; - } - - /** - * get the router syslog - * - * @return array - */ - public function getSyslog() { - if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method'); - - $path = 'data/Syslog.json'; - $fields = array('exporttype' => '0'); - $cookie = 'challengev='.$this->challenge.'; '.$this->session; - $data = $this->sentRequest($path, $fields, $cookie); - - if (empty($data['body'])) { - throw new Exception('unable to get syslog data'); - } - - return explode("\n", $data['body']); - } - - /** - * get the Missed Calls from router - * - * @return array - */ - public function getMissedCalls() { - if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method'); - - $path = 'data/ExportMissedCalls.json'; - $fields = array('exporttype' => '1'); - $cookie = 'challengev='.$this->challenge.'; '.$this->session; - $data = $this->sentRequest($path, $fields, $cookie); - - if (empty($data['body'])) { - throw new Exception('unable to get syslog data'); + else if (!extension_loaded('pcre')) { + throw new Exception("The PHP Extension 'pcre' is missing."); } - - return explode("\n", $data['body']); - } - - /** - * get the Taken Calls from router - * - * @return array - */ - public function getTakenCalls() { - if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method'); - - $path = 'data/ExportTakenCalls.json'; - $fields = array('exporttype' => '2'); - $cookie = 'challengev='.$this->challenge.'; '.$this->session; - $data = $this->sentRequest($path, $fields, $cookie); - - if (empty($data['body'])) { - throw new Exception('unable to get syslog data'); + else if (!extension_loaded('ctype')) { + throw new Exception("The PHP Extension 'ctype' is missing."); } - - return explode("\n", $data['body']); - } - - /** - * get the Dialed Calls from router - * - * @return array - */ - public function getDialedCalls() { - if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method'); - - $path = 'data/ExportDialedCalls.json'; - $fields = array('exporttype' => '3'); - $cookie = 'challengev='.$this->challenge.'; '.$this->session; - $data = $this->sentRequest($path, $fields, $cookie); - - if (empty($data['body'])) { - throw new Exception('unable to get syslog data'); + else if (!extension_loaded('hash')) { + throw new Exception("The PHP Extension 'hash' is missing."); } - - return explode("\n", $data['body']); - } - - /** - * reconnect LTE - * - * @return array - */ - public function reconnectLte () { - if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method'); - - $path = 'data/modules.json'; - $fields = array('csrf_token' => $this->token, 'lte_reconn' => '1'); - $fields = $this->encrypt($fields); - $cookie = 'challengev='.$this->challenge.'; '.$this->session; - $data = $this->sentRequest($path, $fields, $cookie, 2); - $json = json_decode($data['body'], true); - - return $json; - } - - /** - * reset the router to Factory Default - * not tested - * - * @return array - */ - public function resetToFactoryDefault () { - if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method'); - - $path = 'data/resetAllSetting.json'; - $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash, 'reset_all' => 'true'); - $cookie = 'challengev='.$this->challenge.'; '.$this->session; - $data = $this->sentRequest($path, $fields, $cookie); - $json = json_decode($data['body'], true); - - return $json; - } - - - /** - * check if firmware is actual - * - * @return array - */ - public function checkFirmware () { - if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method'); - - $path = 'data/checkfirmware.json'; - $fields = array('checkfirmware' => 'true'); - $cookie = 'challengev='.$this->challenge.'; '.$this->session; - $data = $this->sentRequest($path, $fields, $cookie); - - if (empty($data['body'])) { - throw new Exception('unable to get checkfirmware data'); + else if (!in_array('sha256', hash_algos())) { + throw new Exception('SHA-256 algorithm is not Supported.'); } - - $json = json_decode($data['body'], true); - - return $json; - } - - /** - * decrypt data from router - * - * @param string $data - * @return array - */ - private function decrypt ($data) { - require_once 'CryptLib/CryptLib.php'; - $factory = new CryptLib\Cipher\Factory(); - $aes = $factory->getBlockCipher('rijndael-128'); - - $iv = hex2bin(substr($this->challenge, 16, 16)); - $adata = hex2bin(substr($this->challenge, 32, 16)); - $dkey = hex2bin($this->derivedk); - $enc = hex2bin($data); - - $aes->setKey($dkey); - $mode = $factory->getMode('ccm', $aes, $iv, [ 'adata' => $adata, 'lSize' => 7]); - - $mode->decrypt($enc); - - return $mode->finish(); - } - - /** - * decrypt data for the router - * - * @param array $data - * @return string - */ - private function encrypt ($data) { - require_once 'CryptLib/CryptLib.php'; - $factory = new CryptLib\Cipher\Factory(); - $aes = $factory->getBlockCipher('rijndael-128'); - - $iv = hex2bin(substr($this->challenge, 16, 16)); - $adata = hex2bin(substr($this->challenge, 32, 16)); - $dkey = hex2bin($this->derivedk); - - $aes->setKey($dkey); - $mode = $factory->getMode('ccm', $aes, $iv, [ 'adata' => $adata, 'lSize' => 7]); - $mode->encrypt(http_build_query($data)); - - return bin2hex($mode->finish()); } /** @@ -423,7 +80,26 @@ class SpeedportHybrid { private function getValues($array) { $data = array(); foreach ($array as $item) { - $data[$item['varid']] = $item['varvalue']; + if (!isset($item['vartype']) || !isset($item['varid']) || !isset($item['varvalue'])) continue; + + // thank you telekom for this piece of shit + if ($item['vartype'] == 'template') { + if (is_array($item['varvalue'])) { + $data[$item['varid']][] = $this->getValues($item['varvalue']); + } + else { + // i dont know if we need this + $data[$item['varid']] = $item['varvalue']; + } + } + else { + if (is_array($item['varvalue'])) { + $data[$item['varid']] = $this->getValues($item['varvalue']); + } + else { + $data[$item['varid']] = $item['varvalue']; + } + } } return $data; @@ -438,33 +114,29 @@ class SpeedportHybrid { * @param integer $count * @return array */ - private function sentRequest ($path, $fields, $cookie = '', $count = 0) { + private function sendRequest ($path, $fields, $cookie = false, $count = 0) { $url = $this->url.$path; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); if (!empty($fields)) { + curl_setopt($ch, CURLOPT_POST, true); + if (is_array($fields)) { - curl_setopt($ch, CURLOPT_POST, count($fields)); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields)); } else { - curl_setopt($ch, CURLOPT_POST, $count); curl_setopt($ch, CURLOPT_POSTFIELDS, $fields); } } - if (!empty($cookie)) { - curl_setopt($ch, CURLOPT_COOKIE, $cookie); + if ($cookie === true) { + curl_setopt($ch, CURLOPT_COOKIE, 'challengev='.$this->challenge.'; '.$this->cookie); } curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, true); - if ($cookie) { - - } - $result = curl_exec($ch); $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); @@ -472,42 +144,32 @@ class SpeedportHybrid { $body = substr($result, $header_size); curl_close($ch); - // fix invalid json + // check if response is empty + if (empty($body)) { + throw new RouterException('empty response'); + } + + // check if body is encrypted (hex instead of json) + if (ctype_xdigit($body)) { + $body = $this->decrypt($body); + } + // fix invalid json $body = preg_replace("/(\r\n)|(\r)/", "\n", $body); $body = preg_replace('/\'/i', '"', $body); $body = preg_replace("/\[\s+\]/i", '[ {} ]', $body); $body = preg_replace("/},\s+]/", "}\n]", $body); - return array('header' => $this->parse_headers($header), 'body' => $body); - } - - /** - * get the csrf_token - * - * @return string - */ - private function getToken () { - if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method'); - - $path = 'html/content/overview/index.html?lang=de'; - $fields = array(); - $cookie = 'challengev='.$this->challenge.'; '.$this->session; - $data = $this->sentRequest($path, $fields, $cookie); - - if (empty($data['body'])) { - throw new Exception('unable to get csrf_token'); + // decode json + if (strpos($url, '.json') !== false) { + $json = json_decode($body, true); + + if (is_array($json)) { + $body = $json; + } } - $a = explode('csrf_token = "', $data['body']); - $a = explode('";', $a[1]); - - if (isset($a[0]) && !empty($a[0])) { - return $a[0]; - } - else { - throw new Exception('unable to get csrf_token'); - } + return array('header' => $this->parse_headers($header), 'body' => $body); } /**