Pagsulat sa usa ka yano nga kliyente sa NTP

Hello mga habrausers. Karon gusto nakong hisgutan kung giunsa pagsulat ang imong kaugalingon nga yano nga kliyente sa NTP. Sa panguna, ang panag-istoryahanay mobalik sa istruktura sa pakete ug kung giunsa ang pagproseso sa tubag gikan sa NTP server. Ang code isulat sa python, tungod kay, sa akong opinyon, wala'y mas maayo nga pinulongan alang sa maong mga butang. Ang mga connoisseurs magtagad sa pagkaparehas sa code sa ntplib code - Ako "nadasig" niini.

Busa unsa man ang NTP? Ang NTP usa ka protocol alang sa pagpakigsulti sa mga server sa oras. Kini nga protocol gigamit sa daghang modernong mga makina. Pananglitan, ang serbisyo sa w32tm sa windows.

Adunay 5 nga mga bersyon sa NTP protocol sa kinatibuk-an. Ang una, ang bersyon 0 (1985, RFC958) sa pagkakaron giisip nga wala na magamit. Ang mga bag-o gigamit karon, 1st (1988, RFC1059), 2nd (1989, RFC1119), 3rd (1992, RFC1305) ug 4th (1996, RFC2030). Ang mga bersyon 1-4 nahiuyon sa usag usa, lahi ra sila sa mga algorithm sa mga server.

Format sa Packet

Pagsulat sa usa ka yano nga kliyente sa NTP

Indikasyon sa paglukso (indikasyon sa pagtul-id) usa ka numero nga nagpaila sa paglukso sa ikaduhang pasidaan. Kahulugan:

  • 0 - walay pagtul-id
  • 1 - ang katapusang minuto sa adlaw adunay 61 segundos
  • 2 - ang katapusang minuto sa adlaw adunay 59 segundos
  • 3 - pagkapakyas sa server (oras sa pag-sync)

Numero sa bersyon (numero sa bersyon) – Numero sa bersyon sa NTP protocol (1-4).

mode (mode) β€” mode sa operasyon sa nagpadala sa packet. Ang bili gikan sa 0 ngadto sa 7, labing komon:

  • 3 - kliyente
  • 4 - server
  • 5 - broadcast mode

hut-ong (layering level) - ang gidaghanon sa intermediate layers tali sa server ug sa reference clock (1 - ang server nagkuha og data direkta gikan sa reference clock, 2 - ang server nagkuha og data gikan sa server nga adunay level 1, ug uban pa).
Pool usa ka pinirmahan nga integer nga nagrepresentar sa labing taas nga agwat tali sa sunud-sunod nga mga mensahe. Ang kliyente sa NTP nagtino dinhi sa agwat diin kini nagpaabut sa pag-poll sa server, ug ang NTP server nagtino sa agwat diin kini gilauman nga ma-poll. Ang bili katumbas sa binary logarithm sa mga segundo.
Katukma (precision) maoy usa ka pinirmahan nga integer nga nagrepresentar sa katukma sa sistema nga orasan. Ang bili katumbas sa binary logarithm sa mga segundo.
pagkalangan sa gamut (server latency) mao ang oras nga gikinahanglan aron ang orasan makaabot sa NTP server, isip usa ka fixed-point nga gidaghanon sa mga segundo.
pagkatibulaag sa ugat (Server scatter) - Ang scatter sa NTP server clock isip fixed-point nga gidaghanon sa mga segundo.
Ref id (source id) – tan-awa ang id. Kung ang server adunay stratum 1, nan ang ref id mao ang ngalan sa atomic clock (4 ASCII nga mga karakter). Kung ang server naggamit ug lain nga server, nan ang ref id naglangkob sa adres niini nga server.
Ang kataposang 4 ka field nagrepresentar sa oras - 32 bits - ang integer nga bahin, 32 bits - ang fractional nga bahin.
Pakisayran - ang pinakabag-o nga orasan sa server.
Gigikanan - oras kung kanus-a gipadala ang pakete (gipuno sa server - labi pa sa ubos).
Dawata – panahon sa dihang ang packet nadawat sa server.
padala – panahon nga ang packet gipadala gikan sa server ngadto sa kliyente (gipuno sa kliyente, labaw pa niana sa ubos).

Ang kataposang duha ka field dili hisgotan.

Atong isulat ang atong package:

Kodigo sa pakete

