MikroTik-API

 view release on metacpan or  search on metacpan

lib/MikroTik/API.pm  view on Meta::CPAN

package MikroTik::API;

use 5.006;
use strict;
use warnings FATAL => 'all';

=head1 NAME

MikroTik::API - Client to MikroTik RouterOS API

=head1 VERSION

Version 2.0.1

B<CAUTION:> Dependencies change with version 2.0.0. MikroTik::API now relies on Moo instead of Moose. Please be sure, dependencies are met before upgrading.

=cut

our $VERSION = '2.0.1';


=head1 SYNOPSIS

    use MikroTik::API;

    my $api = MikroTik::API->new({
        host => 'mikrotik.example.org',
        username => 'whoami',
        password => 'SECRET',
        use_ssl => 1,
    });

    my ( $ret_get_identity, @aoh_identity ) = $api->query( '/system/identity/print', {}, {} );
    print "Name of router: $aoh_identity[0]->{name}\n";

    $api->logout();

=head1 DESCRIPTION

=cut

use Carp qw(croak);
use Digest::MD5;
use IO::Socket::IP;
use IO::Socket::SSL;
use Moo;
use MooX::Types::MooseLike::Base qw(Bool CodeRef Int Str);
use Time::Out qw{ timeout };
use Try::Tiny;
use Type::Tiny;
use namespace::autoclean;


=head1 PUBLIC METHODS

=head2 new( \%config )

    my $api = MikroTik::API->new({
        host => 'mikrotik.example.org',
        username => 'whoami',
        password => 'SECRET',
        autoconnect => 1, # optional (set to 0 if you do not want to connect during construction, default: 1)
        use_ssl => 1, # optional (0 for non ssl / 1 for ssl)
        port => 8729, # optonal (needed if you use another port then 8728 for non-ssl or 8729 for ssl)
        debug => 0, # optional (set beween 0 (none) and 5 (most) for debug messages)
        timeout => 3, # optional (timeout after 3 seconds during connect)
        probe_before_talk => 3, # optional (probe connection before each actual command)
        reconnect_after_failed_probe => 1, # optional (reconnect if probe failed)

    });

=cut

sub BUILD {
    my ($self) = @_;
    if ( $self->get_autoconnect() && $self->get_host() ) {
        $self->connect();
        if ( $self->get_username() && defined( $self->get_password() ) ) {
            $self->login();
        }
    }
    return $self;
}

=head2 $api->connect()

Connect happens on construction if you provide host address

    my $api = MikroTik::API->new();

    $api->set_host('mikrotik.example.org');
    $api->set_port(1234);
    $api->set_use_ssl(1);

    $api->connect();

=cut

sub connect {
    my ( $self ) = @_;

    if ( ! $self->get_host() ) {
        croak 'host must be set before connect()'
    }

    if ( $self->get_use_ssl ) {
        my $socket
            = try {
                IO::Socket::SSL->new(
                    PeerHost        => $self->get_host,
                    PeerPort        => $self->get_port,
                    Proto           => 'tcp',
                    SSL_cipher_list => 'HIGH',
                    SSL_verify_mode => $self->get_ssl_verify(),
                    Timeout         => $self->get_timeout,
                );
            }
            catch {
                croak sprintf
                    'failed connect %s:%s or ssl handshake (%s: %s)',
                    $self->get_host,
                    $self->get_port,
                    $_,
                    IO::Socket::SSL::errstr();
            };
        # obsolete?
        $socket
            or croak sprintf
                'failed connect %s:%s or ssl handshake (%s: %s)',
                $self->get_host,
                $self->get_port,
                $!,
                IO::Socket::SSL::errstr();
        $self->set_socket($socket);
    }
    else {
        $self->set_socket(
            IO::Socket::IP->new(
                PeerHost => $self->get_host,
                PeerPort => $self->get_port,
                Proto    => 'tcp',
                Timeout  => $self->get_timeout,
            )
            or croak sprintf
                'failed connect %s:%s (%s)',
                $self->get_host,
                $self->get_port,
                $@,
        );
    }
    if ( ! $self->get_socket ) {
        croak "socket creation failed ($!)";
    }
    $self->get_socket()->sockopt(SO_KEEPALIVE,1);
    $self->get_socket()->sockopt(SO_RCVTIMEO,$self->get_timeout());
    $self->get_socket()->sockopt(SO_SNDTIMEO,$self->get_timeout());
    return $self;
}

=head2 $api->login()

Connect happens on construction if you provide host address, username and password

    my $api = MikroTik::API->new({ host => 'mikrotik.example.org' });

    $api->set_username('whoami');
    $api->set_password('SECRET');

    $api->login();

=cut

sub login {
    my ( $self ) = @_;

    if ( ! $self->get_username() && defined( $self->get_password() ) ) {
        croak 'username and password must be set before connect()';
    }
    if ( ! $self->get_socket() ) {
        $self->connect();
    }

    # RouterOS post v6.43 has new authentication method, thus pushing login/pass in the very first request.
    my @command = ('/login');
    push( @command, '=name=' . $self->get_username() );
    push( @command, '=password=' . $self->get_password() );
    my ( $retval, @results ) = $self->talk( \@command );
    croak 'disconnected while logging in' if !defined $retval;
    if ( $retval > 1 ) {
        croak 'error during establishing login: ' . $results[0]{'message'};
    }

    # if we got "=ret=" in response - then assuming this is old style AUTH



( run in 1.466 second using v1.01-cache-2.11-cpan-39bf76dae61 )