source: trunk/Modules/NetworkConfigRouterOS/RouterboardAPI.php

Last change on this file was 942, checked in by chronos, 2 years ago
  • Fixed: Do not terminate script with error if fsockopen fails.
File size: 7.8 KB
Line 
1<?php
2
3class RouterosAPI
4{
5 public int $ErrorNo; // Variable for storing connection error number, if any
6 public string $ErrorStr; // Variable for storing connection error text, if any
7 public int $Attempts; // Connection attempt count
8 public bool $Connected; // Connection state
9 public int $Delay; // Delay between connection attempts in seconds
10 public int $Port; // Port to connect to
11 public int $Timeout; // Connection attempt timeout and data read timeout
12 public $Socket; // Variable for storing socket resource
13 public bool $Debug;
14 public bool $SSL; // If SSL API connection is used. You need also change port to 8729
15
16 function __construct()
17 {
18 $this->Attempts = 5;
19 $this->Connected = false;
20 $this->Delay = 3;
21 $this->Port = 8728;
22 $this->Timeout = 3;
23 $this->Debug = false;
24 $this->SSL = false;
25 $this->ErrorNo = 0;
26 $this->ErrorStr = "";
27 }
28
29 function EncodeLength(int $Length): string
30 {
31 if ($Length < 0x80)
32 {
33 $Length = chr($Length);
34 } else if ($Length < 0x4000)
35 {
36 $Length |= 0x8000;
37 $Length = chr(($Length >> 8) & 0xFF).chr($Length & 0xFF);
38 } else if ($Length < 0x200000)
39 {
40 $Length |= 0xC00000;
41 $Length = chr(($Length >> 16) & 0xFF).chr(($Length >> 8) & 0xFF).chr($Length & 0xFF);
42 } else if ($Length < 0x10000000)
43 {
44 $Length |= 0xE0000000;
45 $Length = chr(($Length >> 24) & 0xFF).chr(($Length >> 16) & 0xFF).chr(($Length >> 8) & 0xFF).chr($Length & 0xFF);
46 } else if ($Length >= 0x10000000)
47 $Length = chr(0xF0).chr(($Length >> 24) & 0xFF).chr(($Length >> 16) & 0xFF).chr(($Length >> 8) & 0xFF).chr($Length & 0xFF);
48 return $Length;
49 }
50
51 function ConnectOnce(string $IP, string $Login, string $Password): void
52 {
53 if ($this->Connected) $this->Disconnect();
54 if ($this->SSL)
55 {
56 $IP = 'ssl://'.$IP;
57 }
58 try
59 {
60 $LastErrorReporting = error_reporting();
61 error_reporting(0);
62
63 $this->Socket = @fsockopen($IP, $this->Port, $this->ErrorNo, $this->ErrorStr, $this->Timeout);
64 if ($this->Socket)
65 {
66 socket_set_timeout($this->Socket, $this->Timeout);
67 $this->Write('/login', false);
68 $this->Write('=name=' . $Login, false);
69 $this->Write('=password='.$Password);
70 $Response = $this->Read(false);
71 if ((count($Response) > 0) and ($Response[0] == '!done')) $this->Connected = true;
72 if (!$this->Connected) fclose($this->Socket);
73 }
74 } finally
75 {
76 error_reporting($LastErrorReporting);
77 }
78 }
79
80 function Connect(string $IP, string $Login, string $Password): bool
81 {
82 for ($Attempt = 1; $Attempt <= $this->Attempts; $Attempt++)
83 {
84 $this->ConnectOnce($IP, $Login, $Password);
85 if ($this->Connected) break;
86 sleep($this->Delay);
87 }
88 return $this->Connected;
89 }
90
91 function Disconnect(): void
92 {
93 if ($this->Connected)
94 {
95 fclose($this->Socket);
96 $this->Connected = false;
97 }
98 }
99
100 function ParseResponse(array $Response): array
101 {
102 if (is_array($Response))
103 {
104 $Parsed = array();
105 $Current = null;
106 $SingleValue = null;
107 $count = 0;
108 foreach ($Response as $x)
109 {
110 if (in_array($x, array(
111 '!fatal',
112 '!re',
113 '!trap'
114 )))
115 {
116 if ($x == '!re')
117 {
118 $Current =& $Parsed[];
119 } else
120 $Current =& $Parsed[$x][];
121 } else if ($x != '!done')
122 {
123 if (preg_match_all('/[^=]+/i', $x, $Matches))
124 {
125 if ($Matches[0][0] == 'ret') {
126 $SingleValue = $Matches[0][1];
127 }
128 $Current[$Matches[0][0]] = (isset($Matches[0][1]) ? $Matches[0][1] : '');
129 }
130 }
131 }
132 if (empty($Parsed) && !is_null($SingleValue))
133 {
134 $Parsed = $SingleValue;
135 }
136 return $Parsed;
137 } else
138 return array();
139 }
140
141 function ArrayChangeKeyName(array &$array): array
142 {
143 if (is_array($array))
144 {
145 foreach ($array as $k => $v)
146 {
147 $tmp = str_replace("-", "_", $k);
148 $tmp = str_replace("/", "_", $tmp);
149 if ($tmp)
150 {
151 $array_new[$tmp] = $v;
152 } else
153 {
154 $array_new[$k] = $v;
155 }
156 }
157 return $array_new;
158 } else
159 {
160 return $array;
161 }
162 }
163
164 function Read(bool $Parse = true): array
165 {
166 $Line = '';
167 $Response = array();
168 while (true)
169 {
170 // Read the first byte of input which gives us some or all of the length
171 // of the remaining reply.
172 $Byte = ord(fread($this->Socket, 1));
173 $Length = 0;
174 // If the first bit is set then we need to remove the first four bits, shift left 8
175 // and then read another byte in.
176 // We repeat this for the second and third bits.
177 // If the fourth bit is set, we need to remove anything left in the first byte
178 // and then read in yet another byte.
179 if ($Byte & 0x80)
180 {
181 if (($Byte & 0xc0) == 0x80)
182 {
183 $Length = (($Byte & 63) << 8) + ord(fread($this->Socket, 1));
184 } else
185 {
186 if (($Byte & 0xe0) == 0xc0)
187 {
188 $Length = (($Byte & 31) << 8) + ord(fread($this->Socket, 1));
189 $Length = ($Length << 8) + ord(fread($this->Socket, 1));
190 } else
191 {
192 if (($Byte & 0xf0) == 0xe0)
193 {
194 $Length = (($Byte & 15) << 8) + ord(fread($this->Socket, 1));
195 $Length = ($Length << 8) + ord(fread($this->Socket, 1));
196 $Length = ($Length << 8) + ord(fread($this->Socket, 1));
197 } else
198 {
199 $Length = ord(fread($this->Socket, 1));
200 $Length = ($Length << 8) + ord(fread($this->Socket, 1));
201 $Length = ($Length << 8) + ord(fread($this->Socket, 1));
202 $Length = ($Length << 8) + ord(fread($this->Socket, 1));
203 }
204 }
205 }
206 } else
207 {
208 $Length = $Byte;
209 }
210 // If we have got more characters to read, read them in.
211 if ($Length > 0)
212 {
213 $Line = '';
214 $RetLen = 0;
215 while ($RetLen < $Length)
216 {
217 $ToRead = $Length - $RetLen;
218 $Line .= fread($this->Socket, $ToRead);
219 $RetLen = strlen($Line);
220 }
221 $Response[] = $Line;
222 }
223 if ($this->Debug) echo($Line);
224 // If we get a !done, make a note of it.
225 if ($Line == "!done") $ReceivedDone = true;
226 else $ReceivedDone = false;
227 $Status = socket_get_status($this->Socket);
228 if ((!$this->Connected && !$Status['unread_bytes']) ||
229 ($this->Connected && !$Status['unread_bytes'] && $ReceivedDone))
230 break;
231 }
232 if ($Parse) $Response = $this->ParseResponse($Response);
233 return $Response;
234 }
235
236 function Write(string $Command, bool $Param2 = true): bool
237 {
238 if ($Command)
239 {
240 $Data = explode("\n", $Command);
241 foreach ($Data as $Com)
242 {
243 $Com = trim($Com);
244 fwrite($this->Socket, $this->EncodeLength(strlen($Com)).$Com);
245 }
246 if (gettype($Param2) == 'integer')
247 {
248 fwrite($this->Socket, $this->EncodeLength(strlen('.tag='.$Param2)).'.tag='.$Param2.chr(0));
249 } else if (gettype($Param2) == 'boolean')
250 fwrite($this->Socket, ($Param2 ? chr(0) : ''));
251 return true;
252 } else
253 return false;
254 }
255
256 function Comm(string $Com, array $Arr = array()): array
257 {
258 $Count = count($Arr);
259 $this->write($Com, !$Arr);
260 $i = 0;
261 foreach ($Arr as $k => $v)
262 {
263 switch ($k[0])
264 {
265 case "?":
266 $el = "$k=$v";
267 break;
268 case "~":
269 $el = "$k~$v";
270 break;
271 default:
272 $el = "=$k=$v";
273 break;
274 }
275 $Last = ($i++ == $Count - 1);
276 $this->Write($el, $Last);
277 }
278 return $this->Read();
279 }
280}
Note: See TracBrowser for help on using the repository browser.