#!/usr/local/cpanel/3rdparty/bin/perl
# cpanel - bin/rdate                               Copyright 2020 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited

package rdate;

use strict;
use warnings;
use Socket;
use Getopt::Std;
use Cpanel::Binaries ();

# Time protocol client , https://tools.ietf.org/html/rfc868

# mimic binary rdate behavior
our $rdate_mode = 0;

if ( !caller() ) {
    $rdate_mode = 1;
    main();
}

=head1 SYNOPSIS

    require 'bin/rdate';
    my $epoch_time = rdate::main('time-e-b.nist.gov');
    print localtime($epoch_time) . "\n";

=head1 main

Connects to a TIME / RFC 868 server and returns the epoch time

=head2 Input

=over 3

=item C<SCALAR> server

    The server to connect to.
    Defaults to rdate.cpanel.net

=back

=head2 Output

=over 3

This function returns the epoch provided by the server, or dies.

=back

=cut

sub main {
    my ($server) = @_;
    my $opt_string = 'psh';
    my %opt;
    getopts( "$opt_string", \%opt ) or help();
    help() if $opt{'h'};

    my $rdate_server = $ARGV[0] || $server || 'rdate.cpanel.net';

    my $socket;
    socket( $socket, PF_INET, SOCK_STREAM, ( getprotobyname('tcp') )[2] ) or die "Can't open socket $!\n";
    binmode($socket);
    connect( $socket, pack_sockaddr_in( 37, inet_aton($rdate_server) ) ) or die "Can't connect to $rdate_server:37 $! \n";
    my $time_data;
    recv( $socket, $time_data, 32, 0 );
    close($socket);

    # Make sure we actually got some data back
    if ( !$time_data ) {
        print "Server returned empty data, please try a different server.\n";
        return;
    }

    # Take the binary data we get from the time server, convert to hex, sprintf to decimal then
    # subtract the number of seconds from Jan 1st 1900 to 1970 ( 2208988800 ) to get our epoch, es defined in RFC868

    my $epoch = unpack( "N*", $time_data ) - 2208988800;

    # mimic binary rdate behavior
    if ( $opt{'p'} || ( !$opt{'s'} ) ) {
        print "rdate: [$rdate_server]\t" . scalar localtime($epoch) . "\n" if $rdate_mode == 1;
    }

    if ( $opt{'s'} ) {
        set_system_time($epoch);
    }
    return $epoch;
}

sub set_system_time {
    my ($epoch) = @_;

    # We only want to allow for a 10 digit number, anything else is nonsensical here
    if ( $epoch !~ m/^\d{10}$/ ) {
        die "Invalid epoch given to set_system_time.\n";
    }

    # Find date binary on CentOS 6 and 7+
    my $date_bin_path = Cpanel::Binaries::path('date');
    my $output        = `$date_bin_path -s \@$epoch`;
    chomp $output;

    # mimic binary rdate behavior
    print "Set system time to “$output”\n" if !$rdate_mode;
    return;
}

# Emulate `rdate -h`
sub help {
    print "Usage: rdate [-s] [-p] <host> ...\n";
    exit 0;    ## no critic qw(Cpanel::NoExitsFromSubroutines) # this is exactly what should happen here
}

1;
