stupid AES mode CCM -.-
[GitHub/Stricted/speedport-hybrid-php-api.git] / speedport.class.php
CommitLineData
a91317a6 1<?php
1d934afe
S
2/**
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)
6 */
a91317a6
S
7class speedport {
8 /**
9 * password-challenge
10 * @var string
11 */
12 private $challenge = '';
13
14 /**
15 * hashed password
16 * @var string
17 */
18 private $hash = '';
19
20 /**
21 * session cookie
22 * @var string
23 */
24 private $session = '';
25
26 /**
27 * router url
28 * @var string
29 */
b5a532a8 30 private $url = '';
a91317a6 31
c9e082da
S
32 /**
33 * derivedk cookie
34 * @var string
35 */
36 private $derivedk = '';
37
b5a532a8
S
38 public function __construct ($password, $url = 'http://speedport.ip/') {
39 $this->url = $url;
a91317a6
S
40 $this->getChallenge();
41
42 if (empty($this->challenge)) {
43 throw new Exception('unable to get the challenge from the router');
44 }
45
46 $login = $this->login($password);
47
48 if ($login === false) {
49 throw new Exception('unable to login');
50 }
51 }
52
53 /**
54 * Requests the password-challenge from the router.
55 */
56 public function getChallenge () {
b5a532a8 57 $path = 'data/Login.json';
a91317a6 58 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'challengev' => 'null');
b5a532a8 59 $data = $this->sentRequest($path, $fields);
a91317a6
S
60 $data = json_decode($data['body'], true);
61 if ($data[1]['varid'] == 'challengev') {
62 $this->challenge = $data[1]['varvalue'];
63 }
64 }
65
66 /**
67 * login into the router with the given password
5e44cffa 68 *
a91317a6
S
69 * @param string $password
70 * @return boolean
71 */
72 public function login ($password) {
b5a532a8 73 $path = 'data/Login.json';
a91317a6
S
74 $this->hash = hash('sha256', $this->challenge.':'.$password);
75 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash);
b5a532a8 76 $data = $this->sentRequest($path, $fields);
a91317a6
S
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];
83 }
84 else {
85 throw new Exception('unable to get the session cookie from the router');
86 }
87
c9e082da
S
88 // calculate derivedk
89 $this->derivedk = hash_pbkdf2('sha1', hash('sha256', $password), substr($this->challenge, 0, 16), 1000, 32);
90
a91317a6
S
91 return true;
92 }
93 }
94
95 return false;
96 }
97
98 /**
99 * logout
5e44cffa 100 *
a91317a6
S
101 * @return array
102 */
103 public function logout () {
b5a532a8 104 $path = 'data/Login.json';
a91317a6 105 $fields = array('logout' => 'byby');
b5a532a8 106 $data = $this->sentRequest($path, $fields);
a91317a6
S
107 // reset challenge and session
108 $this->challenge = '';
109 $this->session = '';
110
111 $json = json_decode($data['body'], true);
112
113 return $json;
114 }
115
116 /**
117 * reboot the router
5e44cffa 118 *
a91317a6
S
119 * @return array
120 */
121 public function reboot () {
b5a532a8 122 $path = 'data/Reboot.json';
a91317a6
S
123 $fields = array('csrf_token' => 'nulltoken', 'showpw' => 0, 'password' => $this->hash, 'reboot_device' => 'true');
124 $cookie = 'challengev='.$this->challenge.'; '.$this->session;
b5a532a8 125 $data = $this->sentRequest($path, $fields, $cookie);
a91317a6
S
126 $json = json_decode($data['body'], true);
127
128 return $json;
129 }
130
87026df3
S
131 /**
132 * change dsl connection status
5e44cffa 133 *
87026df3
S
134 * @param string $status
135 */
136 public function changeConnectionStatus ($status) {
b5a532a8 137 $path = 'data/Connect.json';
87026df3
S
138
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;
b5a532a8 142 $this->sentRequest($path, $fields, $cookie);
87026df3
S
143 }
144 else {
145 throw new Exception();
146 }
147 }
148
a91317a6
S
149 /**
150 * return the given json as array
5e44cffa 151 *
a91317a6
S
152 * the following paths are known to be valid:
153 * /data/dsl.json
154 * /data/interfaces.json
155 * /data/arp.json
156 * /data/session.json
157 * /data/dhcp_client.json
158 * /data/dhcp_server.json
159 * /data/ipv6.json
160 * /data/dns.json
161 * /data/routing.json
162 * /data/igmp_proxy.json
163 * /data/igmp_snooping.json
164 * /data/wlan.json
165 * /data/module.json
166 * /data/memory.json
167 * /data/speed.json
168 * /data/webdav.json
169 * /data/bonding_client.json
170 * /data/bonding_tunnel.json
171 * /data/filterlist.json
172 * /data/bonding_tr181.json
173 * /data/letinfo.json
5e44cffa 174 *
a91317a6 175 * /data/Status.json (No login needed)
5e44cffa 176 *
a91317a6
S
177 * @param string $file
178 * @return array
179 */
180 public function getData ($file) {
b5a532a8 181 $path = 'data/'.$file.'.json';
a91317a6
S
182 $fields = array();
183 $cookie = 'challengev='.$this->challenge.'; '.$this->session;
b5a532a8 184 $data = $this->sentRequest($path, $fields, $cookie);
a91317a6
S
185
186 if (empty($data['body'])) {
187 throw new Exception('unable to get '.$file.' data');
188 }
189
190 $json = json_decode($data['body'], true);
191
192 return $json;
193 }
194
5e44cffa
S
195 /**
196 * get the router syslog
197 *
198 * @return array
199 */
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);
205
206 if (empty($data['body'])) {
207 throw new Exception('unable to get syslog data');
208 }
209
210 return explode("\n", $data['body']);
211 }
212
a35e3e67
S
213 /**
214 * get the Missed Calls from router
215 *
216 * @return array
217 */
218 public function getMissedCalls() {
abf641bb 219 $path = 'data/ExportMissedCalls.json';
a35e3e67
S
220 $fields = array('exporttype' => '1');
221 $cookie = 'challengev='.$this->challenge.'; '.$this->session;
222 $data = $this->sentRequest($path, $fields, $cookie);
223
224 if (empty($data['body'])) {
225 throw new Exception('unable to get syslog data');
226 }
227
228 return explode("\n", $data['body']);
229 }
230
15260eeb
S
231 /**
232 * get the Taken Calls from router
233 *
234 * @return array
235 */
236 public function getTakenCalls() {
abf641bb 237 $path = 'data/ExportTakenCalls.json';
15260eeb
S
238 $fields = array('exporttype' => '2');
239 $cookie = 'challengev='.$this->challenge.'; '.$this->session;
240 $data = $this->sentRequest($path, $fields, $cookie);
241
242 if (empty($data['body'])) {
243 throw new Exception('unable to get syslog data');
244 }
245
246 return explode("\n", $data['body']);
247 }
248
abf641bb
S
249 /**
250 * get the Dialed Calls from router
251 *
252 * @return array
253 */
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);
9bd25bb4 259
abf641bb
S
260 if (empty($data['body'])) {
261 throw new Exception('unable to get syslog data');
262 }
263
264 return explode("\n", $data['body']);
265 }
266
c9e082da
S
267 /*
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);
276
277 return $json;
278 }
279 */
9bd25bb4
S
280 /*
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);
287
288 return $json;
289 }
290 */
291
4ae464d1
S
292 /**
293 * check if firmware is actual
294 *
295 * @return array
296 */
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);
302
303 if (empty($data['body'])) {
304 throw new Exception('unable to get checkfirmware data');
305 }
306
307 $json = json_decode($data['body'], true);
308
309 return $json;
310 }
311
a91317a6
S
312 /**
313 * sends the request to router
5e44cffa 314 *
b5a532a8 315 * @param string $path
a91317a6
S
316 * @param array $fields
317 * @param string $cookie
318 * @return array
319 */
b5a532a8
S
320 private function sentRequest ($path, $fields = array(), $cookie = '') {
321 $url = $this->url.$path;
a91317a6
S
322 $ch = curl_init();
323 curl_setopt($ch, CURLOPT_URL, $url);
324
325 if (!empty($fields)) {
326 curl_setopt($ch, CURLOPT_POST, count($fields));
327 curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields));
328 }
329
330 if (!empty($cookie)) {
331 curl_setopt($ch, CURLOPT_COOKIE, $cookie);
332 }
333
334 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
335 curl_setopt($ch, CURLOPT_HEADER, true);
336
337
338 if ($cookie) {
339
340 }
341
342 $result = curl_exec($ch);
343
344 $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
345 $header = substr($result, 0, $header_size);
346 $body = substr($result, $header_size);
347 curl_close($ch);
348
349 // fix invalid json
350 $body = preg_replace("/(\r\n)|(\r)/", "\n", $body);
351 $body = preg_replace('/\'/i', '"', $body);
5e44cffa 352 $body = preg_replace("/\[\s+\]/i", '[ {} ]', $body);
dae16c50 353 $body = preg_replace("/},\s+]/", "}\n]", $body);
a91317a6
S
354
355 return array('header' => $this->parse_headers($header), 'body' => $body);
356 }
357
358 /**
359 * parse the curl return header into an array
5e44cffa 360 *
a91317a6
S
361 * @param string $response
362 * @return array
363 */
364 private function parse_headers($response) {
365 $headers = array();
366 $header_text = substr($response, 0, strpos($response, "\r\n\r\n"));
367
368 foreach (explode("\r\n", $header_text) as $i => $line) {
369 if ($i === 0) {
370 $headers['http_code'] = $line;
371 }
372 else {
373 list ($key, $value) = explode(': ', $line);
374 $headers[$key] = $value;
375 }
376 }
377
378 return $headers;
379 }
380}