Gagnrýni á siðareglur og skipulagsaðferðir Telegram. Hluti 1, tæknileg: reynsla af því að skrifa viðskiptavin frá grunni - TL, MT

Nýlega hafa færslur farið að birtast oftar á Habré um hversu gott Telegram er, hversu frábærir og reyndir Durov-bræðurnir eru í uppbyggingu netkerfa o.s.frv. Á sama tíma hafa mjög fáir sökkt sér í tæknibúnaðinn - í mesta lagi nota þeir frekar einfalt (og nokkuð ólíkt MTProto) Bot API byggt á JSON, og samþykkja venjulega bara á trú allt lof og PR sem snúast um sendiboðann. Fyrir tæpu einu og hálfu ári síðan byrjaði félagi minn hjá Eshelon félagasamtökunum Vasily (því miður var reikningur hans á Habré eytt ásamt drögunum) að skrifa sinn eigin Telegram viðskiptavin frá grunni í Perl, og síðar bættist höfundur þessara lína við. Hvers vegna Perl, munu sumir strax spyrja? Vegna þess að slík verkefni eru þegar til á öðrum tungumálum. Reyndar er þetta ekki málið, það gæti verið hvaða tungumál sem er þar sem ekki er tilbúið bókasafn, og í samræmi við það verður höfundur að fara alla leið frá grunni. Þar að auki er dulmál spurning um traust, en staðfestu. Með vöru sem miðar að öryggi geturðu ekki bara treyst á tilbúið bókasafn frá framleiðanda og treyst því í blindni (þetta er hins vegar umræðuefni í seinni hlutanum). Í augnablikinu virkar bókasafnið nokkuð vel á „meðal“ stigi (gerir þér kleift að gera allar API beiðnir).

Hins vegar verður ekki mikið af dulmáli eða stærðfræði í þessari röð af færslum. En það verða margar aðrar tæknilegar upplýsingar og byggingarhækjur (einnig gagnlegar fyrir þá sem vilja ekki skrifa frá grunni, heldur nota bókasafnið á hvaða tungumáli sem er). Svo, meginmarkmiðið var að reyna að innleiða viðskiptavininn frá grunni samkvæmt opinberum gögnum. Það er, við skulum gera ráð fyrir að frumkóði opinberra viðskiptavina sé lokaður (aftur, í seinni hlutanum munum við fjalla nánar um þá staðreynd að þetta er satt gerist svo), en eins og í gamla daga, til dæmis, er staðall eins og RFC - er hægt að skrifa viðskiptavin í samræmi við forskriftina eina, "án þess að skoða" frumkóðann, hvort sem það er opinbert (Telegram Desktop, farsíma), eða óopinber Telethon?

Lýsing:

Skjöl... það er til, ekki satt? Er það satt?..

Byrjað var að safna brotum af athugasemdum við þessa grein síðasta sumar. Allan þennan tíma á opinberu vefsíðunni https://core.telegram.org Skjölin voru frá og með 23. lagi, þ.e. fastur einhvers staðar árið 2014 (manstu að það voru ekki einu sinni rásir þá?). Auðvitað, fræðilega séð, hefði þetta átt að gera okkur kleift að innleiða viðskiptavin með virkni á þeim tíma árið 2014. En jafnvel í þessu ástandi voru skjölin í fyrsta lagi ófullnægjandi og í öðru lagi á stöðum sem þau voru í mótsögn við sjálfa sig. Fyrir rúmum mánuði, í september 2019, var það tilviljun Það kom í ljós að það var mikil uppfærsla á skjölunum á síðunni, fyrir alveg nýlegt Layer 105, með athugasemd að nú þarf að lesa allt aftur. Reyndar voru margar greinar endurskoðaðar, en margar voru óbreyttar. Þess vegna, þegar þú lest gagnrýnina hér að neðan um skjölin, ættir þú að hafa í huga að sumt af þessu er ekki lengur viðeigandi, en sumt er samt alveg. Eftir allt saman, 5 ár í nútíma heimi er ekki bara langur tími, heldur mjög mikið af. Frá þeim tímum (sérstaklega ef þú tekur ekki tillit til fargaðra og endurvakinna geochat-svæða síðan þá), hefur fjöldi API-aðferða í kerfinu vaxið úr hundrað í meira en tvö hundruð og fimmtíu!

Hvar á að byrja sem ungur rithöfundur?

Það skiptir ekki máli hvort þú skrifar frá grunni eða notar til dæmis tilbúin bókasöfn eins og Telethon fyrir Python eða Madeline fyrir PHP, í öllum tilvikum, þú þarft fyrst skrá umsókn þína - fáðu breytur api_id и api_hash (Þeir sem hafa unnið með VKontakte API skilja strax) þar sem þjónninn mun auðkenna forritið. Þetta verða að gera það af lagalegum ástæðum, en við munum tala meira um hvers vegna bókasafnshöfundar geta ekki birt það í seinni hlutanum. Þú gætir verið ánægður með prófgildin, þó þau séu mjög takmörkuð - staðreyndin er sú að nú geturðu skráð þig aðeins einn app, svo ekki drífa þig út í það.

Nú, frá tæknilegu sjónarmiði, ættum við að hafa áhuga á því að eftir skráningu ættum við að fá tilkynningar frá Telegram um uppfærslur á skjölum, samskiptareglum o.s.frv. Það er að segja mætti ​​gera ráð fyrir að lóðin með bryggjunni hafi einfaldlega verið yfirgefin og haldið áfram að vinna sérstaklega með þeim sem fóru að búa til viðskiptavini, vegna þess að það er auðveldara. En nei, ekkert slíkt varð vart, engar upplýsingar komu.

Og ef þú skrifar frá grunni, þá er enn langt í land að nota færibreyturnar sem fengust. Samt https://core.telegram.org/ og talar um þau í Byrjun fyrst af öllu, í rauninni verður þú fyrst að innleiða MTProto samskiptareglur - en ef þú trúðir skipulag samkvæmt OSI líkani aftast á síðunni fyrir almenna lýsingu á samskiptareglunum, þá er það algjörlega til einskis.

Reyndar, bæði fyrir og eftir MTProto, á nokkrum stigum í einu (eins og erlendir netþjónar sem vinna í OS kjarnanum segja, lagabrot), mun stórt, sársaukafullt og hræðilegt umræðuefni koma í veg fyrir...

Tvöfaldur serialization: TL (Type Language) og kerfi þess, og lög, og mörg önnur skelfileg orð

Þetta efni er í raun lykillinn að vandamálum Telegram. Og það verður mikið af hræðilegum orðum ef þú reynir að kafa ofan í það.

Svo, hér er skýringarmyndin. Ef þetta orð kemur þér í hug, segðu: JSON kerfi, Þú hugsaðir rétt. Markmiðið er það sama: eitthvað tungumál til að lýsa mögulegu mengi sendra gagna. Þetta er þar sem líkindin enda. Ef af síðunni MTProto samskiptareglur, eða frá upprunatré opinbera biðlarans, munum við reyna að opna eitthvað skema, við munum sjá eitthvað eins og:

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;

Sá sem sér þetta í fyrsta skipti mun innsæi geta þekkt aðeins hluta af því sem er skrifað - jæja, þetta eru greinilega mannvirki (þó hvar er nafnið, til vinstri eða hægra megin?), það eru reitir í þeim, þar á eftir kemur tegundin á eftir ristil... líklega. Hér innan hornsviga eru líklega sniðmát eins og í C++ (reyndar, ekki alveg). Og hvað þýða öll hin táknin, spurningamerki, upphrópunarmerki, prósentur, kjötkássamerki (og augljóslega þýða þau mismunandi hluti á mismunandi stöðum), stundum til staðar og stundum ekki, sextánda tölur - og síðast en ekki síst, hvernig á að komast út úr þessu reglulega (sem verður ekki hafnað af þjóninum) byte stream? Þú verður að lesa skjölin (já, það eru tenglar á skemað í JSON útgáfunni í nágrenninu - en það gerir það ekki skýrara).

Opnaðu síðuna Tvöfaldur Data Serialization og kafa inn í töfraheim sveppa og stakrar stærðfræði, eitthvað svipað og matan á 4. ári. Stafróf, tegund, gildi, combinator, functional combinator, venjulegt form, samsett tegund, polymorphic tegund... og það er allt bara fyrsta síða! Næst bíður þín TL Tungumál, sem, þó að það innihaldi nú þegar dæmi um léttvæg beiðni og svar, veitir alls ekki svar við dæmigerðri tilfellum, sem þýðir að þú verður að vaða í gegnum endursögn á stærðfræði þýddum úr rússnesku yfir á ensku á öðrum átta innbyggðum síður!

Lesendur sem þekkja hagnýt tungumál og sjálfvirka tegundaályktun munu að sjálfsögðu sjá lýsingarmálið á þessu tungumáli, jafnvel frá dæminu, sem mun kunnugra, og geta sagt að þetta sé í raun ekki slæmt í grundvallaratriðum. Mótmælin við þessu eru:

  • markið hljómar vel, en því miður, hún ekki náð
  • Menntun í rússneskum háskólum er mismunandi, jafnvel eftir sérgreinum í upplýsingatækni - ekki allir hafa tekið samsvarandi námskeið
  • Að lokum, eins og við munum sjá, er það í reynd ekki krafist, þar sem aðeins takmarkað hlutmengi af jafnvel TL sem lýst var er notað

Sem sagt Leónerður á rásinni #perl í FreeNode IRC netinu, sem reyndi að útfæra hlið frá Telegram til Matrix (þýðing á tilvitnuninni er ónákvæm eftir minni):

Það líður eins og einhver hafi verið kynntur fyrir leturfræði í fyrsta skipti, hafi orðið spenntur og byrjaður að reyna að leika sér með það, ekki alveg sama um hvort það væri þörf í reynd.

Athugaðu sjálfur hvort þörfin fyrir berum týpum (int, long, o.s.frv.) sem eitthvað grunnatriði vekur ekki upp spurningar - á endanum verður að útfæra þær handvirkt - við skulum til dæmis taka tilraun til að draga úr þeim vektor. Það er í rauninni fylki, ef þú kallar hlutina sem myndast réttum nöfnum.

En áður

Stutt lýsing á undirmengi TL setningafræði fyrir þá sem ekki lesa opinberu skjölin

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;

Skilgreining byrjar alltaf framkvæmdaraðila, eftir það valfrjálst (í reynd - alltaf) í gegnum táknið # ætti Effaclar H úr staðlaða lýsingarstrengnum af þessari gerð. Næst kemur lýsing á reitunum; ef þeir eru til gæti tegundin verið tóm. Þetta endar allt með jöfnunarmerki, nafni tegundarinnar sem þessi smiður - það er í raun undirgerðin - tilheyrir. Gaurinn hægra megin við jafnaðarmerkið er fjölbreytilegur - það er, nokkrar sérstakar gerðir geta samsvarað því.

Ef skilgreiningin kemur á eftir línunni ---functions---, þá verður setningafræðin sú sama, en merkingin verður önnur: smiðurinn verður nafn RPC fallsins, reitirnir verða færibreytur (tja, það er, það verður nákvæmlega sama uppbygging, eins og lýst er hér að neðan , þetta mun einfaldlega vera merkingin sem er úthlutað), og „fjölbreytilega gerð“ - tegund niðurstöðunnar sem skilað er. Að vísu mun það enn vera fjölbreytilegt - bara skilgreint í kaflanum ---types---, en þessi smiður mun „ekki koma til greina“. Ofhleðsla tegunda kallaðra falla með rökum þeirra, þ.e. Af einhverjum ástæðum er ekki gert ráð fyrir nokkrum aðgerðum með sama nafni en mismunandi undirskriftum, eins og í C++, í TL.

Hvers vegna "smiður" og "fjölbreytilegur" ef það er ekki OOP? Reyndar mun það vera auðveldara fyrir einhvern að hugsa um þetta í OOP skilmálum - margbreytileg gerð sem óhlutbundinn flokkur, og smiðir eru beint afkomandi flokkar hans, og final í hugtökum fjölda tungumála. Reyndar auðvitað bara hér líkindi með raunverulegum ofhlöðnum smíðaaðferðum í OO forritunarmálum. Þar sem hér eru bara gagnastrúktúrar, þá eru engar aðferðir (þó að lýsingin á föllum og aðferðum frekar sé alveg til þess fallin að skapa rugling í hausnum á því að þær séu til, en það er allt annað mál) - þú getur hugsað þér smið sem gildi frá sem er verið að smíða slá inn þegar bætastraumur er lesinn.

Hvernig gerist þetta? Deserializer, sem les alltaf 4 bæti, sér gildið 0xcrc32 - og skilur hvað mun gerast næst field1 með gerð int, þ.e. les nákvæmlega 4 bæti, á þessu er yfirliggjandi reiturinn með gerðinni PolymorType lesa. Sér 0x2crc32 og skilur að það eru tveir reitir lengra, fyrst long, sem þýðir að við lesum 8 bæti. Og svo aftur flókin tegund, sem er afserialized á sama hátt. Til dæmis, Type3 gæti verið lýst yfir í hringrásinni um leið og tveir smiðir, hvort um sig, þá verða þeir að mæta öðru hvoru 0x12abcd34, eftir það þarftu að lesa 4 bæti í viðbót intEða 0x6789cdef, eftir það verður ekkert. Allt annað - þú þarft að henda undantekningu. Allavega, eftir þetta förum við aftur að lesa 4 bæti int framlegð field_c в constructorTwo og þar með klárum við að lesa okkar PolymorType.

