thanos1983 has asked for the wisdom of the Perl Monks concerning the following question:
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.
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: SNTP pack and unpack
by Anonymous Monk on Mar 07, 2015 at 00:04 UTC | |
by thanos1983 (Parson) on Mar 07, 2015 at 01:47 UTC | |
by Anonymous Monk on Mar 07, 2015 at 04:08 UTC | |
|
Re: SNTP pack and unpack
by cheako (Beadle) on Mar 07, 2015 at 00:00 UTC | |
by thanos1983 (Parson) on Mar 07, 2015 at 01:33 UTC | |
by choroba (Cardinal) on Mar 07, 2015 at 02:03 UTC | |
by thanos1983 (Parson) on Mar 07, 2015 at 17:23 UTC | |
by Anonymous Monk on Mar 07, 2015 at 11:06 UTC | |
by Anonymous Monk on Mar 07, 2015 at 00:42 UTC | |
| |
by thanos1983 (Parson) on Mar 07, 2015 at 12:13 UTC | |
by Anonymous Monk on Mar 07, 2015 at 00:07 UTC | |
| |
by cheako (Beadle) on Mar 07, 2015 at 02:09 UTC |