3 * @author Jan Altensen (Stricted)
4 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
5 * @copyright 2015 Jan Altensen (Stricted)
7 class SpeedportHybrid
{
12 const VERSION
= '1.0.2';
18 private $challenge = '';
36 private $session = '';
48 private $derivedk = '';
50 public function __construct ($url = 'http://speedport.ip/') {
55 * Requests the password-challenge from the router.
57 private function getChallenge () {
58 $path = 'data/Login.json';
59 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'challengev' => 'null');
60 $data = $this->sentRequest($path, $fields);
61 $data = json_decode($data['body'], true);
62 $data = $this->getValues($data);
64 if (isset($data['challengev']) && !empty($data['challengev'])) {
65 $this->challenge
= $data['challengev'];
70 * login into the router with the given password
72 * @param string $password
75 public function login ($password) {
76 $this->getChallenge();
78 if (empty($this->challenge
)) {
79 throw new Exception('unable to get the challenge from the router');
82 $path = 'data/Login.json';
83 $this->hash
= hash('sha256', $this->challenge
.':'.$password);
84 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash
);
85 $data = $this->sentRequest($path, $fields);
86 $json = json_decode($data['body'], true);
87 $json = $this->getValues($json);
88 if (isset($json['login']) && $json['login'] == 'success') {
89 if (isset($data['header']['Set-Cookie']) && !empty($data['header']['Set-Cookie'])) {
90 preg_match('/^.*(SessionID_R3=[a-z0-9]*).*/i', $data['header']['Set-Cookie'], $match);
91 if (isset($match[1]) && !empty($match[1])) {
92 $this->session
= $match[1];
95 throw new Exception('unable to get the session cookie from the router');
99 if (!function_exists("hash_pbkdf2")) {
100 require_once 'CryptLib/CryptLib.php';
101 $pbkdf2 = new CryptLib\Key\Derivation\PBKDF\
PBKDF2(array('hash' => 'sha1'));
102 $this->derivedk
= bin2hex($pbkdf2->derive(hash('sha256', $password), substr($this->challenge
, 0, 16), 1000, 32));
103 $this->derivedk
= substr($this->derivedk
, 0, 32);
106 $this->derivedk
= hash_pbkdf2('sha1', hash('sha256', $password), substr($this->challenge
, 0, 16), 1000, 32);
109 // get the csrf_token
110 $this->token
= $this->getToken();
112 if ($this->checkLogin() === true) {
122 * check if we are logged in
126 public function checkLogin () {
127 if (empty($this->challenge
) && empty($this->session
)) {
131 $path = 'data/SecureStatus.json';
133 $data = $this->sentRequest($path, $fields, true);
135 if (empty($data['body'])) {
136 throw new Exception('unable to get SecureStatus data');
139 $json = json_decode($data['body'], true);
140 $json = $this->getValues($json);
142 if ($json['loginstate'] != 1) {
154 public function logout () {
155 if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method');
157 $path = 'data/Login.json';
158 $fields = array('csrf_token' => $this->token
, 'logout' => 'byby');
159 $data = $this->sentRequest($path, $fields, true);
160 if ($this->checkLogin() === false) {
161 // reset challenge and session
162 $this->challenge
= '';
166 $json = json_decode($data['body'], true);
171 throw new Exception('logout failed');
180 public function reboot () {
181 if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method');
183 $path = 'data/Reboot.json';
184 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash
, 'reboot_device' => 'true');
185 $data = $this->sentRequest($path, $fields, true);
187 $json = json_decode($data['body'], true);
193 * change dsl connection status
195 * @param string $status
197 public function changeConnectionStatus ($status) {
198 if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method');
200 $path = 'data/Connect.json';
202 if ($status == 'online' ||
$status == 'offline') {
203 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash
, 'req_connect' => $status);
204 $data = $this->sentRequest($path, $fields, true);
206 $json = json_decode($this->decrypt($data['body']), true);
211 throw new Exception();
216 * return the given json as array
218 * @param string $file
221 public function getData ($file) {
222 if ($this->checkLogin() !== true && $file != "Status") throw new Exception('you musst be logged in to use this method');
224 $path = 'data/'.$file.'.json';
226 $data = $this->sentRequest($path, $fields, true);
228 if (empty($data['body'])) {
229 throw new Exception('unable to get '.$file.' data');
232 $json = json_decode($data['body'], true);
238 * get the router syslog
242 public function getSyslog() {
243 return $this->exportData('0');
247 * get the Missed Calls from router
251 public function getMissedCalls() {
252 return $this->exportData('1');
256 * get the Taken Calls from router
260 public function getTakenCalls() {
261 return $this->exportData('2');
265 * get the Dialed Calls from router
269 public function getDialedCalls() {
270 return $this->exportData('3');
274 * export data from router
278 private function exportData ($type) {
279 if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method');
281 $path = 'data/Syslog.json';
282 $fields = array('exporttype' => $type);
283 $data = $this->sentRequest($path, $fields, true);
285 if (empty($data['body'])) {
286 throw new Exception('unable to get export data');
289 return explode("\n", $data['body']);
297 public function reconnectLte () {
298 if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method');
300 $path = 'data/modules.json';
301 $fields = array('csrf_token' => $this->token
, 'lte_reconn' => '1');
302 $fields = $this->encrypt($fields);
303 $data = $this->sentRequest($path, $fields, true, 2);
304 $json = json_decode($data['body'], true);
310 * reset the router to Factory Default
315 public function resetToFactoryDefault () {
316 if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method');
318 $path = 'data/resetAllSetting.json';
319 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash
, 'reset_all' => 'true');
320 $data = $this->sentRequest($path, $fields, true);
321 $json = json_decode($data['body'], true);
328 * check if firmware is actual
332 public function checkFirmware () {
333 if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method');
335 $path = 'data/checkfirmware.json';
336 $fields = array('checkfirmware' => 'true');
337 $data = $this->sentRequest($path, $fields, true);
339 if (empty($data['body'])) {
340 throw new Exception('unable to get checkfirmware data');
343 $json = json_decode($data['body'], true);
349 * decrypt data from router
351 * @param string $data
354 private function decrypt ($data) {
355 require_once 'CryptLib/CryptLib.php';
356 $factory = new CryptLib\Cipher\
Factory();
357 $aes = $factory->getBlockCipher('rijndael-128');
359 $iv = hex2bin(substr($this->challenge
, 16, 16));
360 $adata = hex2bin(substr($this->challenge
, 32, 16));
361 $dkey = hex2bin($this->derivedk
);
362 $enc = hex2bin($data);
365 $mode = $factory->getMode('ccm', $aes, $iv, [ 'adata' => $adata, 'lSize' => 7]);
367 $mode->decrypt($enc);
369 return $mode->finish();
373 * decrypt data for the router
378 private function encrypt ($data) {
379 require_once 'CryptLib/CryptLib.php';
380 $factory = new CryptLib\Cipher\
Factory();
381 $aes = $factory->getBlockCipher('rijndael-128');
383 $iv = hex2bin(substr($this->challenge
, 16, 16));
384 $adata = hex2bin(substr($this->challenge
, 32, 16));
385 $dkey = hex2bin($this->derivedk
);
388 $mode = $factory->getMode('ccm', $aes, $iv, [ 'adata' => $adata, 'lSize' => 7]);
389 $mode->encrypt(http_build_query($data));
391 return bin2hex($mode->finish());
395 * get the values from array
397 * @param array $array
400 private function getValues($array) {
402 foreach ($array as $item) {
403 $data[$item['varid']] = $item['varvalue'];
410 * sends the request to router
412 * @param string $path
413 * @param mixed $fields
414 * @param string $cookie
415 * @param integer $count
418 private function sentRequest ($path, $fields, $cookie = false, $count = 0) {
419 $url = $this->url
.$path.'?lang=en';
421 curl_setopt($ch, CURLOPT_URL
, $url);
423 if (!empty($fields)) {
424 if (is_array($fields)) {
425 curl_setopt($ch, CURLOPT_POST
, count($fields));
426 curl_setopt($ch, CURLOPT_POSTFIELDS
, http_build_query($fields));
429 curl_setopt($ch, CURLOPT_POST
, $count);
430 curl_setopt($ch, CURLOPT_POSTFIELDS
, $fields);
434 if ($cookie === true) {
435 curl_setopt($ch, CURLOPT_COOKIE
, 'challengev='.$this->challenge
.'; '.$this->session
);
438 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, true);
439 curl_setopt($ch, CURLOPT_HEADER
, true);
445 $result = curl_exec($ch);
447 $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE
);
448 $header = substr($result, 0, $header_size);
449 $body = substr($result, $header_size);
453 $body = preg_replace("/(\r\n)|(\r)/", "\n", $body);
454 $body = preg_replace('/\'/i', '"', $body);
455 $body = preg_replace("/\[\s+\]/i", '[ {} ]', $body);
456 $body = preg_replace("/},\s+]/", "}\n]", $body);
458 return array('header' => $this->parse_headers($header), 'body' => $body);
466 private function getToken () {
467 if ($this->checkLogin() !== true) throw new Exception('you musst be logged in to use this method');
469 $path = 'html/content/overview/index.html';
471 $data = $this->sentRequest($path, $fields, true);
473 if (empty($data['body'])) {
474 throw new Exception('unable to get csrf_token');
477 $a = explode('csrf_token = "', $data['body']);
478 $a = explode('";', $a[1]);
480 if (isset($a[0]) && !empty($a[0])) {
484 throw new Exception('unable to get csrf_token');
489 * parse the curl return header into an array
491 * @param string $response
494 private function parse_headers($response) {
496 $header_text = substr($response, 0, strpos($response, "\r\n\r\n"));
498 $header_text = explode("\r\n", $header_text);
499 foreach ($header_text as $i => $line) {
501 $headers['http_code'] = $line;
504 list ($key, $value) = explode(': ', $line);
505 $headers[$key] = $value;