Að lokum, ef þú verður gripinn 0xdeadcrc í constructorThree, þá verður allt flóknara. Fyrsti völlurinn okkar er bit_flags_of_what_really_present með gerð # - í rauninni er þetta bara samnefni fyrir týpuna nat, sem þýðir "náttúruleg tala". Það er í raun og veru, óundirritaður int er, við the vegur, eina tilvikið þegar ómerktar tölur eiga sér stað í raunverulegum hringrásum. Svo næst er smíði með spurningarmerki, sem þýðir að þetta svið - það verður aðeins til staðar á vírnum ef samsvarandi biti er stilltur á reitnum sem vísað er til (um það bil eins og þrískiptur rekstraraðili). Svo við skulum gera ráð fyrir að þessi hluti hafi verið stilltur, sem þýðir að við þurfum að lesa reit eins og Type, sem í okkar dæmi hefur 2 smiðir. Annað er tómt (samanstendur aðeins af auðkenni), hitt hefur reit ids með gerð ids:Vector<long>.

Þú gætir haldið að bæði sniðmát og almenn lyf séu í kostum eða Java. En nei. Næstum. Þetta einn tilvik um að nota hornsviga í raunverulegum hringrásum og það er AÐEINS notað fyrir Vector. Í bætastraumi verða þetta 4 CRC32 bæti fyrir Vector gerðina sjálfa, alltaf þau sömu, síðan 4 bæti - fjöldi fylkisþátta og síðan þessir þættir sjálfir.

Við þetta bætist sú staðreynd að raðgreining á sér alltaf stað í orðum upp á 4 bæti, allar gerðir eru margfeldi af henni - innbyggðu tegundunum er einnig lýst bytes и string með handvirkri röðun á lengdinni og þessari röðun um 4 - ja, það virðist hljóma eðlilegt og jafnvel tiltölulega áhrifaríkt? Þrátt fyrir að haldið sé fram að TL sé áhrifarík tvíundarraðgreining, til fjandans með þá, með stækkun nánast hvað sem er, jafnvel Boolean gildi og einstafa strengir í 4 bæti, mun JSON samt vera miklu þykkari? Sko, jafnvel óþarfa reiti er hægt að sleppa með bitflöggum, allt er nokkuð gott og jafnvel stækkanlegt til framtíðar, svo hvers vegna ekki að bæta nýjum valfrjálsum reitum við smiðinn seinna?

En nei, ef þú lest ekki stutta lýsingu mína, heldur öll skjölin, og hugsar um framkvæmdina. Í fyrsta lagi er CRC32 smiðsins reiknað út í samræmi við staðlaða línu í textalýsingu kerfisins (fjarlægja auka bil, o.s.frv.) - þannig að ef nýjum reit er bætt við breytist tegundarlýsingarlínan og þar af leiðandi CRC32 hennar og , þar af leiðandi, serialization. Og hvað myndi gamli viðskiptavinurinn gera ef hann fengi reit með nýjum fánum settum og hann veit ekki hvað hann á að gera við þá næst? ..

Í öðru lagi skulum við muna Effaclar H, sem hér er notað í meginatriðum sem kjötkássaaðgerðir til að ákvarða nákvæmlega hvaða tegund er verið að (af)raðgreina. Hér stöndum við frammi fyrir árekstrum - og nei, líkurnar eru ekki einn á móti 232, heldur miklu meiri. Hver mundi eftir því að CRC32 er hannað til að greina (og leiðrétta) villur í samskiptarásinni og bætir í samræmi við það þessa eiginleika í óhag fyrir aðra? Til dæmis, það er sama um að endurraða bætum: ef þú reiknar CRC32 út frá tveimur línum, í annarri skiptir þú um fyrstu 4 bæti með næstu 4 bætum - það verður það sama. Þegar inntak okkar eru textastrengir úr latneska stafrófinu (og smá greinarmerki), og þessi nöfn eru ekki sérlega tilviljunarkennd, aukast líkurnar á slíkri endurröðun mjög.

Við the vegur, hver athugaði hvað var þarna? virkilega CRC32? Einn af fyrstu frumkóðum (jafnvel á undan Waltman) var með kjötkássaaðgerð sem margfaldaði hvern staf með tölunni 239, svo elskaður af þessu fólki, ha ha!

Að lokum, allt í lagi, komumst við að því að smiðir með sviði tegund Vector<int> и Vector<PolymorType> mun hafa mismunandi CRC32. Hvað með frammistöðu á netinu? Og frá fræðilegu sjónarhorni, verður þetta hluti af gerðinni? Segjum að við komumst yfir fjölda tíu þúsund talna, vel með Vector<int> allt er á hreinu, lengdin og önnur 40000 bæti. Og ef þetta Vector<Type2>, sem samanstendur af aðeins einu sviði int og það er eitt í tegundinni - þurfum við að endurtaka 10000xabcdef0 34 sinnum og svo 4 bæti int, eða tungumálið er fær um að Óháð því fyrir okkur frá byggingaraðilanum fixedVec og í staðinn fyrir 80000 bæti, flytja aftur aðeins 40000?

Þetta er alls ekki aðgerðalaus fræðileg spurning - ímyndaðu þér að þú fáir lista yfir hópnotendur, sem hver um sig hefur auðkenni, fornafn, eftirnafn - munurinn á magni gagna sem flutt er um farsímatengingu getur verið verulegur. Það er einmitt skilvirkni Telegram serialization sem er auglýst fyrir okkur.

Svo ...

Vector, sem kom aldrei út

Ef þú reynir að vaða í gegnum lýsingarsíðurnar á samsettum og svo framvegis, muntu sjá að vektor (og jafnvel fylki) er formlega að reyna að birtast í gegnum túlla af nokkrum blöðum. En á endanum gleyma þeir, lokaskrefinu er sleppt og skilgreining á vektor er einfaldlega gefin, sem er ekki enn bundin við tegund. Hvað er að? Á tungumálum forritun, sérstaklega hagnýtur sjálfur, það er nokkuð dæmigert að lýsa uppbyggingu endurkvæmt - þýðandinn með lata mati þess mun skilja og gera allt sjálfur. Í tungumálinu raðgreining gagna það sem þarf er skilvirkni: það er nóg að lýsa einfaldlega lista, þ.e. uppbygging tveggja þátta - sá fyrri er gagnaþáttur, hinn er sama uppbyggingin sjálf eða tómt rými fyrir skottið (pakki (cons) í Lisp). En þetta mun augljóslega krefjast af hverju þáttur eyðir 4 bætum til viðbótar (CRC32 í tilvikinu í TL) til að lýsa gerð sinni. Einnig er auðvelt að lýsa fylki föst stærð, en ef um er að ræða fylki af óþekktri lengd fyrirfram, brjótum við af.

Þess vegna, þar sem TL leyfir ekki að gefa út vektor, varð að bæta honum við hliðina. Að lokum segir skjölin:

Serialization notar alltaf sama smiðinn „vektor“ (const 0x1cb5c415 = crc32(“vector t:Type # [ t ] = Vector t“) sem er ekki háð sérstöku gildi breytunnar af gerðinni t.

Gildi valfrjálsu færibreytunnar t kemur ekki við sögu í raðgreiningunni þar sem það er dregið af niðurstöðugerðinni (alltaf þekkt fyrir raðgreiningu).

Skoðaðu nánar: vector {t:Type} # [ t ] = Vector t - en hvergi Þessi skilgreining sjálf segir ekki að fyrsta talan þurfi að vera jöfn lengd vigursins! Og það kemur hvergi frá. Þetta er gefið sem þarf að hafa í huga og útfæra með höndum þínum. Annars staðar nefnir skjölin jafnvel heiðarlega að tegundin sé ekki raunveruleg:

Vector t fjölbreytilega gervigerðin er „gerð“ þar sem gildið er röð gilda af hvaða gerð sem er t, annað hvort í kassa eða berum.

... en einblínir ekki á það. Þegar þú, þreyttur á að vaða í gegnum teygjur í stærðfræði (kannski jafnvel þekktur af þér frá háskólanámskeiði), ákveður að gefast upp og skoða í raun hvernig á að vinna með það í reynd, þá er tilfinningin eftir í höfðinu á þér að þetta sé alvarlegt Stærðfræði í kjarna, hún var greinilega fundin upp af Cool People (tveir stærðfræðingar - ACM sigurvegari), en ekki hverjum sem er. Markmiðinu - að sýna sig - hefur verið náð.

Við the vegur, um fjölda. Við skulum minna þig á það # það er samheiti nat, náttúruleg tala:

Það eru tegund tjáningar (tegund-útr) og töluleg orðtök (nat-expr). Hins vegar eru þau skilgreind á sama hátt.

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

en í málfræðinni er þeim lýst á sama hátt, þ.e. Þennan mismun verður aftur að muna og koma í framkvæmd með höndunum.

Jæja, já, sniðmátsgerðir (vector<int>, vector<User>) hafa sameiginlegt auðkenni (#1cb5c415), þ.e. ef þú veist að símtalið er tilkynnt sem

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

þá ertu ekki lengur að bíða eftir vektor, heldur vektor af notendum. Nánar tiltekið, ætti bíddu - í raunkóða mun sérhver þáttur, ef ekki ber tegund, hafa smiði, og á góðan hátt í útfærslu væri nauðsynlegt að athuga - en við vorum sendur nákvæmlega í öllum þáttum þessa vektor þeirri tegund? Hvað ef það væri einhvers konar PHP, þar sem fylki getur innihaldið mismunandi gerðir í mismunandi þáttum?

Á þessum tímapunkti byrjarðu að hugsa - er slíkur TL nauðsynlegur? Kannski fyrir kerruna væri hægt að nota mannlega serializer, sama frumefni og var þá þegar til? Það var kenningin, skoðum framkvæmdina.

Núverandi TL útfærslur í kóða

TL fæddist í djúpum VKontakte jafnvel fyrir frægu atburðina með sölu á hlut Durov og (örugglega), jafnvel áður en þróun Telegram hófst. Og í opnum hugbúnaði frumkóða fyrstu útfærslu þú getur fundið fullt af fyndnum hækjum. Og tungumálið sjálft var útfært meira þar en það er núna í Telegram. Til dæmis er kjötkássa alls ekki notuð í kerfinu (sem þýðir innbyggð gervigerð (eins og vektor) með frávikshegðun). Eða

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

en við skulum íhuga, til fullnustu, að rekja, ef svo má að orði komast, þróun Hugsunarrisans.

#define ZHUKOV_BYTES_HACK

#ifdef ZHUKOV_BYTES_HACK

/* dirty hack for Zhukov request */

Eða þessi fallega:

    static const char *reserved_words_polymorhic[] = {

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

      };

Þetta brot er um sniðmát eins og:

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

Þetta er skilgreiningin á hashmap sniðmátsgerð sem vektor af int - Type pörum. Í C++ myndi það líta eitthvað svona út:

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

svo, alpha - lykilorð! En aðeins í C++ geturðu skrifað T, en þú ættir að skrifa alfa, beta... En ekki meira en 8 breytur, þar endar fantasían. Svo virðist sem eitt sinn hafi átt sér stað í Sankti Pétursborg svona samræður:

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

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

En þetta var um fyrstu birtu útfærsluna á TL „almennt“. Við skulum halda áfram að íhuga útfærslur í Telegram viðskiptavinunum sjálfum.

Orð til Vasily:

Vasily, [09.10.18 17:07] Mest af öllu er rassinn heitur vegna þess að þeir bjuggu til fullt af abstraktum, og slógu síðan bolta á þá og huldu kóðarafallið með hækjum
Þess vegna, fyrst frá dock pilot.jpg
Síðan frá kóðanum dzhekichan.webp

Auðvitað, frá fólki sem þekkir reiknirit og stærðfræði, getum við búist við því að þeir hafi lesið Aho, Ullmann og þekki verkfærin sem hafa orðið í raun staðall í greininni í gegnum áratugina til að skrifa DSL þýðendur sína, ekki satt? ..

Eftir höfundinn símskeyti-cli er Vitaly Valtman, eins og skilja má af því að TLO sniðið kom fyrir utan (cli) marka þess, meðlimur í teyminu - nú hefur bókasafni fyrir TL þáttun verið úthlutað sérstaklega, hver er hrifningin af henni TL flokkari? ..

16.12 04:18 Vasily: Ég held að einhver hafi ekki náð tökum á lex+yacc
16.12 04:18 Vasily: Ég get ekki útskýrt það öðruvísi
16.12 04:18 Vasily: jæja, eða þeir fengu greitt fyrir fjölda lína í VK
16.12 04:19 Vasily: 3k+ línur o.fl.<censored> í stað greiningartækis

Kannski undantekning? Við skulum sjá hvernig gerir Þetta er OFFICIAL viðskiptavinurinn - 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+ línur í Python, nokkrar reglubundnar tjáningar + sértilvik eins og vektor, sem auðvitað er lýst yfir í kerfinu eins og það ætti að vera samkvæmt TL setningafræðinni, en þeir treystu á þessa setningafræði til að flokka hana. Spurningin vaknar, hvers vegna var þetta allt kraftaverk?иÞað er meira lagskipt ef enginn ætlar að flokka það samkvæmt skjölunum samt?!

Við the vegur... Manstu að við töluðum um CRC32 eftirlit? Svo, í Telegram Desktop kóðarafallinu er listi yfir undantekningar fyrir þær gerðir þar sem reiknaður CRC32 passar ekki með þeim sem tilgreindur er á skýringarmyndinni!

Vasily, [18.12/22 49:XNUMX] og hér myndi ég velta því fyrir mér hvort svona TL sé þörf
ef ég vildi skipta mér af öðrum útfærslum myndi ég byrja að setja inn línuskil, helmingur þáttanna mun brjóta á fjöllínuskilgreiningum
tdesktop hins vegar líka

Mundu punktinn um einnar línu, við munum koma aftur að því aðeins síðar.

Allt í lagi, telegram-cli er óopinbert, Telegram Desktop er opinbert, en hvað með hina? Hver veit? .. Í Android biðlara kóðanum var alls enginn schema parser (sem vekur spurningar um opinn hugbúnað, en þetta er fyrir seinni hlutann), en það voru nokkrir aðrir fyndnir kóðar, en meira um þá í undirkafla hér að neðan.

Hvaða aðrar spurningar vekur serialization í reynd? Til dæmis gerðu þeir ýmislegt, auðvitað, með bitareiti og skilyrtum reitum:

Vasily: flags.0? true
þýðir að reiturinn er til staðar og jafngildir satt ef fáninn er stilltur

Vasily: flags.1? int
þýðir að völlurinn er til staðar og þarf að afsera

Vasily: Asni, ekki hafa áhyggjur af því sem þú ert að gera!
Vasily: Það er minnst á einhvers staðar í skjalinu að satt sé núll-lengd tegund, en það er ómögulegt að setja neitt saman úr skjalinu þeirra
Vasily: Í opnum útfærslum er þetta ekki heldur, en það er fullt af hækjum og stuðningi

Hvað með Telethon? Þegar horft er fram á við til efnis MTProto, dæmi - í skjölunum eru slíkir hlutir, en táknið % því er aðeins lýst sem „samsvarandi tiltekinni bergerð“, þ.e. í dæmunum hér að neðan er annað hvort villa eða eitthvað óskráð:

Vasily, [22.06.18 18:38] Á einum stað:

msg_container#73f1f8dc messages:vector message = MessageContainer;

Í öðru lagi:

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

Og þetta eru tveir stórir munar, í raunveruleikanum kemur einhvers konar nakinn vektor

Ég hef ekki séð beina vektorskilgreiningu og hef ekki rekist á hana

Greining er skrifuð í höndunum í telethon

Í skýringarmynd hans er skilgreiningin gerð athugasemd við msg_container

Aftur er spurningin áfram um %. Því er ekki lýst.

Vadim Goncharov, [22.06.18 19:22] og í tdesktop?

Vasily, [22.06.18 19:23] En TL flokkarinn þeirra á venjulegum vélum mun líklegast ekki borða þetta heldur

// parsed manually

TL er falleg abstrakt, enginn útfærir hana alveg

Og % er ekki í þeirra útgáfu af kerfinu

En hér stangast skjölin á við sjálfa sig, þannig að Idk

Það fannst í málfræðinni, þeir hefðu einfaldlega getað gleymt að lýsa merkingarfræðinni

Þú sást skjalið á TL, þú getur ekki fundið það út án hálfs lítra

„Jæja, við skulum segja,“ mun annar lesandi segja, „þú gagnrýnir eitthvað, svo sýndu mér hvernig það ætti að gera það.

Vasily svarar: „Hvað varðar þáttarann, þá líkar mér við hluti eins og

    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 ) ); }
            ;

líkar það einhvern veginn betur en

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;
}

eða

        # 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)

þetta er HEILI lexerinn:

    ---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];

þeim. einfaldara er vægt til orða tekið."

Almennt séð, þar af leiðandi passa þátttakandi og kóðarafall fyrir raunverulegt notaða undirmengi TL inn í um það bil 100 línur af málfræði og ~300 línur rafallsins (með allt printmyndakóða), þar á meðal tegund upplýsingabollur fyrir sjálfskoðun í hverjum flokki. Hver fjölbreytileg tegund breytist í tóman óhlutbundinn grunnflokk og smiðir erfa það og hafa aðferðir til að raðgreina og afserða.

Skortur á gerðum í leturmálinu

Sterk vélritun er af hinu góða, ekki satt? Nei, þetta er ekki holivar (þó ég vilji frekar kraftmikil tungumál), heldur postulata innan ramma TL. Miðað við það ætti tungumálið að veita okkur alls kyns ávísanir. Jæja, allt í lagi, kannski ekki hann sjálfur, heldur framkvæmdina, en hann ætti að minnsta kosti að lýsa þeim. Og hvers konar tækifæri viljum við?

Fyrst af öllu, skorður. Hér sjáum við í skjölunum til að hlaða upp skrám:

Tvíundir innihaldi skrárinnar er síðan skipt í hluta. Allir hlutar verða að hafa sömu stærð ( hluta_stærð ) og eftirfarandi skilyrði verða að vera uppfyllt:

  • part_size % 1024 = 0 (deilanlegt með 1KB)
  • 524288 % part_size = 0 (512KB verður að vera jafnt deilt með hluta_stærð)

Síðasti hlutinn þarf ekki að uppfylla þessi skilyrði, að því gefnu að stærð hans sé minni en part_size.

Hver hluti ætti að hafa raðnúmer, skráarhluti, með gildi á bilinu 0 til 2,999.

Eftir að skráin hefur verið skipt í skipting þarftu að velja aðferð til að vista hana á þjóninum. Notaðu upload.saveBigFilePart ef heildarstærð skráarinnar er meira en 10 MB og upload.saveFilePart fyrir smærri skrár.
[…] Ein af eftirfarandi gagnainnsláttarvillum gæti skilað sér:

  • FILE_PARTS_INVALID — Ógildur fjöldi hluta. Gildið er ekki á milli 1..3000

Er eitthvað af þessu á skýringarmyndinni? Er þetta einhvern veginn hægt að tjá með TL? Nei. En fyrirgefðu, meira að segja Turbo Pascal hans afa gat lýst tilgreindum gerðum svið. Og hann vissi eitt enn, nú betur þekktur sem enum - gerð sem samanstendur af upptalningu á föstum (litlum) fjölda gilda. Í tungumálum eins og C - tölustafi, athugaðu að hingað til höfum við aðeins talað um tegundir tölur. En það eru líka fylki, strengir... til dæmis væri gaman að lýsa því að þessi strengur getur bara innihaldið símanúmer, ekki satt?

Ekkert af þessu er í TL. En það er til dæmis í JSON Schema. Og ef einhver annar gæti deilt um deilanleika 512 KB, að þetta þurfi enn að athuga í kóða, þá vertu viss um að viðskiptavinurinn einfaldlega gat ekki senda númer utan sviðs 1..3000 (og samsvarandi villa gæti ekki hafa komið upp) það hefði verið hægt, ekki satt?..

Við the vegur, um villur og skilagildi. Jafnvel þeir sem hafa unnið með TL þoka augun - það rann ekki strax upp fyrir okkur það hver og einn fall í TL getur í raun ekki aðeins skilað skilagerðinni sem lýst er heldur einnig villu. En þetta er ekki hægt að álykta á nokkurn hátt með því að nota TL sjálfan. Auðvitað er það þegar ljóst og það er engin þörf á neinu í reynd (þótt í raun sé hægt að gera RPC á mismunandi vegu, við munum koma aftur að þessu síðar) - en hvað með hreinleika hugtakanna Mathematics of Abstract Types frá himneskum heimi?.. Ég tók upp togið - svo passaðu það.

Og að lokum, hvað með læsileika? Jæja, þarna, almennt, myndi ég vilja lýsing hafa það rétt í stefinu (í JSON stefinu, aftur, það er það), en ef þú ert nú þegar þreyttur á því, hvað þá með hagnýtu hliðina - að minnsta kosti léttvæg að horfa á mismuni við uppfærslur? Sjáið sjálf kl alvöru dæmi:

-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;

eða

-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;

Það fer eftir öllum, en GitHub, til dæmis, neitar að varpa ljósi á breytingar inni í svona löngum línum. Leikurinn “finndu 10 mismunandi”, og það sem heilinn sér strax er að upphaf og endar í báðum dæmunum eru eins, þú þarft að lesa leiðinlega einhvers staðar í miðjunni... Að mínu mati er þetta ekki bara í orði, en eingöngu sjónrænt skítugt og slétt.

Við the vegur, um hreinleika kenningarinnar. Af hverju þurfum við bitareiti? Svo virðist sem þeir lykt slæmt frá sjónarhóli tegundafræðinnar? Skýringuna má sjá í fyrri útgáfum af skýringarmyndinni. Í fyrstu, já, svona var það, fyrir hvert hnerra var búið til ný tegund. Þessir grunnar eru enn til í þessu formi, til dæmis:

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;

En ímyndaðu þér nú, ef þú ert með 5 valfrjálsa reiti í uppbyggingunni þinni, þá þarftu 32 tegundir fyrir alla mögulega valkosti. Samsett sprenging. Þannig brotnaði kristalshreinleiki TL kenningarinnar enn og aftur gegn steypujárni hins harkalega veruleika raðgerðar.

Að auki, sums staðar brjóta þessir krakkar sjálfir gegn eigin tegundarfræði. Til dæmis, í MTProto (næsta kafla) er hægt að þjappa svarinu með Gzip, allt er í lagi - nema að lögin og hringrásin eru brotin. Enn og aftur var það ekki RpcResult sjálft sem var uppskorið, heldur innihald þess. Jæja, af hverju að gera þetta?.. Ég þurfti að skera í hækju svo að þjöppunin virkaði hvar sem er.

Eða annað dæmi, við uppgötvuðum einu sinni villu - hún var send InputPeerUser í staðinn fyrir InputUser. Eða öfugt. En það tókst! Það er, þjóninum var alveg sama um gerðina. Hvernig getur þetta verið? Svarið getur verið gefið okkur með kóðabrotum frá telegram-cli:

  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);

Með öðrum orðum, þetta er þar sem serialization er gert HANDLEGT, ekki myndaður kóða! Kannski er þjónninn útfærður á svipaðan hátt?.. Í grundvallaratriðum mun þetta virka ef það er gert einu sinni, en hvernig er hægt að styðja það síðar við uppfærslur? Er þetta ástæðan fyrir því að kerfið var fundið upp? Og hér höldum við áfram að næstu spurningu.

Útgáfa. Lög

Hvers vegna skýringarútgáfurnar eru kallaðar lag er aðeins hægt að velta fyrir sér út frá sögu birtra skýringa. Greinilega, í fyrstu töldu höfundar að hægt væri að gera grunnatriði með óbreyttu kerfi, og aðeins þar sem nauðsyn krefur, fyrir sérstakar beiðnir, gefa til kynna að þeir væru gerðir með annarri útgáfu. Í grundvallaratriðum, jafnvel góð hugmynd - og það nýja verður, eins og það var, "blandað", lagskipt ofan á það gamla. En við skulum sjá hvernig það var gert. Að vísu gat ég ekki horft á það frá upphafi - það er fyndið, en skýringarmynd grunnlagsins er einfaldlega ekki til. Lög byrjuðu á 2. Skjölin segja okkur frá sérstökum TL eiginleika:

Ef viðskiptavinur styður Layer 2, þá verður að nota eftirfarandi smið:

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

Í reynd þýðir þetta að fyrir hvert API símtal er int með gildinu 0x289dd1f6 verður að bæta við á undan aðferðarnúmerinu.

Hljómar eðlilegt. En hvað gerðist næst? Svo birtist

invokeWithLayer3#b7475268 query:!X = X;

Svo hvað er næst? Eins og þú gætir giska á,

invokeWithLayer4#dea0d430 query:!X = X;

Fyndið? Nei, það er of snemmt að hlæja, hugsaðu um það hver beiðni frá öðru lagi þarf að pakka inn í svona sérstaka gerð - ef þær eru allar mismunandi fyrir þig, hvernig geturðu annars greint þær að? Og að bæta við aðeins 4 bætum fyrir framan er frekar skilvirk aðferð. Svo,

invokeWithLayer5#417a57ae query:!X = X;

En það er augljóst að eftir nokkurn tíma verður þetta að einhvers konar bacchanalia. Og lausnin kom:

Uppfærsla: Byrjar með Layer 9, hjálparaðferðir invokeWithLayerN má aðeins nota ásamt initConnection

Húrra! Eftir 9 útgáfur komumst við loksins að því sem var gert í netsamskiptareglum á níunda áratugnum - við vorum sammála um útgáfuna einu sinni í upphafi tengingarinnar!

Svo hvað er næst? ..

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

En nú geturðu samt hlegið. Aðeins eftir önnur 9 lög var loksins bætt við alhliða smiði með útgáfunúmeri, sem þarf að kalla aðeins einu sinni í upphafi tengingarinnar, og merking laganna virtist vera horfin, nú er það bara skilyrt útgáfa, eins og alls staðar annars staðar. Vandamál leyst.

Nákvæmlega?..

Vasily, [16.07.18 14:01] Jafnvel á föstudaginn hugsaði ég:
Símaþjónninn sendir atburði án þess að beðið sé um það. Beiðnir verða að vera pakkaðar inn í InvokeWithLayer. Þjónninn pakkar ekki uppfærslum; það er engin uppbygging til að vefja svör og uppfærslur.

Þeir. viðskiptavinurinn getur ekki tilgreint í hvaða lagi hann vill uppfærslur

Vadim Goncharov, [16.07.18 14:02] er InvokeWithLayer ekki hækja í grundvallaratriðum?

Vasily, [16.07.18 14:02] Þetta er eina leiðin

Vadim Goncharov, [16.07.18 14:02] sem ætti í meginatriðum að þýða að samþykkja lagið í upphafi fundarins

Við the vegur, það leiðir að niðurfærsla viðskiptavina er ekki veitt

Uppfærslur, þ.e. gerð Updates í kerfinu er þetta það sem þjónninn sendir til viðskiptavinarins ekki sem svar við API beiðni, heldur sjálfstætt þegar atburður á sér stað. Þetta er flókið efni sem verður fjallað um í annarri færslu, en í bili er mikilvægt að vita að þjónninn vistar uppfærslur jafnvel þegar viðskiptavinurinn er ótengdur.

Svona, ef þú neitar að vefja af hverju pakka til að gefa til kynna útgáfu hans, þetta leiðir rökrétt til eftirfarandi hugsanlegra vandamála:

  • þjónninn sendir uppfærslur til biðlarans jafnvel áður en viðskiptavinurinn hefur upplýst hvaða útgáfu hann styður
  • hvað ætti ég að gera eftir að hafa uppfært viðskiptavininn?
  • hver ábyrgðirað álit þjónsins um lagnúmerið breytist ekki á meðan á ferlinu stendur?

Heldurðu að þetta séu eingöngu fræðilegar vangaveltur og í reynd getur þetta ekki gerst, vegna þess að þjónninn er rétt skrifaður (a.m.k. er hann prófaður vel)? Ha! Sama hvernig það er!

Þetta er einmitt það sem við lentum í í ágúst. Þann 14. ágúst komu skilaboð um að eitthvað væri að uppfæra á Telegram netþjónum... og svo í logs:

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.

og svo nokkur megabæti af staflasporum (jæja, á sama tíma var skráningin lagfærð). Þegar öllu er á botninn hvolft, ef eitthvað er ekki viðurkennt í TL þínum, er það tvíundarlegt með undirskrift, neðar í röðinni ALLT fer, afkóðun verður ómöguleg. Hvað ættir þú að gera í slíkum aðstæðum?

Jæja, það fyrsta sem einhverjum dettur í hug er að aftengjast og reyna aftur. Hjálpaði ekki. Við gúglum CRC32 - þetta reyndust vera hlutir úr skema 73, þó við unnum að 82. Við skoðum annálana vel - það eru auðkenni frá tveimur mismunandi kerfum!

Kannski er vandamálið eingöngu í óopinberum viðskiptavini okkar? Nei, við ræsum Telegram Desktop 1.2.17 (útgáfa fylgir nokkrum Linux dreifingum), það skrifar í undantekningarskrána: MTP Óvænt tegund auðkenni #b5223b0f lesið í MTPMessageMedia...

Gagnrýni á siðareglur og skipulagsaðferðir Telegram. Hluti 1, tæknileg: reynsla af því að skrifa viðskiptavin frá grunni - TL, MT

Google sýndi að svipað vandamál hafði þegar komið upp hjá einum af óopinberu viðskiptavinunum, en þá voru útgáfunúmerin og þar af leiðandi aðrar forsendur...

Svo hvað ættum við að gera? Ég og Vasily hættum saman: hann reyndi að uppfæra hringrásina í 91, ég ákvað að bíða í nokkra daga og prófa 73. Báðar aðferðirnar virkuðu, en þar sem þær eru reynslusögulegar er enginn skilningur á því hversu margar útgáfur upp eða niður þú þarft að hoppa, eða hversu lengi þú þarft að bíða.

Seinna gat ég endurskapað ástandið: við ræsum biðlarann, slökkvum á honum, settum hringrásina saman í annað lag, endurræsum, tökum vandamálið aftur, snúum aftur í það fyrra - úps, ekkert magn af hringrásarskiptingu og viðskiptavinurinn endurræsir í a. nokkrar mínútur munu hjálpa. Þú munt fá blöndu af gagnaskipulagi frá mismunandi lögum.

Skýring? Eins og þú getur giskað á af ýmsum óbeinum einkennum samanstendur þjónninn af mörgum ferlum af mismunandi gerðum á mismunandi vélum. Líklegast er að þjónninn sem er ábyrgur fyrir að „buffa“ setti inn í biðröðina það sem yfirmenn hans gáfu honum og þeir gáfu það í kerfinu sem var við lýði við kynslóðina. Og þangað til þessi biðröð „rotin“ var ekkert hægt að gera í því.

Kannski... en þetta er hræðileg hækja?!.. Nei, áður en við veltum fyrir okkur vitlausum hugmyndum skulum við líta á kóða opinberu viðskiptavinanna. Í Android útgáfunni finnum við engan TL þáttara, en við finnum stífa skrá (GitHub neitar að snerta hana) með (af)raðgreiningu. Hér eru kóðabútarnir:

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;

eða

    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... lítur villt út. En, líklega, þetta er myndaður kóða, þá allt í lagi? .. En það styður vissulega allar útgáfur! Að vísu er ekki ljóst hvers vegna öllu er blandað saman, leynileg spjall og alls konar _old7 einhvern veginn lítur ekki út eins og vélakynslóð... Hins vegar var ég hrifinn af öllu

TL_message_layer104
TL_message_layer104_2
TL_message_layer104_3

Krakkar, getið þið ekki einu sinni ákveðið hvað er inni í einu lagi?! Jæja, allt í lagi, segjum að „tveir“ hafi verið gefnir út með villu, jæja, það gerist, en ÞRÍR?.. Strax, sama hrífan aftur? Hvers konar klám er þetta, fyrirgefðu?

Í frumkóða Telegram Desktop, við the vegur, gerist svipað - ef svo er, breyta nokkrir skuldbindingar í röð við kerfið ekki laganúmerinu, heldur laga eitthvað. Við aðstæður þar sem engin opinber uppspretta gagna er fyrir kerfið, hvaðan er hægt að nálgast þau, nema upprunakóða opinbera viðskiptavinarins? Og ef þú tekur það þaðan geturðu ekki verið viss um að kerfið sé alveg rétt fyrr en þú prófar allar aðferðir.

Hvernig er jafnvel hægt að prófa þetta? Ég vona að aðdáendur eininga, hagnýtra og annarra prófa muni deila í athugasemdunum.

Allt í lagi, við skulum skoða annan kóða:

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;

Þessi athugasemd „handvirkt búin til“ bendir til þess að aðeins hluti þessarar skráar hafi verið skrifaður handvirkt (geturðu ímyndað þér alla viðhaldsmartröðina?), og restin var vélgerð. Hins vegar vaknar önnur spurning - að heimildirnar séu fyrir hendi ekki alveg (a la GPL blobbar í Linux kjarnanum), en þetta er nú þegar efni í seinni hlutann.

En nóg. Við skulum halda áfram að samskiptareglunni sem öll þessi serialization keyrir ofan á.

MT frumefni

Svo, við skulum opna Almenn lýsing и nákvæma lýsingu á bókuninni og það fyrsta sem við rekumst á er hugtökin. Og með gnægð af öllu. Almennt séð virðist þetta vera séreign Telegram - að kalla hluti öðruvísi á mismunandi stöðum, eða mismunandi hluti með einu orði, eða öfugt (til dæmis, í API á háu stigi, ef þú sérð límmiðapakka, er það ekki það sem þú hugsaðir).

Til dæmis, „skilaboð“ og „lota“ þýða eitthvað annað hér en í venjulegu Telegram viðskiptavinaviðmóti. Jæja, allt er á hreinu með skilaboðin, það gæti verið túlkað í OOP skilmálum, eða einfaldlega kallað orðið „pakki“ - þetta er lágt flutningsstig, það eru ekki sömu skilaboð og í viðmótinu, það eru mörg þjónustuskilaboð . En þingið... en fyrst og fremst.

flutningslag

Það fyrsta er samgöngur. Þeir munu segja okkur um 5 valkosti:

  • TCP
  • Veftengi
  • Veftengi yfir HTTPS
  • HTTP
  • HTTPS

Vasily, [15.06.18 15:04] Það er líka UDP flutningur, en hann er ekki skjalfestur

Og TCP í þremur afbrigðum

Sá fyrsti er svipaður og UDP yfir TCP, hver pakki inniheldur raðnúmer og crc
Af hverju er svona sársaukafullt að lesa skjöl í körfu?

Jæja, þarna er það núna TCP þegar í 4 afbrigðum:

  • stytt
  • Intermediate
  • Bólstrað millistig
  • Full

Jæja, allt í lagi, Padded intermediate fyrir MTProxy, þetta var síðar bætt við vegna þekktra atburða. En hvers vegna tvær útgáfur í viðbót (þrjár alls) þegar þú gætir komist af með eina? Allir fjórir eru í meginatriðum aðeins frábrugðnir því hvernig á að stilla lengd og hleðslu á aðal MTProto, sem verður rætt frekar:

  • í Abridged er það 1 eða 4 bæti, en ekki 0xef, þá líkaminn
  • í Intermediate er þetta 4 bæti af lengd og reit, og í fyrsta skipti sem viðskiptavinurinn verður að senda 0xeeeeeeee til að gefa til kynna að það sé millistig
  • í heild mest ávanabindandi, frá sjónarhóli netverja: lengd, raðnúmer, og EKKI ÞAÐ sem er aðallega MTProto, líkami, CRC32. Já, allt er þetta ofan á TCP. Sem veitir okkur áreiðanlegan flutning í formi raðbætastraums; engar raðir eru nauðsynlegar, sérstaklega eftirlitssummur. Allt í lagi, nú mun einhver mótmæla því að TCP sé með 16 bita eftirlitssummu, þannig að gagnaspilling gerist. Frábært, en við erum í raun með dulmálssamskiptareglur með kjötkássa sem eru lengri en 16 bæti, allar þessar villur - og jafnvel fleiri - verða gripnar af SHA misræmi á hærra stigi. Það er ENGINN tilgangur með CRC32 ofan á þetta.

Við skulum bera saman Abridged, þar sem eitt bæti af lengd er mögulegt, við Intermediate, sem réttlætir „Ef 4-bæta gagnajöfnun er þörf,“ sem er algjört bull. Hvað, það er talið að Telegram forritarar séu svo óhæfir að þeir geti ekki lesið gögn úr fals inn í samstillt biðminni? Þú verður samt að gera þetta, því lestur getur skilað þér hvaða bæti sem er (og það eru líka til dæmis proxy-þjónar...). Eða á hinn bóginn, hvers vegna að loka á Abridged ef við verðum enn með stífa fyllingu ofan á 16 bæti - sparaðu 3 bæti stundum ?

Maður fær á tilfinninguna að Nikolai Durov líkar mjög við að finna upp hjól að nýju, þar á meðal netsamskiptareglur, án raunverulegrar hagnýtrar þörf.

Aðrir samgöngumöguleikar, þ.m.t. Web og MTProxy, við munum ekki íhuga núna, kannski í annarri færslu, ef það er beiðni. Um þetta sama MTProxy, skulum við aðeins muna núna að stuttu eftir útgáfu þess árið 2018, lærðu veitendur fljótt að loka fyrir það, ætlað fyrir framhjáblokkunsamkvæmt pakkningastærð! Og líka sú staðreynd að MTProxy þjónninn skrifaður (aftur af Waltman) í C var of bundinn við Linux sérstöðu, þó þess væri alls ekki krafist (Phil Kulin mun staðfesta), og að svipaður þjónn annaðhvort í Go eða Node.js myndi passa í minna en hundrað línur.

En við drögum ályktanir um tæknilæsi þessa fólks í lok kaflans, eftir að hafa skoðað önnur atriði. Í bili skulum við halda áfram í OSI lag 5, lotu - sem þeir settu MTProto lotu á.

Lyklar, skilaboð, fundir, Diffie-Hellman

Þeir settu það þar ekki alveg rétt... Session er ekki sama fundur og sést í viðmótinu undir Active sessions. En í röð.

Gagnrýni á siðareglur og skipulagsaðferðir Telegram. Hluti 1, tæknileg: reynsla af því að skrifa viðskiptavin frá grunni - TL, MT

Þannig að við fengum bætastreng af þekktri lengd frá flutningslaginu. Þetta er annað hvort dulkóðuð skilaboð eða látlaus texti - ef við erum enn á lykilsamningsstigi og erum í raun að gera það. Hvaða hugtaka sem kallast „lykill“ erum við að tala um? Við skulum skýra þetta mál fyrir Telegram teyminu sjálfu (ég biðst afsökunar á því að hafa þýða mína eigin skjöl úr ensku með þreyttan heila klukkan 4, það var auðveldara að skilja nokkrar setningar eins og þær eru):

Það eru tveir aðilar sem kallast fundur - einn í notendaviðmóti opinberra viðskiptavina undir „núverandi lotum“, þar sem hver lota samsvarar heilu tæki / stýrikerfi.
Annað - MTProto fundur, sem hefur raðnúmer skilaboðanna (í lág-stigi merkingu) í sér, og hver getur varað á milli mismunandi TCP tenginga. Hægt er að setja upp nokkrar MTProto lotur á sama tíma, til dæmis til að flýta fyrir niðurhali skráa.

Á milli þessara tveggja fundur það er hugtak heimild. Í úrkynjaða tilvikinu getum við sagt það HÍ fundur er það sama og heimild, en því miður er allt flókið. Við skulum skoða:

  • Notandinn á nýja tækinu býr fyrst til auth_key og bindur það við reikning, til dæmis með SMS - þess vegna heimild
  • Það gerðist inni í fyrsta MTProto fundur, sem hefur session_id innra með þér.
  • Í þessu skrefi, samsetningin heimild и session_id mætti ​​kalla dæmi - þetta orð kemur fyrir í skjölum og kóða sumra viðskiptavina
  • Þá getur viðskiptavinurinn opnað sumar MTProto fundir undir sama auth_key - til sama DC.
  • Síðan, einn daginn mun viðskiptavinurinn þurfa að biðja um skrána frá annar DC - og fyrir þetta DC verður nýtt auth_key !
  • Að upplýsa kerfið um að það sé ekki nýr notandi sem er að skrá sig heldur sá sami heimild (HÍ fundur), notar viðskiptavinurinn API símtöl auth.exportAuthorization á heimili DC auth.importAuthorization í nýja DC.
  • Allt er eins, nokkrir geta verið opnir MTProto fundir (hver með sínu session_id) til þessa nýja DC, undir það auth_key.
  • Að lokum gæti viðskiptavinurinn viljað Perfect Forward Secrecy. Hvert auth_key var varanleg lykill - á DC - og viðskiptavinurinn getur hringt auth.bindTempAuthKey til notkunar tímabundið auth_key - og aftur, aðeins einn temp_auth_key á DC, sameiginlegt öllum MTProto fundir til þessa DC.

Athugið að salt (og framtíðarsölt) er líka einn á auth_key þeim. deilt á milli allra MTProto fundir til sama DC.

Hvað þýðir "milli mismunandi TCP tenginga"? Þannig að þetta þýðir eitthvað eins og heimildaköku á vefsíðu - hún heldur áfram (lifir af) margar TCP-tengingar við tiltekinn netþjón, en einn daginn fer hún illa. Aðeins ólíkt HTTP, í MTProto eru skilaboð innan lotu númeruð og staðfest í röð; ef þau fóru inn í göngin rofnaði tengingin - eftir að ný tenging hefur verið komið á mun þjónninn vinsamlegast senda allt í þessari lotu sem hann skilaði ekki í fyrri TCP tenging.

Hins vegar eru upplýsingarnar hér að ofan teknar saman eftir margra mánaða rannsókn. Í millitíðinni, erum við að innleiða viðskiptavini okkar frá grunni? - snúum okkur aftur til upphafsins.

Svo skulum búa til auth_key á Diffie-Hellman útgáfur frá Telegram. Við skulum reyna að skilja skjölin...

Vasily, [19.06.18 20:05] data_with_hash := SHA1(gögn) + gögn + (hvað sem er af handahófi bæti); þannig að lengdin jafngildir 255 bætum;
dulkóðuð_gögn := RSA(gögn_með_hash, miðlara_almannalykill); 255 bæta löng tala (stór endían) er hækkuð upp í tilskilið vald yfir nauðsynlegum stuðli og niðurstaðan er geymd sem 256 bæta tala.

Þeir eru með dóp-DH

Lítur ekki út eins og DH hjá heilbrigðum einstaklingi
Það eru engir tveir opinberir lyklar í dx

Jæja, á endanum var þetta reddað, en leifar var eftir - sönnun um vinnu er unnin af viðskiptavininum um að hann hafi getað reiknað fjöldann. Tegund verndar gegn DoS árásum. Og RSA lykillinn er aðeins notaður einu sinni í eina átt, aðallega til dulkóðunar new_nonce. En á meðan þessi að því er virðist einfalda aðgerð muni takast, hvað þarftu að horfast í augu við?

Vasily, [20.06.18/00/26 XNUMX:XNUMX] Ég hef ekki enn fengið viðeigandi beiðni

Ég sendi þessa beiðni til DH

Og í flutningsbryggjunni segir að það geti svarað með 4 bætum af villukóða. Það er allt og sumt

Jæja, hann sagði mér -404, hvað svo?

Svo ég sagði honum: "Gríptu kjaftæðið þitt dulkóðað með netþjónslykli með fingrafari eins og þessu, ég vil DH," og það svaraði með heimskulegum 404

Hvað myndi þér finnast um þetta svar netþjónsins? Hvað skal gera? Það er enginn að spyrja (en meira um það í seinni hlutanum).

Hér er allur áhugi gerður á bryggju

Ég hef ekkert annað að gera, mig dreymdi bara um að breyta tölum fram og til baka

Tvær 32 bita tölur. Ég pakkaði þeim eins og öllum öðrum

En nei, þetta tvennt þarf að bæta við línuna fyrst sem BE

Vadim Goncharov, [20.06.18 15:49] og vegna þessa 404?

Vasilí, [20.06.18 15:49] JÁ!

Vadim Goncharov, [20.06.18 15:50] svo ég skil ekki hvað hann "fann ekki"

Vasily, [20.06.18 15:50] um það bil

Ég gat ekki fundið slíka niðurbrot í frumþætti%)

Við stjórnuðum ekki einu sinni villutilkynningu

Vasily, [20.06.18 20:18] Ó, það er líka MD5. Nú þegar þrír mismunandi kjötkássa

Fingrafar lykilsins er reiknað út sem hér segir:

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

SHA1 og sha2

Svo skulum við setja það auth_key við fengum 2048 bita að stærð með Diffie-Hellman. Hvað er næst? Næst komumst við að því að neðri 1024 bitarnir af þessum lykli eru ekki notaðir á nokkurn hátt ... en við skulum hugsa um þetta í bili. Í þessu skrefi höfum við sameiginlegt leyndarmál með þjóninum. Komið hefur verið á fót hliðstæðu við TLS lotuna, sem er mjög dýr aðferð. En þjónninn veit samt ekkert um hver við erum! Ekki ennþá, reyndar. heimild. Þeir. ef þú hugsaðir út frá "innskráningarlykilorði", eins og þú gerðir einu sinni í ICQ, eða að minnsta kosti "innskráningarlykil", eins og í SSH (til dæmis á einhverju gitlab/github). Við fengum nafnlausan. Hvað ef þjónninn segir okkur „þessi símanúmer eru þjónustað af öðru DC“? Eða jafnvel „símanúmerið þitt er bannað“? Það besta sem við getum gert er að geyma lykilinn í von um að hann komi að gagni og verði ekki rotinn þá.

Við the vegur, við "tókum" það með fyrirvara. Til dæmis, treystum við þjóninum? Hvað ef það er falsað? Þörf væri á dulmálsskoðun:

Vasily, [21.06.18 17:53] Þeir bjóða farsíma viðskiptavinum að athuga 2kbit númer fyrir frumleika%)

En það er alls ekki ljóst, nafeijoa

Vasily, [21.06.18 18:02] Skjalið segir ekki hvað eigi að gera ef það reynist ekki einfalt

Ekki sagt. Við skulum sjá hvað opinberi Android viðskiptavinurinn gerir í þessu tilfelli? A þetta er hvað (og já, öll skráin er áhugaverð) - eins og sagt er, ég læt þetta bara vera hér:

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

Nei, auðvitað er það enn til staðar sumir Það eru próf fyrir frumleika tölu, en persónulega hef ég ekki lengur nægilega þekkingu á stærðfræði.

Allt í lagi, við fengum aðallykilinn. Til að skrá sig inn, þ.e. senda beiðnir, þú þarft að framkvæma frekari dulkóðun með AES.

Skilaboðalykillinn er skilgreindur sem 128 miðbitar SHA256 í meginmáli skilaboða (þar á meðal lotu, skilaboðaauðkenni, osfrv.), þar á meðal fyllingarbæti, ásamt 32 bætum tekin úr heimildarlyklinum.

Vasily, [22.06.18 14:08] Meðaltal, tík, bitar

Móttekið auth_key. Allt. Fyrir utan þá ... það er ekki ljóst af skjalinu. Ekki hika við að kynna þér opna kóðann.

Athugaðu að MTProto 2.0 krefst frá 12 til 1024 bætum af fyllingu, enn með fyrirvara um að lengd skilaboða sem myndast sé deilanleg með 16 bætum.

Svo hversu mikið bólstrun ættir þú að bæta við?

Og já, það er líka 404 ef um villu er að ræða

Ef einhver rannsakaði skýringarmyndina og texta skjalanna vandlega tók hann eftir því að það er enginn MAC þar. Og að AES sé notað í ákveðinni IGE ham sem er ekki notaður annars staðar. Þeir skrifa að sjálfsögðu um þetta í algengum spurningum sínum... Hér er t.d. skilaboðalykillinn sjálfur líka SHA-hash af dulkóðuðu gögnunum, notað til að athuga heilleikann - og ef það er ósamræmi, skjölin af einhverjum ástæðum mælir með því að hunsa þá þegjandi (en hvað með öryggi, hvað ef þeir brjóta okkur?).

Ég er ekki dulmálsfræðingur, kannski er ekkert athugavert við þennan hátt í þessu tilfelli frá fræðilegu sjónarhorni. En ég get greinilega nefnt hagnýtt vandamál, með Telegram Desktop sem dæmi. Það dulkóðar staðbundna skyndiminni (allar þessar D877F783D5D3EF8C) á sama hátt og skilaboð í MTProto (aðeins í þessu tilfelli útgáfu 1.0), þ.e. fyrst skilaboðalykillinn, síðan gögnin sjálf (og einhvers staðar til hliðar helstu stóru auth_key 256 bæti, án þeirra msg_key gagnslaus). Svo vandamálið verður áberandi á stórum skrám. Þú þarft nefnilega að geyma tvö afrit af gögnunum - dulkóðuð og afkóðuð. Og ef það eru megabæti, eða straumspilun myndbands, til dæmis?.. Klassísk kerfi með MAC á eftir dulmálstextanum gera þér kleift að lesa það streyma, senda það strax. En með MTProto verður þú að gera það í fyrstu dulkóða eða afkóða öll skilaboðin, aðeins þá flytja þau yfir á netið eða á diskinn. Þess vegna, í nýjustu útgáfum af Telegram Desktop í skyndiminni í user_data Annað snið er einnig notað - með AES í CTR ham.

Vasily, [21.06.18 01:27] Ó, ég komst að því hvað IGE er: IGE var fyrsta tilraunin til að „staðfesta dulkóðunarham,“ upphaflega fyrir Kerberos. Það var misheppnuð tilraun (það veitir ekki heilindum) og þurfti að fjarlægja það. Þetta var upphaf 20 ára leit að auðkenningar dulkóðunarham sem virkar, sem nýlega náði hámarki í stillingum eins og OCB og GCM.

Og nú rökin frá körfuhliðinni:

Liðið á bakvið Telegram, undir forystu Nikolai Durov, samanstendur af sex ACM-meisturum, þar af helmingur Ph.D í stærðfræði. Það tók þá um tvö ár að setja út núverandi útgáfu af MTProto.

Þetta er fyndið. Tvö ár á lægra stigi

Eða þú gætir bara tekið tls

Allt í lagi, segjum að við höfum gert dulkóðunina og önnur blæbrigði. Er loksins hægt að senda beiðnir sem eru raðaðar í TL og deserialize svörin? Svo hvað og hvernig ættir þú að senda? Hér skulum við segja, aðferðin initConnection, kannski er þetta það?

Vasily, [25.06.18 18:46] Frumstillir tengingu og vistar upplýsingar um tæki og forrit notandans.

Það samþykkir app_id, device_model, system_version, app_version og lang_code.

Og einhver fyrirspurn

Skjöl eins og alltaf. Ekki hika við að kynna þér opinn uppspretta

Ef allt var um það bil skýrt með invokeWithLayer, hvað er þá að hér? Það kemur í ljós, segjum að við höfum - viðskiptavinurinn hafði þegar eitthvað til að spyrja þjóninn um - það er beiðni sem við vildum senda:

Vasily, [25.06.18 19:13] Af kóðanum að dæma er fyrsta símtalið vafið inn í þetta drasl, og vitleysan sjálf er vafin í invokewithlayer

Af hverju gæti initConnection ekki verið sérstakt símtal, en verður að vera umbúðir? Já, eins og það kom í ljós, það verður að gera það í hvert skipti í upphafi hverrar lotu, en ekki einu sinni, eins og með aðallykilinn. En! Það er ekki hægt að hringja í það af óviðkomandi notanda! Nú erum við komin á það stig að það á við Þessi skjalasíða - og hún segir okkur að...

Aðeins lítill hluti af API aðferðum er í boði fyrir óviðkomandi notendur:

  • 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

