Dear Monks,

Several days now, I have been trying to find the correct TEMPLATE combinations on pack and unpack. I have tried any possible combination that I can understand through reading the documentation and I can not figure it out how to print the correct output.

The code seems to be working fine, or at least some printouts are coming correctly the minor details is that are messing up completely my result.

Can someone assist me who is more experienced can help me with my problem?

I was reading also the Net::NTP source code which is which the result is what I want to have, I mean the number and precision not the output.

Update: polishing code removing unnecessary parts. Also including sample of output.

Update 2: modifying code based on elaborating input from cheako. Also including sample of output.

How to execute the script: perl sample.pl 0.se.pool.ntp.org:123

Sample of code:

#!/usr/bin/perl use strict; use warnings; #use diagnostics; use Data::Dumper; use IO::Socket::INET; use POSIX qw( CLOCKS_PER_SEC ); use Time::HiRes qw( gettimeofday CLOCK_REALTIME clock_getres ); use constant TRUE => scalar 1; use constant ARGUMENTS => scalar 1; use constant MAXBYTES => scalar 512; use constant MAX_PORT => scalar 65536; use constant MIN_PORT => scalar 1; my $dot = "."; my $rcvSntpPacket; if ( @ARGV > ARGUMENTS ) { print "\nPlease no more than ".ARGUMENTS." argument input!\n"; print "\nCorrect Syntax: perl $0 IP:PORT (e.g. 127.0.0.1:12345)\n" +; exit(0); } elsif ( @ARGV < ARGUMENTS ) { print "\nPlease no less than ".ARGUMENTS." argument input!\n"; print "\nCorrect Syntax: perl $0 IP:PORT (e.g. 127.0.0.1:12345)\n" +; exit(0); } else { my $info = $ARGV[0]; # User input IP:PORT; my $string = index($info, ':'); if ($string == '-1') { die "Please include ':' in between the IP and Port - ".$info."\n"; } my @input = split( ':' , $info ); die "\nPlease use PORT number between ".MIN_PORT." - ".MAX_PORT."\ +n\n" if ( ( $input[1] > MAX_PORT ) || ( $input[1] < MIN_PORT ) ); my $client_socket = new IO::Socket::INET ( PeerHost => $input[0], Type => SOCK_DGRAM, PeerPort => $input[1], # Default NTP port 123, due to permission d +enied switch to client set Proto => 'udp' ) or die "ERROR in Socket Creation: $@\n"; my $Peer_Port = $client_socket->peerport(); my $client_li_vm_mode = '00100011'; # 8 bit my $client_stratum = '0'; # 8 bit my $client_poll = '0'; # 8 bit my $client_precision = '0'; # 8 bit my $client_root_delay = '0'; # 32 bit my $client_dispersion = '0'; # 32 bit my $client_reference_identifier = '0'; # 32 bit my $client_reference_timestamp_sec = '0'; # 32 bit my $client_reference_timestamp_microsec = '0'; # 32 bit my $client_receive_timestamp_sec = '0'; # 32 bit my $client_receive_timestamp_microsec = '0'; # 32 bit my ( $client_transmit_sec , $client_transmit_microsec ) = gettimeo +fday(); # 64 bit my $client_originate_timestamp_sec = $client_transmit_sec; my $client_originate_timestamp_microsec = $client_transmit_microse +c; my @arraySendSntpPacket = ($client_li_vm_mode , $client_stratum , +$client_poll , $client_precision , $client_root_delay , $client_dispe +rsion , $client_reference_identifier , $client_reference_timestamp_se +c , $client_reference_timestamp_microsec , $client_originate_timestam +p_sec , $client_originate_timestamp_microsec , $client_receive_timest +amp_sec , $client_receive_timestamp_microsec , $client_transmit_sec , + $client_transmit_microsec ); warn Dumper \@arraySendSntpPacket; my $sendSntpPacket = pack "B8 C3 N11", @arraySendSntpPacket; $client_socket->send( $sendSntpPacket ) or die "Client error while send: $!\n"; $client_socket->recv( $rcvSntpPacket , MAXBYTES ) or die "Client error while received: $!\n"; my ( $client_rcv_sec , $client_rcv_microsec ) = gettimeofday(); $client_socket->close(); # Close socket() my @arrayRcvSntpPacket = unpack("B8 C3 N11" , $rcvSntpPacket); my ( $server_li_vm_mode , $server_stratum , $server_poll_interval +, $server_precision , $server_root_delay , $server_dispersion , $serv +er_reference_identifier , $server_reference_timestamp_sec , $server_r +eference_timestamp_microsec , $server_originate_timestamp_sec , $serv +er_originate_timestamp_microsec , $server_rcv_timestamp_sec , $server +_rcv_timestamp_microsec , $server_transmit_timestamp_sec , $server_tr +ansmit_timestamp_microsec ) = @arrayRcvSntpPacket; warn Dumper \@arrayRcvSntpPacket; } # End of else ARGV provided

Sample of output.

$VAR1 = [ '00100011', '0', '0', '0', '0', '0', '0', '0', '0', 1425730335, 839953, '0', '0', 1425730335, 839953 ]; $VAR1 = [ '00100100', 1, 110, 0, 0, 4294942144, 2214694912, 1425730335, 2080374784, 1425730335, 268435456, 1425730335, 0, 0, 0 ];

Expected output:

$VAR1 = [ '00100011', '0', '0', '0', '0', '0', '0', '0', '0', 1425730335, 839953, '0', '0', 1425730335, 839953 ]; $VAR1 = [ '00100100', 1, 110, 0, 0, 4294942144, 2214694912, 1425730335, 2080374784, # instead 839953, 1425730335, 268435456, # instead 839953, 1425730335, 0, 0, 0 ];

Final Update with solution: In case of future reference. The solution to my question is the following.

#!/usr/bin/perl use strict; use warnings; use IO::Socket::INET; use Time::HiRes qw( gettimeofday ); use constant TRUE => scalar 1; use constant ARGUMENTS => scalar 1; use constant MAXBYTES => scalar 512; use constant MAX_PORT => scalar 65536; use constant MIN_PORT => scalar 1; use constant UNIX_EPOCH => 2208988800; my $unpack_ip = sub { my $ip; my $stratum = shift; my $tmp_ip = shift; if($stratum < 2){ $ip = unpack("A4", pack("H8", $tmp_ip) ); }else{ $ip = sprintf("%d.%d.%d.%d", unpack("C4", pack("H8", $tmp_ip) ) ); } return $ip; }; my $frac2bin = sub { my $bin = ''; my $frac = shift; while ( length($bin) < 32 ) { $bin = $bin . int( $frac * 2 ); $frac = ( $frac * 2 ) - ( int( $frac * 2 ) ); } return $bin; }; my $bin2frac = sub { my @bin = split '', shift; my $frac = 0; while (@bin) { $frac = ( $frac + pop @bin ) / 2; } return $frac; }; my $dot = "."; my $rcvSntpPacket; my $serverPrecision; my $info = $ARGV[0]; # User input IP:PORT; my $string = index($info, ':'); if ($string == '-1') { die "Please include ':' in between the IP and Port - ".$info."\n"; } my @input = split( ':' , $info ); my $client_socket = new IO::Socket::INET ( PeerHost => $input[0], Type => SOCK_DGRAM, PeerPort => $input[1], Proto => 'udp' ) or die "ERROR in Socket Creation: $@\n"; my $Peer_Port = $client_socket->peerport(); my $client_li_vn_mode = '00100011'; # 8 bit my $client_stratum = '0'; # 8 bit my $client_poll = '0'; # 8 bit my $client_precision = '0'; # 8 bit my $client_root_delay = '0'; # 32 bit my $client_dispersion = '0'; # 32 bit my $client_reference_identifier = '0'; # 32 bit my $client_reference_timestamp_sec = '0'; # 32 bit my $client_reference_timestamp_microsec = '0'; # 32 bit my $client_originate_timestamp_sec = 0; # 32 bit my $client_originate_timestamp_microsec = 0; # 32 bit my $client_receive_timestamp_sec = '0'; # 32 bit my $client_receive_timestamp_microsec = '0'; # 32 bit my ($client_transmit_sec , $client_transmit_microsec) = gettimeofday() +; my $client_transmit_timestamp = $client_transmit_sec . $dot . $client_ +transmit_microsec; my @arraySendSntpPacket = ($client_li_vn_mode , $client_stratum , $cli +ent_poll , $client_precision , $client_root_delay , $client_dispersio +n , $client_reference_identifier , $client_reference_timestamp_sec , +$client_reference_timestamp_microsec , $client_originate_timestamp_se +c , $client_originate_timestamp_microsec , $client_receive_timestamp_ +sec , $client_receive_timestamp_microsec , $client_transmit_sec , $cl +ient_transmit_microsec ); my $sendSntpPacket = pack "B8 C3 N10 B32", @arraySendSntpPacket; $client_socket->send( $sendSntpPacket ) or die "Client error while send: $!\n"; $client_socket->recv( $rcvSntpPacket , MAXBYTES ) or die "Client error while received: $!\n"; my ($client_rcv_sec , $client_rcv_microsec) = gettimeofday(); my $client_rcv_timestamp = $client_rcv_sec . $dot . $client_rcv_micros +ec; $client_socket->close(); # Close socket() my @arrayRcvSntpPacket = unpack("a C3 n B16 n B16 H8 N B32 N B32 + N B32 N B32" , $rcvSntpPacket); my ( $server_li_vm_mode , $server_stratum , $server_poll_interval , $s +erver_precision , $server_root_delay_sec , $server_root_delay_microse +c , $server_dispersion_sec , $server_dispersion_microsec , $server_re +ference_identifier , $server_reference_timestamp_sec , $server_refere +nce_timestamp_microsec , $server_originate_timestamp_sec , $server_or +iginate_timestamp_microsec , $server_rcv_timestamp_sec , $server_rcv_ +timestamp_microsec , $server_transmit_timestamp_sec , $server_transmi +t_timestamp_microsec ) = @arrayRcvSntpPacket; my @serverLiVnMode = ((unpack( "C", $server_li_vm_mode & "\xC0" ) >> 6 +), (unpack( "C", $server_li_vm_mode & "\x38" ) >> 3), (unpack( "C", $server_li_vm_mode & "\x07" ))); my $serverPoll = (sprintf("%0.1d", $server_poll_interval)); if ($server_precision < 127) { $server_precision = 0; } else { $server_precision = $server_precision - 255; } my $serverRootDelay = ($bin2frac -> ($server_root_delay_microsec)); my $serverDispersionSeconds = sprintf("%0.10f", $server_dispersion_sec +); $serverDispersionSeconds =~ s/\..*$//; my $serverDispersionFinalSeconds = $bin2frac->($serverDispersionSecond +s); my $serverDispersionMicroSeconds = sprintf("%0.10f", $server_dispersio +n_microsec); $serverDispersionMicroSeconds =~ s/\..*$//; my $serverDispersionFinalMicroSeconds = $bin2frac->($serverDispersionM +icroSeconds); my $serverReferenceIdentifier = $unpack_ip->($server_stratum,$server_r +eference_identifier); (($server_reference_timestamp_sec += $bin2frac->($server_reference_tim +estamp_microsec)) -= UNIX_EPOCH); ($server_originate_timestamp_sec += $bin2frac->($server_originate_time +stamp_microsec)); (($server_rcv_timestamp_sec += $bin2frac->($server_rcv_timestamp_micro +sec)) -= UNIX_EPOCH); (($server_transmit_timestamp_sec += $bin2frac->($server_transmit_times +tamp_microsec)) -= UNIX_EPOCH); # RFC2030 reference https://tools.ietf.org/html/rfc4330 my $d = ( ( $client_rcv_timestamp - $client_transmit_timestamp ) - ( +$server_rcv_timestamp_sec - $server_transmit_timestamp_sec ) ); my $t = ( ( ( $server_rcv_timestamp_sec - $client_transmit_timestamp +) + ( $server_transmit_timestamp_sec - $client_rcv_timestamp ) ) / 2 +); # Clear screen for viewing the output system $^O eq 'MSWin32' ? 'cls' : 'clear'; print "Round Trip delay: ".$d."\n"; print "Clock offset: ".$t."\n";

Sample of output:

Round Trip delay: 0.0255537033081055 Clock offset: 0.000513792037963867

Analysis of expected output:

Based on RFC 4330 we can find how the sequence of sending and receiving bytes format should be. I think I have made a major mistake somewhere where I am receiving the message. I am using unpack("B8 C3 N11" , $rcvSntpPacket); where N I think is ok for the timestamp sec but it modifies the timestamp microsec. Both are 32 bit but upon receiving it is a mess.

Thank you in advance for your time and effort.

Seeking for Perl wisdom...on the process of learning...not there...yet!

In reply to SNTP pack and unpack by thanos1983

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.