Kritik vum Protokoll an organisatoresch Approche vum Telegram. Deel 1, technesch: Erfahrung vun Schreiwen engem Client vun Null - TL, MT

Viru kuerzem sinn Posts iwwer wéi gutt Telegram ass, wéi brillant an erlieft d'Durov Bridder am Bau vun Netzwierksystemer, etc. Zur selwechter Zäit hu ganz wéineg Leit sech wierklech an den techneschen Apparat ënnerholl - héchstens benotze se eng zimlech einfach (a ganz anescht wéi MTProto) Bot API baséiert op JSON, an akzeptéieren normalerweis just op Glawen all Luef a PR déi ronderëm de Messenger dréinen. Viru bal annerhallwem Joer huet mäi Kolleg vun der Eshelon ONG Vasily (leider, säi Kont op Habré gouf zesumme mam Entworf geläscht) ugefaang säin eegenen Telegram Client vun Null an Perl ze schreiwen, a spéider huet den Auteur vun dëse Linnen ugeschloss. Firwat Perl, froen e puer direkt? Well esou Projeten existéieren schonn an anere Sproochen.Et ass eigentlech net de Punkt, et kéint eng aner Sprooch ginn, wou et keng fäerdeg Bibliothéik, an deementspriechend muss den Auteur de ganze Wee goen vun Ufank un. Ausserdeem ass Kryptografie eng Saach vu Vertrauen, awer z'iwwerpréiwen. Mat engem Produkt dat op Sécherheet zielt, kënnt Dir net einfach op eng fäerdeg Bibliothéik vum Hiersteller vertrauen an et blann vertrauen (awer dëst ass en Thema fir den zweeten Deel). Am Moment funktionnéiert d'Bibliothéik ganz gutt um "Duerchschnëtt" Niveau (erlaabt Iech all API Ufroen ze maachen).