Sá allra fyrsti af þeim, auth.sendCode, og það er þessi kærkomna fyrsta beiðni þar sem við sendum api_id og api_hash, og eftir það fáum við SMS með kóða. Og ef við erum í röngum DC (símanúmer hér á landi eru til dæmis þjónað af öðrum) þá fáum við villu með númerinu á viðkomandi DC. Hjálpaðu okkur til að finna út hvaða IP tölu eftir DC númeri þú þarft að tengjast help.getConfig. Á sínum tíma voru aðeins 5 færslur en eftir fræga atburði 2018 hefur þeim fjölgað verulega.

Nú skulum við muna að við komumst á þetta stig á þjóninum nafnlaust. Er ekki of dýrt að fá sér bara IP tölu? Af hverju ekki að gera þetta og aðrar aðgerðir í ódulkóðaða hluta MTProto? Ég heyri andmælin: „hvernig getum við tryggt að það sé ekki RKN sem svarar með fölskum heimilisföngum? Að þessu munum við að almennt opinberir viðskiptavinir RSA lyklar eru innbyggðir, þ.e. geturðu bara að skrifa undir þessar upplýsingar. Reyndar er þetta nú þegar gert fyrir upplýsingar um að komast framhjá blokkun sem viðskiptavinir fá í gegnum aðrar rásir (rökrétt er þetta ekki hægt að gera í MTProto sjálfu; þú þarft líka að vita hvar á að tengjast).

Allt í lagi. Á þessu stigi viðskiptaleyfis höfum við ekki enn heimild og höfum ekki skráð umsókn okkar. Við viljum bara sjá í bili hvað þjónninn bregst við aðferðum sem eru tiltækar fyrir óviðkomandi notanda. Og hér…

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;

Í kerfinu kemur fyrstur í öðru sæti

Í tdesktop skemanum er þriðja gildið

Já, síðan þá hafa skjölin að sjálfsögðu verið uppfærð. Þó það gæti fljótlega orðið óviðkomandi aftur. Hvernig ætti nýliði að vita það? Kannski láta þeir þig vita ef þú skráir umsókn þína? Vasily gerði þetta, en því miður, þeir sendu honum ekki neitt (aftur, við munum tala um þetta í seinni hlutanum).

...Þú tókst eftir því að við höfum þegar einhvern veginn flutt yfir í API, þ.e. á næsta stig, og missti af einhverju í MTProto efninu? Kemur ekki á óvart:

Vasily, [28.06.18 02:04] Mm, þeir eru að grúska í gegnum nokkur af reikniritunum á e2e

Mtproto skilgreinir dulkóðunaralgrím og lykla fyrir bæði lénin, sem og smá umbúðir

En þeir blanda stöðugt saman mismunandi stigum stafla, svo það er ekki alltaf ljóst hvar mtproto endaði og næsta stig hófst

Hvernig blandast þau saman? Jæja, hér er sami bráðabirgðalykill fyrir PFS, til dæmis (við the vegur, Telegram Desktop getur ekki gert það). Það er keyrt af API beiðni auth.bindTempAuthKey, þ.e. frá efsta stigi. En á sama tíma truflar það dulkóðun á neðra stigi - eftir það, til dæmis, þarftu að gera það aftur initConnection o.s.frv., þetta er ekki bara eðlileg beiðni. Það sem er líka sérstakt er að þú getur aðeins haft EINN tímabundinn lykil á hvern DC, þó reitinn auth_key_id í hverju skeyti gerir þér kleift að breyta lyklinum að minnsta kosti hverju skeyti, og að þjónninn hafi rétt á að „gleyma“ tímabundnum lyklinum hvenær sem er - skjölin segja ekki hvað á að gera í þessu tilfelli... jæja, hvers vegna gæti það ekki ertu ekki með nokkra lykla, eins og með sett af framtíðarsöltum, og ?..

Það eru nokkur önnur atriði sem vert er að hafa í huga varðandi MTProto þemað.

Skilaboð, msg_id, msg_seqno, staðfestingar, ping í ranga átt og önnur sérkenni

Af hverju þarftu að vita um þá? Vegna þess að þeir „leka“ á hærra stig og þú þarft að vera meðvitaður um þá þegar þú vinnur með API. Gerum ráð fyrir að við höfum ekki áhuga á msg_key; neðra stigið hefur afkóðað allt fyrir okkur. En inni í afkóðuðu gögnunum höfum við eftirfarandi reiti (einnig lengd gagna, svo við vitum hvar fyllingin er, en það er ekki mikilvægt):

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

Við skulum minna þig á að það er aðeins eitt salt fyrir allan DC. Af hverju að vita af henni? Ekki bara vegna þess að það er beiðni get_future_salts, sem segir þér hvaða millibil mun gilda, en líka vegna þess að ef saltið þitt er "rotið", þá tapast skilaboðin (beiðnin) einfaldlega. Miðlarinn mun að sjálfsögðu tilkynna um nýja saltið með útgáfu new_session_created - en með þeim gamla verður þú að senda það aftur einhvern veginn, til dæmis. Og þetta mál hefur áhrif á arkitektúr forritsins.

Netþjóninum er heimilt að sleppa fundum alveg og svara á þennan hátt af mörgum ástæðum. Reyndar, hvað er MTProto fundur frá hlið viðskiptavinarins? Þetta eru tvær tölur session_id и seq_no skilaboð innan þessa lotu. Jæja, og undirliggjandi TCP tengingin, auðvitað. Segjum að viðskiptavinur okkar viti ekki enn hvernig á að gera marga hluti, hann aftengdi og tengdist aftur. Ef þetta gerðist hratt - gamla lotan hélt áfram í nýju TCP tengingunni, aukið seq_no lengra. Ef það tekur langan tíma gæti þjónninn eytt því, því á hliðinni er það líka biðröð eins og við komumst að.

Hvað skyldi það vera seq_no? Ó, þetta er erfið spurning. Reyndu að skilja heiðarlega hvað var átt við:

Innihaldstengd skilaboð

Skilaboð sem krefjast skýrrar viðurkenningar. Þar á meðal eru öll notendaskilaboðin og mörg þjónustuskilaboð, nánast öll að undanskildum gámum og viðurkenningum.

Skilaboðaraðarnúmer (msg_seqno)

32 bita tala sem jafngildir tvöföldum fjölda „efnistengdra“ skilaboða (þau sem krefjast staðfestingar, og sérstaklega þau sem eru ekki ílát) sem sendandi hefur búið til fyrir þessi skilaboð og síðan aukið um eitt ef núverandi skilaboð eru efnistengd skilaboð. Ílát er alltaf búið til eftir allt innihald þess; því er raðnúmer þess stærra en eða jafnt raðnúmerum skilaboðanna sem það inniheldur.

Hvers konar sirkus er þetta með aukningu um 1, og svo annan um 2?.. Mig grunar að upphaflega hafi þeir átt við "minnst marktæka hluti fyrir ACK, restin er tala", en útkoman er ekki alveg sú sama - sérstaklega, það kemur út, er hægt að senda sumar staðfestingar hafa það sama seq_no! Hvernig? Jæja, til dæmis, þjónninn sendir okkur eitthvað, sendir það og við þegjum sjálf og svörum aðeins með þjónustuskilaboðum sem staðfesta móttöku skilaboðanna. Í þessu tilviki munu útgöngustaðfestingar okkar hafa sama útsendingarnúmer. Ef þú þekkir TCP og hélt að þetta hljómi einhvern veginn villt, en það virðist ekki mjög villt, því í TCP seq_no breytist ekki, en staðfesting fer til seq_no hinum megin mun ég flýta mér að styggja þig. Staðfestingar eru veittar í MTProto EKKI á seq_no, eins og í TCP, en eftir msg_id !

Hvað er þetta msg_id, mikilvægasta þessara sviða? Einstakt skilaboðaauðkenni, eins og nafnið gefur til kynna. Það er skilgreint sem 64 bita tala, lægstu bitarnir hafa aftur „þjónn-ekki-þjónn“ galdur, og restin er Unix tímastimpill, þar með talið brotahlutinn, færður 32 bita til vinstri. Þeir. tímastimpill í sjálfu sér (og skilaboð með tíma sem eru of mismunandi verða hafnað af þjóninum). Af þessu kemur í ljós að almennt er þetta auðkenni sem er alþjóðlegt fyrir viðskiptavininn. Í ljósi þess - við skulum muna session_id - við erum tryggð: Ekki er undir neinum kringumstæðum hægt að senda skilaboð sem ætluð eru fyrir eina lotu í aðra lotu. Það er, það kemur í ljós að það er nú þegar þrír stig - lota, lotunúmer, skilaboðakenni. Hvers vegna slík of mikil flækja, þessi ráðgáta er mjög mikil.

Svo, msg_id þarf fyrir...

RPC: beiðnir, svör, villur. Staðfestingar.

Eins og þú hefur kannski tekið eftir er engin sérstök „gera RPC beiðni“ gerð eða aðgerð neins staðar á skýringarmyndinni, þó að það séu svör. Þegar öllu er á botninn hvolft erum við með efnistengd skilaboð! Það er, hvaða skilaboðin gætu verið beiðni! Eða að vera ekki. Eftir allt, af hverju есть msg_id. En það eru svör:

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

Hér er tilgreint hvaða skilaboðum er svarað. Þess vegna, á efsta stigi API, verður þú að muna hvað númerið á beiðni þinni var - ég held að það sé engin þörf á að útskýra að vinnan sé ósamstilltur og það geta verið nokkrar beiðnir í gangi á sama tíma, svörin sem hægt er að skila í hvaða röð sem er? Í grundvallaratriðum, út frá þessu og villuskilaboðum eins og engir starfsmenn, er hægt að rekja arkitektúrinn á bak við þetta: þjónninn sem heldur TCP tengingu við þig er framhlið jafnvægistæki, hann framsendir beiðnir til bakenda og safnar þeim til baka í gegnum message_id. Hér virðist allt vera skýrt, rökrétt og gott.

Já?.. Og ef þú hugsar um það? Þegar öllu er á botninn hvolft hefur RPC svarið sjálft líka reit msg_id! Þurfum við að öskra á netþjóninn „þú ert ekki að svara svarinu mínu!“? Og já, hvað var um fermingar? Um síðu skilaboð um skilaboð segir okkur hvað er

msgs_ack#62d6b459 msg_ids:Vector long = MsgsAck;

og það verður að gera á hvorri hlið. En ekki alltaf! Ef þú fékkst RpcResult þjónar það sjálft sem staðfesting. Það er, þjónninn getur svarað beiðni þinni með MsgsAck - eins og, "Ég fékk hana." RpcResult getur svarað strax. Það gæti verið hvort tveggja.

Og já, þú verður samt að svara svarinu! Staðfesting. Annars mun þjónninn telja það óafhendanlegt og senda það aftur til þín. Jafnvel eftir endurtengingu. En hér kemur auðvitað spurningin um tímamörkin upp. Við skulum skoða þær aðeins síðar.

Í millitíðinni skulum við skoða hugsanlegar villur í framkvæmd fyrirspurna.

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

Ó, einhver mun hrópa, hér er mannúðlegra snið - það er lína! Taktu þinn tíma. Hérna lista yfir villur, en auðvitað ekki fullkomið. Af því lærum við að kóðinn er eitthvað eins og HTTP villur (jæja, auðvitað er merkingarfræði svaranna ekki virt, sums staðar er þeim dreift af handahófi á milli kóðanna), og línan lítur út eins og CAPITAL_LETTERS_AND_NUMBERS. Til dæmis, PHONE_NUMBER_OCCUPIED eða FILE_PART_Х_MISSING. Jæja, það er, þú munt enn þurfa þessa línu flokka. Til dæmis, FLOOD_WAIT_3600 mun þýða að þú þarft að bíða í klukkutíma, og PHONE_MIGRATE_5, að símanúmer með þessu forskeyti þurfi að vera skráð í 5. DC. Við höfum tegund tungumál, ekki satt? Við þurfum ekki rök úr strengi, venjulegir munu gera það, allt í lagi.

Aftur, þetta er ekki á þjónustuskilaboðasíðunni, en eins og þegar er venjulega með þetta verkefni er hægt að finna upplýsingarnar á annarri skjalasíðu. Eða varpað tortryggni. Í fyrsta lagi, útlit, innsláttur/lagbrot - RpcError hægt að hreiðra inn RpcResult. Af hverju ekki úti? Hvað tókum við ekki tillit til?.. Samkvæmt því, hvar er tryggingin fyrir því RpcError má EKKI vera felld inn í RpcResult, en vera beint eða hreiður í aðra tegund?.. Og ef það getur það ekki, hvers vegna er það ekki á efsta stigi, þ.e. það vantar req_msg_id ? ..

En höldum áfram um þjónustuskilaboð. Viðskiptavinurinn gæti haldið að þjónninn sé að hugsa í langan tíma og leggja fram þessa frábæru beiðni:

rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;

Það eru þrjú svarmöguleikar við þessari spurningu, sem aftur skerast staðfestingarkerfið; að reyna að skilja hvað þau ættu að vera (og hver almenni listi yfir tegundir sem þarfnast ekki staðfestingar) er skilið eftir lesandanum sem heimavinnu (athugið: upplýsingarnar í Telegram Desktop frumkóði er ekki tæmandi).

Fíkniefnafíkn: skilaboðastöður

