pear2 /
Net_RouterOS
| 1 | <?php |
||||
| 2 | |||||
| 3 | /** |
||||
| 4 | * ~~summary~~ |
||||
| 5 | * |
||||
| 6 | * ~~description~~ |
||||
| 7 | * |
||||
| 8 | * PHP version 5.3 |
||||
| 9 | * |
||||
| 10 | * @category Net |
||||
| 11 | * @package PEAR2_Net_RouterOS |
||||
| 12 | * @author Vasil Rangelov <[email protected]> |
||||
| 13 | * @copyright 2011 Vasil Rangelov |
||||
| 14 | * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 |
||||
| 15 | * @version GIT: $Format:%x24Commit:%H%x24$ |
||||
| 16 | * @link http://pear2.php.net/PEAR2_Net_RouterOS |
||||
| 17 | */ |
||||
| 18 | |||||
| 19 | /** |
||||
| 20 | * Used as a "catch all" for errors when connecting. |
||||
| 21 | */ |
||||
| 22 | use Exception as E; |
||||
| 23 | |||||
| 24 | /** |
||||
| 25 | * Used to register dependency paths, if needed. |
||||
| 26 | */ |
||||
| 27 | use PEAR2\Autoload; |
||||
| 28 | |||||
| 29 | /** |
||||
| 30 | * Used for coloring the output, if the "--colors" argument is specified. |
||||
| 31 | */ |
||||
| 32 | use PEAR2\Console\Color; |
||||
| 33 | |||||
| 34 | /** |
||||
| 35 | * Used for parsing the command line arguments. |
||||
| 36 | */ |
||||
| 37 | use PEAR2\Console\CommandLine; |
||||
| 38 | |||||
| 39 | /** |
||||
| 40 | * The whole application is around that. |
||||
| 41 | */ |
||||
| 42 | use PEAR2\Net\RouterOS; |
||||
| 43 | |||||
| 44 | /** |
||||
| 45 | * Used for error handling when connecting or receiving. |
||||
| 46 | */ |
||||
| 47 | use PEAR2\Net\Transmitter\SocketException as SE; |
||||
| 48 | |||||
| 49 | //Detect disallowed direct runs of either this file or "roscon". |
||||
| 50 | if (PHP_SAPI !== 'cli') { |
||||
| 51 | $includedFiles = get_included_files(); |
||||
| 52 | $rosconPos = array_search( |
||||
| 53 | dirname(__FILE__) . DIRECTORY_SEPARATOR . 'roscon', |
||||
| 54 | $includedFiles, |
||||
| 55 | true |
||||
| 56 | ); |
||||
| 57 | if (false !== $rosconPos) { |
||||
| 58 | unset($includedFiles[$rosconPos]); |
||||
| 59 | } |
||||
| 60 | |||||
| 61 | if (count($includedFiles) === 1) { |
||||
| 62 | header('Content-Type: text/plain;charset=UTF-8'); |
||||
| 63 | echo <<<HEREDOC |
||||
| 64 | For security reasons, this file can not be ran DIRECTLY, except from the |
||||
| 65 | command line. It can be included however, even when not using the command line. |
||||
| 66 | HEREDOC; |
||||
| 67 | return; |
||||
| 68 | } |
||||
| 69 | } |
||||
| 70 | |||||
| 71 | //If there's no appropriate autoloader, add one |
||||
| 72 | if (!class_exists('PEAR2\Net\RouterOS\Communicator', true)) { |
||||
| 73 | $cwd = getcwd(); |
||||
| 74 | chdir(__DIR__); |
||||
| 75 | |||||
| 76 | $composerAutoloaderPaths = array(); |
||||
| 77 | $vendorDir = getenv('COMPOSER_VENDOR_DIR'); |
||||
| 78 | if (false !== $vendorDir) { |
||||
| 79 | $composerAutoloaderPaths[] = $vendorDir . '/autoload.php'; |
||||
| 80 | unset($vendorDir); |
||||
| 81 | } |
||||
| 82 | $composerAutoloaderPaths[] = '../vendor/autoload.php'; |
||||
| 83 | $composerAutoloaderPaths[] = '../../../autoload.php'; |
||||
| 84 | foreach ($composerAutoloaderPaths as $autoloaderPath) { |
||||
| 85 | $autoloader = stream_resolve_include_path($autoloaderPath); |
||||
| 86 | if (false !== $autoloader) { |
||||
| 87 | include_once $autoloader; |
||||
| 88 | if (class_exists('PEAR2\Net\RouterOS\Communicator', true)) { |
||||
| 89 | break; |
||||
| 90 | } |
||||
| 91 | $autoloader = false; |
||||
| 92 | } |
||||
| 93 | } |
||||
| 94 | unset($autoloaderPath, $composerAutoloaderPaths); |
||||
| 95 | if (false === $autoloader) { |
||||
| 96 | //PEAR2_Autoload, most probably installed globally. |
||||
| 97 | $autoloader = stream_resolve_include_path('PEAR2/Autoload.php'); |
||||
| 98 | if (false !== $autoloader) { |
||||
| 99 | include_once $autoloader; |
||||
| 100 | Autoload::initialize( |
||||
| 101 | realpath('../src') |
||||
| 102 | ); |
||||
| 103 | Autoload::initialize( |
||||
| 104 | realpath('../../Net_Transmitter.git/src') |
||||
| 105 | ); |
||||
| 106 | Autoload::initialize( |
||||
| 107 | realpath('../../Console_Color.git/src') |
||||
| 108 | ); |
||||
| 109 | Autoload::initialize( |
||||
| 110 | realpath('../../Console_CommandLine.git/src') |
||||
| 111 | ); |
||||
| 112 | } else { |
||||
| 113 | $phpIniLocation = php_ini_loaded_file(); |
||||
| 114 | $phpIncludePath = get_include_path(); |
||||
| 115 | $defaultDirPyrus = __DIR__ . DIRECTORY_SEPARATOR . 'php'; |
||||
| 116 | $defaultDirPear = __DIR__ . DIRECTORY_SEPARATOR . 'pear'; |
||||
| 117 | fwrite( |
||||
| 118 | STDERR, |
||||
| 119 | <<<HEREDOC |
||||
| 120 | No recognized autoloader is available. |
||||
| 121 | Please install this package with Pyrus, PEAR or Composer. |
||||
| 122 | |||||
| 123 | If using PEAR or Pyrus, also install PEAR2_Autoload if it's not installed. |
||||
| 124 | |||||
| 125 | If it's already installed and yet this message appears, |
||||
| 126 | make sure the folder with PHP files is in the list of paths in php.ini's |
||||
| 127 | include_path directive. |
||||
| 128 | |||||
| 129 | The loaded php.ini is at |
||||
| 130 | ``` |
||||
| 131 | {$phpIniLocation} |
||||
| 132 | ``` |
||||
| 133 | |||||
| 134 | and the computed include_path value is |
||||
| 135 | ``` |
||||
| 136 | {$phpIncludePath} |
||||
| 137 | ``` |
||||
| 138 | |||||
| 139 | If you use the default settings for Pyrus, the folder to add is probably |
||||
| 140 | ``` |
||||
| 141 | {$defaultDirPyrus} |
||||
| 142 | ``` |
||||
| 143 | |||||
| 144 | and for PEAR's default settings, the folder to add is probably |
||||
| 145 | ``` |
||||
| 146 | {$defaultDirPear} |
||||
| 147 | ``` |
||||
| 148 | |||||
| 149 | HEREDOC |
||||
| 150 | ); |
||||
| 151 | chdir($cwd); |
||||
| 152 | exit(10); |
||||
| 153 | } |
||||
| 154 | } |
||||
| 155 | |||||
| 156 | chdir($cwd); |
||||
| 157 | unset($autoloader, $cwd); |
||||
| 158 | } |
||||
| 159 | |||||
| 160 | //PEAR2_Console_CommandLine is bundled in the archive, but may not be |
||||
| 161 | //available if this package was installed with a package manager. |
||||
| 162 | if (!class_exists('PEAR2\Console\CommandLine', true)) { |
||||
| 163 | fwrite( |
||||
| 164 | STDERR, |
||||
| 165 | <<<HEREDOC |
||||
| 166 | PEAR2_Console_CommandLine was not found. |
||||
| 167 | Please install it with the package manager used to install PEAR2_Net_RouterOS. |
||||
| 168 | (i.e. Pyrus, PEAR or Composer) |
||||
| 169 | |||||
| 170 | HEREDOC |
||||
| 171 | ); |
||||
| 172 | exit(11); |
||||
| 173 | } |
||||
| 174 | |||||
| 175 | // Locate the data dir, in preference as: |
||||
| 176 | // 1. If outside of PHAR file |
||||
| 177 | // 1.1. The data folder filled at install time by Pyrus. |
||||
| 178 | // 1.2. The PHP_PEAR_DATA_DIR environment variable, if available. |
||||
| 179 | // 1.3. The data folder filled at install time by PEAR. |
||||
| 180 | // 2. The source layout's data folder, inside a channel/package subfolder |
||||
| 181 | // (in case this package itself is bundled by another Pyrus package) |
||||
| 182 | // 3. The source layout's data folder, inside a package subfolder |
||||
| 183 | // (in case this package itself is bundled by another PEAR package) |
||||
| 184 | // 4. The source layout's data folder (used with Composer or from Git). |
||||
| 185 | $dataDirPaths = array(); |
||||
| 186 | if (!class_exists('Phar', false) || !Phar::running()) { |
||||
| 187 | $dataDirPaths[] = '@PEAR2_DATA_DIR@/@PACKAGE_CHANNEL@/@PACKAGE_NAME@'; |
||||
| 188 | if (($pearDataDir = getenv('PHP_PEAR_DATA_DIR'))) { |
||||
| 189 | $dataDirPaths[] = $pearDataDir . '/@PACKAGE_NAME@'; |
||||
| 190 | } |
||||
| 191 | $dataDirPaths[] = '@PEAR2_DATA_DIR@/@PACKAGE_NAME@'; |
||||
| 192 | } |
||||
| 193 | $dataDirPaths[] = __DIR__ . '/../data/@PACKAGE_CHANNEL@/@PACKAGE_NAME@'; |
||||
| 194 | $dataDirPaths[] = __DIR__ . '/../data/@PACKAGE_NAME@'; |
||||
| 195 | $dataDirPaths[] = __DIR__ . '/../data'; |
||||
| 196 | $dataDir = false; |
||||
| 197 | foreach ($dataDirPaths as $dataDirPath) { |
||||
| 198 | if (is_dir($dataDirPath)) { |
||||
| 199 | $dataDir = $dataDirPath; |
||||
| 200 | break; |
||||
| 201 | } |
||||
| 202 | } |
||||
| 203 | unset($dataDirPaths); |
||||
| 204 | |||||
| 205 | if (false === $dataDir) { |
||||
| 206 | fwrite( |
||||
| 207 | STDERR, |
||||
| 208 | <<<HEREDOC |
||||
| 209 | Unable to find data dir. |
||||
| 210 | |||||
| 211 | HEREDOC |
||||
| 212 | ); |
||||
| 213 | exit(11); |
||||
| 214 | } |
||||
| 215 | $consoleDefFile = is_file($dataDir . '/roscon.xml') |
||||
| 216 | ? $dataDir . '/roscon.xml' |
||||
| 217 | : false; |
||||
| 218 | if (false === $consoleDefFile) { |
||||
| 219 | fwrite( |
||||
| 220 | STDERR, |
||||
| 221 | <<<HEREDOC |
||||
| 222 | The console definition file (roscon.xml) was not found at the data dir, which |
||||
| 223 | was found to be at |
||||
| 224 | {$dataDir} |
||||
| 225 | |||||
| 226 | HEREDOC |
||||
| 227 | ); |
||||
| 228 | exit(12); |
||||
| 229 | } |
||||
| 230 | |||||
| 231 | $cmdParser = CommandLine::fromXmlFile($consoleDefFile); |
||||
| 232 | try { |
||||
| 233 | $cmd = $cmdParser->parse(); |
||||
| 234 | } catch (CommandLine\Exception $e) { |
||||
| 235 | fwrite( |
||||
| 236 | STDERR, |
||||
| 237 | "Error when parsing command line: {$e->getMessage()}\n" |
||||
| 238 | ); |
||||
| 239 | $cmdParser->displayUsage(13); |
||||
| 240 | } |
||||
| 241 | |||||
| 242 | $comTimeout = null === $cmd->options['conTime'] |
||||
| 243 | ? (null === $cmd->options['time'] |
||||
| 244 | ? (int)ini_get('default_socket_timeout') |
||||
| 245 | : $cmd->options['time']) |
||||
| 246 | : $cmd->options['conTime']; |
||||
| 247 | $cmd->options['time'] = $cmd->options['time'] ?: 3; |
||||
| 248 | $comContext = null; |
||||
| 249 | if ($cmd->options['crypto']) { |
||||
| 250 | $comContextOpts = array( |
||||
| 251 | 'ssl' => array( |
||||
| 252 | 'capture_peer_cert' => !$cmd->options['fingerprint'] |
||||
| 253 | && function_exists('openssl_x509_fingerprint'), |
||||
| 254 | 'verify_peer' => isset($cmd->options['caPath']), |
||||
| 255 | 'verify_peer_name' => isset($cmd->options['caPath']), |
||||
| 256 | ) |
||||
| 257 | ); |
||||
| 258 | if ($cmd->options['caPath']) { |
||||
| 259 | $comContextOpts['ssl'][ |
||||
| 260 | is_file($cmd->options['caPath']) ? 'cafile' : 'capath' |
||||
| 261 | ] = $cmd->options['caPath']; |
||||
| 262 | } elseif (!$cmd->options['fingerprint']) { |
||||
| 263 | $comContextOpts['ssl']['ciphers'] = 'ADH'; |
||||
| 264 | } |
||||
| 265 | |||||
| 266 | if ($cmd->options['fingerprint']) { |
||||
| 267 | $comContextOpts['ssl']['peer_fingerprint'] = array( |
||||
| 268 | 'sha256' => $cmd->options['fingerprint'] |
||||
| 269 | ); |
||||
| 270 | } |
||||
| 271 | |||||
| 272 | if ($cmd->options['ciphers']) { |
||||
| 273 | $comContextOpts['ssl']['ciphers'] = $cmd->options['ciphers']; |
||||
| 274 | } |
||||
| 275 | |||||
| 276 | $comContext = stream_context_create($comContextOpts); |
||||
| 277 | } |
||||
| 278 | |||||
| 279 | $cColors = array( |
||||
| 280 | 'SEND' => '', |
||||
| 281 | 'SENT' => '', |
||||
| 282 | 'RECV' => '', |
||||
| 283 | 'ERR' => '', |
||||
| 284 | 'NOTE' => '', |
||||
| 285 | '' => '' |
||||
| 286 | ); |
||||
| 287 | if ('auto' === $cmd->options['isColored']) { |
||||
| 288 | $cmd->options['isColored'] = ((strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' |
||||
| 289 | || getenv('ANSICON_VER') != false) |
||||
|
0 ignored issues
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||||
| 290 | && class_exists('PEAR2\Console\Color', true)) ? 'yes' : 'no'; |
||||
| 291 | } |
||||
| 292 | if ('yes' === $cmd->options['isColored']) { |
||||
| 293 | if (class_exists('PEAR2\Console\Color', true)) { |
||||
| 294 | $cColors['SEND'] = new Color( |
||||
| 295 | Color\Fonts::PURPLE |
||||
| 296 | ); |
||||
| 297 | $cColors['SENT'] = clone $cColors['SEND']; |
||||
| 298 | $cColors['SENT']->setStyles(Color\Styles::UNDERLINE, true); |
||||
| 299 | $cColors['RECV'] = new Color( |
||||
| 300 | Color\Fonts::GREEN |
||||
| 301 | ); |
||||
| 302 | $cColors['ERR'] = new Color( |
||||
| 303 | Color\Fonts::WHITE, |
||||
| 304 | Color\Backgrounds::RED |
||||
| 305 | ); |
||||
| 306 | $cColors['NOTE'] = new Color( |
||||
| 307 | Color\Fonts::BLUE, |
||||
| 308 | Color\Backgrounds::YELLOW |
||||
| 309 | ); |
||||
| 310 | $cColors[''] = new Color(); |
||||
| 311 | |||||
| 312 | foreach ($cColors as $mode => $color) { |
||||
| 313 | $cColors[$mode] = ((string)$color) . "\033[K"; |
||||
| 314 | } |
||||
| 315 | } else { |
||||
| 316 | fwrite( |
||||
| 317 | STDERR, |
||||
| 318 | <<<HEREDOC |
||||
| 319 | Warning: Color was forced, but PEAR2_Console_Color is not available. |
||||
| 320 | Resuming with colors disabled. |
||||
| 321 | |||||
| 322 | HEREDOC |
||||
| 323 | ); |
||||
| 324 | } |
||||
| 325 | } |
||||
| 326 | |||||
| 327 | try { |
||||
| 328 | $com = new RouterOS\Communicator( |
||||
| 329 | $cmd->args['hostname'], |
||||
| 330 | $cmd->options['portNum'], |
||||
| 331 | false, |
||||
| 332 | $comTimeout, |
||||
| 333 | '', |
||||
| 334 | (string)$cmd->options['crypto'], |
||||
| 335 | $comContext |
||||
| 336 | ); |
||||
| 337 | } catch (E $e) { |
||||
| 338 | fwrite(STDERR, "Error upon connecting: {$e->getMessage()}\n"); |
||||
| 339 | $previous = $e->getPrevious(); |
||||
| 340 | if ($previous instanceof SE) { |
||||
| 341 | fwrite( |
||||
| 342 | STDERR, |
||||
| 343 | "Details: ({$previous->getSocketErrorNumber()}) " |
||||
| 344 | . $previous->getSocketErrorMessage() . "\n\n" |
||||
| 345 | ); |
||||
| 346 | } |
||||
| 347 | if ($e instanceof RouterOS\SocketException |
||||
| 348 | && $e->getCode() === RouterOS\SocketException::CODE_CONNECTION_FAIL |
||||
| 349 | ) { |
||||
| 350 | $phpBin = defined('PHP_BINARY') |
||||
| 351 | ? PHP_BINARY |
||||
| 352 | : (PHP_BINDIR . DIRECTORY_SEPARATOR . |
||||
| 353 | (PHP_SAPI === 'cli' ? 'php' : 'php-cgi') . |
||||
| 354 | (stristr(PHP_OS, 'win') === false ? '' : '.exe')); |
||||
| 355 | fwrite( |
||||
| 356 | STDERR, |
||||
| 357 | <<<HEREDOC |
||||
| 358 | Possible reasons: |
||||
| 359 | |||||
| 360 | 1. You haven't enabled the API service at RouterOS or you've enabled it on a |
||||
| 361 | different TCP port. |
||||
| 362 | Make sure the "api" service at the "/ip service" menu is enabled, |
||||
| 363 | and with that same TCP port (8728 by default or 8729 for "api-ssl"). |
||||
| 364 | |||||
| 365 | 2. You've mistyped the IP and/or port. |
||||
| 366 | Check the IP and port you've specified are the ones you intended. |
||||
| 367 | |||||
| 368 | 3. Your web server's IP is not in the list of subnets allowed to use the API. |
||||
| 369 | Check the "address" property at the "/ip service" menu. |
||||
| 370 | If it's empty, that's not the problem for sure. If it's non-empty however, |
||||
| 371 | make sure your IP is in that list, or is at least matched as part of an |
||||
| 372 | otherwise larger subnet. |
||||
| 373 | |||||
| 374 | 4. The router is not reachable from your web server for some reason. |
||||
| 375 | Try to reach the router (!!!)from the web server(!!!) by other means |
||||
| 376 | (e.g. Winbox, ping) using the same IP, and if you're unable to reach it, |
||||
| 377 | check the network settings on your server, router and any intermediate nodes |
||||
| 378 | under your control that may affect the connection. |
||||
| 379 | |||||
| 380 | 5. Your web server is configured to forbid that outgoing connection. |
||||
| 381 | If you're the web server administrator, check your web server's firewall |
||||
| 382 | settings. The binary you need to whitelist is probably |
||||
| 383 | ``` |
||||
| 384 | {$phpBin} |
||||
| 385 | ``` |
||||
| 386 | |||||
| 387 | If you're on a hosting plan... Typically, shared hosts block all |
||||
| 388 | outgoing connections, but it's also possible that only connections to that |
||||
| 389 | port are blocked. Try to connect to a host on a popular port (21, 80, 443, |
||||
| 390 | etc.), and if successful, change the API service port to that port. If the |
||||
| 391 | connection fails even then, ask your host to configure their firewall so as |
||||
| 392 | to allow you to make outgoing connections to the ip:port you've set the API |
||||
| 393 | service on. |
||||
| 394 | |||||
| 395 | Note that using the library may require a different binary to be |
||||
| 396 | whitelisted, depending on how PHP is running as. |
||||
| 397 | |||||
| 398 | 6. The router has a firewall filter/mangle/nat rule that overrides the settings |
||||
| 399 | at the "/ip service" menu. |
||||
| 400 | Usually, those are rules in the "input" chain. |
||||
| 401 | Theoretically (rarely in practice), rules in the "prerouting", "dstnat", |
||||
| 402 | "output" and/or "postrouting" chains can also cause such an effect. |
||||
| 403 | By default, many RouterBOARD devices have a filter rule in the "input" chain |
||||
| 404 | that drops any incoming connections to the router from its WAN interface, |
||||
| 405 | so if your web server is not in the LAN, the connection may be dropped |
||||
| 406 | because of that. |
||||
| 407 | If that's the case, either disable that rule (not recommended), or |
||||
| 408 | explicitly whitelist the API port. You can whitelist the API port on all |
||||
| 409 | interfaces by issuing the following command from a terminal: |
||||
| 410 | ``` |
||||
| 411 | /ip firewall filter add \ |
||||
| 412 | place-before=[:pick [find where chain="input"] 0] \ |
||||
| 413 | chain="input" action="accept" \ |
||||
| 414 | protocol="tcp" dst-port=[/ip service get "api" "port"] |
||||
| 415 | ``` |
||||
| 416 | |||||
| 417 | HEREDOC |
||||
| 418 | ); |
||||
| 419 | if ($cmd->options['crypto']) { |
||||
| 420 | fwrite( |
||||
| 421 | STDERR, |
||||
| 422 | <<<HEREDOC |
||||
| 423 | |||||
| 424 | 7. Encryption misconfiguration or impersonation attempt by an attacker. |
||||
| 425 | Check carefully the values and presense of encryption related options, i.e. |
||||
| 426 | the "--ca", "--ciphers" and "--fingerprint" options. |
||||
| 427 | If not using a certifcate, make sure to NOT specify any of those options, |
||||
| 428 | or explicitly include only ADH ciphers in the "--ciphers" option. |
||||
| 429 | If using a certificate, make sure to specify at least one of those options |
||||
| 430 | with a valid value. Also make sure the clock of this device and the router |
||||
| 431 | are accurate (check the date in particular), and that the certificate |
||||
| 432 | has not expired yet. |
||||
| 433 | If using a certificate and the "--ca" option, keep in mind the name is |
||||
| 434 | also checked (just like with certificates in a web browser). Make sure |
||||
| 435 | your certificate includes a subject alt name for the hostname you use to |
||||
| 436 | connect to the router. |
||||
| 437 | If using a certificate and the "--fingerprint" option, keep in mind that |
||||
| 438 | renewing a certificate changes the fingerprint. |
||||
| 439 | |||||
| 440 | You can check details for the current certificate by checking the output of |
||||
| 441 | the following command from a terminal: |
||||
| 442 | ``` |
||||
| 443 | /certificate print from=[/ip service get "api-ssl" "certificate"] detail |
||||
| 444 | ``` |
||||
| 445 | |||||
| 446 | If everything appears OK, there may be an attacker in your network trying |
||||
| 447 | to impersonate RouterOS. |
||||
| 448 | |||||
| 449 | HEREDOC |
||||
| 450 | ); |
||||
| 451 | } |
||||
| 452 | } |
||||
| 453 | return; |
||||
| 454 | } |
||||
| 455 | if (null !== $cmd->args['username']) { |
||||
| 456 | try { |
||||
| 457 | if (!RouterOS\Client::login( |
||||
| 458 | $com, |
||||
| 459 | $cmd->args['username'], |
||||
| 460 | (string)$cmd->args['password'], |
||||
| 461 | $comTimeout |
||||
| 462 | ) |
||||
| 463 | ) { |
||||
| 464 | fwrite( |
||||
| 465 | STDERR, |
||||
| 466 | <<<HEREDOC |
||||
| 467 | Login refused. Possible reasons: |
||||
| 468 | |||||
| 469 | 1. No such username. |
||||
| 470 | Make sure you have spelled it correctly. |
||||
| 471 | |||||
| 472 | 2. The user does not have the "api" privilege. |
||||
| 473 | Check the permissions of the user's group at "/user group". |
||||
| 474 | |||||
| 475 | 3. The user is not allowed to access the router from your web server's IP. |
||||
| 476 | Check the "address" property at the "/user" menu. |
||||
| 477 | If it's empty, that's not the problem for sure. If it's non-empty however, |
||||
| 478 | make sure your IP is in that list, or is at least matched as part of an |
||||
| 479 | otherwise larger subnet. |
||||
| 480 | |||||
| 481 | 4. Mistyped password. |
||||
| 482 | Make sure you have spelled it correctly. |
||||
| 483 | If it contains spaces, don't forget to quote the whole password. |
||||
| 484 | If it contains non-ASCII characters, be careful of your locale. |
||||
| 485 | It must match that of the terminal you set your password on, or you must |
||||
| 486 | type the equivalent code points in your current locale, which may display as |
||||
| 487 | different characters. |
||||
| 488 | |||||
| 489 | HEREDOC |
||||
| 490 | ); |
||||
| 491 | return; |
||||
| 492 | } |
||||
| 493 | } catch (RouterOS\SocketException $e) { |
||||
| 494 | fwrite(STDERR, "Error upon login: " . $e->getMessage()); |
||||
| 495 | return; |
||||
| 496 | } |
||||
| 497 | } |
||||
| 498 | |||||
| 499 | if ($cmd->options['verbose']) { |
||||
| 500 | $cSep = ' | '; |
||||
| 501 | $cColumns = array( |
||||
| 502 | 'mode' => 4, |
||||
| 503 | 'length' => 11, |
||||
| 504 | 'encodedLength' => 12 |
||||
| 505 | ); |
||||
| 506 | $cColumns['contents'] = $cmd->options['size'] - 1//row length |
||||
| 507 | - array_sum($cColumns) |
||||
| 508 | - (3/*strlen($c_sep)*/ * count($cColumns)); |
||||
| 509 | fwrite( |
||||
| 510 | STDOUT, |
||||
| 511 | implode( |
||||
| 512 | "\n", |
||||
| 513 | array( |
||||
| 514 | implode( |
||||
| 515 | $cSep, |
||||
| 516 | array( |
||||
| 517 | str_pad( |
||||
| 518 | 'MODE', |
||||
| 519 | $cColumns['mode'], |
||||
| 520 | ' ', |
||||
| 521 | STR_PAD_RIGHT |
||||
| 522 | ), |
||||
| 523 | str_pad( |
||||
| 524 | 'LENGTH', |
||||
| 525 | $cColumns['length'], |
||||
| 526 | ' ', |
||||
| 527 | STR_PAD_BOTH |
||||
| 528 | ), |
||||
| 529 | str_pad( |
||||
| 530 | 'LENGTH', |
||||
| 531 | $cColumns['encodedLength'], |
||||
| 532 | ' ', |
||||
| 533 | STR_PAD_BOTH |
||||
| 534 | ), |
||||
| 535 | ' CONTENTS' |
||||
| 536 | ) |
||||
| 537 | ), |
||||
| 538 | implode( |
||||
| 539 | $cSep, |
||||
| 540 | array( |
||||
| 541 | str_repeat(' ', $cColumns['mode']), |
||||
| 542 | str_pad( |
||||
| 543 | '(decoded)', |
||||
| 544 | $cColumns['length'], |
||||
| 545 | ' ', |
||||
| 546 | STR_PAD_BOTH |
||||
| 547 | ), |
||||
| 548 | str_pad( |
||||
| 549 | '(encoded)', |
||||
| 550 | $cColumns['encodedLength'], |
||||
| 551 | ' ', |
||||
| 552 | STR_PAD_BOTH |
||||
| 553 | ), |
||||
| 554 | '' |
||||
| 555 | ) |
||||
| 556 | ), |
||||
| 557 | implode( |
||||
| 558 | '-|-', |
||||
| 559 | array( |
||||
| 560 | str_repeat('-', $cColumns['mode']), |
||||
| 561 | str_repeat('-', $cColumns['length']), |
||||
| 562 | str_repeat('-', $cColumns['encodedLength']), |
||||
| 563 | str_repeat('-', $cColumns['contents']) |
||||
|
0 ignored issues
–
show
$cColumns['contents'] of type double is incompatible with the type integer expected by parameter $times of str_repeat().
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 564 | ) |
||||
| 565 | ) |
||||
| 566 | ) |
||||
| 567 | ) . "\n" |
||||
| 568 | ); |
||||
| 569 | |||||
| 570 | $cRegexWrap = '/([^\n]{1,' . ($cColumns['contents']) . '})/sS'; |
||||
| 571 | |||||
| 572 | $printWord = function ( |
||||
| 573 | $mode, |
||||
| 574 | $word, |
||||
| 575 | $msg = '' |
||||
| 576 | ) use ( |
||||
| 577 | $cSep, |
||||
| 578 | $cColumns, |
||||
| 579 | $cRegexWrap, |
||||
| 580 | $cColors |
||||
| 581 | ) { |
||||
| 582 | $wordFragments = preg_split( |
||||
| 583 | $cRegexWrap, |
||||
| 584 | $word, |
||||
| 585 | null, |
||||
| 586 | PREG_SPLIT_DELIM_CAPTURE |
||||
| 587 | ); |
||||
| 588 | for ($i = 0, $l = count($wordFragments); $i < $l; $i += 2) { |
||||
| 589 | unset($wordFragments[$i]); |
||||
| 590 | } |
||||
| 591 | if ('' !== $cColors['']) { |
||||
| 592 | $wordFragments = str_replace("\033", "\033[27@", $wordFragments); |
||||
| 593 | } |
||||
| 594 | |||||
| 595 | $isAbnormal = 'ERR' === $mode || 'NOTE' === $mode; |
||||
| 596 | if ($isAbnormal) { |
||||
| 597 | $details = str_pad( |
||||
| 598 | $msg, |
||||
| 599 | $cColumns['length'] + $cColumns['encodedLength'] + 3, |
||||
| 600 | ' ', |
||||
| 601 | STR_PAD_BOTH |
||||
| 602 | ); |
||||
| 603 | } else { |
||||
| 604 | $length = strlen($word); |
||||
| 605 | $lengthBytes = RouterOS\Communicator::encodeLength($length); |
||||
| 606 | $encodedLength = ''; |
||||
| 607 | for ($i = 0, $l = strlen($lengthBytes); $i < $l; ++$i) { |
||||
| 608 | $encodedLength .= str_pad( |
||||
| 609 | dechex(ord($lengthBytes[$i])), |
||||
| 610 | 2, |
||||
| 611 | '0', |
||||
| 612 | STR_PAD_LEFT |
||||
| 613 | ); |
||||
| 614 | } |
||||
| 615 | |||||
| 616 | $details = str_pad( |
||||
| 617 | $length, |
||||
| 618 | $cColumns['length'], |
||||
| 619 | ' ', |
||||
| 620 | STR_PAD_LEFT |
||||
| 621 | ) . |
||||
| 622 | $cSep . |
||||
| 623 | str_pad( |
||||
| 624 | '0x' . strtoupper($encodedLength), |
||||
| 625 | $cColumns['encodedLength'], |
||||
| 626 | ' ', |
||||
| 627 | STR_PAD_LEFT |
||||
| 628 | ); |
||||
| 629 | } |
||||
| 630 | fwrite( |
||||
| 631 | STDOUT, |
||||
| 632 | $cColors[$mode] . |
||||
| 633 | str_pad($mode, $cColumns['mode'], ' ', STR_PAD_RIGHT) . |
||||
| 634 | $cColors[''] . |
||||
| 635 | "{$cSep}{$details}{$cSep}{$cColors[$mode]}" . |
||||
| 636 | implode( |
||||
| 637 | "\n{$cColors['']}" . |
||||
| 638 | str_repeat(' ', $cColumns['mode']) . |
||||
| 639 | $cSep . |
||||
| 640 | implode( |
||||
| 641 | ($isAbnormal ? ' ' : $cSep), |
||||
| 642 | array( |
||||
| 643 | str_repeat(' ', $cColumns['length']), |
||||
| 644 | str_repeat(' ', $cColumns['encodedLength']) |
||||
| 645 | ) |
||||
| 646 | ) . $cSep . $cColors[$mode], |
||||
| 647 | $wordFragments |
||||
| 648 | ) . "\n{$cColors['']}" |
||||
| 649 | ); |
||||
| 650 | }; |
||||
| 651 | } else { |
||||
| 652 | $printWord = function ($mode, $word, $msg = '') use ($cColors) { |
||||
| 653 | if ('' !== $cColors['']) { |
||||
| 654 | $word = str_replace("\033", "\033[27@", $word); |
||||
| 655 | $msg = str_replace("\033", "\033[27@", $msg); |
||||
| 656 | } |
||||
| 657 | |||||
| 658 | if ('ERR' === $mode || 'NOTE' === $mode) { |
||||
| 659 | fwrite(STDERR, "{$cColors[$mode]}-- {$msg}"); |
||||
| 660 | if ('' !== $word) { |
||||
| 661 | fwrite(STDERR, ": {$word}"); |
||||
| 662 | } |
||||
| 663 | fwrite(STDERR, "{$cColors['']}\n"); |
||||
| 664 | } elseif ('SENT' !== $mode) { |
||||
| 665 | fwrite(STDOUT, "{$cColors[$mode]}{$word}{$cColors['']}\n"); |
||||
| 666 | } |
||||
| 667 | }; |
||||
| 668 | } |
||||
| 669 | |||||
| 670 | if ($com->getTransmitter()->isAvailable()) { |
||||
| 671 | $printWord('NOTE', '', 'Connection started'); |
||||
| 672 | |||||
| 673 | if ($cmd->options['crypto'] |
||||
| 674 | && $comContextOpts['ssl']['capture_peer_cert'] |
||||
| 675 | ) { |
||||
| 676 | $contextParams = $com->getTransmitter()->getContextParams(); |
||||
| 677 | $cert = $contextParams['options']['ssl']['peer_certificate']; |
||||
| 678 | $printWord( |
||||
| 679 | 'NOTE', |
||||
| 680 | openssl_x509_fingerprint($cert, 'sha256'), |
||||
| 681 | 'Certificate fingerprint' |
||||
| 682 | ); |
||||
| 683 | } |
||||
| 684 | } |
||||
| 685 | |||||
| 686 | //Input/Output cycle |
||||
| 687 | while (true) { |
||||
| 688 | $prevWord = null; |
||||
| 689 | $word = ''; |
||||
| 690 | $words = array(); |
||||
| 691 | |||||
| 692 | |||||
| 693 | if (!$com->getTransmitter()->isAvailable()) { |
||||
| 694 | $printWord('NOTE', '', 'Connection terminated'); |
||||
| 695 | break; |
||||
| 696 | } |
||||
| 697 | |||||
| 698 | //Input cycle |
||||
| 699 | while (true) { |
||||
| 700 | if ($cmd->options['verbose']) { |
||||
| 701 | fwrite( |
||||
| 702 | STDOUT, |
||||
| 703 | implode( |
||||
| 704 | $cSep, |
||||
| 705 | array( |
||||
| 706 | $cColors['SEND'] . |
||||
| 707 | str_pad('SEND', $cColumns['mode'], ' ', STR_PAD_RIGHT) |
||||
| 708 | . $cColors[''], |
||||
| 709 | str_pad( |
||||
| 710 | '<prompt>', |
||||
| 711 | $cColumns['length'], |
||||
| 712 | ' ', |
||||
| 713 | STR_PAD_LEFT |
||||
| 714 | ), |
||||
| 715 | str_pad( |
||||
| 716 | '<prompt>', |
||||
| 717 | $cColumns['encodedLength'], |
||||
| 718 | ' ', |
||||
| 719 | STR_PAD_LEFT |
||||
| 720 | ), |
||||
| 721 | '' |
||||
| 722 | ) |
||||
| 723 | ) |
||||
| 724 | ); |
||||
| 725 | } |
||||
| 726 | |||||
| 727 | fwrite(STDOUT, (string)$cColors['SEND']); |
||||
| 728 | |||||
| 729 | if ($cmd->options['multiline']) { |
||||
| 730 | while (true) { |
||||
| 731 | $line = stream_get_line(STDIN, PHP_INT_MAX, PHP_EOL); |
||||
| 732 | if (chr(3) === $line) { |
||||
| 733 | break; |
||||
| 734 | } |
||||
| 735 | if ((chr(3) . chr(3)) === $line) { |
||||
| 736 | $word .= chr(3); |
||||
| 737 | } else { |
||||
| 738 | $word .= $line . PHP_EOL; |
||||
| 739 | } |
||||
| 740 | if ($cmd->options['verbose']) { |
||||
| 741 | fwrite( |
||||
| 742 | STDOUT, |
||||
| 743 | "\n{$cColors['']}" . |
||||
| 744 | implode( |
||||
| 745 | $cSep, |
||||
| 746 | array( |
||||
| 747 | str_repeat(' ', $cColumns['mode']), |
||||
| 748 | str_repeat(' ', $cColumns['length']), |
||||
| 749 | str_repeat(' ', $cColumns['encodedLength']), |
||||
| 750 | '' |
||||
| 751 | ) |
||||
| 752 | ) |
||||
| 753 | . $cColors['SEND'] |
||||
| 754 | ); |
||||
| 755 | } |
||||
| 756 | } |
||||
| 757 | if ('' !== $word) { |
||||
| 758 | $word = substr($word, 0, -strlen(PHP_EOL)); |
||||
| 759 | } |
||||
| 760 | } else { |
||||
| 761 | $word = stream_get_line(STDIN, PHP_INT_MAX, PHP_EOL); |
||||
| 762 | } |
||||
| 763 | |||||
| 764 | if ($cmd->options['verbose']) { |
||||
| 765 | fwrite(STDOUT, "\n"); |
||||
| 766 | } |
||||
| 767 | fwrite(STDOUT, (string)$cColors['']); |
||||
| 768 | |||||
| 769 | $words[] = $word; |
||||
| 770 | if ('w' === $cmd->options['commandMode']) { |
||||
| 771 | break; |
||||
| 772 | } |
||||
| 773 | if ('' === $word) { |
||||
| 774 | if ('s' === $cmd->options['commandMode']) { |
||||
| 775 | break; |
||||
| 776 | } elseif ('' === $prevWord) {//'e' === $cmd->options['commandMode'] |
||||
| 777 | array_pop($words); |
||||
| 778 | break; |
||||
| 779 | } |
||||
| 780 | } |
||||
| 781 | $prevWord = $word; |
||||
| 782 | $word = ''; |
||||
| 783 | } |
||||
| 784 | |||||
| 785 | //Input flush |
||||
| 786 | foreach ($words as $word) { |
||||
| 787 | try { |
||||
| 788 | $com->sendWord($word); |
||||
| 789 | $printWord('SENT', $word); |
||||
| 790 | } catch (SE $e) { |
||||
| 791 | if (0 === $e->getFragment()) { |
||||
| 792 | $printWord('ERR', '', 'Failed to send word'); |
||||
| 793 | } else { |
||||
| 794 | $printWord( |
||||
| 795 | 'ERR', |
||||
| 796 | substr($word, 0, $e->getFragment()), |
||||
|
0 ignored issues
–
show
It seems like
$e->getFragment() can also be of type resource and string; however, parameter $length of substr() does only seem to accept integer|null, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 797 | 'Partial word sent' |
||||
| 798 | ); |
||||
| 799 | } |
||||
| 800 | } |
||||
| 801 | } |
||||
| 802 | |||||
| 803 | //Output cycle |
||||
| 804 | while (true) { |
||||
| 805 | if (!$com->getTransmitter()->isAvailable()) { |
||||
| 806 | break; |
||||
| 807 | } |
||||
| 808 | |||||
| 809 | if (!$com->getTransmitter()->isDataAwaiting($cmd->options['time'])) { |
||||
| 810 | $printWord('NOTE', '', 'Receiving timed out'); |
||||
| 811 | break; |
||||
| 812 | } |
||||
| 813 | |||||
| 814 | try { |
||||
| 815 | $word = $com->getNextWord(); |
||||
| 816 | $printWord('RECV', $word); |
||||
| 817 | |||||
| 818 | if ('w' === $cmd->options['replyMode'] |
||||
| 819 | || ('s' === $cmd->options['replyMode'] && '' === $word) |
||||
| 820 | ) { |
||||
| 821 | break; |
||||
| 822 | } |
||||
| 823 | } catch (SE $e) { |
||||
| 824 | if ('' === $e->getFragment()) { |
||||
| 825 | $printWord('ERR', '', 'Failed to receive word'); |
||||
| 826 | } else { |
||||
| 827 | $printWord('ERR', $e->getFragment(), 'Partial word received'); |
||||
| 828 | } |
||||
| 829 | break; |
||||
| 830 | } catch (RouterOS\NotSupportedException $e) { |
||||
| 831 | $printWord('ERR', $e->getValue(), 'Unsupported control byte'); |
||||
| 832 | break; |
||||
| 833 | } catch (E $e) { |
||||
| 834 | $printWord('ERR', (string)$e, 'Unknown error'); |
||||
| 835 | break; |
||||
| 836 | } |
||||
| 837 | } |
||||
| 838 | } |
||||
| 839 |