Wéi och ëmmer, et gëtt net vill Kryptografie oder Mathematik an dëser Serie vu Posts. Mä et wäert vill aner technesch Detailer an architektonesch Crutches ginn (och nëtzlech fir déi, déi net vun Null schreiwen wäert, mä wäert d'Bibliothéik an all Sprooch benotzen). Also, d'Haaptziel war ze probéieren de Client vun Null ëmzesetzen laut offiziellen Dokumentatioun. Dat ass, loosst eis unhuelen datt de Quellcode vun den offiziellen Clienten zou ass (erëm, am zweeten Deel wäerte mir méi detailléiert d'Thema vun der Tatsaach ofdecken datt dëst richteg ass et geschitt sou), awer, wéi an den alen Deeg, zum Beispill, gëtt et e Standard wéi RFC - ass et méiglech e Client no der Spezifizéierung eleng ze schreiwen, "ouni ze kucken" de Quellcode, sief et offiziell (Telegram Desktop, mobil), oder inoffiziellen Telethon?

Beschreiwung:

Dokumentatioun ... et existéiert, richteg? Ass et wouer?..

Fragmenter vun Notizen fir dësen Artikel hunn am leschte Summer ugefaang ze sammelen. All dës Kéier op der offizieller Websäit https://core.telegram.org D'Dokumentatioun war wéi Layer 23, d.h. 2014 iergendwou festgehalen (erënnert Iech drun, et waren deemools nach keng Kanäl?). Natierlech, an der Theorie, dëst sollt et erlaabt hunn, e Client mat Funktionalitéit zu där Zäit am Joer 2014 ëmzesetzen. Awer och an dësem Zoustand war d'Dokumentatioun éischtens onkomplett, an zweetens op Plazen wou se sech selwer widdersprécht. Virun engem Mount, am September 2019, war et zoufälleg Et gouf entdeckt datt et e groussen Update vun der Dokumentatioun um Site war, fir déi zimlech rezent Layer 105, mat enger Notiz datt elo alles erëm muss gelies ginn. Tatsächlech goufen vill Artikelen iwwerschafft, awer vill sinn onverännert bliwwen. Dofir, wann Dir d'Kritik hei drënner iwwer d'Dokumentatioun liest, sollt Dir am Kapp behalen datt e puer vun dëse Saachen net méi relevant sinn, awer e puer sinn nach ëmmer ganz. No all, 5 Joer an der moderner Welt ass net nëmmen eng laang Zäit, mä ganz e ganze Koup. Zënter deenen Zäiten (besonnesch wann Dir déi verworf an erëmbelieft Geochat-Siten zënterhier net berücksichtegt), ass d'Zuel vun API-Methoden am Schema vun honnert op méi wéi zweehonnert a fofzeg gewuess!

Wou ufänken als jonken Auteur?

Et ass egal ob Dir vun Null schreift oder benotzt, zum Beispill, fäerdege Bibliothéiken wéi Telethon fir Python oder Madeline fir PHP, op alle Fall, Dir wäert éischt brauchen aschreiwen Är Demande - kréien Parameteren api_id и api_hash (déi, déi mat der VKontakte API geschafft hunn direkt verstoen) duerch déi de Server d'Applikatioun identifizéieren. Dëst musst maachen et aus juristesche Grënn, awer mir wäerte méi schwätzen iwwer firwat d'Bibliothéikautoren et net am zweeten Deel publizéieren kënnen. Dir kënnt mat den Testwäerter zefridden sinn, obwuel se ganz limitéiert sinn - de Fakt ass datt Dir Iech elo registréiere kënnt nëmmen een App, also presséiert net Kapp an et.

Elo, aus enger technescher Siicht, sollte mir drun interesséiert sinn datt mir no der Aschreiwung Notifikatioune vum Telegram iwwer Aktualiséierunge vun der Dokumentatioun, Protokoll, asw. Dat heescht, ee kéint dovun ausgoen, datt de Site mat den Docks einfach opginn gouf a weider speziell mat deenen geschafft huet, déi ugefaang hunn Clienten ze maachen, well et ass méi einfach. Mee nee, sou gouf näischt beobachtet, keng Informatioun koum.

A wann Dir vun Null schreift, dann ass d'Benotzung vun de kritt Parameteren eigentlech nach wäit ewech. Obwuel https://core.telegram.org/ a schwätzt iwwer si am Ufank vun Ufank un, tatsächlech musst Dir als éischt ëmsetzen MTProto Protokoll - mee wann Dir gegleeft Layout no dem OSI Modell um Enn vun der Säit fir eng allgemeng Beschreiwung vum Protokoll, dann ass et komplett ëmsoss.

Tatsächlech, souwuel virun an no MTProto, op e puer Niveauen gläichzäiteg (wéi auslännesch Netzwierker, déi am OS Kernel schaffen, soen, Schichtverletzung), wäert e grousst, schmerzhafte a schrecklecht Thema am Wee stoen ...

Binär Serialiséierung: TL (Typ Sprooch) a säi Schema, a Schichten, a vill aner grujeleg Wierder

Dëst Thema ass tatsächlech de Schlëssel fir d'Problemer vum Telegram. An et gi vill schrecklech Wierder, wann Dir probéiert an et ze verdéiwen.

Also, hei ass den Diagramm. Wann dëst Wuert an Ärem Kapp kënnt, sot, JSON Schema, Dir hutt richteg geduecht. D'Zil ass datselwecht: eng Sprooch fir e méigleche Set vun iwwerdroenen Donnéeën ze beschreiwen. Dëst ass wou d'Ähnlechkeeten ophalen. Wann vun der Säit MTProto Protokoll, oder aus dem Quellbaum vum offiziellen Client, mir probéieren e Schema opzemaachen, mir gesinn eppes wéi:

int ? = Int;
long ? = Long;
double ? = Double;
string ? = String;

vector#1cb5c415 {t:Type} # [ t ] = Vector t;

rpc_error#2144ca19 error_code:int error_message:string = RpcError;

rpc_answer_unknown#5e2ad36e = RpcDropAnswer;
rpc_answer_dropped_running#cd78e586 = RpcDropAnswer;
rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer;

msg_container#73f1f8dc messages:vector<%Message> = MessageContainer;

---functions---

set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:bytes = Set_client_DH_params_answer;

ping#7abe77ec ping_id:long = Pong;
ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;

invokeAfterMsg#cb9f372d msg_id:long query:!X = X;
invokeAfterMsgs#3dc4b4f0 msg_ids:Vector<long> query:!X = X;

account.updateProfile#78515775 flags:# first_name:flags.0?string last_name:flags.1?string about:flags.2?string = User;
account.sendChangePhoneCode#8e57deb flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode;

Eng Persoun, déi dëst fir d'éischte Kéier gesäit, kann intuitiv nëmmen en Deel vun deem wat geschriwwe steet erkennen - gutt, dat sinn anscheinend Strukturen (obwuel wou ass den Numm, lénks oder riets?), et gi Felder dran, no deem en Typ no engem Colon kënnt ... wahrscheinlech. Hei an de Wénkelklammern ginn et wahrscheinlech Template wéi an C++ (tatsächlech, net wierklech). A wat bedeiten all déi aner Symboler, Fragezeechen, Ausrufezeeche, Prozentzuelen, Hashzeichen (a selbstverständlech bedeiten se verschidde Saachen op verschiddene Plazen), heiansdo präsent an heiansdo net, hexadezimal Zuelen - a virun allem, wéi kënnt een dovunner richteg ass (déi gëtt net vum Server verworf ginn) Byte Baach? Dir musst d'Dokumentatioun liesen (jo, et gi Linken zum Schema an der JSON Versioun an der Géigend - awer dat mécht et net méi kloer).

Open der Säit Binär Daten Serialiséierung an daucht an déi magesch Welt vu Champignonen an diskret Mathematik, eppes ähnlech wéi matan am 4. Joer. Alphabet, Typ, Wäert, Combinator, funktionell Combinator, Normal Form, Composite Type, Polymorphic Typ ... an dat ass alles just déi éischt Säit! Nächst waart op Iech TL Sprooch, déi, obwuel et schonn e Beispill vun enger trivialer Ufro an Äntwert enthält, guer keng Äntwert op méi typesch Fäll gëtt, dat heescht, datt Dir duerch eng Retelling vun Mathematik iwwersat aus Russesch an Englesch op en anert aacht embedded muss wade. Säiten!

Lieser vertraut mat funktionell Sproochen an automatesch Typ Inferenz wäert, natierlech, gesinn d'Beschreiwung Sprooch an dëser Sprooch, och aus dem Beispill, wéi vill méi vertraut, a kënne soen, datt dëst am Prinzip net schlecht ass. D'Objete géint dëst sinn:

  • jo, Zweck Kläng gutt, mee leider, si net erreecht
  • Ausbildung op russesch Universitéiten schwankt och ënnert IT Spezialitéiten - net jiddereen huet déi entspriechend natierlech geholl
  • Endlech, wéi mir wäerte gesinn, an der Praxis ass et net erfuerderlech, well nëmmen e limitéierten Ënnerdeel vu souguer dem TL deen beschriwwe gouf benotzt gëtt

Wéi gesot LéoNerd um Kanal #perl am FreeNode IRC Netz, probéiert e Paart vun Telegram op Matrix ëmzesetzen (Iwwersetzung vum Zitat ass ongenau aus der Erënnerung):

Et fillt sech wéi wann een fir d'éischt Kéier an d'Typtheorie agefouert gouf, sech opgereegt huet an ugefaang huet ze probéieren domat ze spillen, net wierklech egal ob et an der Praxis gebraucht gouf.

Kuckt fir Iech selwer, wann d'Bedierfnes fir Bare-Typen (int, laang, asw.) als Elementarescht keng Froen opwerft - schlussendlech musse se manuell ëmgesat ginn - zum Beispill, loosst eis e Versuch huelen aus hinnen ofzeleeën Vecteure. Dat ass, tatsächlech, Array, wann Dir déi resultéierend Saache mat hiren eegenen Nimm nennt.

Awer virdrun

Eng kuerz Beschreiwung vun engem Ënnerdeel vun der TL Syntax fir déi déi déi offiziell Dokumentatioun net liesen

constructor = Type;
myVec ids:Vector<long> = Type;

fixed#abcdef34 id:int = Type2;

fixedVec set:Vector<Type2> = FixedVec;

constructorOne#crc32 field1:int = PolymorType;
constructorTwo#2crc32 field_a:long field_b:Type3 field_c:int = PolymorType;
constructorThree#deadcrc bit_flags_of_what_really_present:# optional_field4:bit_flags_of_what_really_present.1?Type = PolymorType;

an_id#12abcd34 id:int = Type3;
a_null#6789cdef = Type3;

Definitioun fänkt ëmmer un Konstruktor, no deem fakultativ (an der Praxis - ëmmer) duerch d'Symbol # muss CRC 32 vun der normaliséierter Beschreiwungsstring vun dësem Typ. Als nächst kënnt eng Beschreiwung vun de Felder; wa se existéieren, kann den Typ eidel sinn. Dat alles endet mat engem Gläichzeechen, den Numm vun deem Typ, zu deem dëse Konstruktor - dat ass, tatsächlech, den Ënnertyp - gehéiert. De Guy riets vum Gläichzeechen ass polymorphesch - dat ass, et kann e puer spezifesch Zorte entspriechen.

Wann d'Definitioun no der Linn geschitt ---functions---, da bleift d'Syntax d'selwecht, awer d'Bedeitung wäert anescht sinn: de Konstruktor gëtt den Numm vun der RPC Funktioun, d'Felder ginn Parameteren (gutt, dat ass, et bleift genau déiselwecht gegebene Struktur, wéi hei ënnendrënner beschriwwen , dëst wäert einfach déi zougewisen Bedeitung sinn), an de "polymorpheschen Typ" - den Typ vum zréckginn Resultat. True, et wäert nach ëmmer polymorphesch bleiwen - just an der Rubrik definéiert ---types---, mä dëst constructor wäert "net considéréiert ginn". Iwwerlaascht d'Zorte vu genannte Funktiounen duerch hir Argumenter, d.h. Aus e puer Grënn sinn e puer Funktiounen mam selwechten Numm awer ënnerschiddlech Ënnerschrëften, wéi an C++, net am TL virgesinn.

Firwat "constructor" an "polymorphic" wann et net OOP ass? Gutt, tatsächlech wäert et méi einfach sinn fir een iwwer dëst an OOP Begrëffer ze denken - e polymorpheschen Typ als abstrakt Klass, a Konstruktoren sinn seng direkt Nofolgerklassen, an final an der Terminologie vun enger Rei vu Sproochen. Tatsächlech, natierlech, nëmmen hei Ähnlechkeet mat realen iwwerlaaschte Konstruktormethoden an OO Programméierungssproochen. Well hei just Datestrukture sinn, ginn et keng Methoden (obwuel d'Beschreiwung vu Funktiounen a Methoden weider zimmlech fäeg ass fir Duercherneen am Kapp ze kreéieren datt se existéieren, awer dat ass eng aner Saach) - Dir kënnt un e Konstruktor als Wäert ausdenken déi gëtt gebaut Typ wann Dir e Byte Stream liest.

Wéi geschitt dat? Den Deserializer, deen ëmmer 4 Bytes liest, gesäit de Wäert 0xcrc32 - a versteet wat duerno geschitt field1 mat Typ int, d.h. liest genee 4 Bytes, op dëser den iwwerlageren Feld mat der Zort PolymorType liesen. Gesäit 0x2crc32 a versteet datt et zwee Felder weider sinn, éischtens long, dat heescht mir liesen 8 Bytes. An dann erëm e komplexen Typ, deen op déiselwecht Manéier deserialiséiert gëtt. Zum Beispill, Type3 kéint am Circuit deklaréiert ginn, soubal zwee Konstrukteuren respektiv, da musse se sech entweder treffen 0x12abcd34, duerno musst Dir 4 méi Bytes liesen int, oder 0x6789cdef, duerno gëtt et näischt. Alles anescht - Dir musst eng Ausnam werfen. Jiddefalls, no dësem gi mir zréck op 4 Bytes liesen int Rand field_c в constructorTwo an domat schléissen mer eis färdeg PolymorType.

Endlech, wann Dir gefaangen 0xdeadcrc fir constructorThree, da gëtt alles méi komplizéiert. Eis éischt Terrain ass bit_flags_of_what_really_present mat Typ # - Tatsächlech ass dëst just en Alias ​​​​fir den Typ nat, dat heescht "natierlech Zuel". Dat ass, an Tatsaach, unsigned int ass, iwwregens, deen eenzege Fall wann unsigned Zuelen an real Circuits geschéien. Also, nächst ass eng Konstruktioun mat engem Fragezeechen, dat heescht datt dëst Feld - et wäert nëmmen um Drot präsent sinn wann de entspriechende Bit am Feld bezeechent gëtt (ongeféier wéi en ternäre Bedreiwer). Also, loosst eis unhuelen datt dëst Bit gesat gouf, wat heescht datt mir weider e Feld liesen wéi Type, déi an eisem Beispill 2 Konstruktoren huet. Een ass eidel (besteet nëmmen aus dem Identifizéierer), deen aneren huet e Feld ids mat Typ ids:Vector<long>.

Dir mengt vläicht datt béid Templates a Generik an de Profien oder Java sinn. Awer nee. Bal. Dëst déi eenzeg Fall vun Benotzung vun Wénkel Klammeren an real Kreesleef, an et gëtt NËMMEN fir Vector benotzt. An engem Byte Stream sinn dës 4 CRC32 Bytes fir de Vector Typ selwer, ëmmer d'selwecht, dann 4 Bytes - d'Zuel vun den Array Elementer, an dann dës Elementer selwer.

Füügt dozou datt d'Serialiséierung ëmmer a Wierder vu 4 Bytes geschitt, all Zorte si Multiple dovun - déi agebauten Typen ginn och beschriwwen bytes и string mat manueller Serialiséierung vun der Längt an dëser Ausriichtung vun 4 - gutt, et schéngt normal an och relativ effektiv ze kléngen? Och wann TL behaapt gëtt eng effektiv binär Serialiséierung ze sinn, an d'Häll mat hinnen, mat der Expansioun vu just iwwer alles, och boolesch Wäerter an eenzel Charakter Strings op 4 Bytes, wäert JSON nach ëmmer vill méi déck sinn? Kuckt, och onnéideg Felder kënne mat Bit Fändelen iwwersprangen ginn, alles ass ganz gutt, a souguer fir d'Zukunft erweiterbar, also firwat net méi spéit nei fakultativ Felder un de Konstruktor derbäi? ..

Awer nee, wann Dir net meng kuerz Beschreiwung liest, awer déi voll Dokumentatioun, an iwwer d'Ëmsetzung denkt. Als éischt gëtt de CRC32 vum Konstruktor no der normaliséierter Linn vun der Textbeschreiwung vum Schema berechent (extra Wäissraum ewechhuelen, etc.) - also wann en neit Feld derbäigesat gëtt, ännert sech d'Typbeschreiwungslinn, an dofir seng CRC32 an , dowéinst, serialization. A wat géif den ale Client maachen wann hien e Feld mat neie Fändelen gesat kritt, an hie weess net wat hien duerno maache soll? ..

Zweetens, loosst eis erënneren CRC 32, déi hei am Fong als hash Funktiounen fir eendeiteg ze bestëmmen wéi eng Zort (de)serialiséiert gëtt. Hei si mir mam Problem vun de Kollisiounen konfrontéiert - a nee, d'Wahrscheinlechkeet ass net eng bei 232, awer vill méi grouss. Wien huet sech drun erënnert datt CRC32 entwéckelt ass fir Feeler am Kommunikatiounskanal z'entdecken (a korrigéieren), an deementspriechend dës Eegeschaften zum Nodeel vun aneren verbesseren? Zum Beispill, et ass egal iwwer d'Rearrangement vun Bytes: wann Dir CRC32 aus zwou Linnen berechent, an der zweeter tauscht Dir déi éischt 4 Bytes mat den nächsten 4 Bytes - et wäert d'selwecht sinn. Wann eisen Input Text Saiten aus dem laténgesche Alphabet ass (an e bësse Punktuatioun), an dës Nimm net besonnesch zoufälleg sinn, erhéicht d'Wahrscheinlechkeet vun esou enger Ëmännerung staark.

Iwwregens, wien huet gekuckt wat do war? wierklech CRC32? Ee vun de fréie Quellcoden (souguer virum Waltman) hat eng Hashfunktioun déi all Charakter multiplizéiert mat der Nummer 239, sou beléift vun dëse Leit, ha ha!

Endlech, okay, mir gemierkt, datt constructors mat engem Feld Typ Vector<int> и Vector<PolymorType> wäert verschidden CRC32 hunn. Wat iwwer online Leeschtung? An aus theoretesch Siicht, gëtt dëst Deel vum Typ? Loosst d'soen mir Passe eng Rei vun zéng dausend Zuelen, gutt mat Vector<int> alles ass kloer, d'Längt an aner 40000 Bytes. A wann dëst Vector<Type2>, déi nëmmen aus engem Feld besteet int an et ass eleng am Typ - musse mir 10000xabcdef0 34 Mol widderhuelen an dann 4 Bytes int, oder d'Sprooch ass fäeg et fir eis vum Konstruktor INDEPEND fixedVec an amplaz 80000 Bytes, Transfert erëm nëmmen 40000?

Dëst ass guer keng Idle theoretesch Fro - stellt Iech vir, Dir kritt eng Lëscht vu Gruppebenotzer, jidderee vun deenen eng ID, Virnumm, Familljennumm huet - den Ënnerscheed an der Quantitéit vun Daten iwwer eng mobil Verbindung kann bedeitend sinn. Et ass genau d'Effektivitéit vun der Telegram Serialiséierung déi eis ugekënnegt gëtt.

Also ...

Vector, deen ni verëffentlecht gouf

Wann Dir probéiert duerch d'Säite vun der Beschreiwung vu Combinatoren ze waden a sou weider, gesitt Dir datt e Vektor (a souguer eng Matrix) formell probéiert duerch Tuples vu verschiddene Blieder auszeginn. Awer um Enn vergiessen se, de leschte Schrëtt gëtt iwwersprangen, an eng Definitioun vun engem Vektor gëtt einfach uginn, deen nach net un engem Typ gebonnen ass. Ëm wat geet et? A Sproochen programméiere, besonnesch funktionell, ass et zimlech typesch d'Struktur rekursiv ze beschreiwen - de Compiler mat senger fauler Evaluatioun wäert alles verstoen an alles selwer maachen. An der Sprooch daten serialization wat gebraucht gëtt ass Effizienz: et ass genuch fir einfach ze beschreiwen Lëscht, d.h. Struktur vun zwee Elementer - dat éischt ass en Dateelement, dat zweet ass déiselwecht Struktur selwer oder en eidele Raum fir de Schwanz (Pack (cons) zu Lisp). Mä dëst wäert selbstverständlech verlaangen vun all eenzel Element verbréngt zousätzlech 4 Bytes (CRC32 am Fall an TL) fir seng Aart ze beschreiwen. Eng Array kann och einfach beschriwwe ginn fix Gréisst, awer am Fall vun enger Array vun onbekannter Längt am Viraus, briechen mir of.

Dofir, well TL net erlaabt e Vektor auszeginn, huet et op der Säit bäigefüügt. Schlussendlech seet d'Dokumentatioun:

Serialiséierung benotzt ëmmer dee selwechte Konstruktor "Vektor" (const 0x1cb5c415 = crc32 ("Vektor t: Typ # [t] = Vector t"), deen net vum spezifesche Wäert vun der Variabel vum Typ t ofhängeg ass.

De Wäert vum fakultativen Parameter t ass net an der Serialiséierung involvéiert well et vum Resultattyp ofgeleet ass (ëmmer bekannt virun der Deserialiséierung).

Kuckt méi no: vector {t:Type} # [ t ] = Vector t - awer néierens Dës Definitioun selwer seet net datt déi éischt Zuel gläich muss mat der Längt vum Vektor sinn! An et kënnt net vun iwwerall. Dëst ass eng Gegebenheet déi am Kapp behalen muss an mat Ären Hänn ëmgesat ginn. Anzwousch anescht ernimmt d'Dokumentatioun souguer éierlech datt den Typ net wierklech ass:

De Vektor t polymorphesche Pseudotyp ass en "Typ", deem säi Wäert eng Sequenz vu Wäerter vun all Typ t ass, entweder gekëscht oder blo.

... awer konzentréiert sech net drop. Wann Dir, midd vun der Mathematik (vläicht och vun engem Uni Cours bekannt) duerch d'Ausdehnung vun der Mathematik, décidéiert opzeginn an tatsächlech kuckt wéi een an der Praxis domat schafft, ass den Androck am Kapp, datt dëst Serious ass Mathematik am Kär, et war kloer vun Cool Leit erfonnt (zwee Mathematiker - ACM Gewënner), an net just jiddereen. D'Zil - sech ze weisen - ass erreecht ginn.

Iwwregens, iwwer d'Zuel. Loosst eis Iech drun erënneren # et ass e Synonym nat, natierlech Zuel:

Et ginn Typ Ausdréck (Typ-expr) an numeresch Ausdréck (nat-expr). Wéi och ëmmer, si sinn déiselwecht definéiert.

type-expr ::= expr
nat-expr ::= expr

mä an der Grammatik gi se op déiselwecht Manéier beschriwwen, d.h. Dësen Ënnerscheed muss erëm erënnert ginn an manuell ëmgesat ginn.

Gutt, jo, Schablountypen (vector<int>, vector<User>) hunn e gemeinsamen Identifizéierer (#1cb5c415), d.h. wann Dir wësst, datt den Opruff als ugekënnegt ass

users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;

da waart Dir net méi op just e Vektor, mee e Vecteur vu Benotzer. Méi genee, soll waart - am richtege Code wäert all Element, wann net e bloen Typ, e Konstruktor hunn, an op eng gutt Manéier an der Ëmsetzung wier et néideg ze kontrolléieren - awer mir goufen genee an all Element vun dësem Vektor geschéckt deen Typ? Wat wann et eng Aart vu PHP wier, an deem eng Array verschidden Aarte a verschiddenen Elementer enthalen kann?

Zu dësem Zäitpunkt fänkt Dir un ze denken - ass sou en TL néideg? Vläicht wier et fir de Weenchen méiglech, e mënschleche Serializer ze benotzen, deeselwechte Protobuf dee schonn deemools existéiert huet? Dat war d'Theorie, loosst eis d'Praxis kucken.

Bestehend TL Implementatiounen am Code

TL gouf an den Tiefen vu VKontakte gebuer och virun de berühmten Eventer mam Verkaf vum Durov's Undeel an (wahrscheinlech), och ier d'Entwécklung vum Telegram ugefaang huet. An Open Source Quellcode vun der éischter Ëmsetzung Dir kënnt vill witzeg Krutchen fannen. An d'Sprooch selwer gouf do méi voll ëmgesat wéi se elo am Telegram ass. Zum Beispill, Hashes ginn guer net am Schema benotzt (dat heescht en agebaute Pseudotyp (wéi e Vektor) mat deviant Verhalen). Oder

Templates are not used now. Instead, the same universal constructors (for example, vector {t:Type} [t] = Vector t) are used w

awer loosst eis fir d'Wuel vun der Vollständegkeet betruechten, souzesoen d'Evolutioun vum Riese vum Denken ze verfolgen.

#define ZHUKOV_BYTES_HACK

#ifdef ZHUKOV_BYTES_HACK

/* dirty hack for Zhukov request */

Oder dës schéin:

    static const char *reserved_words_polymorhic[] = {

      "alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", NULL

      };

Dëst Fragment ass iwwer Schabloune wéi:

intHash {alpha:Type} vector<coupleInt<alpha>> = IntHash<alpha>;

Dëst ass d'Definitioun vun engem Hashmap Template Typ als Vektor vun int - Type Pairs. An C++ géif et esou ausgesinn:

    template <T> class IntHash {
      vector<pair<int,T>> _map;
    }

also, alpha - Schlësselwuert! Awer nëmmen am C ++ kënnt Dir T schreiwen, awer Dir sollt Alpha, Beta schreiwen ... Awer net méi wéi 8 Parameteren, dat ass wou d'Fantasie ophält. Et schéngt, datt eemol zu St.

-- Надо сделать в TL шаблоны
-- Бл... Ну пусть параметры зовут альфа, бета,... Какие там ещё буквы есть... О, тэта!
-- Грамматика? Ну потом напишем

-- Смотрите, какой я синтаксис придумал для шаблонов и вектора!
-- Ты долбанулся, как мы это парсить будем?
-- Да не ссыте, он там один в схеме, захаркодить -- и ок

Mä dëst war iwwer déi éischt publizéiert Ëmsetzung vun TL "am Allgemengen". Loosst eis weidergoen fir Implementatiounen an den Telegram Clienten selwer ze berücksichtegen.

Wuert zu Vasily:

Vasily, [09.10.18 17:07] Virun allem ass den Aarsch waarm well se eng Rëtsch Abstraktiounen erstallt hunn, an dunn e Bolt op hinnen gehummert hunn, an de Code Generator mat Krut bedeckt
Als Resultat, éischt vun dock pilot.jpg
Dann aus dem Code dzhekichan.webp

Natierlech, vu Leit vertraut mat Algorithmen a Mathematik, kënne mir erwaarden datt se Aho, Ullmann gelies hunn, a vertraut sinn mat den Tools déi de facto Standard an der Industrie iwwer Joerzéngte ginn fir hir DSL Compiler ze schreiwen, richteg?.

Vun telegram-cli ass de Vitaly Valtman, wéi aus dem Optriede vum TLO-Format ausserhalb vun sengen (cli) Grenzen ze verstoen ass, Member vun der Equipe - elo ass eng Bibliothéik fir TL Parsing zougewisen ginn getrennt sinn, wat ass den Androck vun hirem TL Parser? ..

16.12 04:18 Vasily: Ech mengen, een huet lex+yacc net beherrscht
16.12 04:18 Vasily: Ech kann et net anescht erklären
16.12 04:18 Vasily: gutt, oder si goufen fir d'Zuel vun de Linnen am VK bezuelt
16.12 04:19 Vasily: 3k+ Linnen etc.<censored> amplaz vun engem parser

Vläicht eng Ausnam? Loosst eis kucken wéi mécht Dëst ass den OFFIZIELL Client - Telegram Desktop:

    nametype = re.match(r'([a-zA-Z.0-9_]+)(#[0-9a-f]+)?([^=]*)=s*([a-zA-Z.<>0-9_]+);', line);
    if (not nametype):
      if (not re.match(r'vector#1cb5c415 {t:Type} # [ t ] = Vector t;', line)):
         print('Bad line found: ' + line);

1100+ Zeilen am Python, e puer reegelméisseg Ausdréck + speziell Fäll wéi e Vektor, deen natierlech am Schema deklaréiert ass wéi et no der TL-Syntax soll sinn, awer si hunn op dës Syntax vertraut fir se ze analyséieren ... D'Fro stellt sech, firwat war alles e Wonner?иEt ass méi Schichten wann keen et iwwerhaapt no der Dokumentatioun wäert parséieren?!

Iwwregens ... Erënnert Dir Iech drun, datt mir iwwer CRC32-Kontroll geschwat hunn? Also, am Telegram Desktop Code Generator gëtt et eng Lëscht vun Ausnahmen fir déi Typen an deenen de berechent CRC32 passt net mat deem am Diagramm uginn!

Vasily, [18.12/22 49:XNUMX] an hei géif ech nodenken ob esou en TL gebraucht gëtt
wann ech mat alternativen Implementatioune wollt messen, da géif ech ufänken Zeilpausen anzeginn, d'Halschent vun de Parser briechen op Multi-Linn Definitiounen
tdesktop awer och

Denkt un de Punkt iwwer One-Liner, mir kommen e bësse méi spéit drop zréck.

Okay, Telegram-cli ass inoffiziell, Telegram Desktop ass offiziell, awer wat iwwer déi aner? Wien weess? .. Am Android Client Code gouf et guer kee Schema Parser (wat Froen iwwer Open Source opwerft, awer dëst ass fir den zweeten Deel), awer et waren e puer aner witzeg Stécker Code, awer méi iwwer hinnen an der Ënnersektioun ënnert.

Wéi eng aner Froen stellt d'Serialiséierung an der Praxis op? Zum Beispill hu se vill Saache gemaach, natierlech, mat Bitfelder a bedingungsfelder:

Vasily: flags.0? true
heescht datt d'Feld präsent ass a gläich ass wouer wann de Fändel gesat ass

Vasily: flags.1? int
heescht datt d'Feld präsent ass a muss deserialiséiert ginn

Vasily: Ass, maach der keng Suergen iwwer wat Dir maacht!
Vasily: Et gëtt iergendwou am Dokument ernimmt datt et richteg ass eng null-Längt Typ, awer et ass onméiglech eppes aus hirem Dokument ze sammelen
Vasily: An den Open Source Implementatiounen ass dëst och net de Fall, awer et gëtt eng Rëtsch Krutchen an Ënnerstëtzer

Wéi iwwer Telethon? Viraus kucken op d'Thema vun MTProto, e Beispill - an der Dokumentatioun ginn et esou Stécker, mä d'Zeechen % et gëtt nëmme beschriwwen als "entspriechend engem gegebene Bare-Typ", d.h. an de Beispiller hei drënner gëtt et entweder e Feeler oder eppes ondokumentéiert:

Vasily, [22.06.18 18:38] Op enger Plaz:

msg_container#73f1f8dc messages:vector message = MessageContainer;

An enger anerer:

msg_container#73f1f8dc messages:vector<%Message> = MessageContainer;

An dat sinn zwee grouss Differenzen, am richtege Liewen kënnt eng Zort plakeg Vektor

Ech hunn keng blo Vecteure Definitioun gesinn an hunn net op eng begéint

Analyse gëtt mat der Hand am Telethon geschriwwen

A sengem Diagramm gëtt d'Definitioun kommentéiert msg_container

Nach eng Kéier bleift d'Fro iwwer %. Et ass net beschriwwen.

Vadim Goncharov, [22.06.18 19:22] an tdesktop?

Vasily, [22.06.18 19:23] Awer hiren TL Parser op reguläre Motore wäert dat héchstwahrscheinlech och net iessen

// parsed manually

TL ass eng schéin Abstraktioun, keen implementéiert se komplett

An % ass net an hirer Versioun vum Schema

Awer hei widdersprécht d'Dokumentatioun sech selwer, also idk

Et gouf an der Grammatik fonnt, si hätten einfach vergiess d'Semantik ze beschreiwen

Dir hutt d'Dokument op TL gesinn, Dir kënnt et net ouni en hallwe Liter erausfannen

"Ma, loosst eis soen", seet en anere Lieser, "Dir kritiséiert eppes, also weist mir wéi et soll gemaach ginn."

De Vasily äntwert: "Wat de Parser ugeet, hunn ech Saache gär wéi

    args: /* empty */ { $$ = NULL; }
        | args arg { $$ = g_list_append( $1, $2 ); }
        ;

    arg: LC_ID ':' type-term { $$ = tl_arg_new( $1, $3 ); }
            | LC_ID ':' condition '?' type-term { $$ = tl_arg_new_cond( $1, $5, $3 ); free($3); }
            | UC_ID ':' type-term { $$ = tl_arg_new( $1, $3 ); }
            | type-term { $$ = tl_arg_new( "", $1 ); }
            | '[' LC_ID ']' { $$ = tl_arg_new_mult( "", tl_type_new( $2, TYPE_MOD_NONE ) ); }
            ;

iergendwéi gefällt et besser wéi

struct tree *parse_args4 (void) {
  PARSE_INIT (type_args4);
  struct parse so = save_parse ();
  PARSE_TRY (parse_optional_arg_def);
  if (S) {
    tree_add_child (T, S);
  } else {
    load_parse (so);
  }
  if (LEX_CHAR ('!')) {
    PARSE_ADD (type_exclam);
    EXPECT ("!");
  }
  PARSE_TRY_PES (parse_type_term);
  PARSE_OK;
}

oder

        # Regex to match the whole line
        match = re.match(r'''
            ^                  # We want to match from the beginning to the end
            ([w.]+)           # The .tl object can contain alpha_name or namespace.alpha_name
            (?:
                #             # After the name, comes the ID of the object
                ([0-9a-f]+)    # The constructor ID is in hexadecimal form
            )?                 # If no constructor ID was given, CRC32 the 'tl' to determine it

            (?:s              # After that, we want to match its arguments (name:type)
                {?             # For handling the start of the '{X:Type}' case
                w+            # The argument name will always be an alpha-only name
                :              # Then comes the separator between name:type
                [wd<>#.?!]+  # The type is slightly more complex, since it's alphanumeric and it can
                               # also have Vector<type>, flags:# and flags.0?default, plus :!X as type
                }?             # For handling the end of the '{X:Type}' case
            )*                 # Match 0 or more arguments
            s                 # Leave a space between the arguments and the equal
            =
            s                 # Leave another space between the equal and the result
            ([wd<>#.?]+)     # The result can again be as complex as any argument type
            ;$                 # Finally, the line should always end with ;
            ''', tl, re.IGNORECASE | re.VERBOSE)

dëst ass de HEEL Lexer:

    ---functions---         return FUNCTIONS;
    ---types---             return TYPES;
    [a-z][a-zA-Z0-9_]*      yylval.string = strdup(yytext); return LC_ID;
    [A-Z][a-zA-Z0-9_]*      yylval.string = strdup(yytext); return UC_ID;
    [0-9]+                  yylval.number = atoi(yytext); return NUM;
    #[0-9a-fA-F]{1,8}       yylval.number = strtol(yytext+1, NULL, 16); return ID_HASH;

    n                      /* skip new line */
    [ t]+                  /* skip spaces */
    //.*$                 /* skip comments */
    /*.**/              /* skip comments */
    .                       return (int)yytext[0];

déi. méi einfach ass et mëll ze soen."

Am Allgemengen, als Resultat, passt de Parser a Code Generator fir den tatsächlech benotzte Subset vun TL an ongeféier 100 Zeilen Grammatik an ~ 300 Zeilen vum Generator (all zielen) print's generéierte Code), dorënner Typ Informatiounsbullen fir Introspektioun an all Klass. All polymorphic Typ verwandelt an eng eidel abstrakt Basis Klass, an constructors ierwen vun et an hunn Methode fir serialization an deserialization.

Mangel un Typen an der Typsprooch

Staark Tippen ass eng gutt Saach, richteg? Nee, dëst ass keen Holivar (obwuel ech léiwer dynamesch Sprooche léiwer maachen), mee e Postulat am Kader vun TL. Baséiert op et soll d'Sprooch all Zorte vu Schecken fir eis. Gutt, okay, vläicht net hien selwer, mä d'Ëmsetzung, mä hie soll se op d'mannst beschreiwen. A wéi eng Méiglechkeete wëlle mir?

Éischt vun all, Contrainten. Hei gesi mir an der Dokumentatioun fir Dateien eropzelueden:

De binäre Inhalt vun der Datei gëtt dann an Deeler opgedeelt. All Deeler mussen déi selwecht Gréisst hunn ( deel_Gréisst ) an déi folgend Konditioune mussen erfëllt sinn:

  • part_size % 1024 = 0 (deelbar mat 1 KB)
  • 524288 % part_size = 0 (512KB muss gläichméisseg deelbar sinn duerch Part_size)

Dee leschten Deel muss dës Konditiounen net erfëllen, virausgesat datt seng Gréisst manner ass wéi part_size.

All Deel soll eng Sequenznummer hunn, Datei_part, mat engem Wäert vun 0 bis 2,999.

Nodeems d'Datei opgedeelt ass, musst Dir eng Method wielen fir se um Server ze späicheren. Benotzt upload.saveBigFilePart am Fall wou déi voll Gréisst vun der Datei méi wéi 10 MB ass an upload.saveFilePart fir méi kleng Dateien.
[…] ee vun de folgenden Dateinputfehler kann zréckginn:

  • FILE_PARTS_INVALID - Ongülteg Unzuel vun Deeler. De Wäert ass net tëscht 1..3000

Ass eppes vun dësem am Diagramm? Ass dëst iergendwéi ausdrécklech mat TL? Nee. Awer entschëllegt, souguer dem Grousspapp säin Turbo Pascal konnt déi spezifizéiert Zorte beschreiwen rangéiert. An hie wousst nach eng Saach, elo besser bekannt als enum - en Typ, deen aus enger Opzielung vun enger fixer (kleng) Zuel vu Wäerter besteet. A Sprooche wéi C - numeresch, notéiert datt mir bis elo nëmmen iwwer Typen geschwat hunn Zuelen. Mee et ginn och Arrays, Strings ... zum Beispill wier et flott ze beschreiwen, datt dës String nëmmen eng Telefonsnummer enthält, oder?

Keen vun dësem ass am TL. Awer et gëtt zum Beispill am JSON Schema. A wann een aneren iwwer d'Divisibilitéit vu 512 KB streide kann, datt dëst nach ëmmer am Code kontrolléiert muss ginn, da gitt sécher datt de Client einfach konnt net schéckt eng Zuel aus der Rei 1..3000 (an deen entspriechende Feeler konnt net entstoen) et wier méiglech gewiescht, oder?..

Iwwregens, iwwer Feeler an zréck Wäerter. Och déi, déi mat TL geschafft hunn, verschwannen d'Aen - dat huet eis net direkt opgefall jiddereen eng Funktioun an TL kann eigentlech zréck net nëmmen de beschriwwen zréck Typ, awer och e Feeler. Awer dëst kann op keng Manéier mat der TL selwer ofgeleet ginn. Natierlech ass et scho kloer an et brauch näischt an der Praxis (obwuel tatsächlech RPC op verschidde Manéiere ka gemaach ginn, mir kommen méi spéit op dëst zréck) - awer wat iwwer d'Rengheet vun de Konzepter vun der Mathematik vun Abstrakt Typen aus der himmlescher Welt? .. Ech hunn den Tug opgeholl - also passt et.

A schliisslech, wat iwwer d'Liesbarkeet? Ee, do, am Allgemengen, géif ech gären Beschreiwung hunn et richteg am Schema (am JSON Schema, nach eng Kéier, et ass), awer wann Dir scho mat der Spannung sidd, wat ass dann mat der praktescher Säit - op d'mannst trivial kucken Differenzen während Updates? Kuckt Iech selwer op richteg Beispiller:

-channelFull#76af5481 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int = ChatFull;
+channelFull#1c87a71a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int = ChatFull;

oder

-message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message;
+message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message;

Et hänkt vu jidderengem of, awer GitHub, zum Beispill, refuséiert Ännerungen an esou laange Linnen ze markéieren. D'Spill "fënnt 10 Differenzen", a wat d'Gehir direkt gesäit ass datt d'Ufäng an d'Enn an deenen zwee Beispiller d'selwecht sinn, Dir musst iergendwou an der Mëtt ze tediously liesen ... Menger Meenung no ass dat net nëmmen an der Theorie, mee reng visuell dreckeg an sloppy.

Iwwregens, iwwer d'Rengheet vun der Theorie. Firwat brauche mir Bitfelder? Schéngt et net datt si richen schlecht aus der Siicht vun der Typtheorie? D'Erklärung kann a fréiere Versioune vum Diagramm gesi ginn. Am Ufank, jo, dat ass wéi et war, fir all Niess gouf en neien Typ erstallt. Dës Rudimenter existéieren nach ëmmer an dëser Form, zum Beispill:

storage.fileUnknown#aa963b05 = storage.FileType;
storage.filePartial#40bc6f52 = storage.FileType;
storage.fileJpeg#7efe0e = storage.FileType;
storage.fileGif#cae1aadf = storage.FileType;
storage.filePng#a4f63c0 = storage.FileType;
storage.filePdf#ae1e508d = storage.FileType;
storage.fileMp3#528a0677 = storage.FileType;
storage.fileMov#4b09ebbc = storage.FileType;
storage.fileMp4#b3cea0e4 = storage.FileType;
storage.fileWebp#1081464c = storage.FileType;

Awer elo stellt Iech vir, wann Dir 5 fakultativ Felder an Ärer Struktur hutt, da braucht Dir 32 Typen fir all méiglech Optiounen. Kombinatoresch Explosioun. Sou huet d'Kristallrengheet vun der TL-Theorie nach eng Kéier géint de Goss-Eisenarsch vun der haarder Realitéit vun der Serialiséierung gebrach.

Zousätzlech, op e puer Plazen verletzen dës Kärelen selwer hir eege Typologie. Zum Beispill, am MTProto (nächst Kapitel) kann d'Äntwert vum Gzip kompriméiert ginn, alles ass gutt - ausser datt d'Schichten an de Circuit verletzt ginn. Nach eng Kéier war et net RpcResult selwer deen ernannt gouf, mee säin Inhalt. Gutt, firwat maachen dat?.. Ech hu missen an eng Krut schneiden fir datt d'Kompressioun iwwerall funktionnéiert.

Oder en anert Beispill, mir hunn eemol e Feeler entdeckt - et gouf geschéckt InputPeerUser anstatt InputUser. Oder vice versa. Awer et huet geschafft! Dat ass, de Server huet sech net ëm den Typ gekëmmert. Wéi kann dat sinn? D'Äntwert kann eis duerch Code Fragmenter aus Telegram-cli ginn:

  if (tgl_get_peer_type (E->id) != TGL_PEER_CHANNEL || (C && (C->flags & TGLCHF_MEGAGROUP))) {
    out_int (CODE_messages_get_history);
    out_peer_id (TLS, E->id);
  } else {    
    out_int (CODE_channels_get_important_history);

    out_int (CODE_input_channel);
    out_int (tgl_get_peer_id (E->id));
    out_long (E->id.access_hash);
  }
  out_int (E->max_id);
  out_int (E->offset);
  out_int (E->limit);
  out_int (0);
  out_int (0);

An anere Wierder, dëst ass wou d'Serialiséierung gemaach gëtt MANUELL, net generéiert Code! Vläicht ass de Server an enger ähnlecher Aart a Weis ëmgesat?.. Am Prinzip funktionnéiert dëst wann et eemol gemaach gëtt, awer wéi kann et spéider bei Updates ënnerstëtzt ginn? Ass dat firwat de Schema erfonnt gouf? An hei gi mer op déi nächst Fro.

Versionéierung. Schichten

Firwat déi schematesch Versioune Schichten genannt ginn, kann nëmme spekuléiert ginn op Basis vun der Geschicht vu publizéierten Schemaen. Anscheinend hunn d'Auteuren ufanks geduecht datt Basis Saache mam onverännert Schema gemaach kënne ginn, an nëmmen wann néideg, fir spezifesch Ufroen, weisen datt se mat enger anerer Versioun gemaach ginn. Am Prinzip, och eng gutt Iddi - an déi nei gëtt, wéi et war, "gemëscht", Layer op der aler. Awer kucke wéi et gemaach gouf. True, ech konnt et net vun Ufank un kucken - et ass witzeg, awer d'Diagramm vun der Basisschicht existéiert einfach net. Schichten ugefaang mat 2. D'Dokumentatioun erzielt eis iwwer eng speziell TL Feature:

Wann e Client Layer 2 ënnerstëtzt, da muss de folgende Konstruktor benotzt ginn:

invokeWithLayer2#289dd1f6 {X:Type} query:!X = X;

An der Praxis heescht dat, datt virun all API Opruff, en Int mat de Wäert 0x289dd1f6 muss virun der Method Zuel dobäi ginn.

Kléngt normal. Awer wat ass duerno geschitt? Da erschéngt

invokeWithLayer3#b7475268 query:!X = X;

Also wat ass nächst? Wéi Dir kéint roden,

invokeWithLayer4#dea0d430 query:!X = X;

Witzeg? Nee, et ass ze fréi ze laachen, denkt drun all eenzel eng Ufro vun enger anerer Schicht muss an esou engem speziellen Typ gewéckelt ginn - wann Dir se all anescht hutt, wéi soss kënnt Dir se ënnerscheeden? A just 4 Bytes virun ze addéieren ass eng zimlech effizient Method. Also,

invokeWithLayer5#417a57ae query:!X = X;

Awer et ass offensichtlech datt no enger Zäit dëst eng Aart vu Bacchanalia gëtt. An d'Léisung koum:

Update: Ugefaange mat Layer 9, Hëllefsmethoden invokeWithLayerN kann nëmmen zesumme benotzt ginn mat initConnection

Hour! No 9 Versioune si mir endlech zu deem komm, wat an den 80er Joren an den Internetprotokoller gemaach gouf - eng Kéier am Ufank vun der Verbindung mat der Versioun eens ginn!

Also wat ass nächst? ..

invokeWithLayer10#39620c41 query:!X = X;
...
invokeWithLayer18#1c900537 query:!X = X;

Awer elo kënnt Dir nach ëmmer laachen. Eréischt no weideren 9 Schichten gouf endlech en universellen Konstruktor mat enger Versiounsnummer bäigefüügt, deen nëmmen eemol am Ufank vun der Verbindung muss genannt ginn, an d'Bedeitung vun de Schichten schéngt verschwonnen ze sinn, elo ass et just eng bedingungsversioun, wéi z. iwwerall soss. Problem geléist.

Genau?..

Vasily, [16.07.18 14:01] Och e Freideg hunn ech geduecht:
Den Teleserver schéckt Eventer ouni Ufro. Ufroe mussen an InvokeWithLayer gewéckelt ginn. De Server wéckelt keng Updates; et gëtt keng Struktur fir d'Äntwerten an d'Aktualiséierungen ëmzepaken.

Déi. de Client kann net d'Schicht spezifizéieren an där hien Updates wëllt

Vadim Goncharov, [16.07.18 14:02] ass InvokeWithLayer am Prinzip keng Krut?

Vasily, [16.07.18 14:02] Dëst ass deen eenzege Wee

Vadim Goncharov, [16.07.18 14:02] wat am Wesentlechen d'accord ass iwwer d'Schicht am Ufank vun der Sitzung

Iwwregens, et follegt datt de Client Downgrade net geliwwert gëtt

Aktualiséierungen, d.h. Typ Updates am Schema ass dat wat de Server un de Client schéckt net als Äntwert op eng API Ufro, awer onofhängeg wann en Event geschitt. Dëst ass e komplext Thema dat an engem anere Post diskutéiert gëtt, awer fir de Moment ass et wichteg ze wëssen datt de Server Updates späichert och wann de Client offline ass.

Also, wann Dir refuséiert ze wéckelen vun all eenzel Package fir seng Versioun unzeginn, féiert dat logesch zu de folgende méigleche Problemer:

  • de Server schéckt Updates un de Client och ier de Client informéiert huet wéi eng Versioun se ënnerstëtzt
  • wat soll ech nom Upgrade vum Client maachen?
  • wien ass garantéiertdatt d'Meenung vum Server iwwer d'Layernummer net während dem Prozess änneren?

Mengt Dir, datt dat reng theoretesch Spekulatioun ass, an an der Praxis kann dat net geschéien, well de Server richteg geschriwwe gëtt (op d'mannst ass et gutt getest)? Ha! Egal wéi et ass!

Dat ass genee wat mir am August stoungen. De 14. August goufen et Messagen datt eppes op den Telegram Serveren aktualiséiert gouf ... an dann an de Logbicher:

2019-08-15 09:28:35.880640 MSK warn  main: ANON:87: unknown object type: 0x80d182d1 at TL/Object.pm line 213.
2019-08-15 09:28:35.751899 MSK warn  main: ANON:87: unknown object type: 0xb5223b0f at TL/Object.pm line 213.

an dann e puer Megabytes vu Stackspuren (gutt, zur selwechter Zäit gouf de Logbuch fixéiert). No allem, wann eppes net an Ärem TL unerkannt gëtt, ass et binär duerch Ënnerschrëft, méi wäit erof ALL geet, decoding wäert onméiglech ginn. Wat soll Dir an esou enger Situatioun maachen?

Gutt, dat éischt wat iergendeen am Kapp kënnt ass ze trennen an erëm ze probéieren. Huet net gehollef. Mir google CRC32 - dës goufen Objeten aus Schema 73, obwuel mir op 82 geschafft.

Vläicht ass de Problem reng an eisem inoffizielle Client? Nee, mir starten Telegram Desktop 1.2.17 (Versioun geliwwert an enger Zuel vu Linux Verdeelungen), et schreift an den Ausnahmslog: MTP Onerwaart Typ ID #b5223b0f gelies an MTPMessageMedia ...

Kritik vum Protokoll an organisatoresch Approche vum Telegram. Deel 1, technesch: Erfahrung vun Schreiwen engem Client vun Null - TL, MT

Google huet gewisen datt en ähnleche Problem scho mat engem vun den inoffizielle Cliente geschitt ass, awer dann waren d'Versiounsnummeren an deementspriechend d'Annahmen anescht ...

Also wat solle mir maachen? Vasily an ech trennen: hien huet probéiert de Circuit op 91 ze aktualiséieren, Ech hu beschloss e puer Deeg ze waarden a probéieren op 73. Béid Methoden hunn geschafft, awer well se empiresch sinn, gëtt et kee Verständnis wéivill Versiounen erop oder erof Dir braucht ze sprangen, oder wéi laang Dir musst waarden.

Méi spéit konnt ech d'Situatioun reproduzéieren: mir starten de Client, schalten en aus, kompiléieren de Circuit op eng aner Schicht nei, restarten, fänken de Problem erëm op, zréck op dee virdrun - Oops, kee Betrag u Circuitwiesselen a Client restarts fir eng puer Minutten wäert hëllefen. Dir kritt eng Mëschung vun Datestrukturen aus verschiddene Schichten.

Erklärung? Wéi Dir kënnt aus verschiddenen indirekten Symptomer roden, besteet de Server aus ville Prozesser vu verschiddenen Typen op verschiddene Maschinnen. Wahrscheinlech huet de Server, dee verantwortlech ass fir "Pufferen", an d'Schlaang gesat wat seng Superieuren et ginn hunn, a si hunn et am Schema ginn, deen am Moment vun der Generatioun war. A bis dës Schlaang "verrotten" ass, konnt näischt doriwwer gemaach ginn.

Vläicht ... awer dëst ass eng schrecklech Krut?!.. Neen, ier mer iwwer verréckte Iddien denken, kucke mer de Code vun den offiziellen Clienten. An der Android Versioun fanne mir keen TL Parser, awer mir fanne e kräftege Fichier (GitHub refuséiert se ze beréieren) mat (De) Serialiséierung. Hei sinn d'Code Snippets:

public static class TL_message_layer68 extends TL_message {
    public static int constructor = 0xc09be45f;
//...
//еще пачка подобных
//...
    public static class TL_message_layer47 extends TL_message {
        public static int constructor = 0xc992e15c;
        public static Message TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) {
            Message result = null;
            switch (constructor) {
                case 0x1d86f70e:
                    result = new TL_messageService_old2();
                    break;
                case 0xa7ab1991:
                    result = new TL_message_old3();
                    break;
                case 0xc3060325:
                    result = new TL_message_old4();
                    break;
                case 0x555555fa:
                    result = new TL_message_secret();
                    break;
                case 0x555555f9:
                    result = new TL_message_secret_layer72();
                    break;
                case 0x90dddc11:
                    result = new TL_message_layer72();
                    break;
                case 0xc09be45f:
                    result = new TL_message_layer68();
                    break;
                case 0xc992e15c:
                    result = new TL_message_layer47();
                    break;
                case 0x5ba66c13:
                    result = new TL_message_old7();
                    break;
                case 0xc06b9607:
                    result = new TL_messageService_layer48();
                    break;
                case 0x83e5de54:
                    result = new TL_messageEmpty();
                    break;
                case 0x2bebfa86:
                    result = new TL_message_old6();
                    break;
                case 0x44f9b43d:
                    result = new TL_message_layer104();
                    break;
                case 0x1c9b1027:
                    result = new TL_message_layer104_2();
                    break;
                case 0xa367e716:
                    result = new TL_messageForwarded_old2(); //custom
                    break;
                case 0x5f46804:
                    result = new TL_messageForwarded_old(); //custom
                    break;
                case 0x567699b3:
                    result = new TL_message_old2(); //custom
                    break;
                case 0x9f8d60bb:
                    result = new TL_messageService_old(); //custom
                    break;
                case 0x22eb6aba:
                    result = new TL_message_old(); //custom
                    break;
                case 0x555555F8:
                    result = new TL_message_secret_old(); //custom
                    break;
                case 0x9789dac4:
                    result = new TL_message_layer104_3();
                    break;

oder

    boolean fixCaption = !TextUtils.isEmpty(message) &&
    (media instanceof TLRPC.TL_messageMediaPhoto_old ||
     media instanceof TLRPC.TL_messageMediaPhoto_layer68 ||
     media instanceof TLRPC.TL_messageMediaPhoto_layer74 ||
     media instanceof TLRPC.TL_messageMediaDocument_old ||
     media instanceof TLRPC.TL_messageMediaDocument_layer68 ||
     media instanceof TLRPC.TL_messageMediaDocument_layer74)
    && message.startsWith("-1");

Hmm ... gesäit wëll. Awer, wahrscheinlech, dëst ass generéiert Code, dann okay? .. Mee et ënnerstëtzt sécher all Versiounen! Richteg, et ass net kloer firwat alles zesumme gemëscht ass, geheime Chats, an all méiglech _old7 iergendwéi ausgesinn net wéi Maschinn Generatioun ... Allerdéngs war ech virun allem iwwerrascht duerch

TL_message_layer104
TL_message_layer104_2
TL_message_layer104_3

Kärelen, kënnt Dir net emol entscheeden wat an enger Schicht ass?! Gutt, okay, loosst eis soen datt "zwee" mat engem Feeler verëffentlecht goufen, gutt, et geschitt, awer DRÉI? .. Direkt erëm déiselwecht Rake? Wat fir eng Pornographie ass dat, sorry?

Am Quellcode vum Telegram Desktop, iwwregens, geschitt eng ähnlech Saach - wann jo, e puer Verpflichtungen an enger Zeil fir de Schema änneren net seng Layernummer, awer fixéieren eppes. A Konditiounen wou et keng offiziell Quell vun Daten fir de Schema ass, wou kann et kritt ginn, ausser fir de Quellcode vum offiziellen Client? A wann Dir et vun do hëlt, kënnt Dir net sécher sinn datt de Schema komplett richteg ass bis Dir all d'Methoden testen.

Wéi kann dat iwwerhaapt getest ginn? Ech hoffen Fans vun Eenheet, funktionell an aner Tester an de Kommentaren deelen.

Okay, loosst eis en anert Stéck Code kucken:

public static class TL_folders_deleteFolder extends TLObject {
    public static int constructor = 0x1c295881;

    public int folder_id;

    public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) {
        return Updates.TLdeserialize(stream, constructor, exception);
    }

    public void serializeToStream(AbstractSerializedData stream) {
        stream.writeInt32(constructor);
        stream.writeInt32(folder_id);
    }
}

//manually created

//RichText start
public static abstract class RichText extends TLObject {
    public String url;
    public long webpage_id;
    public String email;
    public ArrayList<RichText> texts = new ArrayList<>();
    public RichText parentRichText;

    public static RichText TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) {
        RichText result = null;
        switch (constructor) {
            case 0x1ccb966a:
                result = new TL_textPhone();
                break;
            case 0xc7fb5e01:
                result = new TL_textSuperscript();
                break;

Dëse Kommentar "manuell erstallt" seet datt nëmmen en Deel vun dësem Fichier manuell geschriwwe gouf (kënnt Dir Iech de ganzen Ënnerhalt Albtraum virstellen?), An de Rescht gouf Maschinn generéiert. Wéi och ëmmer, da stellt sech eng aner Fro - datt d'Quelle verfügbar sinn net ganz (a la GPL Blobs am Linux Kernel), awer dëst ass schonn en Thema fir den zweeten Deel.

Awer genuch. Loosst eis op de Protokoll goen, op deem all dës Serialiséierung leeft.

MT Proto

Also, loosst eis opmaachen allgemeng Beschreiwung и detailléiert Beschreiwung vum Protokoll an dat éischt wat mir stéieren ass d'Terminologie. A mat engem Iwwerfloss vun allem. Am Allgemengen schéngt dëst eng propriétaire Feature vum Telegram ze sinn - Saachen anescht op verschiddene Plazen ze ruffen, oder verschidde Saache mat engem Wuert, oder vice versa (zum Beispill, an engem High-Level API, wann Dir e Sticker Pack gesitt, ass et net wat Dir geduecht hutt).

Zum Beispill, "Message" an "Sessioun" bedeiten hei eppes anescht wéi an der üblecher Telegram Client Interface. Gutt, alles ass kloer mam Message, et kéint an OOP Begrëffer interpretéiert ginn, oder einfach d'Wuert "Packet" genannt ginn - dëst ass en nidderegen Transportniveau, et ginn net déiselwecht Messagen wéi an der Interface, et gi vill Servicemeldungen . Awer d'Sessioun ... awer éischt Saache fir d'éischt.

Transport Layer

Déi éischt Saach ass den Transport. Si soen eis iwwer 5 Optiounen:

  • TCP
  • Web Socket
  • Websocket iwwer HTTPS
  • HTTP
  • Majo

Vasily, [15.06.18 15:04] Et gëtt och UDP Transport, awer et ass net dokumentéiert

An TCP an dräi Varianten

Déi éischt ass ähnlech wéi UDP iwwer TCP, all Paket enthält eng Sequenznummer a crc
Firwat ass d'Liesen vun Dokumenter op engem Weenchen sou schmerzhaft?

Gutt, do ass et elo TCP schonn an 4 Varianten:

  • Abrëll
  • Zuelen
  • Gepolstert Mëttelstuf
  • Full

Gutt, ok, Padded Zwëschenzäit fir MTProxy, dëst gouf spéider wéinst bekannten Eventer bäigefüügt. Awer firwat nach zwou Versiounen (dräi am Ganzen) wann Dir mat enger kënnt? All véier ënnerscheede sech wesentlech nëmmen a wéi Dir d'Längt an d'Notzlaascht vum Haapt MTProto feststellt, wat weider diskutéiert gëtt:

  • an Abridged ass et 1 oder 4 Bytes, awer net 0xef, dann de Kierper
  • am Mëttelstuf ass dëst 4 Bytes Längt an e Feld, an déi éischte Kéier muss de Client schécken 0xeeeeeeee fir ze weisen datt et Mëttelstuf ass
  • am Ganzen am meeschte Suchtfaktor, aus der Siicht vun engem Netzwierker: Längt, Sequenznummer, an NET DEEN deen haaptsächlech MTProto, Kierper, CRC32 ass. Jo, dat alles ass uewen op TCP. Wat eis zouverlässeg Transport a Form vun engem sequentielle Byte-Stream ubitt; keng Sequenze si gebraucht, besonnesch Kontrollsummen. Okay, elo wäert iergendeen géint mech protestéieren datt TCP e 16-Bit Checksum huet, sou datt Datekorruptioun geschitt. Super, awer mir hunn tatsächlech e kryptografesche Protokoll mat Hashes méi laang wéi 16 Bytes, all dës Feeler - an nach méi - wäerte vun engem SHA Mëssverständnis op engem méi héijen Niveau gefaangen ginn. Et gëtt KEE Punkt an CRC32 uewen op dëser.

Loosst eis Abridged vergläichen, an deem ee Byte vun der Längt méiglech ass, mat Intermediate, wat justifiéiert "Am Fall wou 4-Byte Datenausrichtung gebraucht gëtt", wat zimlech Nonsens ass. Wat, et gëtt ugeholl datt Telegram Programméierer sou inkompetent sinn datt se Daten aus engem Socket net an en ausgeriichte Puffer liesen kënnen? Dir musst dat nach ëmmer maachen, well d'Liesen Iech all Zuel vu Bytes zréckginn (an et ginn och Proxy-Server, zum Beispill...). Oder op der anerer Säit, firwat blockéiert Abridged wa mir nach ëmmer hefteg Polsterung uewen op 16 Bytes hunn - spuert 3 Bytes heiansdo ?

Et huet een den Androck, datt den Nikolai Durov wierklech gär Rieder nei erfënnt, dorënner Netzwierkprotokoller, ouni wierklech praktesch Bedierfnesser.

Aner Transportméiglechkeeten, inkl. Web an MTProxy wäerte mir elo net berücksichtegen, vläicht an engem anere Post, wann et eng Demande gëtt. Iwwer déiselwecht MTProxy, loosst eis elo erënneren datt kuerz no senger Verëffentlechung am Joer 2018, Ubidder séier geléiert hunn et ze blockéieren, geduecht fir Bypass blockéierenno Package Gréisst! An och d'Tatsaach, datt den MTProxy-Server, deen (erëm vum Waltman) an C geschriwwe gouf, ze vill un Linux Spezifizitéiten gebonnen ass, obwuel dëst guer net erfuerderlech war (Phil Kulin wäert bestätegen), an datt en ähnlechen Server entweder am Go oder Node.js géif fit a manner wéi honnert Linnen.

Mä mir wäerte Conclusiounen iwwer d'technesch Alphabetiséierung vun dëse Leit um Enn vun der Rubrik zéien, nodeems se aner Themen betruecht. Fir elo, loosst eis op OSI Layer 5 weidergoen, Sessioun - op där se MTProto Sessioun gesat hunn.

Schlësselen, Messagen, Sessiounen, Diffie-Hellman

Si hunn et do net ganz korrekt gesat ... Eng Sessioun ass net déiselwecht Sessioun déi am Interface ënner Aktiv Sessiounen ze gesinn ass. Mee an Uerdnung.

Kritik vum Protokoll an organisatoresch Approche vum Telegram. Deel 1, technesch: Erfahrung vun Schreiwen engem Client vun Null - TL, MT

Also hu mir e Byte String vu bekannter Längt vun der Transportschicht kritt. Dëst ass entweder e verschlësselte Message oder Kloertext - wa mir nach ëmmer an der Schlësselvertragsstadium sinn an et tatsächlech maachen. Wéi eng vun de Konzepter genannt "Schlëssel" schwätze mir? Loosst eis dëst Thema fir d'Telegram Team selwer klären (ech entschëllege mech fir meng eegen Dokumentatioun aus Englesch mat engem midd Gehir um 4 Auer ze iwwersetzen, et war méi einfach e puer Ausdréck ze loossen wéi se sinn):

Et ginn zwou Entitéite genannt Sëtzung - een an der UI vun offiziellen Clienten ënner "aktuelle Sessiounen", wou all Sessioun engem ganzen Apparat / OS entsprécht.
Déi zweet ass MTProto Sëtzung, déi d'Sequenznummer vum Message (am nidderegem Sënn) dran huet, a wéi eng kann tëscht verschiddenen TCP Verbindungen daueren. Verschidde MTProto Sessiounen kënnen zur selwechter Zäit installéiert ginn, zum Beispill fir d'Datei erofzelueden ze beschleunegen.

Tëscht dësen zwee Manifestatiounen et gëtt e Konzept Autorisatioun. Am degeneréierte Fall kënne mir dat soen UI Sëtzung ass d'selwecht wéi Autorisatioun, mee leider ass alles komplizéiert. Loosst eis kucken:

  • De Benotzer op den neien Apparat generéiert éischt auth_key a Grenzen et zu Kont, zum Beispill via SMS - datt d'firwat Autorisatioun
  • Et ass am éischte geschitt MTProto Sëtzung, déi huet session_id bannen selwer.
  • Op dësem Schrëtt, d'Kombinatioun Autorisatioun и session_id kéint genannt ginn Beispill - dëst Wuert schéngt an der Dokumentatioun an Code vun e puer Clienten
  • Da kann de Client opmaachen e puer MTProto Sessiounen ënnert der selwechter auth_key - op déi selwecht DC.
  • Dann, enges Daags muss de Client de Fichier ufroen aner DC - a fir dës DC gëtt en neien generéiert auth_key !
  • Fir de System z'informéieren, datt et net en neie Benotzer ass, deen sech registréiert, awer d'selwecht Autorisatioun (UI Sëtzung), benotzt de Client API Uriff auth.exportAuthorization doheem DC auth.importAuthorization an der neier DC.
  • Alles ass d'selwecht, e puer kënnen op sinn MTProto Sessiounen (jidderee mat senger eegener session_id) zu dësem neien DC, ënner sengem auth_key.
  • Schlussendlech kann de Client Perfekt Forward Geheimnis wëllen. All auth_key war Permanent Schlëssel - pro DC - an de Client kann Opruff auth.bindTempAuthKey fir benotzen temporär auth_key - an erëm, nëmmen eng temp_auth_key pro DC, gemeinsam fir all MTProto Sessiounen zu dëser DC.

Notéiert dat Salz (an Zukunft Salzer) ass och een op auth_key déi. gedeelt tëscht jiddereen MTProto Sessiounen an déi selwecht DC.

Wat heescht "tëscht verschiddenen TCP Verbindungen"? Also heescht dat eppes wéi Autorisatioun Cookie op enger Websäit - et bestoe (iwwerlieft) vill TCP Verbindungen zu engem bestëmmte Server, mee enges Daags geet et schlecht. Nëmmen am Géigesaz zu HTTP, an MTProto Messagen bannent enger Sessioun sinn sequenziell nummeréiert a bestätegt; wa se an den Tunnel erakomm sinn, ass d'Verbindung gebrach - nodeems Dir eng nei Verbindung gegrënnt huet, schéckt de Server frëndlech alles an dëser Sessioun wat en net an der viregter geliwwert huet. TCP Verbindung.

Wéi och ëmmer, d'Informatioun hei uewen ass no ville Méint vun der Enquête zesummegefaasst. An der Tëschenzäit implementéiere mir eise Client vun Null? - loosst eis zréck op den Ufank.

Also loosst eis generéieren auth_key Op der Diffie-Hellman Versioune vum Telegram. Loosst eis probéieren d'Dokumentatioun ze verstoen ...

Vasily, [19.06.18 20:05] data_with_hash:= SHA1 (Daten) + Daten + (all zoufälleg Bytes); sou datt d'Längt gläich ass 255 Bytes;
verschlësselte_Donnéeën := RSA(Daten_mat_hash, server_public_key); eng 255-Byte laang Zuel (grouss Endian) gëtt op déi erfuerderlech Kraaft iwwer de erfuerderleche Modul erhéicht, an d'Resultat gëtt als 256-Byte Zuel gespäichert.

Si hunn e bëssen DH

Gesäit net aus wéi eng gesond Persoun DH
Et gi keng zwee ëffentlech Schlësselen an dx

Gutt, schlussendlech war dëst erausgerappt, awer e Rescht ass bliwwen - Beweis vun der Aarbecht gëtt vum Client gemaach datt hien d'Zuel konnt faktoréieren. Aart vu Schutz géint DoS Attacken. An den RSA Schlëssel gëtt nëmmen eemol an eng Richtung benotzt, am Wesentlechen fir Verschlësselung new_nonce. Awer wann dës anscheinend einfach Operatioun geléngt, wat wäert Dir konfrontéieren?

Vasily, [20.06.18/00/26 XNUMX:XNUMX] Ech sinn nach net op déi appid Ufro ukomm

Ech hunn dës Ufro un DH geschéckt

An, am Transportdock seet et datt et mat 4 Bytes vun engem Feelercode ka reagéieren. Dat ass alles

Gutt, hie sot mir -404, also wat?

Also hunn ech him gesot: "Fang Äre Bullshit verschlësselte mat engem Serverschlëssel mat engem Fangerofdrock wéi dësen, ech wëll DH," an et huet mat engem dommen 404 geäntwert

Wat géift Dir vun dëser Server Äntwert denken? Wat kann een maachen? Et gëtt keen ze froen (mee méi doriwwer am zweeten Deel).

Hei gëtt all Interessi um Dock gemaach

Ech hunn soss näischt ze maachen, ech hu just gedreemt, Zuelen zréck an zréck ze konvertéieren

Zwee 32 Bit Zuelen. Ech hunn se gepackt wéi all déi aner

Awer nee, dës zwee mussen als éischt op d'Linn bäigefüügt ginn als BE

Vadim Goncharov, [20.06.18 15:49] a wéinst dëser 404?

Vasily, [20.06.18 15:49] JO!

Vadim Goncharov, [20.06.18 15:50] also ech verstinn net wat hien "net fonnt huet"

Vasily, [20.06.18 15:50] ongeféier uginn

Ech konnt net sou eng Zersetzung an prime Faktoren fannen%)

Mir hunn net emol Feelerberichterstattung verwalten

Vasily, [20.06.18 20:18] Oh, et gëtt och MD5. Schonn dräi verschidden hashes

De Schlëssel Fangerofdrock gëtt wéi follegt berechent:

digest = md5(key + iv)
fingerprint = substr(digest, 0, 4) XOR substr(digest, 4, 4)

SHA1 an Sha2

Also loosst eis et soen auth_key mir kruten 2048 Bits zu Gréisst benotzt Diffie-Hellman. Wat ass nächst? Als nächst entdecken mir datt déi ënnescht 1024 Bits vun dësem Schlëssel net op iergendeng Manéier benotzt ginn ... awer loosst eis elo iwwer dëst denken. Op dësem Schrëtt hu mir e gemeinsame Geheimnis mam Server. En Analog vun der TLS Sessioun gouf etabléiert, wat eng ganz deier Prozedur ass. Awer de Server weess nach ëmmer näischt iwwer wien mir sinn! Nach net, eigentlech. Autorisatioun. Déi. wann Dir am Sënn vun "Login-Passwuert" geduecht hutt, wéi Dir eemol am ICQ gemaach hutt, oder op d'mannst "Login-Schlëssel", wéi an SSH (zum Beispill, op e puer gitlab / github). Mir kruten en anonyme. Wat wann de Server eis seet "dës Telefonsnummer sinn vun engem aneren DC servéiert"? Oder souguer "Är Telefonsnummer ass verbueden"? Dat Bescht wat mir maache kënnen ass de Schlëssel ze halen an der Hoffnung datt et nëtzlech ass a bis dohin net verrotten.

Mir hunn et iwwregens mat Reservatiounen "empfaangen". Zum Beispill, vertraue mir de Server? Wat wann et gefälscht ass? Kryptografesch Schecken wieren néideg:

Vasily, [21.06.18 17:53] Si bidden mobil Clientë fir eng 2kbit Nummer fir Primalitéit ze kontrolléieren%)

Awer et ass guer net kloer, nafeijoa

Vasily, [21.06.18 18:02] D'Dokument seet net wat ze maachen wann et net einfach ass

Net gesot. Loosst eis kucken wat den offiziellen Android Client an dësem Fall mécht? A dat ass dat (an jo, de ganzen Dossier ass interessant) - wéi se soen, ech loossen dat einfach hei:

278     static const char *goodPrime = "c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c3720fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f642477fe96bb2a941d5bcd1d4ac8cc49880708fa9b378e3c4f3a9060bee67cf9a4a4a695811051907e162753b56b0f6b410dba74d8a84b2a14b3144e0ef1284754fd17ed950d5965b4b9dd46582db1178d169c6bc465b0d6ff9ca3928fef5b9ae4e418fc15e83ebea0f87fa9ff5eed70050ded2849f47bf959d956850ce929851f0d8115f635b105ee2e4e15d04b2454bf6f4fadf034b10403119cd8e3b92fcc5b";
279   if (!strcasecmp(prime, goodPrime)) {

Nee, natierlech ass et nach ëmmer do e puer Et ginn Tester fir d'Primalitéit vun enger Zuel, mee perséinlech hunn ech net méi genuch Wëssen iwwer Mathematik.

Okay, mir hunn de Master Schlëssel. Umellen, d.h. schécken Demanden, Dir musst weider Verschlësselung Leeschtunge, benotzt AES.

De Messageschlëssel ass definéiert als 128 mëttlere Bits vun der SHA256 vum Message Kierper (inklusiv Sessioun, Message ID, etc.), Inklusiv de Padding Bytes, virausgesat vun 32 Bytes aus dem Autorisatiounsschlëssel.

Vasily, [22.06.18 14:08] Duerchschnëtt, Bitch, Bits

Krut auth_key. All. Doriwwer eraus ... et ass net kloer aus dem Dokument. Fillt Iech gratis den Open Source Code ze studéieren.

Bedenkt datt MTProto 2.0 vun 12 bis 1024 Bytes Polsterung erfuerdert, nach ënner der Bedingung datt déi resultéierend Messagelängt mat 16 Bytes deelbar ass.

Also wéi vill Polsterung sollt Dir addéieren?

A jo, et gëtt och e 404 am Fall vun engem Feeler

Wann iergendeen d'Diagramm an den Text vun der Dokumentatioun suergfälteg studéiert huet, hu se gemierkt datt et kee MAC do ass. An datt AES an engem bestëmmte IGE Modus benotzt gëtt, deen soss néierens benotzt gëtt. Si schreiwen selbstverständlech doriwwer an hirer FAQ ... Hei ass de Messageschlëssel selwer och den SHA Hash vun den entschlësselten Donnéeën, benotzt fir d'Integritéit ze kontrolléieren - an am Fall vun engem Mëssverständnis, d'Dokumentatioun aus iergendengem Grond recommandéiert se roueg ze ignoréieren (awer wat iwwer Sécherheet, wat wa se eis briechen?).

Ech sinn keen Kryptograf, vläicht ass et näischt falsch mat dësem Modus an dësem Fall aus theoretesch Siicht. Awer ech ka kloer e praktesche Problem nennen, andeems Dir Telegram Desktop als Beispill benotzt. Et verschlësselt de lokalen Cache (all dës D877F783D5D3EF8C) op déiselwecht Manéier wéi Messagen am MTProto (nëmmen an dësem Fall Versioun 1.0), d.h. éischt de Message Schlëssel, dann d'Donnéeën selwer (an iergendwou nieft der Haaptrei grouss auth_key 256 Bytes, ouni déi msg_key nëtzlos). Also gëtt de Problem op grouss Dateien bemierkbar. Nämlech, Dir musst zwee Kopie vun den Donnéeën halen - verschlësselte an entschlësselt. A wann et Megabytes sinn, oder Streaming Video, zum Beispill? .. Klassesch Schemaen mat MAC nom Chiffertext erlaben Iech et ze streamen, direkt iwwerdroen. Awer mat MTProto musst Dir als éischt verschlësselt oder entschlësselt de ganze Message, nëmmen dann iwwerdroe se an d'Netzwierk oder op Disk. Dofir, an de leschten Versioune vum Telegram Desktop am Cache an user_data En anert Format gëtt och benotzt - mat AES am CTR Modus.

Vasily, [21.06.18 01:27] Oh, ech hunn erausfonnt wat IGE ass: IGE war den éischte Versuch vun engem "authentifizéierende Verschlësselungsmodus", ursprénglech fir Kerberos. Et war e gescheitert Versuch (et gëtt keen Integritéit Schutz), an huet geläscht ginn. Dat war den Ufank vun enger 20 Joer Sich no engem authentifizéierende Verschlësselungsmodus dee funktionnéiert, wat viru kuerzem a Modi wéi OCB a GCM kulminéiert huet.

An elo d'Argumenter vun der Weenchen Säit:

D'Team hannert Telegram, gefouert vum Nikolai Durov, besteet aus sechs ACM Championen, d'Halschent vun hinnen Ph.Ds an der Mathematik. Et huet hinnen ongeféier zwee Joer gedauert fir déi aktuell Versioun vum MTProto auszerollen.

Dat ass witzeg. Zwee Joer um ënneschten Niveau

Oder Dir kënnt just tls huelen

Okay, loosst eis soen datt mir d'Verschlësselung an aner Nuancen gemaach hunn. Ass et endlech méiglech Ufroe serialiséiert an TL ze schécken an d'Äntwerten ze deserialiséieren? Also wat a wéi soll Dir schécken? Hei, loosst eis soen, d'Method initConnection, vläicht ass dëst et?

Vasily, [25.06.18 18:46] Initialiséiert d'Verbindung a späichert Informatioun iwwer den Apparat an d'Applikatioun vum Benotzer.

Et akzeptéiert app_id, device_model, system_version, app_version a lang_code.

An e puer Ufro

Dokumentatioun wéi ëmmer. Fillt Iech gratis d'Open Source ze studéieren

Wann alles ongeféier kloer mat invokeWithLayer war, wat ass dann falsch hei? Et stellt sech eraus, loosst eis soen, mir hunn - de Client hat schonn eppes fir de Server ze froen - et gëtt eng Ufro déi mir wollten schécken:

Vasily, [25.06.18 19:13] Laut dem Code beurteelen, ass den éischten Uruff an dësem Schäiss gewéckelt, an de Schäiss selwer ass an Invokewithlayer gewéckelt

Firwat konnt initConnection net en separaten Uruff sinn, awer muss e Wrapper sinn? Jo, wéi et sech erausstellt, muss et all Kéier am Ufank vun all Sessioun gemaach ginn, an net eemol, wéi mam Haaptschlëssel. Awer! Et kann net vun engem onerlaabten Benotzer genannt ginn! Elo si mir op der Etapp ukomm, wou et applicabel ass dësen Dokumentatiounssäit - an et seet eis datt ...

Nëmmen e klengen Deel vun den API Methoden sinn fir onerlaabt Benotzer verfügbar:

  • auth.sendCode
  • auth.resendCode
  • account.getPassword
  • auth.checkPassword
  • auth.checkPhone
  • auth.signUp
  • auth.signIn
  • auth.importAuthorization
  • help.getConfig
  • help.getNearestDc
  • help.getAppUpdate
  • help.getCdnConfig
  • langpack.getLangPack
  • langpack.getStrings
  • langpack.getDifference
  • langpack.getLanguages
  • langpack.getLanguage

Déi éischt vun hinnen, auth.sendCode, an et ass déi geschätzte éischt Ufro an där mir api_id an api_hash schécken, an duerno kréien mir eng SMS mat engem Code. A wa mir an der falscher DC sinn (Telefonsnummeren an dësem Land gi vun engem aneren zerwéiert, zum Beispill), da kréie mir e Feeler mat der Nummer vum gewënschten DC. Fir erauszefannen wéi eng IP Adress no DC Nummer Dir musst konnektéieren, hëlleft eis help.getConfig. Eng Kéier waren et nëmmen 5 Entréen, awer no de berühmten Eventer vun 2018 ass d'Zuel däitlech eropgaang.

Loosst eis elo drun erënneren datt mir anonym op dëser Etapp um Server koumen. Ass et net ze deier fir just eng IP Adress ze kréien? Firwat net dëst, an aner Operatiounen, am onverschlësselten Deel vun MTProto maachen? Ech héieren d'Obligatioun: "wéi kënne mir sécher sinn datt et net RKN ass dee mat falschen Adressen reagéiert?" Fir dëst erënneren mir, datt, am Allgemengen, offiziell Clienten RSA Schlësselen sinn agebaut, d.h. kënnt Dir just abonnéieren dës Informatioun. Eigentlech gëtt dëst scho gemaach fir Informatioun iwwer d'Blockéierung vum Contournement, déi Clienten iwwer aner Kanäl kréien (logesch kann dat net an MTProto selwer gemaach ginn, Dir musst och wësse wou Dir konnektéiert).

OK. Op dëser Etapp vun der Client Autorisatioun si mir nach net autoriséiert an hunn eis Applikatioun net registréiert. Mir wëllen elo just kucken wat de Server op Methoden, déi fir en onerlaabten Benotzer verfügbar sinn, reagéiert. An hei ...

Vasily, [10.07.18 14:45] https://core.telegram.org/method/help.getConfig

config#7dae33e0 [...] = Config;
help.getConfig#c4f9186b = Config;

https://core.telegram.org/api/datacenter

config#232d5905 [...] = Config;
help.getConfig#c4f9186b = Config;

Am Schema kënnt éischt zweet

Am tdesktop Schema ass den drëtte Wäert

Jo, zënterhier ass natierlech d'Dokumentatioun aktualiséiert ginn. Och wann et geschwënn nees irrelevant ka ginn. Wéi soll en Ufänger Entwéckler wëssen? Vläicht wann Dir Är Demande registréiert, wäerte se Iech informéieren? De Vasily huet dat gemaach, awer leider, si hunn him näischt geschéckt (erëm, mir schwätzen iwwer dëst am zweeten Deel).

... Dir hutt gemierkt, datt mir schonn iergendwéi op d'API geplënnert sinn, d.h. op den nächsten Niveau, an eppes am MTProto Thema verpasst? Keng Iwwerraschung:

Vasily, [28.06.18 02:04] Mm, si ruffen duerch e puer vun den Algorithmen op e2e

Mtproto definéiert Verschlësselungsalgorithmen a Schlëssele fir béid Domainen, souwéi e bësse Wrapper Struktur

Awer si vermëschen dauernd verschidden Niveauen vum Stack, sou datt et net ëmmer kloer ass wou mtproto opgehalen huet an den nächsten Niveau ugefaang huet

Wéi vermëschen se? Gutt, hei ass deeselwechten temporäre Schlëssel fir PFS, zum Beispill (iwwregens, Telegram Desktop kann et net maachen). Et gëtt duerch eng API Ufro ausgefouert auth.bindTempAuthKey, d.h. vum Top Niveau. Awer gläichzäiteg stéiert et d'Verschlësselung um ënneschten Niveau - duerno, zum Beispill, musst Dir et nach eng Kéier maachen initConnection etc., dëst ass net einfach normal Ufro. Wat och speziell ass, ass datt Dir nëmmen EEN temporäre Schlëssel pro DC hutt, obwuel d'Feld auth_key_id an all Message erlaabt Iech de Schlëssel op d'mannst all Message z'änneren, an datt de Server d'Recht huet den temporäre Schlëssel zu all Moment ze "vergiess" - d'Dokumentatioun seet net wat an dësem Fall ze maachen ... gutt, firwat konnt net Dir hutt net e puer Schlësselen, wéi mat enger Rei vun zukünftege Salzer, an ?..

Et ginn e puer aner Saachen, déi derwäert sinn iwwer d'MTProto Thema ze notéieren.

Message Messagen, msg_id, msg_seqno, Confirmatiounen, Pings an déi falsch Richtung an aner Idiosyncrasies

Firwat musst Dir iwwer si wëssen? Well se "lecken" op e méi héijen Niveau, an Dir musst Iech bewosst sinn wann Dir mat der API schafft. Loosst eis unhuelen datt mir net un msg_key interesséiert sinn; den ënneschten Niveau huet alles fir eis entschlësselt. Awer bannent de verschlësselten Donnéeën hu mir déi folgend Felder (och d'Längt vun den Donnéeën, sou datt mir wësse wou d'Padding ass, awer dat ass net wichteg):

  • Salz - int64
  • session_id - int64
  • message_id — int64
  • seq_no - int32

Loosst eis Iech drun erënneren datt et nëmmen ee Salz fir de ganze DC ass. Firwat wëssen iwwer hir? Net nëmme well et eng Demande gëtt get_future_salts, wat Iech seet, wéi eng Intervalle gëlteg sinn, awer och well wann Äert Salz "verrotten" ass, da geet d'Botschaft (Ufro) einfach verluer. De Server wäert, natierlech, Rapport déi nei Salz vun erausginn new_session_created - mee mat deem ale muss een se zum Beispill irgendwéi nei verschécken. An dëst Thema beaflosst d'Applikatiounsarchitektur.

De Server ass erlaabt Sessiounen ganz ofzeginn an op dës Manéier aus ville Grënn z'äntwerten. Eigentlech, wat ass eng MTProto Sessioun vun der Client Säit? Dëst sinn zwou Zuelen session_id и seq_no Messagen an dëser Sëtzung. Gutt, an déi ënnerierdesch TCP Verbindung, natierlech. Loosst eis soen datt eise Client nach ëmmer net weess wéi vill Saachen ze maachen, hien huet sech ofgekoppelt an erëm ugeschloss. Wann dat séier geschitt ass - déi al Sessioun weider an der neier TCP Verbindung, Erhéijung seq_no weider. Wann et laang dauert, kann de Server et läschen, well op senger Säit ass et och eng Schlaang, wéi mir erausfonnt hunn.

Wat soll et sinn seq_no? Oh, dat ass eng komplizéiert Fro. Probéiert éierlech ze verstoen wat gemengt war:

Inhalt-Zesummenhang Message

E Message deen eng explizit Unerkennung erfuerdert. Dozou gehéiert all de Benotzer a vill Service Messagen, quasi all mat Ausnam vun Container an Unerkennung.

Message Sequenz Nummer (msg_seqno)

Eng 32-Bit Zuel gläich wéi zweemol d'Zuel vun "inhaltsbezunnen" Messagen (déi erfuerderlech Unerkennung, a besonnesch déi, déi net Container sinn), erstallt vum Sender virun dësem Message an duerno vun engem erhéicht ginn, wann den aktuelle Message eng Inhalt-Zesummenhang Message. E Container gëtt ëmmer no sengem ganzen Inhalt generéiert; dofir ass seng Sequenznummer méi grouss wéi oder gläich wéi d'Sequenznummere vun de Messagen déi dra sinn.

Wat fir een Zirkus ass dat mat engem Inkrement vun 1, an dann een aneren ëm 2? .. Ech de Verdacht datt se am Ufank "de mannst bedeitendst Bit fir ACK gemengt hunn, de Rescht ass eng Zuel", awer d'Resultat ass net ganz d'selwecht - besonnesch, et kënnt aus, kann geschéckt ginn e puer confirmations déi selwecht hunn seq_no! Wéi? Gutt, zum Beispill, de Server schéckt eis eppes, schéckt et, a mir selwer bleiwen roueg, äntweren nëmme mat Servicemeldungen, déi den Empfang vu senge Messagen bestätegt. An dësem Fall wäert eis erausginn Confirmatiouns déi selwecht erausginn Zuel hunn. Wann Dir mat TCP vertraut sidd a geduecht hutt datt dëst iergendwéi wëll kléngt, awer et schéngt net ganz wëll, well am TCP seq_no ännert net, mee Confirmatioun geet un seq_no op där anerer Säit wäert ech séier dech opreegen. Bestätegunge ginn am MTProto geliwwert NET Op der seq_no, wéi an TCP, mä duerch msg_id !

Wat ass dat msg_id, déi wichtegst vun dëse Felder? Eng eenzegaarteg Message Identifizéierer, wéi den Numm et scho seet. Et ass definéiert als eng 64-Bit Zuel, déi ënnescht Bits vun deenen erëm d'"Server-net-Server" Magie hunn, an de Rescht ass en Unix Zäitstempel, inklusiv de Fraktiounsdeel, 32 Bits no lénks verréckelt. Déi. Zäitstempel per se (a Messagen mat Zäiten déi ze vill ënnerscheeden, ginn vum Server verworf). Doraus stellt sech eraus datt dëst allgemeng en Identifizéierer ass dee fir de Client global ass. Gitt dat - loosst eis drun erënneren session_id - mir si garantéiert: Ënner kengen Ëmstänn kann e Message fir eng Sessioun an eng aner Sessioun geschéckt ginn. Dat heescht, et stellt sech eraus datt et schonn ass dräi Niveau - Sessioun, Sessiounsnummer, Message ID. Firwat sou eng Iwwerkomplikatioun, dëst Geheimnis ass ganz grouss.

An dofir, msg_id gebraucht fir...

RPC: Ufroen, Äntwerten, Feeler. Bestätegungen.

Wéi Dir vläicht gemierkt hutt, gëtt et keng speziell "Maacht eng RPC Ufro" Typ oder Funktioun iwwerall am Diagramm, obwuel et Äntwerten gëtt. No all, hu mir Inhalt-Zesummenhang Messagen! Dat ass, iergendeen de Message kéint eng Demande ginn! Oder net ze sinn. Schliisslech, vun all eenzel ass msg_id. Awer et ginn Äntwerten:

rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult;

Hei gëtt uginn op wéi eng Noriicht dëst eng Äntwert ass. Dofir, um Top-Niveau vun der API, musst Dir Iech drun erënneren wat d'Zuel vun Ärer Ufro war - ech mengen et ass net néideg ze erklären datt d'Aarbecht asynchron ass, an et kann e puer Ufroe gläichzäiteg amgaang sinn, d'Äntwerten op déi kann an all Uerdnung zréck ginn? Am Prinzip, vun dësem a Fehlermeldungen wéi keng Aarbechter, kann d'Architektur hannendrun verfollegt ginn: de Server deen eng TCP Verbindung mat Iech ënnerhält ass e Front-End Balancer, et schéckt Ufroen un d'Backends a sammelt se zréck via message_id. Et schéngt, datt alles hei kloer, logesch a gutt ass.

Jo?.. A wann Dir driwwer denkt? No allem huet d'RPC Äntwert selwer och e Feld msg_id! Musse mir um Server ruffen "Dir äntwert meng Äntwert net!"? An jo, wat war do iwwer Confirmatiounen? Iwwer Säit Messagen iwwer Messagen seet eis wat ass

msgs_ack#62d6b459 msg_ids:Vector long = MsgsAck;

an et muss vun all Säit gemaach ginn. Awer net ëmmer! Wann Dir e RpcResult kritt hutt, déngt et selwer als Bestätegung. Dat ass, de Server kann op Är Ufro mat MsgsAck äntweren - wéi "Ech krut et." RpcResult kann direkt äntweren. Et kéint souwuel sinn.

An jo, Dir musst nach ëmmer d'Äntwert beäntweren! Bestätegung. Soss wäert de Server et als net geliwwert betruechten an et erëm un Iech schécken. Och nom Reconnection. Awer hei stellt sech natierlech d'Fro vun den Timeouts op. Loosst eis se e bësse méi spéit kucken.

An der Tëschenzäit kucke mer op méiglech Ufro Ausféierungsfehler.

rpc_error#2144ca19 error_code:int error_message:string = RpcError;

Oh, iergendeen wäert ausruffen, hei ass e méi humant Format - et gëtt eng Linn! Huel der Zäit. Hei Lëscht vu Feeler, awer natierlech net komplett. Doraus léiere mir datt de Code ass eppes wéi HTTP-Feeler (gutt, natierlech, d'Semantik vun den Äntwerte gëtt net respektéiert, op e puer Plazen gi se zoufälleg tëscht de Coden verdeelt), an d'Linn gesäit aus wéi CAPITAL_LETTERS_AND_NUMBERS. Zum Beispill PHONE_NUMBER_OCCUPIED oder FILE_PART_Х_MISSING. Gutt, dat ass, Dir wäert nach ëmmer dës Linn brauchen parse. Zum Beispill FLOOD_WAIT_3600 wäert heeschen, datt Dir eng Stonn wait muss, an PHONE_MIGRATE_5, datt eng Telefonsnummer mat dësem Präfix muss am 5. DC ugemellt ginn. Mir hunn eng Zort Sprooch, richteg? Mir brauche keen Argument vun engem String, reegelméisseg wäerten et maachen, okay.

Erëm, dëst ass net op der Service Messagen Säit, awer, wéi et scho gewinnt ass mat dësem Projet, kann d'Informatioun fonnt ginn op enger anerer DokumentatiounssäitAn. Oder mësstrauen. Als éischt, kuckt, Tippen / Schichtverletzung - RpcError ka verstoppt ginn RpcResult. Firwat net dobaussen? Wat hu mir net Rechnung gedroen?.. Deementspriechend, wou ass d'Garantie datt RpcError kann NET agebonne ginn RpcResult, mee direkt oder nestéiert an engem aneren Typ? .. A wann et net kann, firwat ass et net um Top Niveau, d.h. et feelt req_msg_id ? ..

Awer loosst eis weider iwwer Servicemeldungen. De Client kann denken datt de Server fir eng laang Zäit denkt an dës wonnerbar Ufro mécht:

rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;

Et ginn dräi méiglech Äntwerten op dës Fro, déi erëm mam Bestätegungsmechanismus intersectéieren; probéieren ze verstoen wat se solle sinn (a wat d'allgemeng Lëscht vun Typen déi keng Bestätegung erfuerderen) gëtt dem Lieser als Hausaufgaben iwwerlooss (Notiz: d'Informatioun an den Telegram Desktop Quellcode ass net komplett).

Drogenofhängeger: Message Status

Am Allgemengen, vill Plazen an TL, MTProto an Telegram am Allgemengen verloossen e Gefill vun haartnäckege, mä aus Héiflechkeet, Takt an anerer weich Fäegkeeten Mir hunn héiflech driwwer geschwächt, an d'Obszänitéiten an den Dialogen zensuréiert. Allerdéngs ass dës PlazОmeescht vun der Säit ass iwwer Messagen iwwer Messagen Et schockéiert souguer fir mech, dee scho laang mat Netzwierkprotokoller geschafft huet an Vëloen vu verschiddene Grad vu Kräftheet gesinn huet.

Et fänkt onschëlleg un, mat Bestätegungen. Nächst soen se eis iwwer

bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification;
bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification;

Gutt, jidderee dee mat MTProto ufänkt ze schaffen, muss mat hinnen këmmeren; am "korrigéierten - nei kompiléierten - lancéierten" Zyklus, Zuelfehler oder Salz ze kréien, déi et fäerdeg bruecht huet schlecht ze goen wärend den Ännerungen ass eng gemeinsam Saach. Wéi och ëmmer, et ginn zwee Punkten hei:

  1. Dat heescht, datt d'Original Message verluer ass. Mir mussen e puer Schlaangen erstellen, mir kucken dat méi spéit.
  2. Wat sinn dës komesch Feeler Zuelen? 16, 17, 18, 19, 20, 32, 33, 34, 35, 48, 64 ... wou sinn déi aner Zuelen, Tommy?

D'Dokumentatioun seet:

D'Intent ass datt error_code Wäerter gruppéiert sinn (error_code >> 4): zum Beispill, d'Coden 0x40 - 0x4f entspriechen Feeler am Container Zersetzung.

mä, éischtens, eng Verréckelung an déi aner Richtung, an zweetens, et egal, wou sinn déi aner Coden? Am Kapp vum Auteur?.. Dat sinn awer Klengegkeeten.

Sucht fänkt a Messagen iwwer Messagestatusen a Messagekopien un:

  • Ufro fir Message Status Informatiounen
    Wann eng Partei eng Zäit laang keng Informatioun iwwer de Status vu sengen erausginn Messagen kritt huet, kann se et explizit vun der anerer Partei ufroen:
    msgs_state_req#da69fb52 msg_ids:Vector long = MsgsStateReq;
  • Informatiounsmessage iwwer Status vu Messagen
    msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
    Hei ass info ass eng String déi genee ee Byte vum Message Status fir all Message vun der erakommen msg_ids Lëscht enthält:

    • 1 = näischt iwwer de Message bekannt (msg_id ze niddreg, déi aner Partei kann et vergiess hunn)
    • 2 = Message net kritt (msg_id fällt am Beräich vun de gespäicherten Identifizéierer; awer déi aner Partei huet sécher net esou e Message kritt)
    • 3 = Message net kritt (msg_id ze héich; déi aner Partei huet se awer sécher nach net kritt)
    • 4 = Message kritt (notéiert datt dës Äntwert och zur selwechter Zäit eng Empfangserkennung ass)
    • +8 = Message schonn unerkannt
    • +16 = Message erfuerdert keng Unerkennung
    • +32 = RPC Ufro enthale am Message gëtt veraarbecht oder d'Veraarbechtung scho fäerdeg
    • +64 = Inhalt-Zesummenhang Äntwert op Message scho generéiert
    • +128 = aner Partei weess fir eng Tatsaach, datt Message scho kritt ass
      Dës Äntwert erfuerdert keng Unerkennung. Et ass eng Unerkennung vun der relevant msgs_state_req, an a vu sech selwer.
      Bedenkt datt wann et op eemol erausstellt datt déi aner Partei kee Message huet deen ausgesäit wéi wann se geschéckt gi wier, kann de Message einfach nei geschéckt ginn. Och wann déi aner Partei zwou Exemplare vum Message gläichzäiteg kritt, gëtt d'Duplikat ignoréiert. (Wann ze vill Zäit vergaangen ass, an den ursprénglechen msg_id ass net méi valabel, muss de Message an msg_copy gewéckelt ginn).
  • Fräiwëlleg Kommunikatioun vun Status vun Messagen
    All Partei kann déi aner Partei fräiwëlleg iwwer de Status vun de Messagen informéieren, déi vun der anerer Partei iwwerdroe ginn.
    msgs_all_info#8cc0d131 msg_ids:Vector long info:string = MsgsAllInfo
  • Verlängert fräiwëlleg Kommunikatioun vum Status vun engem Message
    ...
    msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
    msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
  • Explizit Ufro fir Messagen nei ze schécken
    msg_resend_req#7d861a08 msg_ids:Vector long = MsgResendReq;
    D'Fernpartei äntwert direkt andeems se déi ugefrote Messagen nei schécken […]
  • Explizit Ufro fir Äntwerten nei ze schécken
    msg_resend_ans_req#8610baeb msg_ids:Vector long = MsgResendReq;
    D'Remote Partei reagéiert direkt andeems se nei schécken Äntwerten op déi ugefrote Messagen […]
  • Message Kopien
    A verschiddene Situatiounen muss en alen Message mat engem msg_id deen net méi gëlteg ass nei geschéckt ginn. Dann ass et an engem Kopie Container gewéckelt:
    msg_copy#e06046b2 orig_message:Message = MessageCopy;
    Eemol kritt, gëtt de Message veraarbecht wéi wann de Wrapper net do wier. Wann awer sécher bekannt ass datt de Message orig_message.msg_id kritt gouf, da gëtt den neie Message net veraarbecht (während gläichzäiteg en an orig_message.msg_id unerkannt ginn). De Wäert vun orig_message.msg_id muss méi niddereg sinn wéi de Container msg_id.

Loosst eis souguer roueg iwwer wat msgs_state_info erëm d'Oueren vun der ongeschloss TL stécht eraus (mir brauchen e Vecteure vun Bytes, an déi ënnescht zwee Stécker war en enum, an an der héich zwee Stécker waren Fändelen). De Punkt ass anescht. Versteet iergendeen firwat dat alles an der Praxis ass? an engem richtege Client néideg? .. Mat Schwieregkeeten, mä ee kann e puer Virdeel virstellen, wann eng Persoun am Debugging engagéiert ass, an engem interaktiven Modus - froen de Server wat a wéi. Awer hei sinn d'Demande beschriwwen Ronn Rees.

Et folgt datt all Partei net nëmmen Messagen verschlëssele muss a schécken, mee och Daten iwwer sech selwer späicheren, iwwer d'Äntwerten op si, fir eng onbekannt Zäit. D'Dokumentatioun beschreift weder d'Timing oder d'praktesch Applikatioun vun dëse Funktiounen. kee Fall. Wat am meeschte erstaunlech ass, datt se tatsächlech am Code vun offiziellen Clienten benotzt ginn! Anscheinend krute si eppes gesot, wat net an der ëffentlecher Dokumentatioun mat abegraff ass. Verstinn aus dem Code firwat, ass net méi esou einfach wéi am Fall vun TL - et ass net en (relativ) logesch isoléierten Deel, mee e Stéck un d'Applikatiounsarchitektur gebonnen, d.h. wäert däitlech méi Zäit erfuerderen fir den Applikatiounscode ze verstoen.

Pings an Timings. Schlaangen.

Vun allem, wa mir d'Erënnerung iwwer d'Serverarchitektur erënneren (Verdeelung vun Ufroen iwwer Backends), follegt eng zimlech traureg Saach - trotz all Liwwergarantien am TCP (entweder d'Donnéeën geliwwert ginn, oder Dir wäert iwwer d'Lück informéiert ginn, awer d'Donnéeë ginn geliwwert ier de Problem geschitt ass), datt Bestätegungen am MTProto selwer - keng Garantien. De Server kann einfach Äre Message verléieren oder geheien eraus, an näischt kann doriwwer gemaach ginn, benotzen just verschidden Zorte vu crutches.

An als éischt - Message Schlaangen. Gutt, mat enger Saach war alles offensichtlech vun Ufank un - en onbestätegte Message muss gespäichert ginn a verréckt ginn. An no wéi enger Zäit? An de Geck kennt hien. Vläicht léisen déi süchteg Servicemeldungen iergendwéi dëse Problem mat Krutzen, soen, am Telegram Desktop ginn et ongeféier 4 Schlaangen, déi hinnen entspriechen (vläicht méi, wéi scho gesot, dofir musst Dir méi eescht a säi Code an Architektur verdéiwen; gläichzäiteg Zäit, mir Mir wëssen datt et net als Probe geholl ka ginn; eng gewëssen Zuel vun Typen aus dem MTProto Schema ginn net an der benotzt).

Firwat geschitt dat? Wahrscheinlech konnten d'Serverprogramméierer d'Zouverlässegkeet am Cluster net garantéieren, oder souguer op de Frontbalancer bufferen, an hunn dëse Problem un de Client transferéiert. Aus Verzweiflung huet de Vasily probéiert eng alternativ Optioun ëmzesetzen, mat nëmmen zwou Schlaangen, mat Algorithmen vum TCP - d'RTT op de Server moossen an d'Gréisst vun der "Fënster" (a Messagen) upassen ofhängeg vun der Unzuel vun onbeständegen Ufroen. Dat ass, sou eng rau Heuristik fir d'Belaaschtung vum Server ze bewäerten ass wéi vill vun eisen Ufroen et zur selwechter Zäit ka kauen an net verléieren.

Gutt, dat ass, Dir verstitt, richteg? Wann Dir TCP erëm op engem Protokoll implementéiere musst, deen iwwer TCP leeft, beweist dëst e ganz schlecht entwéckelte Protokoll.

Oh jo, firwat braucht Dir méi wéi eng Schlaang, a wat heescht dat iwwerhaapt fir eng Persoun déi mat engem High-Level API schafft? Kuckt, Dir maacht eng Ufro, serialiséiert se, awer dacks kënnt Dir se net direkt schécken. Firwat? Well d'Äntwert wäert sinn msg_id, déi temporär assаEch sinn e Label, deem seng Uerderung am beschten bis sou spéit wéi méiglech ausgestallt gëtt - am Fall wou de Server et refuséiert wéinst engem Mëssverständnis vun der Zäit tëscht eis an him (natierlech kënne mir eng Krut maachen déi eis Zäit vun der heiteger Verréckelung verännert op de Server andeems en Delta bäigefüügt gëtt berechent aus den Äntwerte vum Server - offiziell Clientë maachen dëst, awer et ass rau an ongenau wéinst Puffer). Dofir, wann Dir eng Ufro mat engem lokalen Funktiounsruff aus der Bibliothéik maacht, geet de Message duerch déi folgend Etappen:

  1. Et läit an enger Schlaang a waart op Verschlësselung.
  2. Ernannt msg_id an de Message goung an eng aner Schlaang - méiglech Expeditioun; schécken op de Socket.
  3. a) De Server huet MsgsAck geäntwert - de Message gouf geliwwert, mir läschen et aus der "anerer Schlaang".
    b) Oder vice-versa, hien huet eppes net gär, hien huet Badmsg geäntwert - nei aus "anerer Schlaang" schécken
    c) Näischt ass bekannt, de Message muss aus enger anerer Schlaang zréckgeschéckt ginn - awer et ass net genau gewosst wéini.
  4. De Server huet endlech geäntwert RpcResult - déi tatsächlech Äntwert (oder Feeler) - net nëmme geliwwert, awer och veraarbecht.

Wahrscheinlech, d'Benotzung vu Container kéint de Problem deelweis léisen. Dëst ass wann eng Rëtsch Messagen an ee gepackt sinn, an de Server mat enger Bestätegung op all vun hinnen op eemol geäntwert huet, an engem msg_id. Mä hie wäert dëse Pak och refuséieren, wann eppes falsch gaang ass, a senger ganzer.

An op dësem Punkt kommen net-technesch Iwwerleeungen an d'Spill. Aus Erfahrung hu mir vill Kruuchten gesinn, an zousätzlech wäerte mir elo méi Beispiller vu schlechte Rotschléi an Architektur gesinn - an esou Konditiounen, ass et derwäert ze Vertrauen an esou Entscheedungen ze treffen? D'Fro ass rhetoresch (natierlech net).

Wat schwätzen mir iwwer? Wann Dir iwwer d'Thema "Drogemeldungen iwwer Messagen" nach ëmmer spekuléiere kënnt mat Objektiounen wéi "Dir sidd domm, Dir hutt eise geniale Plang net verstanen!" (also schreift d'Dokumentatioun fir d'éischt, wéi normal Leit sollen, mat Begrënnung a Beispiller vu Paketaustausch, da schwätze mer), da sinn Timings/Timeouts eng reng praktesch a spezifesch Fro, alles hei ass scho laang bekannt. Wat seet d'Dokumentatioun eis iwwer Timeouts?

E Server erkennt normalerweis d'Empfang vun engem Message vun engem Client (normalerweis eng RPC Ufro) mat enger RPC Äntwert. Wann eng Äntwert eng laang Zäit kënnt, kann e Server als éischt eng Empfangserkennung schécken, an e bësse méi spéit d'RPC Äntwert selwer.

E Client erkennt normalerweis d'Empfang vun engem Message vun engem Server (normalerweis eng RPC Äntwert) andeems en eng Unerkennung op déi nächst RPC Ufro bäidréit wann se net ze spéit iwwerdroe gëtt (wann se generéiert gëtt, sot, 60-120 Sekonnen no der Empfang vun engem Message vum Server). Wéi och ëmmer, wann et fir eng laang Zäit kee Grond gëtt fir Messagen un de Server ze schécken oder wann et eng grouss Zuel vun onbekannte Messagen vum Server gëtt (soen iwwer 16), iwwerdréit de Client eng stand-alone Unerkennung.

... Ech iwwersetzen: mir selwer wësse net wéi vill a wéi mir et brauchen, also loosst eis dovun ausgoen, datt et esou ass.

An iwwer Pings:

Ping Messagen (PING/PONG)

ping#7abe77ec ping_id:long = Pong;

Eng Äntwert gëtt normalerweis op déiselwecht Verbindung zréckginn:

pong#347773c5 msg_id:long ping_id:long = Pong;

Dës Messagen erfuerderen keng Unerkennung. E Pong gëtt nëmmen als Äntwert op e Ping iwwerdroen, während e Ping vu béide Säiten initiéiert ka ginn.

Deferred Connection Closure + PING

ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;

Wierker wéi Ping. Zousätzlech, nodeems dëst kritt ass, fänkt de Server en Timer un deen déi aktuell Verbindung disconnect_delay Sekonne méi spéit zoumaacht, ausser et kritt en neie Message vum selwechten Typ deen automatesch all virdrun Timer zrécksetzt. Wann de Client dës Pings eemol all 60 Sekonnen schéckt, zum Beispill, kann et disconnect_delay gläich op 75 Sekonnen setzen.

A soss geet et nach?! A 60 Sekonnen geet den Zuch an d'Gare, fuert of a hëlt Passagéier op, a verléiert erëm de Kontakt am Tunnel. An 120 Sekonnen, während Dir et héiert, kënnt et op eng aner, an d'Verbindung wäert héchstwahrscheinlech briechen. Gutt, et ass kloer wou d'Been hierkommen - "Ech hunn e Réng héieren, awer weess net wou et ass", et gëtt dem Nagl säin Algorithmus an d'TCP_NODELAY Optioun, geduecht fir interaktiv Aarbecht. Awer, entschëllegt mech, halen op säi Standardwäert - 200 MilliSekonnen Wann Dir wierklech eppes Ähnleches duerstellt an op e puer méigleche Pakete späichere wëllt, da setzt se fir 5 Sekonnen of, oder wat och ëmmer de "Benotzer tippt ..." Message Timeout ass elo. Awer net méi.

An endlech, Pings. Dat ass, d'Liveness vun der TCP Verbindung ze kontrolléieren. Et ass witzeg, awer virun ongeféier 10 Joer hunn ech e kriteschen Text iwwer de Messenger vun eiser Fakultéit geschriwwen - d'Auteuren do hunn och de Server vum Client gepingelt, an net vice versa. Awer 3. Joer Studenten sinn eng Saach, an en internationale Büro ass eng aner, richteg? ..

Éischten, e klenge pädagogesche Programm. Eng TCP Verbindung, an der Verontreiung vu Paketaustausch, ka fir Wochen liewen. Dëst ass souwuel gutt a schlecht, ofhängeg vum Zweck. Et ass gutt wann Dir eng SSH Verbindung mam Server opgemaach hutt, Dir sidd vum Computer opgestan, de Router nei gestart, zréck op Är Plaz - d'Sessioun duerch dëse Server gouf net zerräissen (Dir hutt näischt aginn, et waren keng Päckchen) , et ass bequem. Et ass schlecht wann et Dausende vu Clienten um Server sinn, déi all Ressourcen ophuelen (Moien, Postgres!), An de Host vum Client kann viru laanger Zäit nei gestart sinn - awer mir wësse net doriwwer.

Chat / IM Systemer falen an den zweete Fall aus engem zousätzleche Grond - Online Status. Wann de Benotzer "ofgefall ass", musst Dir seng Gespréichspartner doriwwer informéieren. Soss wäert Dir mat engem Feeler ophalen, deen d'Creatoren vu Jabber gemaach hunn (a korrigéiert fir 20 Joer) - de Benotzer huet sech ofgeschalt, awer si schreiwen weider Messagen un him, gleewen datt hien online ass (déi och komplett verluer waren an dësen e puer Minutten ier den Trennung entdeckt gouf). Nee, d'Optioun TCP_KEEPALIVE, déi vill Leit, déi net verstinn wéi TCP Timer funktionnéieren, zoufälleg erabréngen (duerch d'Astellung vun wilde Wäerter wéi Zénger vu Sekonnen), hëlleft hei net - Dir musst sécher sinn datt net nëmmen den OS Kernel vun der Maschinn vum Benotzer lieweg ass, awer och normal funktionéiert, a fäeg ass ze reagéieren, an d'Applikatioun selwer (denkt Dir et kann net afréieren? Telegram Desktop op Ubuntu 18.04 ass fir mech méi wéi eemol gefruer).

Dofir musst Dir pingelen Server Client, an net vice versa - wann de Client dat mécht, wann d'Verbindung gebrach ass, gëtt de Ping net geliwwert, d'Zil gëtt net erreecht.

Wat gesi mir op Telegram? Et ass genee de Géigendeel! Gutt, dat ass. Formell, natierlech, kënnen zwou Säiten all aner Ping. An der Praxis benotzen d'Clienten eng Krut ping_delay_disconnect, deen den Timer op de Server setzt. Gutt, entschëllegt, et ass net un de Client ze entscheeden wéi laang hien do ouni Ping wëll liewen. De Server, baséiert op senger Laascht, weess besser. Awer natierlech, wann Dir d'Ressourcen net këmmert, da sidd Dir Ären eegene béise Pinocchio, an eng Krut wäert maachen ...

Wéi soll et entworf ginn?

Ech gleewen, datt déi uewe genannte Fakten kloer weisen datt d'Telegram/VKontakte Team net ganz kompetent ass am Beräich vum Transport (an nidderegen) Niveau vun Computernetzwierker an hir niddereg Qualifikatiounen an relevante Saachen.

Firwat huet et sech sou komplizéiert erausgestallt, a wéi kënnen Telegram Architekten probéieren ze protestéieren? D'Tatsaach, datt se probéiert hunn eng Sessioun ze maachen, déi TCP-Verbindungspaus iwwerlieft, dh wat elo net geliwwert gouf, wäerte mir spéider liwweren. Si hu wahrscheinlech och probéiert en UDP-Transport ze maachen, awer si hu Schwieregkeeten opgetrueden an hunn se opginn (dofir ass d'Dokumentatioun eidel - et war näischt ze prahle). Awer wéinst engem Mangel u Verständnis wéi Netzwierker am Allgemengen an TCP besonnesch funktionnéieren, wou Dir op et vertraue kënnt, a wou Dir et selwer maache musst (a wéi), an e Versuch dëst mat der Kryptografie ze kombinéieren "zwee Villercher mat ee Steen", dat ass d'Resultat.

Wéi war et néideg? Baséiert op der Tatsaach, datt msg_id ass en Zäitstempel noutwendeg aus enger kryptographescher Siicht fir Replayattacken ze vermeiden, et ass e Feeler fir eng eenzegaarteg Identifizéierungsfunktioun unzeschléissen. Dofir, ouni grondsätzlech déi aktuell Architektur z'änneren (wann den Updates Stream generéiert gëtt, dat ass en High-Level API Thema fir en aneren Deel vun dëser Serie vu Posts), musst Dir:

  1. De Server, deen d'TCP Verbindung mam Client hält, iwwerhëlt Verantwortung - wann et aus der Socket gelies huet, w.e.g. unerkennen, veraarbecht oder e Feeler zréckginn, kee Verloscht. Dann ass d'Bestätegung net e Vektor vun IDen, awer einfach "déi lescht kritt seq_no" - just eng Zuel, wéi am TCP (zwee Zuelen - Är seq an déi bestätegt). Mir sinn ëmmer an der Sëtzung, oder?
  2. Den Zäitstempel fir Replayattacken ze verhënneren gëtt e separaten Terrain, a la nonce. Et gëtt gepréift, awer beaflosst näischt anescht. Genuch an uint32 - wann eis Salz op d'mannst all hallef Dag ännert, kënne mir 16 Bits op déi niddereg Uerdnung Bits vun engem ganzen Deel vun der aktueller Zäit allocéieren, de Rescht - zu engem Fraktiounsdeel vun enger Sekonn (wéi elo).
  3. Geläscht msg_id iwwerhaapt - aus der Siicht vun z'ënnerscheeden Ufroen op der backends, do ass, éischtens, de Client ID, an zweetens, der Sessioun ID, concatenate hinnen. Deementspriechend ass nëmmen eng Saach genuch als Ufroidentifizéierer seq_no.

Dëst ass och net déi erfollegräichst Optioun; e komplette Zoufall kéint als Identifizéierer déngen - dëst gëtt iwwregens schonn an der High-Level API gemaach wann Dir e Message schéckt. Et wier besser d'Architektur komplett vun relativ op absolut nei ze maachen, awer dëst ass en Thema fir en aneren Deel, net dëse Post.

API?

Ta-daam! Also, nodeems mir duerch e Wee voller Péng a Kruuchten gekämpft hunn, konnte mir endlech all Ufro un de Server schécken an all Äntwerten op se kréien, souwéi Updates vum Server kréien (net als Äntwert op eng Ufro, awer et selwer schéckt eis, wéi PUSH, wann iergendeen et méi kloer ass).

Opgepasst, elo gëtt et dat eenzegt Beispill am Perl am Artikel! (fir déi, déi net mat der Syntax vertraut sinn, ass dat éischt Argument vu Segen d'Datestruktur vum Objet, déi zweet ass seng Klass):

2019.10.24 12:00:51 $1 = {
'cb' => 'TeleUpd::__ANON__',
'out' => bless( {
'filter' => bless( {}, 'Telegram::ChannelMessagesFilterEmpty' ),
'channel' => bless( {
'access_hash' => '-6698103710539760874',
'channel_id' => '1380524958'
}, 'Telegram::InputPeerChannel' ),
'pts' => '158503',
'flags' => 0,
'limit' => 0
}, 'Telegram::Updates::GetChannelDifference' ),
'req_id' => '6751291954012037292'
};
2019.10.24 12:00:51 $1 = {
'in' => bless( {
'req_msg_id' => '6751291954012037292',
'result' => bless( {
'pts' => 158508,
'flags' => 3,
'final' => 1,
'new_messages' => [],
'users' => [],
'chats' => [
bless( {
'title' => 'Хулиномика',
'username' => 'hoolinomics',
'flags' => 8288,
'id' => 1380524958,
'access_hash' => '-6698103710539760874',
'broadcast' => 1,
'version' => 0,
'photo' => bless( {
'photo_small' => bless( {
'volume_id' => 246933270,
'file_reference' => '
'secret' => '1854156056801727328',
'local_id' => 228648,
'dc_id' => 2
}, 'Telegram::FileLocation' ),
'photo_big' => bless( {
'dc_id' => 2,
'local_id' => 228650,
'file_reference' => '
'secret' => '1275570353387113110',
'volume_id' => 246933270
}, 'Telegram::FileLocation' )
}, 'Telegram::ChatPhoto' ),
'date' => 1531221081
}, 'Telegram::Channel' )
],
'timeout' => 300,
'other_updates' => [
bless( {
'pts_count' => 0,
'message' => bless( {
'post' => 1,
'id' => 852,
'flags' => 50368,
'views' => 8013,
'entities' => [
bless( {
'length' => 20,
'offset' => 0
}, 'Telegram::MessageEntityBold' ),
bless( {
'length' => 18,
'offset' => 480,
'url' => 'https://alexeymarkov.livejournal.com/[url_вырезан].html'
}, 'Telegram::MessageEntityTextUrl' )
],
'reply_markup' => bless( {
'rows' => [
bless( {
'buttons' => [
bless( {
'text' => '???? 165',
'data' => 'send_reaction_0'
}, 'Telegram::KeyboardButtonCallback' ),
bless( {
'data' => 'send_reaction_1',
'text' => '???? 9'
}, 'Telegram::KeyboardButtonCallback' )
]
}, 'Telegram::KeyboardButtonRow' )
]
}, 'Telegram::ReplyInlineMarkup' ),
'message' => 'А вот и новая книга! 
// [текст сообщения вырезан чтоб не нарушать правил Хабра о рекламе]
напечатаю.',
'to_id' => bless( {
'channel_id' => 1380524958
}, 'Telegram::PeerChannel' ),
'date' => 1571724559,
'edit_date' => 1571907562
}, 'Telegram::Message' ),
'pts' => 158508
}, 'Telegram::UpdateEditChannelMessage' ),
bless( {
'pts' => 158508,
'message' => bless( {
'edit_date' => 1571907589,
'to_id' => bless( {
'channel_id' => 1380524958
}, 'Telegram::PeerChannel' ),
'date' => 1571807301,
'message' => 'Почему Вы считаете Facebook плохой компанией? Можете прокомментировать? По-моему, это шикарная компания. Без долгов, с хорошей прибылью, а если решат дивы платить, то и еще могут нехило подорожать.
Для меня ответ совершенно очевиден: потому что Facebook делает ужасный по качеству продукт. Да, у него монопольное положение и да, им пользуется огромное количество людей. Но мир не стоит на месте. Когда-то владельцам Нокии было смешно от первого Айфона. Они думали, что лучше Нокии ничего быть не может и она навсегда останется самым удобным, красивым и твёрдым телефоном - и доля рынка это красноречиво демонстрировала. Теперь им не смешно.
Конечно, рептилоиды сопротивляются напору молодых гениев: так Цукербергом был пожран Whatsapp, потом Instagram. Но всё им не пожрать, Паша Дуров не продаётся!
Так будет и с Фейсбуком. Нельзя всё время делать говно. Кто-то когда-то сделает хороший продукт, куда всё и уйдут.
#соцсети #facebook #акции #рептилоиды',
'reply_markup' => bless( {
'rows' => [
bless( {
'buttons' => [
bless( {
'data' => 'send_reaction_0',
'text' => '???? 452'
}, 'Telegram::KeyboardButtonCallback' ),
bless( {
'text' => '???? 21',
'data' => 'send_reaction_1'
}, 'Telegram::KeyboardButtonCallback' )
]
}, 'Telegram::KeyboardButtonRow' )
]
}, 'Telegram::ReplyInlineMarkup' ),
'entities' => [
bless( {
'length' => 199,
'offset' => 0
}, 'Telegram::MessageEntityBold' ),
bless( {
'length' => 8,
'offset' => 919
}, 'Telegram::MessageEntityHashtag' ),
bless( {
'offset' => 928,
'length' => 9
}, 'Telegram::MessageEntityHashtag' ),
bless( {
'length' => 6,
'offset' => 938
}, 'Telegram::MessageEntityHashtag' ),
bless( {
'length' => 11,
'offset' => 945
}, 'Telegram::MessageEntityHashtag' )
],
'views' => 6964,
'flags' => 50368,
'id' => 854,
'post' => 1
}, 'Telegram::Message' ),
'pts_count' => 0
}, 'Telegram::UpdateEditChannelMessage' ),
bless( {
'message' => bless( {
'reply_markup' => bless( {
'rows' => [
bless( {
'buttons' => [
bless( {
'data' => 'send_reaction_0',
'text' => '???? 213'
}, 'Telegram::KeyboardButtonCallback' ),
bless( {
'data' => 'send_reaction_1',
'text' => '???? 8'
}, 'Telegram::KeyboardButtonCallback' )
]
}, 'Telegram::KeyboardButtonRow' )
]
}, 'Telegram::ReplyInlineMarkup' ),
'views' => 2940,
'entities' => [
bless( {
'length' => 609,
'offset' => 348
}, 'Telegram::MessageEntityItalic' )
],
'flags' => 50368,
'post' => 1,
'id' => 857,
'edit_date' => 1571907636,
'date' => 1571902479,
'to_id' => bless( {
'channel_id' => 1380524958
}, 'Telegram::PeerChannel' ),
'message' => 'Пост про 1С вызвал бурную полемику. Человек 10 (видимо, 1с-программистов) единодушно написали:
// [текст сообщения вырезан чтоб не нарушать правил Хабра о рекламе]
Я бы добавил, что блестящая у 1С дистрибуция, а маркетинг... ну, такое.'
}, 'Telegram::Message' ),
'pts_count' => 0,
'pts' => 158508
}, 'Telegram::UpdateEditChannelMessage' ),
bless( {
'pts' => 158508,
'pts_count' => 0,
'message' => bless( {
'message' => 'Здравствуйте, расскажите, пожалуйста, чем вредит экономике 1С?
// [текст сообщения вырезан чтоб не нарушать правил Хабра о рекламе]
#софт #it #экономика',
'edit_date' => 1571907650,
'date' => 1571893707,
'to_id' => bless( {
'channel_id' => 1380524958
}, 'Telegram::PeerChannel' ),
'flags' => 50368,
'post' => 1,
'id' => 856,
'reply_markup' => bless( {
'rows' => [
bless( {
'buttons' => [
bless( {
'data' => 'send_reaction_0',
'text' => '???? 360'
}, 'Telegram::KeyboardButtonCallback' ),
bless( {
'data' => 'send_reaction_1',
'text' => '???? 32'
}, 'Telegram::KeyboardButtonCallback' )
]
}, 'Telegram::KeyboardButtonRow' )
]
}, 'Telegram::ReplyInlineMarkup' ),
'views' => 4416,
'entities' => [
bless( {
'offset' => 0,
'length' => 64
}, 'Telegram::MessageEntityBold' ),
bless( {
'offset' => 1551,
'length' => 5
}, 'Telegram::MessageEntityHashtag' ),
bless( {
'length' => 3,
'offset' => 1557
}, 'Telegram::MessageEntityHashtag' ),
bless( {
'offset' => 1561,
'length' => 10
}, 'Telegram::MessageEntityHashtag' )
]
}, 'Telegram::Message' )
}, 'Telegram::UpdateEditChannelMessage' )
]
}, 'Telegram::Updates::ChannelDifference' )
}, 'MTProto::RpcResult' )
};
2019.10.24 12:00:51 $1 = {
'in' => bless( {
'update' => bless( {
'user_id' => 2507460,
'status' => bless( {
'was_online' => 1571907651
}, 'Telegram::UserStatusOffline' )
}, 'Telegram::UpdateUserStatus' ),
'date' => 1571907650
}, 'Telegram::UpdateShort' )
};
2019.10.24 12:05:46 $1 = {
'in' => bless( {
'chats' => [],
'date' => 1571907946,
'seq' => 0,
'updates' => [
bless( {
'max_id' => 141719,
'channel_id' => 1295963795
}, 'Telegram::UpdateReadChannelInbox' )
],
'users' => []
}, 'Telegram::Updates' )
};
2019.10.24 13:01:23 $1 = {
'in' => bless( {
'server_salt' => '4914425622822907323',
'unique_id' => '5297282355827493819',
'first_msg_id' => '6751307555044380692'
}, 'MTProto::NewSessionCreated' )
};
2019.10.24 13:24:21 $1 = {
'in' => bless( {
'chats' => [
bless( {
'username' => 'freebsd_ru',
'version' => 0,
'flags' => 5440,
'title' => 'freebsd_ru',
'min' => 1,
'photo' => bless( {
'photo_small' => bless( {
'local_id' => 328733,
'volume_id' => 235140688,
'dc_id' => 2,
'file_reference' => '
'secret' => '4426006807282303416'
}, 'Telegram::FileLocation' ),
'photo_big' => bless( {
'dc_id' => 2,
'file_reference' => '
'volume_id' => 235140688,
'local_id' => 328735,
'secret' => '71251192991540083'
}, 'Telegram::FileLocation' )
}, 'Telegram::ChatPhoto' ),
'date' => 1461248502,
'id' => 1038300508,
'democracy' => 1,
'megagroup' => 1
}, 'Telegram::Channel' )
],
'users' => [
bless( {
'last_name' => 'Panov',
'flags' => 1048646,
'min' => 1,
'id' => 82234609,
'status' => bless( {}, 'Telegram::UserStatusRecently' ),
'first_name' => 'Dima'
}, 'Telegram::User' )
],
'seq' => 0,
'date' => 1571912647,
'updates' => [
bless( {
'pts' => 137596,
'message' => bless( {
'flags' => 256,
'message' => 'Создать джейл с именем покороче ??',
'to_id' => bless( {
'channel_id' => 1038300508
}, 'Telegram::PeerChannel' ),
'id' => 119634,
'date' => 1571912647,
'from_id' => 82234609
}, 'Telegram::Message' ),
'pts_count' => 1
}, 'Telegram::UpdateNewChannelMessage' )
]
}, 'Telegram::Updates' )
};

Jo, net e Spoiler op Zweck - wann Dir et nach net gelies hutt, maach et!

Oh, wai~~... wéi gesäit dat aus? Eppes ganz vertraut ... vläicht ass dëst d'Datenstruktur vun enger typescher Web API am JSON, ausser datt Klassen och un Objekter befestegt sinn? ..

Also dat ass wéi et sech erausstellt ... Wat ass et alles ëm, Komeroden? .. Sou vill Effort - a mir hunn opgehalen fir ze raschten wou de Webprogramméierer just ugefaangen?..Wär net nëmmen JSON iwwer HTTPS méi einfach?! Wat hu mir am Austausch kritt? War den Effort et wäert?

Loosst eis evaluéieren wat TL+MTProto eis ginn huet a wéi eng Alternativen méiglech sinn. Gutt, HTTP, deen sech op den Ufro-Äntwert Modell fokusséiert, ass e schlechte Fit, awer op d'mannst eppes uewen op TLS?

Kompakt Serialiséierung. Wann ech dës Datestruktur gesinn, ähnlech wéi JSON, erënnere mech datt et binär Versioune sinn. Loosst eis MsgPack als net genuch erweiderbar markéieren, awer et gëtt zum Beispill CBOR - iwwregens e Standard beschriwwen an RFC 7049. Et ass bemierkenswäert fir d'Tatsaach datt et definéiert tags, als Erweiderungsmechanismus, an ënner scho standardiséiert verfügbar:

  • 25 + 256 - ersetzen widderholl Zeilen mat enger Referenz op d'Linnnummer, sou eng bëlleg Kompressiounsmethod
  • 26 - serialiséiert Perl Objet mat Klass Numm an constructor Argumenter
  • 27 - serialiséierter Sproochonofhängegen Objet mat Typnumm a Konstruktorargumenter

Gutt, ech hu probéiert déiselwecht Donnéeën an TL an an CBOR ze serialiséieren mat String an Objektverpackung aktivéiert. D'Resultat huet ugefaang fir CBOR iergendwou vun engem Megabyte ze variéieren:

cborlen=1039673 tl_len=1095092

An dofir, Ausgang: Et gi wesentlech méi einfach Formater déi net dem Problem vun Synchroniséierungsfehler oder onbekannter Identifizéierer ënnerleien, mat vergläichbarer Effizienz.

Schnell Verbindung Etablissement. Dëst bedeit null RTT no Reconnection (wann de Schlëssel schonn eemol generéiert gouf) - applicabel aus dem alleréischten MTProto Message, awer mat e puer Reservatiounen - Hit déi selwecht Salz, der Sëtzung ass net verfault, etc. Wat bitt TLS eis amplaz? Zitat zum Thema:

Wann Dir PFS an TLS benotzt, TLS Sessiounsticketen (RFC 5077) fir eng verschlësselte Sessioun zréckzeféieren ouni Schlësselen nei ze verhandelen an ouni Schlësselinformatioun um Server ze späicheren. Wann Dir déi éischt Verbindung opmaacht an d'Schlëssel erstellt, verschlësselt de Server de Verbindungszoustand an iwwerdréit et un de Client (a Form vun engem Sessiounsticket). Deementspriechend, wann d'Verbindung erëmfonnt gëtt, schéckt de Client e Sessiounsticket, dorënner de Sessiounsschlëssel, zréck op de Server. Den Ticket selwer ass verschlësselt mat engem temporäre Schlëssel (Sessiounsticket-Schlëssel), deen um Server gespäichert ass a muss ënnert all Frontend-Server verdeelt ginn, déi SSL an clusteréierte Léisungen veraarbecht.[10]. Also, d'Aféierung vun engem Sessiounsticket kann PFS verletzen wann temporär Serverschlëssel kompromittéiert sinn, zum Beispill, wa se fir eng laang Zäit gespäichert ginn (OpenSSL, nginx, Apache späicheren se als Standard fir déi ganz Dauer vum Programm; populär Siten benotzen de Schlëssel fir e puer Stonnen, bis zu Deeg).

Hei ass den RTT net null, Dir musst op d'mannst ClientHello a ServerHello austauschen, duerno kann de Client Daten zesumme mat Finished schécken. Awer hei sollte mir drun erënneren datt mir net de Web hunn, mat senger Rëtsch nei opgemaachte Verbindungen, mee e Messenger, deen d'Verbindung dacks eng a méi oder manner laang lieweg, relativ kuerz Ufroe fir Websäiten ass - alles ass multiplexéiert intern. Dat ass, et ass ganz akzeptabel wa mir net op eng wierklech schlecht Subway Sektioun kommen.

Vergiess soss eppes? Schreift an de Kommentaren.

Fortsetzung kënnt no!

Am zweeten Deel vun dëser Serie vu Posts wäerte mir net technesch, mee organisatoresch Themen betruechten - Approche, Ideologie, Interface, Haltung vis-à-vis vun de Benotzer, etc. Baséiert awer op déi technesch Informatioun déi hei virgestallt gouf.

Den drëtten Deel wäert weider d'technesch Komponent / Entwécklungserfarung analyséieren. Dir léiert, besonnesch:

  • Fortsetzung vum Pandemonium mat der Varietéit vun TL Typen
  • onbekannt Saachen iwwer Kanäl a Supergruppen
  • firwat Dialoge si méi schlëmm wéi Roster
  • iwwer absolut vs relativ Message Adresséierung
  • wat ass den Ënnerscheed tëscht Foto an Bild
  • wéi Emoji mat kursiven Text stéieren

an aner krich! Bleift drun!

Source: will.com

Setzt e Commentaire