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)
12 private $challenge = '';
24 private $session = '';
36 private $derivedk = '';
38 public function __construct ($password, $url = 'http://speedport.ip/') {
40 $this->getChallenge();
42 if (empty($this->challenge
)) {
43 throw new Exception('unable to get the challenge from the router');
46 $login = $this->login($password);
48 if ($login === false) {
49 throw new Exception('unable to login');
54 * Requests the password-challenge from the router.
56 public function getChallenge () {
57 $path = 'data/Login.json';
58 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'challengev' => 'null');
59 $data = $this->sentRequest($path, $fields);
60 $data = json_decode($data['body'], true);
61 if ($data[1]['varid'] == 'challengev') {
62 $this->challenge
= $data[1]['varvalue'];
67 * login into the router with the given password
69 * @param string $password
72 public function login ($password) {
73 $path = 'data/Login.json';
74 $this->hash
= hash('sha256', $this->challenge
.':'.$password);
75 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash
);
76 $data = $this->sentRequest($path, $fields);
77 $json = json_decode($data['body'], true);
78 if ($json[15]['varid'] == 'login' && $json[15]['varvalue'] == 'success') {
79 if (isset($data['header']['Set-Cookie']) && !empty($data['header']['Set-Cookie'])) {
80 preg_match('/^.*(SessionID_R3=[a-z0-9]*).*/i', $data['header']['Set-Cookie'], $match);
81 if (isset($match[1]) && !empty($match[1])) {
82 $this->session
= $match[1];
85 throw new Exception('unable to get the session cookie from the router');
89 $this->derivedk
= hash_pbkdf2('sha1', hash('sha256', $password), substr($this->challenge
, 0, 16), 1000, 32);
103 public function logout () {
104 $path = 'data/Login.json';
105 $fields = array('logout' => 'byby');
106 $data = $this->sentRequest($path, $fields);
107 // reset challenge and session
108 $this->challenge
= '';
111 $json = json_decode($data['body'], true);
121 public function reboot () {
122 $path = 'data/Reboot.json';
123 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash
, 'reboot_device' => 'true');
124 $cookie = 'challengev='.$this->challenge
.'; '.$this->session
;
125 $data = $this->sentRequest($path, $fields, $cookie);
127 $json = json_decode($data['body'], true);
133 * change dsl connection status
135 * @param string $status
137 public function changeConnectionStatus ($status) {
138 $path = 'data/Connect.json';
140 if ($status == 'online' ||
$status == 'offline') {
141 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash
, 'req_connect' => $status);
142 $cookie = 'challengev='.$this->challenge
.'; '.$this->session
;
143 $this->sentRequest($path, $fields, $cookie);
146 throw new Exception();
151 * return the given json as array
153 * the following paths are known to be valid:
155 * /data/interfaces.json
158 * /data/dhcp_client.json
159 * /data/dhcp_server.json
163 * /data/igmp_proxy.json
164 * /data/igmp_snooping.json
170 * /data/bonding_client.json
171 * /data/bonding_tunnel.json
172 * /data/filterlist.json
173 * /data/bonding_tr181.json
176 * /data/Status.json (No login needed)
178 * @param string $file
181 public function getData ($file) {
182 $path = 'data/'.$file.'.json';
184 $cookie = 'challengev='.$this->challenge
.'; '.$this->session
;
185 $data = $this->sentRequest($path, $fields, $cookie);
187 if (empty($data['body'])) {
188 throw new Exception('unable to get '.$file.' data');
191 $json = json_decode($data['body'], true);
197 * get the router syslog
201 public function getSyslog() {
202 $path = 'data/Syslog.json';
203 $fields = array('exporttype' => '0');
204 $cookie = 'challengev='.$this->challenge
.'; '.$this->session
;
205 $data = $this->sentRequest($path, $fields, $cookie);
207 if (empty($data['body'])) {
208 throw new Exception('unable to get syslog data');
211 return explode("\n", $data['body']);
215 * get the Missed Calls from router
219 public function getMissedCalls() {
220 $path = 'data/ExportMissedCalls.json';
221 $fields = array('exporttype' => '1');
222 $cookie = 'challengev='.$this->challenge
.'; '.$this->session
;
223 $data = $this->sentRequest($path, $fields, $cookie);
225 if (empty($data['body'])) {
226 throw new Exception('unable to get syslog data');
229 return explode("\n", $data['body']);
233 * get the Taken Calls from router
237 public function getTakenCalls() {
238 $path = 'data/ExportTakenCalls.json';
239 $fields = array('exporttype' => '2');
240 $cookie = 'challengev='.$this->challenge
.'; '.$this->session
;
241 $data = $this->sentRequest($path, $fields, $cookie);
243 if (empty($data['body'])) {
244 throw new Exception('unable to get syslog data');
247 return explode("\n", $data['body']);
251 * get the Dialed Calls from router
255 public function getDialedCalls() {
256 $path = 'data/ExportDialedCalls.json';
257 $fields = array('exporttype' => '3');
258 $cookie = 'challengev='.$this->challenge
.'; '.$this->session
;
259 $data = $this->sentRequest($path, $fields, $cookie);
261 if (empty($data['body'])) {
262 throw new Exception('unable to get syslog data');
265 return explode("\n", $data['body']);
269 // we cant encrypt and decrypt AES with mode CCM, we need AES with CCM mode for the commands
270 // (stupid, all other data are send as plaintext and some 'normal' data are encrypted...)
271 public function reconnectLte () {
272 $path = 'data/modules.json';
273 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash, 'lte_reconn' => 'true');
274 $cookie = 'challengev='.$this->challenge.'; '.$this->session;
275 $data = $this->sentRequest($path, $fields, $cookie);
276 $json = json_decode($data['body'], true);
282 public function resetToFactoryDefault () {
283 $path = 'data/resetAllSetting.json';
284 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash, 'reset_all' => 'true');
285 $cookie = 'challengev='.$this->challenge.'; '.$this->session;
286 $data = $this->sentRequest($path, $fields, $cookie);
287 $json = json_decode($data['body'], true);
294 * check if firmware is actual
298 public function checkFirmware () {
299 $path = 'data/checkfirmware.json';
300 $fields = array('checkfirmware' => 'true');
301 $cookie = 'challengev='.$this->challenge
.'; '.$this->session
;
302 $data = $this->sentRequest($path, $fields, $cookie);
304 if (empty($data['body'])) {
305 throw new Exception('unable to get checkfirmware data');
308 $json = json_decode($data['body'], true);
314 * decrypt data from router
316 * @param string $data
319 public function decrypt ($data) {
320 require_once 'CryptLib/CryptLib.php';
321 $factory = new CryptLib\Cipher\
Factory();
322 $aes = $factory->getBlockCipher('rijndael-128');
324 $iv = hex2bin(substr($this->challenge
, 16, 16));
325 $adata = hex2bin(substr($this->challenge
, 32, 16));
326 $dkey = hex2bin($this->derivedk
);
327 $enc = hex2bin($data);
330 $mode = $factory->getMode('ccm', $aes, $iv, [ 'adata' => $adata, 'lSize' => 7]);
332 $mode->decrypt($enc);
334 return $mode->finish();
338 * decrypt data for the router
343 public function encrypt ($data) {
344 require_once 'CryptLib/CryptLib.php';
345 $factory = new CryptLib\Cipher\
Factory();
346 $aes = $factory->getBlockCipher('rijndael-128');
348 $iv = hex2bin(substr($this->challenge
, 16, 16));
349 $adata = hex2bin(substr($this->challenge
, 32, 16));
350 $dkey = hex2bin($this->derivedk
);
353 $mode = $factory->getMode('ccm', $aes, $iv, [ 'adata' => $adata, 'lSize' => 7]);
354 $mode->encrypt(http_build_query($data));
356 return $mode->finish();
360 * sends the request to router
362 * @param string $path
363 * @param array $fields
364 * @param string $cookie
367 private function sentRequest ($path, $fields = array(), $cookie = '') {
368 $url = $this->url
.$path;
370 curl_setopt($ch, CURLOPT_URL
, $url);
372 if (!empty($fields)) {
373 curl_setopt($ch, CURLOPT_POST
, count($fields));
374 curl_setopt($ch, CURLOPT_POSTFIELDS
, http_build_query($fields));
377 if (!empty($cookie)) {
378 curl_setopt($ch, CURLOPT_COOKIE
, $cookie);
381 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, true);
382 curl_setopt($ch, CURLOPT_HEADER
, true);
389 $result = curl_exec($ch);
391 $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE
);
392 $header = substr($result, 0, $header_size);
393 $body = substr($result, $header_size);
398 $body = preg_replace("/(\r\n)|(\r)/", "\n", $body);
399 $body = preg_replace('/\'/i', '"', $body);
400 $body = preg_replace("/\[\s+\]/i", '[ {} ]', $body);
401 $body = preg_replace("/},\s+]/", "}\n]", $body);
403 return array('header' => $this->parse_headers($header), 'body' => $body);
407 * parse the curl return header into an array
409 * @param string $response
412 private function parse_headers($response) {
414 $header_text = substr($response, 0, strpos($response, "\r\n\r\n"));
416 foreach (explode("\r\n", $header_text) as $i => $line) {
418 $headers['http_code'] = $line;
421 list ($key, $value) = explode(': ', $line);
422 $headers[$key] = $value;