class NTPPacket:
    _FORMAT = "!B B b b 11I"

    def __init__(self, version_number=2, mode=3, transmit=0):
        # Necessary of enter leap second (2 bits)
        self.leap_indicator = 0
        # Version of protocol (3 bits)
        self.version_number = version_number
        # Mode of sender (3 bits)
        self.mode = mode
        # The level of "layering" reading time (1 byte)
        self.stratum = 0
        # Interval between requests (1 byte)
        self.pool = 0
        # Precision (log2) (1 byte)
        self.precision = 0
        # Interval for the clock reach NTP server (4 bytes)
        self.root_delay = 0
        # Scatter the clock NTP-server (4 bytes)
        self.root_dispersion = 0
        # Indicator of clocks (4 bytes)
        self.ref_id = 0
        # Last update time on server (8 bytes)
        self.reference = 0
        # Time of sending packet from local machine (8 bytes)
        self.originate = 0
        # Time of receipt on server (8 bytes)
        self.receive = 0
        # Time of sending answer from server (8 bytes)
        self.transmit = transmit

Aron magpadala (ug makadawat) usa ka pakete sa server, kinahanglan naton nga mahimo kini nga usa ka han-ay sa mga byte.
Alang niini (ug balihon) nga operasyon, magsulat kami og duha ka mga function - pack() ug unpack():

pack function

def pack(self):
        return struct.pack(NTPPacket._FORMAT,
                (self.leap_indicator << 6) + 
                    (self.version_number << 3) + self.mode,
                self.stratum,
                self.pool,
                self.precision,
                int(self.root_delay) + get_fraction(self.root_delay, 16),
                int(self.root_dispersion) + 
                    get_fraction(self.root_dispersion, 16),
                self.ref_id,
                int(self.reference),
                get_fraction(self.reference, 32),
                int(self.originate),
                get_fraction(self.originate, 32),
                int(self.receive),
                get_fraction(self.receive, 32),
                int(self.transmit),
                get_fraction(self.transmit, 32))

unpack function

def unpack(self, data: bytes):
        unpacked_data = struct.unpack(NTPPacket._FORMAT, data)

        self.leap_indicator = unpacked_data[0] >> 6  # 2 bits
        self.version_number = unpacked_data[0] >> 3 & 0b111  # 3 bits
        self.mode = unpacked_data[0] & 0b111  # 3 bits

        self.stratum = unpacked_data[1]  # 1 byte
        self.pool = unpacked_data[2]  # 1 byte
        self.precision = unpacked_data[3]  # 1 byte

        # 2 bytes | 2 bytes
        self.root_delay = (unpacked_data[4] >> 16) +
            (unpacked_data[4] & 0xFFFF) / 2 ** 16
         # 2 bytes | 2 bytes
        self.root_dispersion = (unpacked_data[5] >> 16) +
            (unpacked_data[5] & 0xFFFF) / 2 ** 16 

        # 4 bytes
        self.ref_id = str((unpacked_data[6] >> 24) & 0xFF) + " " + 
                      str((unpacked_data[6] >> 16) & 0xFF) + " " +  
                      str((unpacked_data[6] >> 8) & 0xFF) + " " +  
                      str(unpacked_data[6] & 0xFF)

        self.reference = unpacked_data[7] + unpacked_data[8] / 2 ** 32  # 8 bytes
        self.originate = unpacked_data[9] + unpacked_data[10] / 2 ** 32  # 8 bytes
        self.receive = unpacked_data[11] + unpacked_data[12] / 2 ** 32  # 8 bytes
        self.transmit = unpacked_data[13] + unpacked_data[14] / 2 ** 32  # 8 bytes

        return self

Alang sa tapolan nga mga tawo, ingon usa ka aplikasyon - code nga nagpabalik sa pakete nga usa ka matahum nga hilo

def to_display(self):
        return "Leap indicator: {0.leap_indicator}n" 
                "Version number: {0.version_number}n" 
                "Mode: {0.mode}n" 
                "Stratum: {0.stratum}n" 
                "Pool: {0.pool}n" 
                "Precision: {0.precision}n" 
                "Root delay: {0.root_delay}n" 
                "Root dispersion: {0.root_dispersion}n" 
                "Ref id: {0.ref_id}n" 
                "Reference: {0.reference}n" 
                "Originate: {0.originate}n" 
                "Receive: {0.receive}n" 
                "Transmit: {0.transmit}"
                .format(self)

Pagpadala usa ka pakete sa server

Ipadala ang usa ka pakete nga puno sa mga natad sa server Version, mode ΠΈ padala. ang padala kinahanglan nimong itakda ang kasamtangan nga oras sa lokal nga makina (gidaghanon sa mga segundo sukad Enero 1, 1900), bersyon - bisan unsang 1-4, mode - 3 (mode sa kliyente).

