github.com/sagernet/sing@v0.4.0-beta.19.0.20240518125136-f67a0988a636/common/ntp/ntp.go (about)

     1  package ntp
     2  
     3  import (
     4  	"context"
     5  	"crypto/rand"
     6  	"encoding/binary"
     7  	"time"
     8  
     9  	M "github.com/sagernet/sing/common/metadata"
    10  	N "github.com/sagernet/sing/common/network"
    11  )
    12  
    13  func Exchange(ctx context.Context, dialer N.Dialer, serverAddress M.Socksaddr) (*Response, error) {
    14  	conn, err := dialer.DialContext(ctx, N.NetworkUDP, serverAddress)
    15  	if err != nil {
    16  		return nil, err
    17  	}
    18  	defer conn.Close()
    19  	conn.SetDeadline(time.Now().Add(defaultTimeout))
    20  
    21  	var request msg
    22  	request.setMode(client)
    23  	request.setVersion(defaultNtpVersion)
    24  	request.setLeap(LeapNotInSync)
    25  
    26  	bits := make([]byte, 8)
    27  	_, err = rand.Read(bits)
    28  	var xmitTime time.Time
    29  	if err == nil {
    30  		request.TransmitTime = ntpTime(binary.BigEndian.Uint64(bits))
    31  		xmitTime = time.Now()
    32  	} else {
    33  		xmitTime = time.Now()
    34  		request.TransmitTime = toNtpTime(xmitTime)
    35  	}
    36  
    37  	err = binary.Write(conn, binary.BigEndian, request)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	var response msg
    43  	err = binary.Read(conn, binary.BigEndian, &response)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	recvTime := toNtpTime(xmitTime.Add(time.Since(xmitTime)))
    49  	response.OriginTime = toNtpTime(xmitTime)
    50  	return parseTime(&response, recvTime), nil
    51  }