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);
126 $json = json_decode($data['body'], true);
132 * change dsl connection status
134 * @param string $status
136 public function changeConnectionStatus ($status) {
137 $path = 'data/Connect.json';
139 if ($status == 'online' ||
$status == 'offline') {
140 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash
, 'req_connect' => $status);
141 $cookie = 'challengev='.$this->challenge
.'; '.$this->session
;
142 $this->sentRequest($path, $fields, $cookie);
145 throw new Exception();
150 * return the given json as array
152 * the following paths are known to be valid:
154 * /data/interfaces.json
157 * /data/dhcp_client.json
158 * /data/dhcp_server.json
162 * /data/igmp_proxy.json
163 * /data/igmp_snooping.json
169 * /data/bonding_client.json
170 * /data/bonding_tunnel.json
171 * /data/filterlist.json
172 * /data/bonding_tr181.json
175 * /data/Status.json (No login needed)
177 * @param string $file
180 public function getData ($file) {
181 $path = 'data/'.$file.'.json';
183 $cookie = 'challengev='.$this->challenge
.'; '.$this->session
;
184 $data = $this->sentRequest($path, $fields, $cookie);
186 if (empty($data['body'])) {
187 throw new Exception('unable to get '.$file.' data');
190 $json = json_decode($data['body'], true);
196 * get the router syslog
200 public function getSyslog() {
201 $path = 'data/Syslog.json';
202 $fields = array('exporttype' => '0');
203 $cookie = 'challengev='.$this->challenge
.'; '.$this->session
;
204 $data = $this->sentRequest($path, $fields, $cookie);
206 if (empty($data['body'])) {
207 throw new Exception('unable to get syslog data');
210 return explode("\n", $data['body']);
214 * get the Missed Calls from router
218 public function getMissedCalls() {
219 $path = 'data/ExportMissedCalls.json';
220 $fields = array('exporttype' => '1');
221 $cookie = 'challengev='.$this->challenge
.'; '.$this->session
;
222 $data = $this->sentRequest($path, $fields, $cookie);
224 if (empty($data['body'])) {
225 throw new Exception('unable to get syslog data');
228 return explode("\n", $data['body']);
232 * get the Taken Calls from router
236 public function getTakenCalls() {
237 $path = 'data/ExportTakenCalls.json';
238 $fields = array('exporttype' => '2');
239 $cookie = 'challengev='.$this->challenge
.'; '.$this->session
;
240 $data = $this->sentRequest($path, $fields, $cookie);
242 if (empty($data['body'])) {
243 throw new Exception('unable to get syslog data');
246 return explode("\n", $data['body']);
250 * get the Dialed Calls from router
254 public function getDialedCalls() {
255 $path = 'data/ExportDialedCalls.json';
256 $fields = array('exporttype' => '3');
257 $cookie = 'challengev='.$this->challenge
.'; '.$this->session
;
258 $data = $this->sentRequest($path, $fields, $cookie);
260 if (empty($data['body'])) {
261 throw new Exception('unable to get syslog data');
264 return explode("\n", $data['body']);
268 // we cant encrypt and decrypt AES with mode CCM, we need AES with CCM mode for the commands
269 // (stupid, all other data are send as plaintext and some 'normal' data are encrypted...)
270 public function reconnectLte () {
271 $path = 'data/modules.json';
272 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash, 'lte_reconn' => 'true');
273 $cookie = 'challengev='.$this->challenge.'; '.$this->session;
274 $data = $this->sentRequest($path, $fields, $cookie);
275 $json = json_decode($data['body'], true);
281 public function resetToFactoryDefault () {
282 $path = 'data/resetAllSetting.json';
283 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash, 'reset_all' => 'true');
284 $cookie = 'challengev='.$this->challenge.'; '.$this->session;
285 $data = $this->sentRequest($path, $fields, $cookie);
286 $json = json_decode($data['body'], true);
293 * check if firmware is actual
297 public function checkFirmware () {
298 $path = 'data/checkfirmware.json';
299 $fields = array('checkfirmware' => 'true');
300 $cookie = 'challengev='.$this->challenge
.'; '.$this->session
;
301 $data = $this->sentRequest($path, $fields, $cookie);
303 if (empty($data['body'])) {
304 throw new Exception('unable to get checkfirmware data');
307 $json = json_decode($data['body'], true);
313 * sends the request to router
315 * @param string $path
316 * @param array $fields
317 * @param string $cookie
320 private function sentRequest ($path, $fields = array(), $cookie = '') {
321 $url = $this->url
.$path;
323 curl_setopt($ch, CURLOPT_URL
, $url);
325 if (!empty($fields)) {
326 curl_setopt($ch, CURLOPT_POST
, count($fields));
327 curl_setopt($ch, CURLOPT_POSTFIELDS
, http_build_query($fields));
330 if (!empty($cookie)) {
331 curl_setopt($ch, CURLOPT_COOKIE
, $cookie);
334 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, true);
335 curl_setopt($ch, CURLOPT_HEADER
, true);
342 $result = curl_exec($ch);
344 $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE
);
345 $header = substr($result, 0, $header_size);
346 $body = substr($result, $header_size);
350 $body = preg_replace("/(\r\n)|(\r)/", "\n", $body);
351 $body = preg_replace('/\'/i', '"', $body);
352 $body = preg_replace("/\[\s+\]/i", '[ {} ]', $body);
353 $body = preg_replace("/},\s+]/", "}\n]", $body);
355 return array('header' => $this->parse_headers($header), 'body' => $body);
359 * parse the curl return header into an array
361 * @param string $response
364 private function parse_headers($response) {
366 $header_text = substr($response, 0, strpos($response, "\r\n\r\n"));
368 foreach (explode("\r\n", $header_text) as $i => $line) {
370 $headers['http_code'] = $line;
373 list ($key, $value) = explode(': ', $line);
374 $headers[$key] = $value;