# Name Net::Telnet::Netgear - Generate and send Netgear Telnet-enable packets through Net::Telnet # Synopsis ```perl use Net::Telnet::Netgear; my $telnet = Net::Telnet::Netgear->new ( # Standard Net::Telnet parameters are allowed host => 'example.com', packet_mac => 'AA:BB:CC:DD:EE:FF', # or AABBCCDDEEFF packet_username => 'admin', packet_password => 'hunter2', netgear_defaults => 1 ); # The magic is done transparently: the packet has already been sent, # if necessary, and the standard Net::Telnet API can now be used. my @lines = $telnet->cmd ('whoami'); use Net::Telnet::Netgear::Packet; # Manually create a packet. my $packet = Net::Telnet::Netgear::Packet->new (mac => '...'); say length $packet->get_packet; # or whatever you want $packet = Net::Telnet::Netgear::Packet->from_base64 ('...'); $packet = Net::Telnet::Netgear::Packet->from_string ('...'); ``` # Description This module allows to programmatically generate and send magic Telnet-enabling packets for Netgear routers with a locked Telnet interface. The packet can either be user-provided or it can be automatically generated given the username, password and MAC address of the router. Also, this module is capable of sending packets using TCP or UDP (the latter is used on new firmwares), and can automatically pick the right protocol to use, making it compatible with old and new firmwares without any additional configuration. The work on the Telnet protocol is done by [Net::Telnet](https://metacpan.org/pod/Net::Telnet), which is subclassed by this module. In fact, it's possible to use the entire [Net::Telnet](https://metacpan.org/pod/Net::Telnet) API and configuration parameters. # Methods [Net::Telnet::Netgear](https://metacpan.org/pod/Net::Telnet::Netgear) inherits all methods from [Net::Telnet](https://metacpan.org/pod/Net::Telnet) and implements the following new ones. ## new ```perl my $instance = Net::Telnet::Netgear->new (%options); ``` Creates a new `Net::Telnet::Netgear` instance. Returns `undef` on failure. `%options` can contain any of the options valid with the constructor of [Net::Telnet](https://metacpan.org/pod/Net::Telnet), with the addition of: - `packet_mac => 'AA:BB:CC:DD:EE:FF'` The MAC address of the router where the packet will be sent to. Each non-hexadecimal character (like colons) will be removed. - `packet_username => 'admin'` The username that will be put in the packet. Defaults to `Gearguy` for compatibility reasons. With new firmwares, the username `admin` should be used. Has no effect if `packet_mac` is not specified. - `packet_password => 'password'` The password that will be put in the packet. Defaults to `Geardog` for compatibility reasons. With new firmwares, the password of the router interface should be used. Has no effect if `packet_mac` is not specified. - `packet_content => 'string'` The content of the packet to be sent, as a string. Only makes sense if the packet is not defined elsewhere. - `packet_base64 => 'b64_string'` The content of the packet to be sent, as a Base64 encoded string. Only makes sense if the packet is not defined elsewhere. - `packet_instance => ...` A subclass of [Net::Telnet::Netgear::Packet](https://metacpan.org/pod/Net::Telnet::Netgear::Packet) to be used as the packet. Only makes sense if the packet is not defined elsewhere. **NOTE:** Packets generated with ["new" in Net::Telnet::Netgear::Packet](https://metacpan.org/pod/Net::Telnet::Netgear::Packet#new), ["from\_string" in Net::Telnet::Netgear::Packet](https://metacpan.org/pod/Net::Telnet::Netgear::Packet#from_string) and ["from\_base64" in Net::Telnet::Netgear::Packet](https://metacpan.org/pod/Net::Telnet::Netgear::Packet#from_base64) can be used too. - `packet_delay => .50` The amount of time, in seconds, to wait after sending the packet. In pseudo-code: `send_packet(); wait(packet_delay); connect()` Defaults to `.3` seconds, or 300 milliseconds. Can be `0`. - `packet_wait_timeout => .75` The amount of time, in seconds, to wait for a response from the server before sending the packet. In pseudo-code: `connect(); if !can_read(in packet_wait_timeout seconds) then send_packet()` Only effective when the packet is sent using TCP. Defaults to `1` second. - `packet_send_mode => 'auto|tcp|udp'` Determines how to send the packet. See ["packet\_send\_mode"](#packet_send_mode) below. Defaults to `auto`. - `netgear_defaults => 0|1` If enabled, the default values defined in the hash `%Net::Telnet::Netgear::NETGEAR_DEFAULTS` are applied once the connection is established. See ["DEFAULT VALUES USING %NETGEAR\_DEFAULTS"](#default-values-using-netgear_defaults). Defaults to `0`. - `exit_on_destroy => 0|1` If enabled, the `exit` shell command is sent before the object is destroyed. This is useful to avoid ghost processes when closing a Telnet connection without killing the shell first. Defaults to `0`. ## apply\_netgear\_defaults ```perl $instance->apply_netgear_defaults; $instance->apply_netgear_defaults ( prompt => '/rxp/', cmd_remove_mode => 0 ); %Net::Telnet::Netgear::NETGEAR_DEFAULTS = (exit_on_destroy => 1); $instance->apply_netgear_defaults; ``` Applies the values specified in the hash `%Net::Telnet::Netgear::NETGEAR_DEFAULTS`. If any argument is specified, it is temporarily added to the hash. See ["DEFAULT VALUES USING %NETGEAR\_DEFAULTS"](#default-values-using-netgear_defaults). ## exit\_on\_destroy ```perl my $current_value = $instance->exit_on_destroy; # Set exit_on_destroy to 1 my $old_value = $instance->exit_on_destroy (1); ``` Gets or sets the value of the boolean flag `exit_on_destroy`, which causes the module to send the `exit` shell command before being destroyed. This is to avoid ghost processes when closing a Telnet connection without killing the shell first. ## packet ```perl my $current_value = $instance->packet; # Set the content of the packet to '...' my $old_value = $instance->packet ('...'); ``` Gets or sets the value of the packet **as a string**. This is basically equivalent to the `packet_content` constructor parameter. Note that objects cannot be used - you have to call ["get\_packet" in Net::Telnet::Netgear::Packet](https://metacpan.org/pod/Net::Telnet::Netgear::Packet#get_packet) before passing the value to this method. ## packet\_delay ```perl my $current_value = $instance->packet_delay; # Set packet_delay to .75 seconds my $old_value = $instance->packet_delay (.75); ``` Gets or sets the amount of time, in seconds, to wait after sending the packet. ## packet\_send\_mode ```perl my $current_value = $instance->packet_send_mode; # Set packet_send_mode to 'udp' my $old_value = $instance->packet_send_mode ('udp'); ``` Gets or sets the protocol used to send the packet, between `tcp`, `udp` and `auto`. If it is `auto`, then the module will try to guess the correct protocol to use. More specifically, if the initial `open` performed on the specified `host` and `port` fails, the packet is sent using UDP (and then the connection is reopened). Otherwise, if the `open` succeeds but it's impossible to read within the ["packet\_wait\_timeout"](#packet_wait_timeout), the packet is sent using TCP. If it is `tcp`, the packet is sent using TCP. If it is `udp`, the packet is sent using UDP. Note that in this case the packet is always sent before an `open` call. **NOTE:** Generally, specifying the protocol instead of using `auto` is faster, especially when the packet has to be sent using UDP (due to the additional connection that has to be made). ## packet\_wait\_timeout ```perl my $current_value = $instance->packet_wait_timeout; # Set packet_wait_timeout to 1.25 my $old_value = $instance->packet_wait_timeout (1.25); ``` Gets or sets the the amount of time, in seconds, to wait for a response from the server before sending the packet. Only effective when the packet is sent using TCP. # Implementation details When you open a connection with [Net::Telnet::Netgear](https://metacpan.org/pod/Net::Telnet::Netgear) (either with the `(fh)open` methods inherited from [Net::Telnet](https://metacpan.org/pod/Net::Telnet) or by specifying the `host` constructor parameter), the following actions are performed depending on the value of ["packet\_send\_mode"](#packet_send_mode). **NOTE:** when `fhopen` is used, "socket" refers to the filehandle. - "auto" This is the default. First, [Net::Telnet](https://metacpan.org/pod/Net::Telnet) tries to open the socket. If it succeeds, then it's assumed that the server may want a TCP packet. To check if the server actually needs it, a ["select" in perlfunc](https://metacpan.org/pod/perlfunc#select) call is performed on the socket to determine if data is available to read. If data is available, then nothing is done. Otherwise, the packet is sent using TCP and then the socket is re-opened. If the initial `open` didn't succeed, then the server is not listening on the port. It's assumed that the server wants an UDP packet, and it is immediately sent. The socket is re-opened, and if it fails again the error is propagated. - "tcp" The actions specified in the first case apply, except that if the initial `open` goes wrong the error is immediately propagated. - "udp" The packet is immediately sent before the `open` performed by [Net::Telnet](https://metacpan.org/pod/Net::Telnet). If it fails, the error is immediately propagated. # Default values using %NETGEAR\_DEFAULTS As an added feature, it's possible to enable a set of options suitable for Netgear routers. This is possible with the hash `%Net::Telnet::Netgear::NETGEAR_DEFAULTS`, which contains a list of methods to be called on the current instance along with their parameters. This is done by the method ["apply\_netgear\_defaults"](#apply_netgear_defaults). The current version specifies the following list of default values: ```perl method value ----------------- ----------- cmd_remove_mode 1 exit_on_destroy 1 prompt '/.* # $/' waitfor '/.* # $/' ``` It is possible to edit this list either by interacting directly with it: ```perl $Net::Telnet::Netgear::NETGEAR_DEFAULTS{some_option} = 'some_value'; delete $Net::Telnet::Netgear::NETGEAR_DEFAULTS{some_option}; %Net::Telnet::Netgear::NETGEAR_DEFAULTS = ( option1 => 'value1', option2 => 'value2' ); ``` Or you can supply additional parameters to ["apply\_netgear\_defaults"](#apply_netgear_defaults), which will be temporarily added to the list. Note that user-specified values have priority over the ones in the hash, and if you specify the value of an option as `undef`, it won't be set at all. ```perl # cmd_remove_mode is set to 0 instead of 1, along with all the other # default values $instance->apply_netgear_defaults (cmd_remove_mode => 0); # do not set cmd_remove_mode at all, but apply every other default $instance->apply_netgear_defaults (cmd_remove_mode => undef); # the standard list of default values is applied plus 'some_option' $instance->apply_netgear_defaults (some_option => 'some_value'); # equivalent to: { local %Net::Telnet::Netgear::NETGEAR_DEFAULTS = ( %Net::Telnet::Netgear::NETGEAR_DEFAULTS, some_option => 'some_value' ); $instance->apply_netgear_defaults; } ``` # The magic behind timeouts `Net::Telnet::Netgear` uses a timeout to determine if it should send the packet (using TCP). But what's the magic behind this mysterious decimal number? Timeouts, under normal conditions, are implemented using the ["select" in perlfunc](https://metacpan.org/pod/perlfunc#select) function (which calls the [select(2)](http://man.he.net/man2/select) syscall). This magic function is awesome, and it works beautifully. It would be great if the story ended here, but happy endings are pretty rare in real life. `select` works basically everywhere when dealing with network sockets, but it doesn't work on certain systems when dealing with generic filehandles (_Win32, I'm looking at you!_). [Net::Telnet](https://metacpan.org/pod/Net::Telnet) can make Telnet work on arbitrary filehandles (thanks to ["fhopen" in Net::Telnet](https://metacpan.org/pod/Net::Telnet#fhopen)), but that means that `select` may not be always available. This is a problem, and you can specify what to do in this case with the boolean variable `$Net::Telnet::Netgear::DIE_ON_SELECT_UNAVAILABLE`. If this variable is false (the default), then if `select` is not available the module will simply never send packets using TCP and emit a warning. This may not be always desiderable. If this variable is true, then if `select` is unavailable the module will call `Net::Telnet->error` which, when `errmode` is the default, stops the execution of the script. **NOTE:** If ["packet\_send\_mode"](#packet_send_mode) is set to `udp`, then `select` is never called, thus `$Net::Telnet::Netgear::DIE_ON_SELECT_UNAVAILABLE` won't have any effect even if `select` is unavailable. # Caveats An `open` call may require serious amounts of time, depending on the ["packet\_send\_mode"](#packet_send_mode) and ["packet\_wait\_timeout"](#packet_wait_timeout). Particularly, if no packet has to be sent, then `tcp` or `auto` are the fastest. Otherwise, `udp` is the fastest (because there are no timeouts, and the packet is immediately sent). `auto` is the slowest when the router requires the packet on UDP, because a connection is attempted on the TCP port, while it has the same speed of `tcp` when the packet is expected on TCP. # See also [Net::Telnet](https://metacpan.org/pod/Net::Telnet), [Net::Telnet::Netgear::Packet](https://metacpan.org/pod/Net::Telnet::Netgear::Packet), [http://wiki.openwrt.org/toh/netgear/telnet.console](http://wiki.openwrt.org/toh/netgear/telnet.console), [https://github.com/Robertof/perl-net-telnet-netgear](https://github.com/Robertof/perl-net-telnet-netgear) # Author Roberto Frenna (robertof AT cpan DOT org) # Thanks Thanks to [Derreck "insanid"](https://github.com/insanid) for the precious contribution to the OpenWRT wiki page, and for helping me to discovery the mistery behind the "strange" packets generated with long passwords. Thanks to [the authors of Mojolicious](https://metacpan.org/pod/Mojolicious) for inspiration about the license and the documentation. # License Copyright (C) 2014-2015, Roberto Frenna. This program is free software, you can redistribute it and/or modify it under the terms of the Artistic License version 2.0.