Ang server, nga nakadawat sa hangyo, nagpuno sa tanan nga mga uma sa NTP packet, nagkopya sa uma Gigikanan bili gikan sa padala, nga miabut sa hangyo. Usa kini ka misteryo alang kanako ngano nga ang kliyente dili dayon makapuno sa kantidad sa iyang oras sa uma Gigikanan. Ingon usa ka sangputanan, kung ang pakete mobalik, ang kliyente adunay 4 nga mga kantidad sa oras - ang oras nga gipadala ang hangyo (Gigikanan), ang oras nga nadawat sa server ang hangyo (Dawata), ang oras nga gipadala sa server ang tubag (padala) ug ang oras sa pagkadawat sa tubag sa kliyente - Moabot (wala sa package). Uban niini nga mga kantidad mahimo naton nga itakda ang husto nga oras.

Pagpadala ug pagdawat sa code sa package

# Time difference between 1970 and 1900, seconds
FORMAT_DIFF = (datetime.date(1970, 1, 1) - datetime.date(1900, 1, 1)).days * 24 * 3600
# Waiting time for recv (seconds)
WAITING_TIME = 5
server = "pool.ntp.org"
port = 123
    
packet = NTPPacket(version_number=2, mode=3, transmit=time.time() + FORMAT_DIFF)
answer = NTPPacket()
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
    s.settimeout(WAITING_TIME)
    s.sendto(packet.pack(), (server, port))
    data = s.recv(48)
    arrive_time = time.time() + FORMAT_DIFF
    answer.unpack(data)

Pagproseso sa datos gikan sa server

Ang pagproseso sa mga datos gikan sa server susama sa mga aksyon sa Ingles nga ginoo gikan sa daan nga problema ni Raymond M. Smallian (1978): "Ang usa ka tawo walay relo sa pulso, apan adunay tukma nga orasan sa dingding sa balay, nga iyang usahay makalimot sa hangin. Usa ka adlaw, nakalimot sa pagsugod pag-usab sa orasan, miadto siya sa pagbisita sa iyang higala, nagpalabay sa gabii uban kaniya, ug sa iyang pagpauli, nakahimo siya sa pag-set sa orasan sa husto. Giunsa niya paghimo kini kung ang oras sa pagbiyahe wala mahibal-an nga daan? Ang tubag mao: β€œPaggawas sa balay, ang usa ka tawo magpalong sa orasan ug mahinumdom sa posisyon sa mga kamot. Pag-abot sa usa ka higala ug gibiyaan ang mga bisita, iyang namatikdan ang oras sa iyang pag-abot ug pagbiya. Kini nagtugot kaniya nga mahibal-an kung unsa ka dugay siya wala. Pagbalik sa balay ug pagtan-aw sa orasan, ang usa ka tawo nagtino sa gidugayon sa iyang pagkawala. Kung gikuhaan kini nga oras sa oras nga iyang gigugol sa pagbisita, nahibal-an sa tawo ang oras nga gigugol sa dalan didto ug pabalik. Pinaagi sa pagdugang sa katunga sa oras nga gigugol sa dalan hangtod sa oras sa pagbiya sa mga bisita, nakakuha siya higayon nga mahibal-an ang oras sa pag-abot sa balay ug ipahiangay ang mga kamot sa iyang orasan.

Pangitaa ang oras nga ang server nagtrabaho sa hangyo:

  1. Pagpangita sa oras sa pagbiyahe sa pakete gikan sa kliyente hangtod sa server: ((Pag-abot - Gigikanan) - (Pagpadala - Pagdawat)) / 2
  2. Pangitaa ang kalainan tali sa oras sa kliyente ug server:
    Pagdawat - Gigikanan - ((Pag-abot - Gigikanan) - (Pagpadala - Pagdawat)) / 2 =
    2 * Pagdawat - 2 * Gigikanan - Pag-abot + Gigikanan + Pagpadala - Pagdawat =
    Dawata - Gigikanan - Pag-abot + Pagpadala

Among idugang ang nadawat nga bili sa lokal nga panahon ug malingaw sa kinabuhi.

Resulta nga Output

time_different = answer.get_time_different(arrive_time)
result = "Time difference: {}nServer time: {}n{}".format(
    time_different,
    datetime.datetime.fromtimestamp(time.time() + time_different).strftime("%c"),
    answer.to_display())
print(result)

Nga mapuslanon link.

Source: www.habr.com

Idugang sa usa ka comment