Almennt séð skilja margir staðir í TL, MTProto og Telegram almennt eftir þrjóskutilfinningu, en af ​​kurteisi, háttvísi og öðrum mjúk færni Við þögðum kurteislega um það og ritskoðuðum ósvífnina í samræðunum. Hins vegar þessi staðurОmegnið af síðunni er um skilaboð um skilaboð Það er átakanlegt, jafnvel fyrir mig, sem hef unnið með netsamskiptareglur í langan tíma og hefur séð reiðhjól af mismunandi gráðum af skakka.

Það byrjar saklaust, með staðfestingum. Næst segja þeir okkur frá

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;

Jæja, allir sem byrja að vinna með MTProto verða að takast á við þá; í „leiðréttu - endursamsettu - ræst" hringrásinni er algengt að fá talnavillur eða salt sem hefur tekist að fara illa við breytingar. Hins vegar eru tveir punktar hér:

  1. Þetta þýðir að upprunalega skilaboðin glatast. Við þurfum að búa til nokkrar biðraðir, við skoðum það síðar.
  2. Hvað eru þessar undarlegu villutölur? 16, 17, 18, 19, 20, 32, 33, 34, 35, 48, 64... hvar eru hinar tölurnar, Tommy?

Í skjölunum segir:

Ætlunin er að error_code gildi séu flokkuð (error_code >> 4): til dæmis samsvara kóðarnir 0x40 — 0x4f villum í niðurbroti gáma.

en í fyrsta lagi breyting í hina áttina og í öðru lagi skiptir það ekki máli, hvar eru hinir kóðarnir? Í hausnum á höfundi?.. Þetta eru þó smáræði.

Fíkn byrjar í skilaboðum um skilaboðastöðu og skilaboðaafrit:

  • Beiðni um upplýsingar um stöðu skilaboða
    Ef annar hvor aðilinn hefur ekki fengið upplýsingar um stöðu útgefinna skeyta sinna í nokkurn tíma, getur hann beinlínis óskað eftir því frá hinum aðilanum:
    msgs_state_req#da69fb52 msg_ids:Vector long = MsgsStateReq;
  • Upplýsingaskilaboð um stöðu skilaboða
    msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
    Hér info er strengur sem inniheldur nákvæmlega eitt bæti af skilaboðastöðu fyrir hvert skeyti af msg_ids listanum:

    • 1 = ekkert er vitað um skilaboðin (msg_id of lágt, hinn aðilinn gæti hafa gleymt því)
    • 2 = skilaboð ekki móttekin (msg_id fellur innan sviðs geymdra auðkenna; hins vegar hefur hinn aðilinn örugglega ekki fengið slík skilaboð)
    • 3 = skilaboð ekki móttekin (msg_id of hátt; hins vegar hefur hinn aðilinn örugglega ekki fengið það ennþá)
    • 4 = skilaboð móttekin (athugið að þetta svar er einnig um leið kvittun)
    • +8 = skilaboð þegar samþykkt
    • +16 = skilaboð sem þarfnast ekki staðfestingar
    • +32 = RPC fyrirspurn í skilaboðum í vinnslu eða vinnslu þegar lokið
    • +64 = innihaldstengt svar við skilaboðum sem þegar hafa verið búin til
    • +128 = annar aðili veit fyrir víst að skilaboð eru þegar móttekin
      Þetta svar krefst ekki staðfestingar. Það er staðfesting á viðkomandi msgs_state_req, í sjálfu sér.
      Athugaðu að ef það kemur skyndilega í ljós að hinn aðilinn er ekki með skilaboð sem lítur út fyrir að hafa verið send til hans, þá er einfaldlega hægt að senda skilaboðin aftur. Jafnvel þótt hinn aðilinn ætti að fá tvö eintök af skilaboðunum á sama tíma, verður afritið hunsað. (Ef of langur tími er liðinn og upprunalega msg_id er ekki lengur gilt, á að pakka skilaboðunum inn í msg_copy).
  • Frjáls miðlun á stöðu skilaboða
    Hvor aðili getur af fúsum og frjálsum vilja upplýst hinn aðilann um stöðu skilaboðanna sem hinn aðilinn sendir.
    msgs_all_info#8cc0d131 msg_ids:Vector long info:string = MsgsAllInfo
  • Framlengd sjálfviljug miðlun um stöðu eins skilaboðs
    ...
    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;
  • Skýr beiðni um að endursenda skilaboð
    msg_resend_req#7d861a08 msg_ids:Vector long = MsgResendReq;
    Fjaraðilinn bregst strax við með því að senda aftur umbeðin skilaboð […]
  • Skýr beiðni um að endursenda svör
    msg_resend_ans_req#8610baeb msg_ids:Vector long = MsgResendReq;
    Fjaraðili svarar strax með því að senda aftur svör við umbeðnum skilaboðum […]
  • Skilaboðafrit
    Í sumum tilfellum þarf að endursenda gömul skilaboð með msg_id sem er ekki lengur gilt. Síðan er því pakkað inn í afritsílát:
    msg_copy#e06046b2 orig_message:Message = MessageCopy;
    Þegar þau hafa borist eru skilaboðin unnin eins og umbúðirnar væru ekki til staðar. Hins vegar, ef það er vitað með vissu að skilaboðin orig_message.msg_id hafi verið móttekin, þá er nýja skeytið ekki unnið (á sama tíma og það og orig_message.msg_id eru staðfest). Gildi orig_message.msg_id verður að vera lægra en msg_id ílátsins.

Við skulum jafnvel þegja um hvað msgs_state_info aftur eru eyru ókláruðu TL að standa út (við þurftum vektor af bætum, og í neðri tveimur bitunum var upptalning og í þeim tveimur hærri voru fánar). Málið er annað. Skilur einhver hvers vegna allt þetta er í reynd? í alvöru viðskiptavini nauðsynlegt?.. Með erfiðleikum, en maður getur ímyndað sér einhvern ávinning ef einstaklingur er þátttakandi í villuleit, og í gagnvirkum ham - spurðu þjóninn hvað og hvernig. En hér er beiðnum lýst fram og til baka.

Af því leiðir að hver aðili verður ekki aðeins að dulkóða og senda skilaboð, heldur einnig að geyma gögn um sjálfan sig, um viðbrögð við þeim, í óþekktan tíma. Skjölin lýsa hvorki tímasetningum né hagnýtu notagildi þessara eiginleika. á engan hátt. Það sem er ótrúlegast er að þau eru í raun notuð í kóða opinberra viðskiptavina! Svo virðist sem þeim hafi verið sagt eitthvað sem var ekki innifalið í opinberum gögnum. Skildu út frá kóðanum hvers vegna, er ekki lengur eins einfalt og í tilfelli TL - það er ekki (tiltölulega) rökfræðilega einangraður hluti, heldur stykki sem er bundið við forritaarkitektúrinn, þ.e. mun þurfa verulega meiri tíma til að skilja forritskóðann.

Pings og tímasetningar. Biðraðir.

Ef við munum eftir getgátunum um arkitektúr þjónsins (dreifingu beiðna yfir bakenda), kemur frekar sorglegt atriði í kjölfarið - þrátt fyrir allar afhendingarábyrgðir í TCP (annaðhvort eru gögnin afhent, eða þú verður upplýst um bilið, en gögnin verða afhent áður en vandamálið kemur upp), að staðfestingar í MTProto sjálfu - engar tryggingar. Miðlarinn getur auðveldlega tapað eða hent skilaboðunum þínum og ekkert hægt að gera í því, notaðu bara mismunandi gerðir af hækjum.

Og fyrst af öllu - skilaboða biðraðir. Jæja, með einu var allt augljóst frá upphafi - óstaðfest skilaboð verður að geyma og endursenda. Og eftir hvaða tíma? Og grínið þekkir hann. Kannski leysa þessi ávanaþjónustuskilaboð einhvern veginn þetta vandamál með hækjum, segjum, í Telegram Desktop eru um það bil 4 biðraðir sem samsvara þeim (kannski fleiri, eins og áður hefur verið nefnt, til þess þarftu að kafa meira inn í kóðann og arkitektúr þess; á sama tíma tíma, við Við vitum að það er ekki hægt að taka það sem sýni; ákveðinn fjöldi tegunda úr MTProto kerfinu er ekki notaður í það).

Hvers vegna er þetta að gerast? Sennilega gátu forritarar netþjónsins ekki tryggt áreiðanleika innan þyrpingarinnar, eða jafnvel stuðpúða á fremri jafnvægisbúnaðinum, og fluttu þetta vandamál til viðskiptavinarins. Af örvæntingu reyndi Vasily að innleiða annan valmöguleika, með aðeins tvær biðraðir, með því að nota reiknirit frá TCP - mæla RTT á netþjóninn og stilla stærð „gluggans“ (í skilaboðum) eftir fjölda óstaðfestra beiðna. Það er að segja, svona gróft heuristic til að meta álag netþjónsins er hversu margar beiðnir okkar hann getur tuggið á sama tíma og ekki tapað.

Jæja, það er, þú skilur, ekki satt? Ef þú þarft að innleiða TCP aftur ofan á samskiptareglur sem keyra yfir TCP, þá gefur það til kynna mjög illa hönnuð samskiptareglur.

Ó já, hvers vegna þarftu fleiri en eina biðröð, og hvað þýðir þetta fyrir manneskju sem vinnur með API á háu stigi? Sko, þú gerir beiðni, serialize hana, en oft geturðu ekki sent hana strax. Hvers vegna? Því svarið verður msg_id, sem er tímabundiðаÉg er merki, sem best er að fresta úthlutun eins seint og hægt er - ef þjónninn hafnar því vegna misræmis tíma milli okkar og hans (auðvitað getum við búið til hækju sem færir tíma okkar frá nútíðinni við þjóninn með því að bæta við delta sem er reiknað út frá svörum þjónsins - opinberir viðskiptavinir gera þetta, en það er gróft og ónákvæmt vegna biðminni). Þess vegna, þegar þú leggur fram beiðni með staðbundnu aðgerðasímtali frá bókasafninu, fara skilaboðin í gegnum eftirfarandi stig:

  1. Það liggur í einni biðröð og bíður dulkóðunar.
  2. Skipaður msg_id og skilaboðin fóru í aðra biðröð - möguleg áframsending; senda í innstunguna.
  3. a) Miðlarinn svaraði MsgsAck - skilaboðin voru afhent, við eyðum því úr „öðrum biðröðinni“.
    b) Eða öfugt, honum líkaði ekki eitthvað, hann svaraði badmsg - sendu aftur úr „annarri biðröð“
    c) Ekkert er vitað, skilaboðin þarf að endursenda úr annarri biðröð - en ekki er vitað nákvæmlega hvenær.
  4. Þjónninn svaraði loksins RpcResult - raunverulegt svar (eða villa) - ekki bara afhent, heldur einnig unnið.

Kannski, notkun gáma gæti leyst vandamálið að hluta. Þetta er þegar fullt af skilaboðum er pakkað í eitt og þjónninn svaraði með staðfestingu á þau öll í einu, í einu msg_id. En hann mun líka hafna þessum pakka, ef eitthvað fór úrskeiðis, í heild sinni.

Og á þessum tímapunkti koma ótæknileg sjónarmið inn í. Af reynslu höfum við séð margar hækjur og auk þess munum við nú sjá fleiri dæmi um slæm ráðgjöf og arkitektúr - við slíkar aðstæður, er það þess virði að treysta og taka slíkar ákvarðanir? Spurningin er retorísk (auðvitað ekki).

Hvað erum við að tala um? Ef um efnið „fíkniefnaskilaboð um skilaboð“ geturðu samt velt fyrir þér með andmælum eins og „þú ert heimskur, þú skildir ekki frábæra áætlun okkar!“ (svo skrifaðu skjölin fyrst, eins og venjulegt fólk ætti að gera, með rökstuðningi og dæmum um pakkaskipti, þá tölum við saman), þá eru tímasetningar/tímalokar eingöngu hagnýt og ákveðin spurning, allt hér hefur verið vitað lengi. Hvað segja skjölin okkur um tímamörk?

Miðlari viðurkennir venjulega móttöku skilaboða frá viðskiptavini (venjulega RPC fyrirspurn) með því að nota RPC svar. Ef svar er í langan tíma gæti þjónn fyrst sent kvittunarviðurkenningu og nokkru síðar sjálft RPC svarið.

Viðskiptavinur staðfestir venjulega móttöku skilaboða frá netþjóni (venjulega RPC svar) með því að bæta staðfestingu við næstu RPC fyrirspurn ef hún er ekki send of seint (ef hún myndast td 60-120 sekúndur eftir móttöku af skilaboðum frá þjóninum). Hins vegar, ef í langan tíma er engin ástæða til að senda skilaboð til þjónsins eða ef það er mikill fjöldi óviðurkenndra skilaboða frá þjóninum (td yfir 16), sendir viðskiptavinurinn sjálfstæða staðfestingu.

... ég þýða: við sjálf vitum ekki hversu mikið og hvernig við þurfum á því að halda, svo við skulum gera ráð fyrir að láta það vera svona.

Og um ping:

Ping skilaboð (PING/PONG)

ping#7abe77ec ping_id:long = Pong;

Svar er venjulega skilað í sömu tengingu:

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

Þessi skilaboð þurfa ekki staðfestingar. Pong er aðeins sent sem svar við ping á meðan hægt er að hefja ping af hvorri hlið.

Frestað lokun tengingar + PING

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

