500 lines
18 KiB
Perl
500 lines
18 KiB
Perl
#
|
|
# Copyright 2020 Centreon (http://www.centreon.com/)
|
|
#
|
|
# Centreon is a full-fledged industry-strength solution that meets
|
|
# the needs in IT infrastructure and application monitoring for
|
|
# service performance.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
package centreon::plugins::backend::http::curl;
|
|
|
|
use strict;
|
|
use warnings;
|
|
use URI;
|
|
use centreon::plugins::misc;
|
|
|
|
sub new {
|
|
my ($class, %options) = @_;
|
|
my $self = {};
|
|
bless $self, $class;
|
|
|
|
if (!defined($options{noptions}) || $options{noptions} != 1) {
|
|
$options{options}->add_options(arguments => {
|
|
'curl-opt:s@' => { name => 'curl_opt' }
|
|
});
|
|
$options{options}->add_help(package => __PACKAGE__, sections => 'BACKEND CURL OPTIONS', once => 1);
|
|
}
|
|
|
|
$self->{output} = $options{output};
|
|
|
|
return $self;
|
|
}
|
|
|
|
sub check_options {
|
|
my ($self, %options) = @_;
|
|
|
|
centreon::plugins::misc::mymodule_load(
|
|
output => $self->{output},
|
|
module => 'Net::Curl::Easy',
|
|
error_msg => "Cannot load module 'Net::Curl::Easy'."
|
|
);
|
|
centreon::plugins::misc::mymodule_load(
|
|
output => $self->{output},
|
|
module => 'centreon::plugins::backend::http::curlconstants',
|
|
error_msg => "Cannot load module 'centreon::plugins::backend::http::curlconstants'."
|
|
);
|
|
$self->{constant_cb} = \¢reon::plugins::backend::http::curlconstants::get_constant_value;
|
|
|
|
foreach (('unknown_status', 'warning_status', 'critical_status')) {
|
|
if (defined($options{request}->{$_})) {
|
|
$options{request}->{$_} =~ s/%\{http_code\}/\$self->{response_code}/g;
|
|
}
|
|
}
|
|
|
|
if (!defined($options{request}->{curl_opt})) {
|
|
$options{request}->{curl_opt} = [];
|
|
}
|
|
}
|
|
|
|
my $http_code_explained = {
|
|
100 => 'Continue',
|
|
101 => 'Switching Protocols',
|
|
200 => 'OK',
|
|
201 => 'Created',
|
|
202 => 'Accepted',
|
|
203 => 'Non-Authoritative Information',
|
|
204 => 'No Content',
|
|
205 => 'Reset Content',
|
|
206 => 'Partial Content',
|
|
300 => 'Multiple Choices',
|
|
301 => 'Moved Permanently',
|
|
302 => 'Found',
|
|
303 => 'See Other',
|
|
304 => 'Not Modified',
|
|
305 => 'Use Proxy',
|
|
306 => '(Unused)',
|
|
307 => 'Temporary Redirect',
|
|
400 => 'Bad Request',
|
|
401 => 'Unauthorized',
|
|
402 => 'Payment Required',
|
|
403 => 'Forbidden',
|
|
404 => 'Not Found',
|
|
405 => 'Method Not Allowed',
|
|
406 => 'Not Acceptable',
|
|
407 => 'Proxy Authentication Required',
|
|
408 => 'Request Timeout',
|
|
409 => 'Conflict',
|
|
410 => 'Gone',
|
|
411 => 'Length Required',
|
|
412 => 'Precondition Failed',
|
|
413 => 'Request Entity Too Large',
|
|
414 => 'Request-URI Too Long',
|
|
415 => 'Unsupported Media Type',
|
|
416 => 'Requested Range Not Satisfiable',
|
|
417 => 'Expectation Failed',
|
|
500 => 'Internal Server Error',
|
|
501 => 'Not Implemented',
|
|
502 => 'Bad Gateway',
|
|
503 => 'Service Unavailable',
|
|
504 => 'Gateway Timeout',
|
|
505 => 'HTTP Version Not Supported'
|
|
};
|
|
|
|
sub cb_debug {
|
|
my ($easy, $type, $data, $uservar) = @_;
|
|
|
|
my $msg = '';
|
|
if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_TEXT')) {
|
|
$msg = sprintf("== Info: %s", $data);
|
|
}
|
|
if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_HEADER_OUT')) {
|
|
$msg = sprintf("=> Send header: %s", $data);
|
|
}
|
|
if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_DATA_OUT')) {
|
|
$msg = sprintf("=> Send data: %s", $data);
|
|
}
|
|
if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_SSL_DATA_OUT')) {
|
|
$msg = sprintf("=> Send SSL data: %s", $data);
|
|
}
|
|
if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_HEADER_IN')) {
|
|
$msg = sprintf("=> Recv header: %s", $data);
|
|
}
|
|
if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_DATA_IN')) {
|
|
$msg = sprintf("=> Recv data: %s", $data);
|
|
}
|
|
if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_SSL_DATA_IN')) {
|
|
$msg = sprintf("=> Recv SSL data: %s", $data);
|
|
}
|
|
|
|
$uservar->{output}->output_add(long_msg => $msg, debug => 1);
|
|
return 0;
|
|
}
|
|
|
|
sub curl_setopt {
|
|
my ($self, %options) = @_;
|
|
|
|
eval {
|
|
$self->{curl_easy}->setopt($options{option}, $options{parameter});
|
|
};
|
|
if ($@) {
|
|
$self->{output}->add_option_msg(short_msg => "curl setopt error: '" . $@ . "'.");
|
|
$self->{output}->option_exit();
|
|
}
|
|
}
|
|
|
|
sub set_method {
|
|
my ($self, %options) = @_;
|
|
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CUSTOMREQUEST'), parameter => undef);
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POSTFIELDS'), parameter => undef);
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPGET'), parameter => 1);
|
|
|
|
if ($options{content_type_forced} == 1) {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POSTFIELDS'), parameter => $options{request}->{query_form_post})
|
|
if (defined($options{request}->{query_form_post}));
|
|
} elsif (defined($options{request}->{post_params})) {
|
|
my $uri_post = URI->new();
|
|
$uri_post->query_form($options{request}->{post_params});
|
|
push @{$options{headers}}, 'Content-Type: application/x-www-form-urlencoded';
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POSTFIELDS'), parameter => $uri_post->query);
|
|
}
|
|
|
|
if ($options{request}->{method} eq 'GET') {
|
|
return ;
|
|
}
|
|
|
|
if ($options{request}->{method} eq 'POST') {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POST'), parameter => 1);
|
|
}
|
|
if ($options{request}->{method} eq 'PUT') {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CUSTOMREQUEST'), parameter => $options{request}->{method});
|
|
}
|
|
if ($options{request}->{method} eq 'DELETE') {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CUSTOMREQUEST'), parameter => $options{request}->{method});
|
|
}
|
|
if ($options{request}->{method} eq 'PATCH') {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CUSTOMREQUEST'), parameter => $options{request}->{method});
|
|
}
|
|
}
|
|
|
|
sub set_auth {
|
|
my ($self, %options) = @_;
|
|
|
|
if (defined($options{request}->{credentials})) {
|
|
if (defined($options{request}->{basic})) {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPAUTH'), parameter => $self->{constant_cb}->(name => 'CURLAUTH_BASIC'));
|
|
} elsif (defined($options{request}->{ntlmv2})) {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPAUTH'), parameter => $self->{constant_cb}->(name => 'CURLAUTH_NTLM'));
|
|
} else {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPAUTH'), parameter => $self->{constant_cb}->(name => 'CURLAUTH_ANY'));
|
|
}
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_USERPWD'), parameter => $options{request}->{username} . ':' . $options{request}->{password});
|
|
}
|
|
|
|
if (defined($options{request}->{cert_file}) && $options{request}->{cert_file} ne '') {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLCERT'), parameter => $options{request}->{cert_file});
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLKEY'), parameter => $options{request}->{key_file});
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_KEYPASSWD'), parameter => $options{request}->{cert_pwd});
|
|
}
|
|
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLCERTTYPE'), parameter => "PEM");
|
|
if (defined($options{request}->{cert_pkcs12})) {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLCERTTYPE'), parameter => "P12");
|
|
}
|
|
}
|
|
|
|
sub set_proxy {
|
|
my ($self, %options) = @_;
|
|
|
|
if (defined($options{request}->{proxyurl}) && $options{request}->{proxyurl} ne '') {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_PROXY'), parameter => $options{request}->{proxyurl});
|
|
}
|
|
|
|
if (defined($options{request}->{proxypac}) && $options{request}->{proxypac} ne '') {
|
|
$self->{output}->add_option_msg(short_msg => 'Unsupported proxypac option');
|
|
$self->{output}->option_exit();
|
|
}
|
|
}
|
|
|
|
sub set_extra_curl_opt {
|
|
my ($self, %options) = @_;
|
|
|
|
my $entries = {};
|
|
foreach (@{$options{request}->{curl_opt}}) {
|
|
my ($key, $value) = split /=>/;
|
|
$key = centreon::plugins::misc::trim($key);
|
|
|
|
if (!defined($entries->{$key})) {
|
|
$entries->{$key} = { val => [], force_array => 0 };
|
|
}
|
|
|
|
$value = centreon::plugins::misc::trim($value);
|
|
if ($value =~ /^\[(.*)\]$/) {
|
|
$entries->{$key}->{force_array} = 1;
|
|
$value = centreon::plugins::misc::trim($1);
|
|
}
|
|
if ($value =~ /^CURLOPT|CURL/) {
|
|
$value = $self->{constant_cb}->(name => $value);
|
|
}
|
|
|
|
push @{$entries->{$key}->{val}}, $value;
|
|
}
|
|
|
|
foreach (keys %$entries) {
|
|
my $key = $_;
|
|
if (/^CURLOPT|CURL/) {
|
|
$key = $self->{constant_cb}->(name => $_);
|
|
}
|
|
|
|
if ($entries->{$_}->{force_array} == 1 || scalar(@{$entries->{$_}->{val}}) > 1) {
|
|
$self->curl_setopt(option => $key, parameter => $entries->{$_}->{val});
|
|
} else {
|
|
$self->curl_setopt(option => $key, parameter => pop @{$entries->{$_}->{val}});
|
|
}
|
|
}
|
|
}
|
|
|
|
sub cb_get_header {
|
|
my ($easy, $header, $uservar) = @_;
|
|
|
|
$header =~ s/[\r\n]//g;
|
|
if ($header =~ /^[\r\n]*$/) {
|
|
$uservar->{nheaders}++;
|
|
} else {
|
|
$uservar->{response_headers}->[$uservar->{nheaders}] = {}
|
|
if (!defined($uservar->{response_headers}->[$uservar->{nheaders}]));
|
|
if ($header =~ /^(\S(?:.*?))\s*:\s*(.*)/) {
|
|
my $header_name = lc($1);
|
|
$uservar->{response_headers}->[$uservar->{nheaders}]->{$header_name} = []
|
|
if (!defined($uservar->{response_headers}->[$uservar->{nheaders}]->{$header_name}));
|
|
push @{$uservar->{response_headers}->[$uservar->{nheaders}]->{$header_name}}, $2;
|
|
} else {
|
|
$uservar->{response_headers}->[$uservar->{nheaders}]->{response_line} = $header;
|
|
}
|
|
}
|
|
|
|
return length($_[1]);
|
|
}
|
|
|
|
sub request {
|
|
my ($self, %options) = @_;
|
|
|
|
if (!defined($self->{curl_easy})) {
|
|
$self->{curl_easy} = Net::Curl::Easy->new();
|
|
}
|
|
|
|
if ($self->{output}->is_debug()) {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_DEBUGFUNCTION'), parameter => \&cb_debug);
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_DEBUGDATA'), parameter => $self);
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_VERBOSE'), parameter => 1);
|
|
}
|
|
|
|
if (defined($options{request}->{timeout}) && $options{request}->{timeout} =~ /\d/) {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_TIMEOUT'), parameter => $options{request}->{timeout});
|
|
}
|
|
if (defined($options{request}->{cookies_file})) {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_COOKIEFILE'), parameter => $options{request}->{cookies_file});
|
|
}
|
|
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_FOLLOWLOCATION'), parameter => 1);
|
|
if (defined($options{request}->{no_follow})) {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_FOLLOWLOCATION'), parameter => 0);
|
|
}
|
|
|
|
my $url;
|
|
if (defined($options{request}->{full_url})) {
|
|
$url = $options{request}->{full_url};
|
|
} elsif (defined($options{request}->{port}) && $options{request}->{port} =~ /^[0-9]+$/) {
|
|
$url = $options{request}->{proto}. "://" . $options{request}->{hostname} . ':' . $options{request}->{port} . $options{request}->{url_path};
|
|
} else {
|
|
$url = $options{request}->{proto}. "://" . $options{request}->{hostname} . $options{request}->{url_path};
|
|
}
|
|
|
|
if (defined($options{request}->{http_peer_addr}) && $options{request}->{http_peer_addr} ne '') {
|
|
$url =~ /^(?:http|https):\/\/(.*?)(\/|\:|$)/;
|
|
$self->{curl_easy}->setopt(
|
|
$self->{constant_cb}->(name => 'CURLOPT_RESOLVE'),
|
|
[$1 . ':' . $options{request}->{port_force} . ':' . $options{request}->{http_peer_addr}]
|
|
);
|
|
}
|
|
|
|
my $uri = URI->new($url);
|
|
if (defined($options{request}->{get_params})) {
|
|
$uri->query_form($options{request}->{get_params});
|
|
}
|
|
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_URL'), parameter => $uri);
|
|
|
|
my $headers = [];
|
|
my $content_type_forced = 0;
|
|
foreach my $key (keys %{$options{request}->{headers}}) {
|
|
push @$headers, $key . ':' . (defined($options{request}->{headers}->{$key}) ? $options{request}->{headers}->{$key} : '');
|
|
if ($key =~ /content-type/i) {
|
|
$content_type_forced = 1;
|
|
}
|
|
}
|
|
|
|
$self->set_method(%options, content_type_forced => $content_type_forced, headers => $headers);
|
|
|
|
if (scalar(@$headers) > 0) {
|
|
$self->{curl_easy}->setopt($self->{constant_cb}->(name => 'CURLOPT_HTTPHEADER'), $headers);
|
|
}
|
|
|
|
if (defined($options{request}->{cacert_file}) && $options{request}->{cacert_file} ne '') {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CAINFO'), parameter => $options{request}->{cacert_file});
|
|
}
|
|
if (defined($options{request}->{insecure})) {
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSL_VERIFYPEER'), parameter => 0);
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSL_VERIFYHOST'), parameter => 0);
|
|
}
|
|
|
|
$self->set_auth(%options);
|
|
$self->set_proxy(%options);
|
|
$self->set_extra_curl_opt(%options);
|
|
|
|
$self->{response_body} = '';
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_FILE'), parameter => \$self->{response_body});
|
|
$self->{nheaders} = 0;
|
|
$self->{response_headers} = [{}];
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HEADERDATA'), parameter => $self);
|
|
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HEADERFUNCTION'), parameter => \&cb_get_header);
|
|
|
|
eval {
|
|
$self->{curl_easy}->perform();
|
|
};
|
|
if ($@) {
|
|
$self->{output}->add_option_msg(short_msg => 'curl perform error : ' . $@);
|
|
$self->{output}->option_exit();
|
|
}
|
|
|
|
$self->{response_code} = $self->{curl_easy}->getinfo($self->{constant_cb}->(name => 'CURLINFO_RESPONSE_CODE'));
|
|
|
|
# Check response
|
|
my $status = 'ok';
|
|
my $message;
|
|
|
|
eval {
|
|
local $SIG{__WARN__} = sub { $message = $_[0]; };
|
|
local $SIG{__DIE__} = sub { $message = $_[0]; };
|
|
|
|
if (defined($options{request}->{critical_status}) && $options{request}->{critical_status} ne '' &&
|
|
eval "$options{request}->{critical_status}") {
|
|
$status = 'critical';
|
|
} elsif (defined($options{request}->{warning_status}) && $options{request}->{warning_status} ne '' &&
|
|
eval "$options{request}->{warning_status}") {
|
|
$status = 'warning';
|
|
} elsif (defined($options{request}->{unknown_status}) && $options{request}->{unknown_status} ne '' &&
|
|
eval "$options{request}->{unknown_status}") {
|
|
$status = 'unknown';
|
|
}
|
|
};
|
|
if (defined($message)) {
|
|
$self->{output}->add_option_msg(short_msg => 'filter status issue: ' . $message);
|
|
$self->{output}->option_exit();
|
|
}
|
|
|
|
if (!$self->{output}->is_status(value => $status, compare => 'ok', litteral => 1)) {
|
|
my $short_msg = $self->{response_code} . ' ' .
|
|
(defined($http_code_explained->{$self->{response_code}}) ? $http_code_explained->{$self->{response_code}} : 'unknown');
|
|
|
|
$self->{output}->output_add(
|
|
severity => $status,
|
|
short_msg => $short_msg
|
|
);
|
|
$self->{output}->display();
|
|
$self->{output}->exit();
|
|
}
|
|
|
|
return $self->{response_body};
|
|
}
|
|
|
|
sub get_headers {
|
|
my ($self, %options) = @_;
|
|
|
|
my $headers = '';
|
|
foreach (keys %{$self->{response_headers}->[$options{nheader}]}) {
|
|
next if (/response_line/);
|
|
foreach my $value (@{$self->{response_headers}->[$options{nheader}]->{$_}}) {
|
|
$headers .= "$_: " . $value . "\n";
|
|
}
|
|
}
|
|
|
|
return $headers;
|
|
}
|
|
|
|
sub get_first_header {
|
|
my ($self, %options) = @_;
|
|
|
|
if (!defined($options{name})) {
|
|
return $self->get_headers(nheader => 0);
|
|
}
|
|
|
|
return undef
|
|
if (!defined($self->{response_headers}->[0]->{ lc($options{name}) }));
|
|
return wantarray ? @{$self->{response_headers}->[0]->{ lc($options{name}) }} : $self->{response_headers}->[0]->{ lc($options{name}) }->[0];
|
|
}
|
|
|
|
sub get_header {
|
|
my ($self, %options) = @_;
|
|
|
|
if (!defined($options{name})) {
|
|
return $self->get_headers(nheader => -1);
|
|
}
|
|
|
|
return undef
|
|
if (!defined($self->{response_headers}->[-1]->{ lc($options{name}) }));
|
|
return wantarray ? @{$self->{response_headers}->[-1]->{ lc($options{name}) }} : $self->{response_headers}->[-1]->{ lc($options{name}) }->[0];
|
|
}
|
|
|
|
sub get_code {
|
|
my ($self, %options) = @_;
|
|
|
|
return $self->{response_code};
|
|
}
|
|
|
|
sub get_message {
|
|
my ($self, %options) = @_;
|
|
|
|
return $http_code_explained->{$self->{response_code}};
|
|
}
|
|
|
|
1;
|
|
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
HTTP Curl backend layer.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
HTTP Curl backend layer.
|
|
|
|
=head1 BACKEND CURL OPTIONS
|
|
|
|
=over 8
|
|
|
|
=item B<--curl-opt>
|
|
|
|
Set CURL Options (--curl-opt="CURLOPT_SSL_VERIFYPEER => 0" --curl-opt="CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_1" ).
|
|
|
|
=back
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
B<http>.
|
|
|
|
=cut
|