xref: /freebsd/contrib/ntp/scripts/update-leap/update-leap.in (revision f5f40dd63bc7acbb5312b26ac1ea1103c12352a6)
1276da39aSCy Schubert#! @PATH_PERL@ -w
2*f5f40dd6SCy Schubert# @configure_input
3276da39aSCy Schubert
409100258SXin LI# Copyright (C) 2015, 2017 Network Time Foundation
5276da39aSCy Schubert# Author: Harlan Stenn
609100258SXin LI#
709100258SXin LI# General cleanup and https support: Paul McMath
809100258SXin LI#
9276da39aSCy Schubert# Original shell version:
10276da39aSCy Schubert# Copyright (C) 2014 Timothe Litt litt at acm dot org
1109100258SXin LI#
12276da39aSCy Schubert# This script may be freely copied, used and modified providing that
13276da39aSCy Schubert# this notice and the copyright statement are included in all copies
14276da39aSCy Schubert# and derivative works.  No warranty is offered, and use is entirely at
15276da39aSCy Schubert# your own risk.  Bugfixes and improvements would be appreciated by the
16276da39aSCy Schubert# author.
17276da39aSCy Schubert
1809100258SXin LI######## BEGIN #########
19276da39aSCy Schubertuse strict;
20276da39aSCy Schubert
2109100258SXin LI# Core modules
22276da39aSCy Schubertuse Digest::SHA qw(sha1_hex);
2309100258SXin LIuse File::Basename;
24276da39aSCy Schubertuse File::Copy qw(move);
2509100258SXin LIuse File::Temp qw(tempfile);
26276da39aSCy Schubertuse Getopt::Long qw(:config auto_help no_ignore_case bundling);
2709100258SXin LIuse Sys::Syslog qw(:standard :macros);
28276da39aSCy Schubert
2909100258SXin LI# External modules
3009100258SXin LIuse HTTP::Tiny 0.056;
3109100258SXin LIuse Net::SSLeay 1.49;
3209100258SXin LIuse IO::Socket::SSL 1.56;
33276da39aSCy Schubert
3409100258SXin LImy $VERSION = '1.004';
35276da39aSCy Schubert
3609100258SXin LImy $RUN_DIR = '/tmp';
3709100258SXin LImy $RUN_UID = 0;
3809100258SXin LImy $TMP_FILE;
3909100258SXin LImy $TMP_FH;
4009100258SXin LImy $FILE_MODE = 0644;
41276da39aSCy Schubert
4209100258SXin LI######## DEFAULT CONFIGURATION ##########
4309100258SXin LI# LEAP FILE SRC URIS
4409100258SXin LI#    HTTPS - (default)
4509100258SXin LI#    	https://www.ietf.org/timezones/data/leap-seconds
4609100258SXin LI#    HTTP - No TLS/SSL - (not recommended)
4709100258SXin LI#	http://www.ietf.org/timezones/data/leap-seconds.list
48276da39aSCy Schubert
4909100258SXin LImy $LEAPSRC = 'https://www.ietf.org/timezones/data/leap-seconds.list';
50276da39aSCy Schubertmy $LEAPFILE;
51276da39aSCy Schubert
52276da39aSCy Schubert# How many times to try to download new file
53276da39aSCy Schubertmy $MAXTRIES = 6;
54276da39aSCy Schubertmy $INTERVAL = 10;
55276da39aSCy Schubert
5609100258SXin LImy $NTPCONF='/etc/ntp.conf';
57276da39aSCy Schubert
58276da39aSCy Schubert# How long (in days) before expiration to get updated file
5909100258SXin LImy $PREFETCH = 60;
60276da39aSCy Schubertmy $EXPIRES;
6109100258SXin LImy $FORCE;
62276da39aSCy Schubert
6309100258SXin LI# Output Flags
6409100258SXin LImy $QUIET;
6509100258SXin LImy $DEBUG;
6609100258SXin LImy $SYSLOG;
6709100258SXin LImy $TOTERM;
6809100258SXin LImy $LOGFAC = 'LOG_USER';
69276da39aSCy Schubert
7009100258SXin LI######### PARSE/SET OPTIONS #########
7109100258SXin LImy %SSL_OPTS;
7209100258SXin LImy %SSL_ATTRS = (
7309100258SXin LI    verify_SSL => 1,
7409100258SXin LI    SSL_options => \%SSL_OPTS,
7509100258SXin LI);
76276da39aSCy Schubert
7709100258SXin LIour(%opt);
78276da39aSCy Schubert
7909100258SXin LIGetOptions(\%opt,
8009100258SXin LI	'C=s',
8109100258SXin LI	'D=s',
8209100258SXin LI	'e:60',
8309100258SXin LI	'F',
8409100258SXin LI	'f=s',
8509100258SXin LI	'h|help',
8609100258SXin LI	'i:10',
8709100258SXin LI	'L=s',
8809100258SXin LI	'l=s',
8909100258SXin LI	'q',
9009100258SXin LI	'r:6',
9109100258SXin LI	's',
9209100258SXin LI	't',
9309100258SXin LI	'u=s',
9409100258SXin LI	'v',
9509100258SXin LI	);
96276da39aSCy Schubert
9709100258SXin LI$LOGFAC   = $opt{l} if defined $opt{l};
9809100258SXin LI$LEAPSRC  = $opt{u} if defined $opt{u};
9909100258SXin LI$LEAPFILE = $opt{L} if defined $opt{L};
10009100258SXin LI$PREFETCH = $opt{e} if defined $opt{e};
10109100258SXin LI$NTPCONF  = $opt{f} if defined $opt{f};
10209100258SXin LI$MAXTRIES = $opt{r} if defined $opt{r};
10309100258SXin LI$INTERVAL = $opt{i} if defined $opt{i};
10409100258SXin LI
10509100258SXin LI$FORCE   = 1 if defined $opt{F};
10609100258SXin LI$DEBUG	 = 1 if defined $opt{v};
10709100258SXin LI$QUIET   = 1 if defined $opt{q};
10809100258SXin LI$SYSLOG  = 1 if defined $opt{s};
10909100258SXin LI$TOTERM  = 1 if defined $opt{t};
11009100258SXin LI
11109100258SXin LI$SSL_OPTS{SSL_ca_file} = $opt{C} if (defined($opt{C}));
11209100258SXin LI$SSL_OPTS{SSL_ca_path} = $opt{D} if (defined($opt{D}));
11309100258SXin LI
11409100258SXin LI###############
11509100258SXin LI## START MAIN
11609100258SXin LI###############
11709100258SXin LImy $PROG = basename($0);
11809100258SXin LI
11909100258SXin LI# Logging - Default is to use syslog(3) if STDOUT isn't
12009100258SXin LI# connected to a tty.
12109100258SXin LIif ($SYSLOG || !-t STDOUT) {
12209100258SXin LI    $SYSLOG = 1;
12309100258SXin LI    openlog($PROG, 'pid', $LOGFAC);
12409100258SXin LI}
12509100258SXin LIelse {
12609100258SXin LI    $TOTERM = 1;
12709100258SXin LI}
12809100258SXin LI
12909100258SXin LIif (defined $opt{q} && defined $opt{v}) {
13009100258SXin LI    log_fatal(LOG_ERR, '-q and -v options mutually exclusive');
13109100258SXin LI}
13209100258SXin LI
13309100258SXin LIif (defined $opt{L} && defined $opt{f}) {
13409100258SXin LI    log_fatal(LOG_ERR, '-L and -f options mutually exclusive');
13509100258SXin LI}
13609100258SXin LI
13709100258SXin LI$SIG{INT} = \&signal_catcher;
13809100258SXin LI$SIG{TERM} = \&signal_catcher;
13909100258SXin LI$SIG{QUIT} = \&signal_catcher;
14009100258SXin LI
14109100258SXin LI# Take some security precautions
14209100258SXin LIclose STDIN;
14309100258SXin LI
14409100258SXin LI# Show help
14509100258SXin LIif (defined $opt{h}) {
14609100258SXin LI    show_help();
14709100258SXin LI    exit 0;
14809100258SXin LI}
14909100258SXin LI
15009100258SXin LIif ($< != $RUN_UID) {
15109100258SXin LI    log_fatal(LOG_ERR, 'User ' . getpwuid($<) . " (UID $<) tried to run $PROG");
15209100258SXin LI}
15309100258SXin LI
15409100258SXin LIchdir $RUN_DIR || log_fatal("Failed to change dir to $RUN_DIR");
15509100258SXin LI
15609100258SXin LI# Parse ntp.conf for path to leapfile if not set by user
15709100258SXin LIif (! $LEAPFILE) {
15809100258SXin LI
15909100258SXin LI    open my $LF, '<', $NTPCONF || log_fatal(LOG_ERR, "Can't open <$NTPCONF>: $!");
16009100258SXin LI
16109100258SXin LI    while (<$LF>) {
16209100258SXin LI	chomp;
16309100258SXin LI	$LEAPFILE = $1 if /^ *leapfile\s+"(\S+)"/;
16409100258SXin LI    }
16509100258SXin LI    close $LF;
16609100258SXin LI
16709100258SXin LI    if (! $LEAPFILE) {
16809100258SXin LI	log_fatal(LOG_ERR, "No leapfile directive in $NTPCONF; leapfile location not known");
16909100258SXin LI    }
17009100258SXin LI}
17109100258SXin LI
17209100258SXin LI-s $LEAPFILE || logger(LOG_DEBUG, "Leapfile $LEAPFILE is empty");
17309100258SXin LI
17409100258SXin LI# Download new file if:
17509100258SXin LI#   1. file doesn't exist
17609100258SXin LI#   2. invoked w/ force flag (-F)
17709100258SXin LI#   3. current file isn't valid
17809100258SXin LI#   4. current file expired or expires soon
17909100258SXin LI
18009100258SXin LIif ( !-e $LEAPFILE || $FORCE || ! verifySHA($LEAPFILE) ||
18109100258SXin LI	( $EXPIRES lt ( $PREFETCH * 86400 + time() ) )) {
18209100258SXin LI
18309100258SXin LI    for (my $try = 1; $try <= $MAXTRIES; $try++) {
18409100258SXin LI	logger(LOG_DEBUG, "Attempting download from $LEAPSRC, try $try..");
18509100258SXin LI
18609100258SXin LI	($TMP_FH, $TMP_FILE) = tempfile(UNLINK => 1, SUFFIX => '.list');
18709100258SXin LI
18809100258SXin LI	if (retrieve_file($TMP_FH)) {
18909100258SXin LI
19009100258SXin LI            if ( verifySHA($TMP_FILE) ) {
19109100258SXin LI		move_file($TMP_FILE, $LEAPFILE);
19209100258SXin LI		chmod $FILE_MODE, $LEAPFILE;
19309100258SXin LI		logger(LOG_INFO, "Installed new $LEAPFILE from $LEAPSRC");
19409100258SXin LI	    }
19509100258SXin LI	    else {
19609100258SXin LI                logger(LOG_ERR, "Downloaded file $TMP_FILE rejected -- saved for diagnosis");
19709100258SXin LI		move_file($TMP_FILE, 'leap-seconds.list_corrupt');
19809100258SXin LI		exit 1;
19909100258SXin LI            }
20009100258SXin LI	    # Fall through
20109100258SXin LI            exit 0;
20209100258SXin LI	}
20309100258SXin LI
20409100258SXin LI	# Failure
20509100258SXin LI	unlink $TMP_FILE;
20609100258SXin LI	logger(LOG_INFO, "Download failed. Waiting $INTERVAL minutes before retrying...");
20709100258SXin LI        sleep $INTERVAL * 60 ;
20809100258SXin LI    }
20909100258SXin LI
21009100258SXin LI    # Failed and out of retries
21109100258SXin LI    log_fatal(LOG_ERR, "Download from $LEAPSRC failed after $MAXTRIES attempts");
21209100258SXin LI}
21309100258SXin LI
21409100258SXin LIlogger(LOG_INFO, "Not time to replace $LEAPFILE");
21509100258SXin LI
21609100258SXin LIexit 0;
21709100258SXin LI
21809100258SXin LI######## SUB ROUTINES #########
21909100258SXin LIsub move_file {
22009100258SXin LI
22109100258SXin LI    (my $src, my $dst) = @_;
22209100258SXin LI
22309100258SXin LI    if ( move($src, $dst) ) {
22409100258SXin LI	logger(LOG_DEBUG, "Moved $src to $dst");
22509100258SXin LI    }
22609100258SXin LI    else {
22709100258SXin LI	log_fatal(LOG_ERR, "Moving $src to $dst failed: $!");
22809100258SXin LI    }
22909100258SXin LI}
23009100258SXin LI
23109100258SXin LI# Removes temp file if terminating signal recv'd
23209100258SXin LIsub signal_catcher {
23309100258SXin LI    my $signame = shift;
23409100258SXin LI
23509100258SXin LI    close $TMP_FH;
23609100258SXin LI    unlink $TMP_FILE;
23709100258SXin LI    log_fatal(LOG_INFO, "Recv'd SIG${signame}. Terminating.");
23809100258SXin LI}
23909100258SXin LI
24009100258SXin LIsub log_fatal {
24109100258SXin LI    my ($p, $msg) = @_;
24209100258SXin LI    logger($p, $msg);
24309100258SXin LI    exit 1;
24409100258SXin LI}
24509100258SXin LI
24609100258SXin LIsub logger {
24709100258SXin LI    my ($p, $msg) = @_;
24809100258SXin LI
24909100258SXin LI    # Suppress LOG_DEBUG msgs unless $DEBUG set
25009100258SXin LI    return if (!$DEBUG && $p eq LOG_DEBUG);
25109100258SXin LI
25209100258SXin LI    # Suppress all but LOG_ERR msgs if $QUIET set
25309100258SXin LI    return if ($QUIET && $p ne LOG_ERR);
25409100258SXin LI
25509100258SXin LI    if ($TOTERM) {
25609100258SXin LI        if ($p eq LOG_ERR) {	# errors should go to STDERR
25709100258SXin LI	    print STDERR "$msg\n";
25809100258SXin LI	}
25909100258SXin LI	else {
26009100258SXin LI	    print STDOUT "$msg\n";
26109100258SXin LI	}
26209100258SXin LI    }
26309100258SXin LI
26409100258SXin LI    if ($SYSLOG) {
26509100258SXin LI	syslog($p, $msg)
26609100258SXin LI    }
26709100258SXin LI}
26809100258SXin LI
26909100258SXin LI#################################
27009100258SXin LI# Connect to server and retrieve file
27109100258SXin LI#
27209100258SXin LI# Since we make as many as $MAXTRIES attempts to connect to the remote
27309100258SXin LI# server to download the file, the network socket should be closed after
27409100258SXin LI# each attempt, rather than let it be reused (because it may be in some
27509100258SXin LI# unknown state).
27609100258SXin LI#
27709100258SXin LI# HTTP::Tiny doesn't export a method to explicitly close a connected
27809100258SXin LI# socket, therefore, we instantiate the lexically scoped $http object in
27909100258SXin LI# a function; when the function returns, the object goes out of scope
28009100258SXin LI# and is destroyed, closing the socket.
28109100258SXin LIsub retrieve_file {
28209100258SXin LI
28309100258SXin LI    my $fh = shift;
28409100258SXin LI    my $http;
28509100258SXin LI
28609100258SXin LI    if ($LEAPSRC =~ /^https\S+/) {
28709100258SXin LI	$http = HTTP::Tiny->new(%SSL_ATTRS);
28809100258SXin LI	(my $ok, my $why) = $http->can_ssl;
28909100258SXin LI	log_fatal(LOG_ERR, "TLS/SSL config error: $why") if ! $ok;
29009100258SXin LI    }
29109100258SXin LI    else {
29209100258SXin LI	$http = HTTP::Tiny->new();
29309100258SXin LI    }
29409100258SXin LI
29509100258SXin LI    my $reply = $http->get($LEAPSRC);
29609100258SXin LI
29709100258SXin LI    if ($reply->{success}) {
29809100258SXin LI	logger(LOG_DEBUG, "Download of $LEAPSRC succeeded");
29909100258SXin LI	print $fh $reply->{content} ||
30009100258SXin LI	    log_fatal(LOG_ERR, "Couldn't write new file contents to temp file: $!");
30109100258SXin LI	close $fh;
30209100258SXin LI	return 1;
30309100258SXin LI    }
30409100258SXin LI    else {
30509100258SXin LI	close $fh;
30609100258SXin LI	return 0;
30709100258SXin LI    }
30809100258SXin LI}
30909100258SXin LI
31009100258SXin LI########################
31109100258SXin LI# Validate a leap-seconds file checksum
31209100258SXin LI#
31309100258SXin LI# File format: (full description in file)
31409100258SXin LI# Pound sign (#) marks comments, EXCEPT:
31509100258SXin LI# 	#$ number : the NTP date of the last update
31609100258SXin LI# 	#@ number : the NTP date that the file expires
31709100258SXin LI# 	#h hex hex hex hex hex : the SHA-1 checksum of the data & dates,
31809100258SXin LI#	   excluding whitespace w/o leading zeroes
31909100258SXin LI#
32009100258SXin LI# Date (seconds since 1900) leaps : leaps is the # of seconds to add
32109100258SXin LI#  for times >= Date
32209100258SXin LI# Date lines have comments.
32309100258SXin LI#
32409100258SXin LI# Returns:
32509100258SXin LI#   0	Invalid Checksum/Expired
32609100258SXin LI#   1	File is valid
32709100258SXin LI
32809100258SXin LIsub verifySHA {
32909100258SXin LI
33009100258SXin LI    my $file = shift;
33109100258SXin LI    my $fh;
33209100258SXin LI    my $data;
33309100258SXin LI    my $FSHA;
33409100258SXin LI
33509100258SXin LI    open $fh, '<', $file || log_fatal(LOG_ERR, "Can't open $file: $!");
33609100258SXin LI
33709100258SXin LI    # Remove comments, except those that are markers for last update,
33809100258SXin LI    # expires and hash
33909100258SXin LI    while (<$fh>) {
34009100258SXin LI	if (/^#\$/) {
34109100258SXin LI	    s/^..//;
34209100258SXin LI	    $data .= $_;
34309100258SXin LI	}
34409100258SXin LI	elsif (/^#\@/) {
34509100258SXin LI	    s/^..//;
34609100258SXin LI	    $data .= $_;
34709100258SXin LI	    s/\s+//g;
34809100258SXin LI	    $EXPIRES = $_ - 2208988800;
34909100258SXin LI	}
35009100258SXin LI	elsif (/^#h\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)/) {
35109100258SXin LI	    chomp;
35209100258SXin LI	    $FSHA = sprintf("%08s%08s%08s%08s%08s", $1, $2, $3, $4, $5);
35309100258SXin LI	}
35409100258SXin LI	elsif (/^#/) {
35509100258SXin LI	    # ignore it
35609100258SXin LI	}
35709100258SXin LI	elsif (/^\d/) {
35809100258SXin LI	    s/#.*$//;
35909100258SXin LI	    $data .= $_;
36009100258SXin LI	}
36109100258SXin LI	else {
36209100258SXin LI	    chomp;
36309100258SXin LI	    print "Unexpected line: <$_>\n";
36409100258SXin LI	}
36509100258SXin LI    }
36609100258SXin LI    close $fh;
36709100258SXin LI
36809100258SXin LI    if ( $EXPIRES < time() ) {
36909100258SXin LI        logger(LOG_DEBUG, 'File expired on ' . gmtime($EXPIRES));
37009100258SXin LI        return 0;
37109100258SXin LI    }
37209100258SXin LI
37309100258SXin LI    if (! $FSHA) {
37409100258SXin LI	logger(LOG_NOTICE, "no checksum record found in file");
37509100258SXin LI	return 0;
37609100258SXin LI    }
37709100258SXin LI
37809100258SXin LI    # Remove all white space
37909100258SXin LI    $data =~ s/\s//g;
38009100258SXin LI
38109100258SXin LI    # Compute the SHA hash of the data, removing the marker and filename
38209100258SXin LI    # Computed in binary mode, which shouldn't matter since whitespace has been removed
38309100258SXin LI    my $DSHA = sha1_hex($data);
38409100258SXin LI
38509100258SXin LI    if ($FSHA eq $DSHA) {
38609100258SXin LI	logger(LOG_DEBUG, "Checksum of $file validated");
38709100258SXin LI	return 1;
38809100258SXin LI    }
38909100258SXin LI    else {
39009100258SXin LI        logger(LOG_NOTICE, "Checksum of $file is invalid EXPECTED: $FSHA COMPUTED: $DSHA");
39109100258SXin LI        return 0;
39209100258SXin LI    }
39309100258SXin LI}
39409100258SXin LI
39509100258SXin LIsub show_help {
39609100258SXin LIprint <<EOF
39709100258SXin LI
39809100258SXin LIUsage: $PROG [options]
399276da39aSCy Schubert
400276da39aSCy SchubertVerifies and if necessary, updates leap-second definition file
401276da39aSCy Schubert
402276da39aSCy SchubertAll arguments are optional:  Default (or current value) shown:
40309100258SXin LI    -C    Absolute path to CA Cert (see SSL/TLS Considerations)
40409100258SXin LI    -D    Path to a CAdir (see SSL/TLS Considerations)
405276da39aSCy Schubert    -e    Specify how long (in days) before expiration the file is to be
406276da39aSCy Schubert    	  refreshed.  Note that larger values imply more frequent refreshes.
40709100258SXin LI          $PREFETCH
408276da39aSCy Schubert    -F    Force update even if current file is OK and not close to expiring.
40909100258SXin LI    -f    Absolute path ntp.conf file (default /etc/ntp.conf)
41009100258SXin LI          $NTPCONF
41109100258SXin LI    -h    show help
412276da39aSCy Schubert    -i    Specify number of minutes between retries
413276da39aSCy Schubert          $INTERVAL
41409100258SXin LI    -L    Absolute path to leapfile on the local system
41509100258SXin LI	  (overrides value in ntp.conf)
41609100258SXin LI    -l    Specify the syslog(3) facility for logging
417276da39aSCy Schubert          $LOGFAC
41809100258SXin LI    -q    Only report errors (cannot be used with -v)
41909100258SXin LI    -r    Specify number of attempts to retrieve file
42009100258SXin LI          $MAXTRIES
42109100258SXin LI    -s    Send output to syslog(3) - implied if STDOUT has no tty or redirected
42209100258SXin LI    -t    Send output to terminal - implied if STDOUT attached to terminal
42309100258SXin LI    -u    Specify the URL of the master copy to download
42409100258SXin LI          $LEAPSRC
42509100258SXin LI    -v    Verbose - show debug messages (cannot be used with -q)
426276da39aSCy Schubert
427276da39aSCy SchubertThe following options are not (yet) implemented in the perl version:
428276da39aSCy Schubert    -4    Use only IPv4
429276da39aSCy Schubert    -6    Use only IPv6
430276da39aSCy Schubert    -c    Command to restart NTP after installing a new file
431276da39aSCy Schubert          <none> - ntpd checks file daily
432276da39aSCy Schubert    -p 4|6
433276da39aSCy Schubert          Prefer IPv4 or IPv6 (as specified) addresses, but use either
434276da39aSCy Schubert
43509100258SXin LI$PROG will validate the file currently on the local system.
436276da39aSCy Schubert
43709100258SXin LIOrdinarily, the leapfile is found using the 'leapfile' directive in
43809100258SXin LI$NTPCONF.  However, an alternate location can be specified on the
43909100258SXin LIcommand line with the -L flag.
440276da39aSCy Schubert
44109100258SXin LIIf the leapfile does not exist, is not valid, has expired, or is
44209100258SXin LIexpiring soon, a new copy will be downloaded.  If the new copy is
44309100258SXin LIvalid, it is installed.
444276da39aSCy Schubert
445276da39aSCy SchubertIf the current file is acceptable, no download or restart occurs.
446276da39aSCy Schubert
44709100258SXin LIThis can be run as a cron job.  As the file is rarely updated, and
44809100258SXin LIleap seconds are announced at least one month in advance (usually
44909100258SXin LIlonger), it need not be run more frequently than about once every
45009100258SXin LIthree weeks.
451276da39aSCy Schubert
45209100258SXin LISSL/TLS Considerations
45309100258SXin LI-----------------------
45409100258SXin LIThe perl modules can usually locate the CA certificate used to verify
45509100258SXin LIthe peer's identity.
456276da39aSCy Schubert
45709100258SXin LIOn BSDs, the default is typically the file /etc/ssl/certs.pem.  On
45809100258SXin LILinux, the location is typically a path to a CAdir - a directory of
45909100258SXin LIsymlinks named according to a hash of the certificates' subject names.
460276da39aSCy Schubert
46109100258SXin LIThe -C or -D options are available to pass in a location if no CA cert
46209100258SXin LIis found in the default location.
463276da39aSCy Schubert
46409100258SXin LIExternal Dependencies
46509100258SXin LI---------------------
46609100258SXin LIThe following perl modules are required:
46709100258SXin LIHTTP::Tiny 	- version >= 0.056
46809100258SXin LIIO::Socket::SSL - version >= 1.56
46909100258SXin LINET::SSLeay 	- version >= 1.49
470276da39aSCy Schubert
47109100258SXin LIVersion: $VERSION
472276da39aSCy Schubert
47309100258SXin LIEOF
474276da39aSCy Schubert}
475276da39aSCy Schubert
476