Virkar eins og ping. Að auki, eftir að þetta hefur borist, ræsir þjónninn tímamæli sem lokar núverandi tengingu disconnect_delay sekúndum síðar nema hann fái ný skilaboð af sömu gerð sem endurstillir sjálfkrafa alla fyrri tímamæla. Ef viðskiptavinurinn sendir þessi ping einu sinni á 60 sekúndna fresti, til dæmis, gæti hann stillt disconnect_delay jafnt og 75 sekúndur.

Ertu brjálaður?! Eftir 60 sekúndur mun lestin fara inn á stöðina, hætta og sækja farþega og aftur missa sambandið í göngunum. Eftir 120 sekúndur, á meðan þú heyrir það, mun það koma á annan og tengingin mun líklega rofna. Jæja, það er ljóst hvaðan fæturnir koma - „Ég heyrði hringingu, en veit ekki hvar það er“, það er reiknirit Nagl og TCP_NODELAY valkosturinn, ætlaður fyrir gagnvirka vinnu. En, fyrirgefðu, haltu áfram við sjálfgefið gildi þess - 200 Millisekúndur Ef þú vilt virkilega sýna eitthvað svipað og vista á mögulega nokkra pakka, þá skaltu fresta því í 5 sekúndur, eða hvað sem „Notandi er að skrifa...“ skilaboðatímamörkin eru núna. En ekki meir.

Og að lokum, pings. Það er að segja að athuga lífgildi TCP tengingarinnar. Það er fyndið, en fyrir um 10 árum síðan skrifaði ég gagnrýninn texta um boðbera heimavistar deildarinnar okkar - höfundarnir þar pinguðu líka netþjóninum frá viðskiptavininum, en ekki öfugt. En 3. árs nemendur eru eitt og alþjóðleg skrifstofa er annað, ekki satt?

Í fyrsta lagi smá fræðsludagskrá. TCP tenging, ef ekki er um pakkaskipti að ræða, getur lifað í margar vikur. Þetta er bæði gott og slæmt, allt eftir tilgangi. Það er gott ef þú varst með SSH tengingu opna við netþjóninn, þú stóðst upp úr tölvunni, endurræsir routerinn, fór aftur á þinn stað - lotan í gegnum þennan netþjón var ekki rifin (þú skrifaðir ekki neitt, það voru engir pakkar) , það er þægilegt. Það er slæmt ef það eru þúsundir viðskiptavina á þjóninum, sem hver og einn tekur upp auðlindir (halló, Postgres!), og gestgjafi viðskiptavinarins gæti hafa verið endurræstur fyrir löngu síðan - en við munum ekki vita af því.

Spjall/spjallkerfi falla inn í annað tilvikið af einni ástæðu til viðbótar - stöður á netinu. Ef notandinn „ datt af“ þarftu að upplýsa viðmælendur hans um þetta. Annars endar þú með mistök sem höfundar Jabber gerðu (og leiðréttu í 20 ár) - notandinn hefur aftengst, en þeir halda áfram að skrifa skilaboð til hans og telja að hann sé nettengdur (sem voru líka algjörlega glataðir í þessum nokkrum mínútum áður en sambandsleysið uppgötvaðist). Nei, TCP_KEEPALIVE valkosturinn, sem margir sem skilja ekki hvernig TCP tímamælir virka, henda inn af handahófi (með því að stilla villt gildi eins og tugi sekúndna), mun ekki hjálpa hér - þú þarft að ganga úr skugga um að ekki aðeins OS kjarninn af vél notandans er á lífi, en virkar líka eðlilega, getur svarað og forritið sjálft (heldurðu að það geti ekki frjósið? Telegram Desktop á Ubuntu 18.04 fraus fyrir mig oftar en einu sinni).

Þess vegna þarftu að pinga netþjóni viðskiptavinur, og ekki öfugt - ef viðskiptavinurinn gerir þetta, ef tengingin er rofin, verður pingið ekki afhent, markmiðinu verður ekki náð.

Hvað sjáum við á Telegram? Það er einmitt hið gagnstæða! Jæja, það er. Formlega geta báðir aðilar auðvitað pingað hvort annað. Í reynd nota viðskiptavinir hækju ping_delay_disconnect, sem stillir tímamælirinn á þjóninum. Jæja, afsakaðu mig, það er ekki undir viðskiptavininum komið að ákveða hversu lengi hann vill búa þar án þess að pinga. Miðlarinn, miðað við álag hans, veit betur. En auðvitað, ef þér er sama um auðlindirnar, þá muntu vera þinn eigin vondi Pinocchio, og hækja mun duga...

Hvernig hefði það átt að vera hannað?

Ég tel að ofangreindar staðreyndir gefi skýrt til kynna að Telegram/VKontakte teymið sé ekki mjög hæft á sviði flutninga (og lægra) tölvuneta og lágt hæfi þeirra í viðeigandi málum.

Hvers vegna reyndist þetta vera svona flókið og hvernig geta Telegram arkitektar reynt að mótmæla? Sú staðreynd að þeir reyndu að gera lotu sem lifir af TCP-tengingarrof, þ.e. það sem var ekki afhent núna, munum við skila síðar. Þeir reyndu líklega líka að gera UDP flutning, en þeir lentu í erfiðleikum og yfirgáfu það (þess vegna eru skjölin tóm - það var ekkert til að monta sig af). En vegna skorts á skilningi á því hvernig netkerfi almennt og TCP sérstaklega virka, hvar þú getur reitt þig á það og hvar þú þarft að gera það sjálfur (og hvernig), og tilraun til að sameina þetta við dulmál „tveir fuglar með einn steinn“, þetta er niðurstaðan.

Hvernig var það nauðsynlegt? Byggt á því að msg_id er tímastimpill sem er nauðsynlegur frá dulmálslegu sjónarhorni til að koma í veg fyrir endurspilunarárásir, það eru mistök að tengja við hann einkvæma auðkennisaðgerð. Þess vegna, án þess að breyta núverandi arkitektúr í grundvallaratriðum (þegar uppfærslustraumurinn er búinn til, það er API á háu stigi fyrir annan hluta þessarar færsluröðar), þyrfti maður að:

  1. Miðlarinn sem heldur TCP tengingunni við viðskiptavininn tekur ábyrgð - ef hann hefur lesið úr innstungunni, vinsamlegast viðurkenna, vinna úr eða skila villu, ekkert tap. Þá er staðfestingin ekki vektor auðkenni, heldur einfaldlega „síðasta móttekna seq_no“ - bara tala, eins og í TCP (tvær tölur - sekúndu þín og sú staðfesta). Við erum alltaf innan þingsins, er það ekki?
  2. Tímastimpillinn til að koma í veg fyrir endurspilunarárásir verður sérstakt svæði, a la nonce. Það er athugað, en hefur ekki áhrif á neitt annað. Nóg og uint32 - ef saltið okkar breytist að minnsta kosti á hálfs dags fresti, getum við úthlutað 16 bitum til lágra bita af heiltölu hluta núverandi tíma, afganginn - í brot úr sekúndu (eins og núna).
  3. Fjarlægt msg_id yfirleitt - frá sjónarhóli aðgreina beiðnir á bakendunum, þá er í fyrsta lagi auðkenni viðskiptavinarins, og í öðru lagi, lotuauðkenni, sameina þær. Samkvæmt því nægir aðeins eitt sem auðkenni beiðni seq_no.

Þetta er heldur ekki farsælasti kosturinn; algjört handahóf gæti þjónað sem auðkenni - þetta er nú þegar gert í API á háu stigi þegar skilaboð eru send. Það væri betra að endurgera arkitektúrinn algjörlega frá hlutfalli yfir í algert, en þetta er umræðuefni fyrir annan hluta, ekki þessa færslu.

API?

Ta-daam! Svo, eftir að hafa barist í gegnum leið fulla af sársauka og hækjum, gátum við loksins sent allar beiðnir til þjónsins og fengið svör við þeim, auk þess að fá uppfærslur frá þjóninum (ekki sem svar við beiðni, heldur hann sjálfur sendir okkur, eins og PUSH, ef einhver er það skýrara þannig).

Athugið, nú verður eina dæmið í Perl í greininni! (fyrir þá sem ekki þekkja setningafræðina, þá eru fyrstu rökin fyrir blessun gagnabygging hlutarins, önnur er flokkurinn hans):

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' )
};

Já, ekki spoiler af ásettu ráði - ef þú hefur ekki lesið hana enn, farðu þá og gerðu það!

Ó, vá~~... hvernig lítur þetta út? Eitthvað mjög kunnuglegt... kannski er þetta gagnauppbygging dæmigerðs vef-API í JSON, nema að flokkar eru líka tengdir hlutum?..

Svo þetta kemur út... Um hvað snýst þetta, félagar?.. Svo mikið átak - og við stoppuðum til að hvíla okkur þar sem vefforritararnir bara að byrja?..Væri ekki bara JSON yfir HTTPS einfaldara?! Hvað fengum við í skiptum? Var fyrirhöfnin þess virði?

Við skulum meta hvað TL+MTProto gaf okkur og hvaða valkostir eru mögulegir. Jæja, HTTP, sem leggur áherslu á beiðni-svar líkanið, passar illa, en að minnsta kosti eitthvað ofan á TLS?

Samningur serialization. Þegar ég sá þessa gagnauppbyggingu, svipað og JSON, man ég að það eru til tvöfaldar útgáfur af því. Við skulum merkja MsgPack sem ófullnægjandi stækkanlegt, en það er til dæmis CBOR - við the vegur, staðall sem lýst er í RFC 7049. Það er athyglisvert fyrir þá staðreynd að það skilgreinir merki, sem stækkunarkerfi, og meðal þegar staðlað laus:

  • 25 + 256 - að skipta út endurteknum línum með tilvísun í línunúmerið, svo ódýr þjöppunaraðferð
  • 26 - Serialized Perl hlutur með flokksheiti og smiðjurökum
  • 27 - raðnúmeraður tungumálóháður hlutur með tegundarheiti og smíðaröksemdum

Jæja, ég reyndi að raðgreina sömu gögnin í TL og í CBOR með strengja- og hlutpökkun virkt. Niðurstaðan fór að vera breytileg CBOR í hag einhvers staðar frá megabæti:

cborlen=1039673 tl_len=1095092

Svo, niðurstaða: Það eru til mun einfaldari snið sem eru ekki háð vandamálum samstillingarbilunar eða óþekkt auðkenni, með sambærilegri skilvirkni.

Fljótleg tenging. Þetta þýðir núll RTT eftir endurtengingu (þegar lykillinn hefur þegar verið búinn til einu sinni) - á við frá fyrstu MTProto skilaboðum, en með nokkrum fyrirvörum - sláðu í sama saltið, lotan er ekki rotin o.s.frv. Hvað býður TLS okkur í staðinn? Tilvitnun í efnið:

Þegar PFS er notað í TLS, TLS fundarmiðar (RFC 5077) til að hefja dulkóðaða lotu aftur án þess að semja aftur um lykla og án þess að geyma lykilupplýsingar á þjóninum. Þegar fyrstu tengingin er opnuð og lyklar eru búnir til dulkóðar þjónninn tengingarástandið og sendir það til viðskiptavinarins (í formi fundarmiða). Í samræmi við það, þegar tengingin er hafin á ný, sendir viðskiptavinurinn setumiða, þar á meðal lotulykilinn, aftur til netþjónsins. Miðinn sjálfur er dulkóðaður með tímabundnum lykli (session ticket key), sem er geymdur á þjóninum og þarf að dreifa á alla framenda netþjóna sem vinna SSL í klasalausnum.[10]. Þannig getur kynning á setumiða brotið gegn PFS ef tímabundnir miðlaralyklar eru í hættu, til dæmis þegar þeir eru geymdir í langan tíma (OpenSSL, nginx, Apache geyma þá sjálfgefið fyrir allan tíma forritsins; vinsælar síður nota lykillinn í nokkrar klukkustundir, allt að daga).

Hér er RTT ekki núll, þú þarft að skiptast á að minnsta kosti ClientHello og ServerHello, eftir það getur viðskiptavinurinn sent gögn ásamt Finished. En hér ættum við að muna að við höfum ekki vefinn, með fullt af nýopnuðum tengingum, heldur boðbera, sem tengingin er oft ein og meira og minna langvarandi, tiltölulega stuttar beiðnir á vefsíður - allt er margfaldað innbyrðis. Það er, það er alveg ásættanlegt ef við komumst ekki yfir mjög slæman neðanjarðarlestarkafla.

Gleymdirðu einhverju öðru? Skrifaðu í athugasemdir.

Framhald!

Í seinni hluta þessarar færsluröðar munum við ekki fjalla um tæknileg, heldur skipulagsatriði - nálganir, hugmyndafræði, viðmót, viðhorf til notenda osfrv. Byggt þó á þeim tækniupplýsingum sem hér komu fram.

Þriðji hlutinn mun halda áfram að greina tæknilega hluti / þróunarupplifun. Þú munt læra, sérstaklega:

  • framhald heimsfaraldursins með fjölbreytni TL gerða
  • óþekkt atriði um rásir og ofurhópa
  • hvers vegna samræður eru verri en listi
  • um alger vs afstæð skilaboðavörslu
  • hver er munurinn á mynd og mynd
  • hvernig emoji truflar skáletraðan texta

og aðrar hækjur! Fylgstu með!

Heimild: www.habr.com

Bæta við athugasemd