package protoimport ()// Message identifiers are coupled to message creation time.//// https://core.telegram.org/mtproto/description#message-identifier-msg-idconst (yieldClient = 0yieldServerResponse = 1yieldFromServer = 3messageIDModulo = 4)func ( int64, int) int64 {const = 1e9// Must approximately equal unixtime*2^32.// Important: to counter replay-attacks the lower 32 bits of msg_id // passed by the client must not be empty and must present a // fractional part of the time point when the message was created. := / := % // Ensure that fracPart % 4 == 0. &= -messageIDModulo// Adding modulo 4 yield to ensure message type. += int64()return ( << 32) | }// MessageID represents 64-bit message id.typeMessageIDint64func ( MessageID) () string {returnfmt.Sprintf("%x (%s, %s)",int64(), .Type(), .Time().Format(time.RFC3339), )}// MessageType is type of message determined by message id.//// A message is rejected over 300 seconds after it is created or// 30 seconds before it is created (this is needed to protect from replay attacks).//// The identifier of a message container must be strictly greater than those of// its nested messages.typeMessageTypebyteconst (// MessageUnknown reports that message id has unknown time and probably // should be ignored.MessageUnknownMessageType = iota// MessageFromClient is client message identifiers.MessageFromClient// MessageServerResponse is a response to a client message.MessageServerResponse// MessageFromServer is a message from the server.MessageFromServer)func ( MessageType) () string {switch {caseMessageFromClient:return"FromClient"caseMessageServerResponse:return"ServerResponse"caseMessageFromServer:return"FromServer"default:return"Unknown" }}// Time returns approximate time when MessageID were generated.func ( MessageID) () time.Time { := int64() >> 32 := int64(int32())returntime.Unix(, ).UTC()}// Type returns message type.func ( MessageID) () MessageType {switch % messageIDModulo {caseyieldClient:returnMessageFromClientcaseyieldServerResponse:returnMessageServerResponsecaseyieldFromServer:returnMessageFromServerdefault:returnMessageUnknown }}// NewMessageID returns new message id for provided time and type.func ( time.Time, MessageType) MessageID {returnNewMessageIDNano(.UnixNano(), )}// NewMessageIDNano returns new message id for provided current unix// nanoseconds and type.func ( int64, MessageType) MessageID {varintswitch {caseMessageFromClient: = yieldClientcaseMessageFromServer: = yieldFromServercaseMessageServerResponse: = yieldServerResponsedefault: = yieldClient }returnMessageID(newMessageID(, ))}// MessageIDGen is message id generator that provides collision prevention.//// The main reason of such structure is that now() can return same time during// multiple calls and that leads to duplicate message id.typeMessageIDGenstruct {muxsync.Mutexnanoint64nowfunc() time.Time}// New generates new message id for provided type, protecting from collisions// that are caused by low system time resolution.func ( *MessageIDGen) ( MessageType) int64 { .mux.Lock()defer .mux.Unlock()// Minimum resolution is required because id is only approximately // equal to unix nano time, some part is replaced by message type.const = 10 := .now().UnixNano()if > .nano { .nano = } else { .nano += }returnint64(NewMessageIDNano(.nano, ))}// NewMessageIDGen creates new message id generator.//// Current time will be provided by now() function.//// This generator compensates time resolution problem removing// probability of id collision.//// Such problem can be observed for relatively high RPS, sequential calls to// time.Now() will return same time which leads to equal ids.func ( func() time.Time) *MessageIDGen {return &MessageIDGen{now: , }}// MessageIDBuf stores last N message ids and is used in replay attack mitigation.typeMessageIDBufstruct {muxsync.Mutexbuf []int64}// NewMessageIDBuf initializes new message id buffer for last N stored values.func ( int) *MessageIDBuf {return &MessageIDBuf{buf: make([]int64, ), }}// Consume returns false if message should be discarded.func ( *MessageIDBuf) ( int64) bool {// In addition, the identifiers (msg_id) of the last N messages received // from the other side must be stored, and if a message comes in with an // msg_id lower than all or equal to any of the stored values, that message // is to be ignored. Otherwise, the new message msg_id is added to the set, // and, if the number of stored msg_id values is greater than N, the oldest // (i. e. the lowest) is discarded. // // https://core.telegram.org/mtproto/security_guidelines#checking-msg-id .mux.Lock()defer .mux.Unlock()var (intint64 )for , := range .buf {if == {// Equal to stored value.returnfalse }// Searching for minimum value.if < { = = } }if < {// Lower than all stored values.returnfalse }// Message is accepted. Replacing lowest message id with new id. .buf[] = returntrue}
The pages are generated with Goldsv0.6.7. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds.