Merge pull request #1908 from webmin/dev/add-url-redirect-tester

Add API to check if given URL redirects somewhere
This commit is contained in:
Jamie Cameron
2023-05-19 16:16:58 -07:00
committed by GitHub
2 changed files with 135 additions and 1 deletions

File diff suppressed because one or more lines are too long

View File

@@ -13006,6 +13006,140 @@ if (!$@ && $locale_system) {
return $locale_def;
}
=head2 get_http_redirect(url, [page], [timeout])
Check if given URL redirects somewhere
The parameters are :
=item url - Given URL to check if it redirects anywhere, e.g. https://google.com
=item page - Path in URL, e.g. /about as in https://www.google.com/about
=item timeout - Timeout for connections, defaults to 15s
Example of usage and return data:
Call:
&get_http_redirect('https://google.com', '/about')
Return:
{
'hops' => [
{ [...] }
],
'host' => 'about.google',
'path' => '',
'port' => '443',
'proto' => 'https',
'resolved' => {
'ipv4' => '216.239.32.29'
},
'url' => 'https://about.google'
}
=cut
sub get_http_redirect
{
my ($url, $page, $timeout) = @_;
my ($out, $error, $con_err);
state (@hops, %redirects);
my ($proto, $host, $uport) = $url =~ /^(https?):\/\/([^:\/?#]*)(?:\:([0-9]+))?/;
my ($ssl, $port) = (0, undef);
$port = '80' if (!$uport);
if ($proto eq 'https') {
$ssl = 1;
$port = '443' if (!$uport);
}
# Set default port, page and timeout
$port ||= $uport;
$page ||= '/';
$timeout ||= 15;
# Original request
my (%redirect);
$redirect{'url'} = $url;
$redirect{'proto'} = $proto;
$redirect{'host'} = $host;
$redirect{'port'} = $port;
$redirect{'path'} = $page;
push(@hops, \%redirect);
# Build headers
my @headers;
push(@headers, [ "Host", $host ]);
push(@headers, [ "User-agent", "Webmin" ]);
push(@headers, [ "Accept-language", "en" ]);
# Actually download it
$main::download_timed_out = undef;
local $SIG{ALRM} = \&download_timeout;
alarm($timeout);
my $h = &make_http_connection($host, $port, $ssl, "GET", $page, \@headers);
alarm(0);
$h = $main::download_timed_out if ($main::download_timed_out);
if (ref($h)) {
&write_http_connection($h, "\r\n");
}
&complete_http_download($h, undef, \$error, undef, undef, $host, $port,
undef, $ssl, 1, $timeout);
if (ref($h)) {
$redirect{'response'} = $h->{'buffer'};
}
else {
return { 'error' => $h };
}
if (ref($h)) {
if ($h->{'buffer'} =~ /has\s+moved\s+<a\s+href=['"](.*?)['"]/mi) {
my $rurl = $1;
$rurl =~ s/\/$//;
$redirect{'redir'}->{'url'} = $rurl;
my ($proto, $host, $uport, $path) = $rurl =~ /^(https?):\/\/([^:\/?#]*)(?:\:([0-9]+))?(.*)/;
my $port = '80' if (!$uport);
if ($proto eq 'https') {
$port = '443' if (!$uport);
}
$port ||= $uport;
$redirect{'redir'}->{'proto'} = $proto;
$redirect{'redir'}->{'host'} = $host;
$redirect{'redir'}->{'port'} = $port;
$redirect{'redir'}->{'path'} = $path;
}
}
# Finally test if redirected URL can be resolved
my $redir_host = $redirect{'redir'}->{'host'};
if ($redir_host) {
my $resolved4 = &to_ipaddress($redir_host);
my $resolved6 = &to_ip6address($redir_host);
if (!$resolved4 && !$resolved6) {
delete $redirect{'redir'};
}
else {
$redirect{'redir'}->{'resolved'}->{'ipv4'} = $resolved4
if ($resolved4);
$redirect{'redir'}->{'resolved'}->{'ipv6'} = $resolved6
if ($resolved6);
if ($redirects{'redir'}->{'host'} ne $redir_host) {
%redirects = %redirect;
my $rport = $redirect{'redir'}->{'port'};
$rport = undef if ($rport =~ /80|443/);
$rport = ":$rport" if ($rport);
my $rpath = $redirect{'redir'}->{'path'};
$rpath = undef if ($rpath eq "/");
return &get_http_redirect("$proto://$redir_host$rport", $rpath, $timeout);
}
}
}
%redirect = %redirects if (keys %redirects);
%redirect = %{$redirect{'redir'}};
@hops = grep { $_->{'redir'} } @hops;
$redirect{'hops'} = \@hops
if (@hops);
return \%redirect;
}
=head2 get_http_cookie(cookie-name)
Returns a cookie value based on its name