Полный текст выпусков с анимированными SVG-схемами: «Письмо в сортировочном центре», «Что видит наблюдатель», «Читаем командную строку zapret2», «Где резать: метки», «multisplit: резать на части», «Перемешать: disorder», «Внутри программы». Список видео в Zapret2/Zapret2.md обновлён. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
88 KiB
Executable file
| title | date | tags | aliases | description | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Видео 4. Где резать: метки | 2026-10-25 |
|
|
Видео 4 серии о Zapret 2: позиции разреза, метки host, sld, midsld, endhost, списки меток и границы, с анимированными схемами. |
[!info] О чём заметка Это полный текст видео номер 4 из серии о том, как работает Zapret2/Zapret2, разбитый по сценам, с анимированными схемами. Каждую сцену можно читать отдельно.
[!tip] Как читать пометки «В нашем тесте» значит настоящий запуск в лаборатории проекта: клиент, роутер, сервер. «По коду» значит, что мы прочитали исходники, но не запускали. «По общим сведениям» значит, что это вне нашего кода и лаборатории. О настоящем оборудовании ТСПУ мы ничего не утверждаем: наш «инспектор» это модель.
Повтор и вопрос: как сказать программе «режь здесь»
Добро пожаловать в выпуск о метках позиций. В нём один вопрос: как сказать программе, в каком месте резать пакет. Сначала короткий повтор, чтобы выпуск можно было смотреть отдельно.
В выпуске «Письмо в сортировочном центре» мы познакомились с игрушечным инспектором. Это модель, которую мы сами собрали в лаборатории: правило в роутере, которое смотрит на каждый пакет отдельно, всегда в одно и то же место, и ничего не помнит.
В выпуске «Что видит наблюдатель» мы разобрали по байтам первое сообщение защищённого соединения, приветствие ClientHello. В нашей лаборатории оно занимает двести пятьдесят байт, и имя сайта www.example.com лежит в нём с девяносто седьмого байта по сто одиннадцатый. Здесь и дальше мы считаем с нуля: самый первый байт данных имеет номер ноль.
А в самом начале серии это приветствие разрезали посередине слова example. Вместо одного пакета ушли два: сто четыре байта и сто сорок шесть. Первый кончается буквами «www точка exa», второй начинается с «mple точка com». В нашем тесте разрезанное приветствие прошло мимо игрушечного инспектора.
Тот разрез делала одна строка: опция lua-desync равно multisplit, двоеточие, pos равно midsld. Как читать такие строки, мы разобрали в выпуске «Читаем командную строку zapret2»: до двоеточия стоит имя стратегии, то есть имя функции, которая работает с пакетом, а после него аргументы вида «название равно значение». Стратегия multisplit режет пакет с данными на части в заданных местах. Как именно, расскажет следующий выпуск.
Сегодня нас интересует одно слово из этой строки: midsld, «середина слова example». Мы не писали «сто четыре». Мы написали слово, а программа сама нашла сто четвёртый байт. Такое слово мы будем называть меткой: это слово вместо числа, которое программа сама превращает в номер байта. Как она это делает? Что ещё можно написать вместо числа? И что будет, если метка не подходит к пакету?
!wiki-anim-video04-s01-word-to-number.svg
Сначала зачем вообще слова. Представьте курьера, пример выдуманный. Вы просите: «разрежь письмо на четвёртом слове». Для одного письма это сработает. В другом письме четвёртое слово окажется совсем не там, где имя получателя. А просьба «разрежь перед именем получателя» подходит к любому письму: курьер сам находит имя. Метка в настройке это такая просьба.
!wiki-anim-video04-s01-toy-courier.svg
Договоримся о двух выражениях. «В нашем тесте» значит: мы запустили настоящую программу nfqws2, главную программу zapret2, в лаборатории, с клиентом, роутером и сервером, и посмотрели, что ушло на провод. «По общим сведениям» значит: это не из нашего кода и не из нашей лаборатории. Где мы просто считаем на бумаге, так и скажем: «по счёту». О настоящем ТСПУ мы по-прежнему ничего не знаем: здесь только zapret2 и наша лаборатория.
План такой. Сначала на проводе: числа, метки для имени сайта, метки вместе со списками и места, где метки не работают. Потом три настоящих запуска с размерами частей. И только в конце, коротко, код: как метка становится числом и откуда берётся сдвиг на единицу.
Итак, вопрос выпуска: как сказать «режь здесь». Можно числом, а можно меткой, словом, которое программа сама превращает в номер байта. Начнём с чисел.
Позиция это номер байта: счёт с нуля, разрез перед байтом
Начнём с самого простого способа сказать «режь здесь»: числом. Вопрос сцены: что такое позиция и как по ней получаются части?
Строка из восьми байт, пример выдуманный: слово «То», двоеточие, пробел и имя «Анна». Номера считаем с нуля: тэ ноль, о один, двоеточие два, пробел три, а четыре, эн пять, эн шесть, а семь.
Скажем «режь на позиции четыре». Разрез делается перед байтом с этим номером: часть до него кончается байтом раньше, а часть после начинается с самого этого байта. Получаются «То, двоеточие, пробел» и «Анна».
Сколько байт в первой части? Столько, какой номер у разреза: четыре. Число байт в первой части равно позиции.
А вторая часть это остаток: размер минус позиция. Восемь минус четыре, тоже четыре.
Отрицательное число считается от конца. «Минус четыре» в нашей строке это восемь минус четыре, то есть снова четыре: тот же разрез, перед именем. А «минус один» это самый последний байт.
!wiki-anim-video04-s02-toy-cut.svg
Это и есть позиция: номер байта, перед которым делается разрез. Если в настройке стоит просто число, такая позиция называется абсолютной: она не зависит от содержимого пакета, только от места в данных.
Во всех опытах мы включали отладочный журнал программы: это отдельная опция. В него multisplit пишет строку «позиции разреза» с номерами байтов, где она собралась резать; без отладки такой строки нет. А слова «на провод ушли пакеты» значат, что мы смотрели записанные пакеты между клиентом и сервером.
Теперь на настоящем приветствии, двести пятьдесят байт. Позиция один: в нашем тесте на провод ушли два пакета, в первом один байт, во втором двести сорок девять. Первый пакет это самый первый байт приветствия.
Позиция двести сорок девять даёт два пакета наоборот: двести сорок девять байт и один, последний.
Дальше идти нельзя. Позиция должна быть меньше размера данных: разрез на двести пятидесятой оставил бы вторую часть пустой. В нашем тесте с такой позицией программа ничего не разрезала, написала в журнал «нет допустимых позиций», и на провод ушёл один пакет в двести пятьдесят байт.
Минус десять на настоящем приветствии это байт номер двести сорок: двести пятьдесят минус десять. В нашем тесте на провод ушли два пакета, двести сорок байт и десять.
Тот же принцип на запросе HTTP, он занимает восемьдесят один байт. В нашем тесте позиция тридцать два разрезала запрос перед именем сайта: тридцать два байта в первом пакете и сорок девять во втором, потому что восемьдесят один минус тридцать два сорок девять.
Проверь себя. Позиция минус один на нашем приветствии. Какие два пакета уйдут в сеть?
Двести сорок девять байт и один: минус один это последний байт, двести сорок девятый, и разрез стоит перед ним. В нашем тесте с позицией минус один журнал показал двести сорок девять, а на провод ушли именно двести сорок девять байт и один байт.
Позиция ноль, разрез перед самым первым байтом, оставила бы пустую первую часть. Что с ней делает программа, посмотрим в разборе запусков в конце выпуска.
А если позицию не написать совсем, multisplit по коду возьмёт два. В нашем тесте без позиции на провод ушли два байта и двести сорок восемь.
!wiki-anim-video04-s02-real-cuts.svg
Итак, позиция это номер байта, перед которым режут, счёт с нуля, и первая часть по длине равна позиции. Отрицательное число считается от конца, а позиция должна быть меньше размера данных. Но число привязано к пакету: сто четыре годится для нашего приветствия, а для запроса в восемьдесят один байт сто четыре за границей. Нужны слова, которые находят место сами.
Где в приветствии лежит имя: ходьба по длинам
Чтобы метка «начало имени» превратилась в номер, программа должна сначала найти имя. Вопрос сцены: как она находит его в приветствии? Мы уже шли по этим байтам руками в выпуске «Что видит наблюдатель». Программа идёт так же, и нам важны три места, через которые она проходит.
Одно уточнение. Программа не гадает, что лежит в пакете. Сначала она приклеивает к пакету ярлык, тип нагрузки: так называется вид данных, например приветствие TLS, запрос HTTP или начальный пакет QUIC. Как она решает, разбирает выпуск «Тип нагрузки и имя сайта». Нам достаточно знать, что у нашего приветствия ярлык «приветствие TLS», и что от ярлыка зависит, какие слова вообще понимаются.
Игрушка, пример выдуманный. Лента, где перед каждым блоком записано, сколько в нём байт: «четыре» и четыре байта данных, «пять» и пять байт данных. Чтобы добраться до третьего блока, читаем число, прыгаем через столько байт и читаем следующее число. Так и ходят по длинам.
Первые пять байт приветствия это заголовок записи, потом четыре байта заголовка сообщения, два байта версии и тридцать два байта случайного числа.
Пять плюс четыре плюс два плюс тридцать два: сорок три. С сорок третьего байта начинаются поля, у которых длина записана внутри. Первое из них длина идентификатора сессии, у нас там тридцать два.
Программа читает эту длину и прыгает: сорок три, плюс один байт самой длины, плюс тридцать два байта идентификатора, семьдесят шесть.
На семьдесят шестом байте два байта длины списка наборов шифров, там шесть. Семьдесят шесть, плюс два, плюс шесть: восемьдесят четыре.
На восемьдесят четвёртом байте один байт длины списка способов сжатия, там один. Восемьдесят четыре, плюс один, плюс один: восемьдесят шесть. По коду пропускаются именно эти три части: идентификатор сессии, наборы шифров и способы сжатия.
На восемьдесят шестом байте лежат два байта длины всех расширений: сто шестьдесят два. Это первое важное место. Его имя, то есть метка, extlen.
!wiki-anim-video04-s03-walk-lengths.svg
Дальше сами расширения, блоки «тип, длина, данные». Программа перепрыгивает блоки по длинам, пока не найдёт тип ноль, «имя сервера». В нашем приветствии он первый, на восемьдесят восьмом байте: два байта типа, два байта длины, а данные начинаются с девяносто второго. Это второе место, метка sniext.
Внутри данных идут три поля: длина списка, восемнадцать; один байт типа имени, ноль; и два байта длины имени, пятнадцать. Имя начинается на девяносто седьмом байте. Это третье место, метка host.
!wiki-anim-video04-s03-name-fields.svg
Восемьдесят шесть, девяносто два, девяносто семь: те же числа, что мы читали глазами. В большом приветствии на тысячу четыреста восемьдесят четыре байта они те же: всё, что стоит перед именем, там той же длины.
Это верно для наших двух приветствий. По общим сведениям, у других клиентов расширения могут идти в другом порядке, и тогда имя окажется дальше: sniext и host сдвинутся, а extlen останется на месте, пока не изменятся поля до него. Вот почему в настройках пишут метку, а не число.
Проверь себя. Допустим, всё то же, но идентификатор сессии пустой: длина ноль вместо тридцати двух. Куда сдвинется начало имени?
На тридцать два байта левее, на шестьдесят пятый, если всё остальное осталось тем же. Прыжок через пустой идентификатор короче на тридцать два байта, и все места после него уезжают назад на столько же. Это счёт, а не запуск.
Итак, программа находит имя ходьбой по длинам и по пути проходит три места: extlen на восемьдесят шестом байте, sniext на девяносто втором и host на девяносто седьмом.
А в запросе HTTP: другая упаковка, другое число, и девять имён
В запросе HTTP то же имя лежит иначе. Вопрос сцены: где и как программа находит его там, и почему из этого следует, что нужны слова?
Наш запрос, восемьдесят один байт. Первая строка: «GET, косая черта, index точка html, пробел, HTTP, косая черта, один, точка, один». Потом строка «Host, двоеточие, пробел, www точка example точка com», а за ней ещё две строки заголовков, User-Agent и Accept.
Сначала как программа узнаёт запрос. По коду, пакет считается запросом HTTP, если он начинается с одного из девяти слов, заглавными буквами: GET, POST, HEAD, OPTIONS, PUT, DELETE, CONNECT, TRACE или PATCH. Дальше должны идти пробел, адрес и слова «HTTP, косая черта, один, точка».
Теперь имя. Программа ищет строку, которая начинается со слова Host с двоеточием, в любом регистре, и читает имя до пробела или до конца строки. Это отдельный разбор, не тот, что считает метки: у меток, как увидим в сцене про запрос, свои правила.
В нашем запросе слово Host с двоеточием занимает байты с двадцать шестого по тридцатый, пробел стоит на тридцать первом, а имя идёт с тридцать второго по сорок шестой. Те же пятнадцать байт, что и в приветствии, но на другом месте: тридцать два, а не девяносто семь.
Для программы это две разные задачи. В приветствии нужно прыгать по длинам. В запросе нужно найти слово Host с двоеточием: длин там нет, есть строки текста. А результат один и тот же: номер байта, с которого начинается имя.
!wiki-anim-video04-s04-two-strips.svg
Вот почему одно число на все пакеты не напишешь. Метка host значит «начало имени там, где оно лежит в этом пакете». Для приветствия это девяносто семь, для запроса тридцать два.
Вернёмся к игрушке со строкой «То, двоеточие, пробел, Анна». Поставим впереди слово подлиннее: «Hello, двоеточие, пробел, Anna». Теперь имя начинается не на четвёртом байте, а на седьмом. Число изменилось, а просьба «перед именем» осталась верной.
Такое слово в настройках называется меткой позиции. Это короткое имя, иногда со знаком плюс или минус и числом. Программа превращает его в номер байта в данных пакета, считая с нуля, и разрез делается перед этим байтом.
Имён у меток девять, список будет на экране. Первое, abs, это просто число, абсолютная позиция, мы её уже видели. Начало имени сайта называется host, а первый байт сразу после имени, endhost.
Ещё три имени про второе справа слово имени сайта: в адресе www точка example точка com это слово example. Его начало называется sld, середина midsld, конец endsld.
Последние три привязаны к типу пакета. Method это начало слова-метода в запросе HTTP, например слова GET. Extlen и sniext вы уже встретили в приветствии.
!wiki-anim-video04-s04-nine-names.svg
Проверь себя. Запрос начинается не с «GET», а с маленьких букв «get». Признает ли программа такой пакет запросом HTTP?
Нет: в списке только заглавные слова. Пакет получит ярлык «неизвестно», а от ярлыка зависит, какие слова понимаются, так что для него будут работать только числа. Это по коду.
Итак, в приветствии имя находят ходьбой по длинам, в запросе поиском строки Host. Места разные, а метки одни. Метка позиции это слово, которое превращается в номер байта, и таких слов девять.
Метки на приветствии: все флажки на одной ленте
Теперь метки на одной ленте. Приветствие, двести пятьдесят байт, и флажки по очереди выезжают на свои места. Числа читаем по таблице на экране, а в конце сцены проверим её настоящим запуском.
!wiki-anim-video04-s05-hello-markers.svg
Метка host, начало имени: девяносто седьмой байт, там стоит первая буква имени, дабл-ю. Имя занимает пятнадцать байт, с девяносто седьмого по сто одиннадцатый.
Метка endhost, первый байт сразу после имени: сто двенадцатый. Разрез на host оставляет имя целиком во второй части, разрез на endhost оставляет его целиком в первой.
В нашем тесте разрез по host дал два пакета: девяносто семь байт и сто пятьдесят три. Второй пакет начинается с самого имени, «www точка example точка com».
Выбор между host и endhost не мелочь. В нашей лаборатории есть игрушечный инспектор: он ищет имя в одном и том же месте каждого пакета, на девяносто седьмом байте данных, и ничего не склеивает. Это модель, а не оборудование. В нашем тесте, когда мы разрезали по endhost, имя осталось в первом пакете на привычном месте, игрушка его нашла и выбросила пакет, и сервер не получил ни одного байта. Когда по host, имя переехало в начало второго пакета, и сервер получил все двести пятьдесят байт.
Две метки ведут внутрь приветствия. Extlen, поле длины расширений: восемьдесят шестой байт, первый байт двухбайтового поля.
Sniext, данные расширения с именем сайта: девяносто второй байт. Его легко принять за начало расширения. Но само расширение, вместе с заголовком, началось раньше, с восемьдесят восьмого, а метка указывает на первый байт данных, после типа и длины.
Три метки про слово example. Оно занимает байты с сто первого по сто седьмой, семь букв. Sld, начало слова, сто один; midsld, середина, сто четыре; endsld, первый байт после слова, сто восемь. Откуда эти числа, разберём через две сцены, а пока возьмём их из таблицы.
В нашем тесте мы проверили таблицу настоящим запуском. Список из семи имён мы написали в таком порядке: host, sld, midsld, endsld, endhost, sniext, extlen. Программа записала в журнал числа по возрастанию: восемьдесят шесть, девяносто два, девяносто семь, сто один, сто четыре, сто восемь, сто двенадцать. Все семь совпали с таблицей.
!wiki-anim-video04-s05-list-sorting.svg
На большом приветствии, тысяча четыреста восемьдесят четыре байта, список из тех же семи имён, только в другом порядке, в нашем тесте дал те же семь чисел. Это приветствие в сети приходит несколькими пакетами; что с этим делает программа, ещё будет.
Проверь себя. Разрез на метке sniext. Сколько байт в первой части и чем она кончается?
Девяносто два: число байт в первой части равно позиции. Она кончается заголовком расширения, то есть типом и длиной, а имя ещё не началось. Это счёт по таблице, такого запуска у нас не было.
Итак, на нашем приветствии: extlen восемьдесят шесть, sniext девяносто два, host девяносто семь, sld сто один, midsld сто четыре, endsld сто восемь, endhost сто двенадцать. Метка method на приветствии не находится.
Метки на запросе HTTP, метка method и две ловушки
Тот же приём на запросе HTTP. Вопрос сцены: какие числа у тех же меток на запросе в восемьдесят один байт?
!wiki-anim-video04-s06-http-markers.svg
Method, начало слова GET: ноль. Host: тридцать два. Sld, начало слова example: тридцать шесть.
Midsld: тридцать девять. Endsld: сорок три. Endhost: сорок семь.
В нашем тесте настоящий запуск со списком из этих шести меток записал в журнал именно эти числа, по возрастанию.
Слово example в запросе занимает байты с тридцать шестого по сорок второй, те же семь букв, что в приветствии. Середина: тридцать шесть плюс три, тридцать девять.
Посмотрим на midsld. В приветствии сто четыре, в запросе тридцать девять. Числа разные, а место одно: и там и там разрез падает между «а» и «эм», «exa» и «mple». Метка находит нужное место в любом из двух пакетов, и поэтому она удобнее числа.
В нашем тесте со списком «host, midsld» запрос HTTP ушёл тремя пакетами. Тридцать два байта: первая строка и «Host, двоеточие, пробел». Потом семь байт «www точка exa» и сорок два байта, начиная с «mple точка com».
Метка method нужна, когда разрез хочется поставить в самом слове-методе. Это начало слова GET, POST или другого из девяти методов, когда пакет уже признан запросом. Method плюс два это разрез после двух букв: в нашем тесте запрос разошёлся на «GE» и ещё семьдесят девять байт.
Имя в запросе на сорок шестом байте кончается, а endhost равен сорока семи: это уже конец строки, возврат каретки, первый байт после имени. Разрез на нём оставляет имя целиком в первой части.
Игрушка, пример выдуманный. Вы просите «перед именем после слова Кому, двоеточие». А после двоеточия вдруг два пробела, и имя начинается буквой позже. Просьбе нужно точно знать, сколько пробелов пропускать.
!wiki-anim-video04-s06-http-traps.svg
Теперь ловушки запроса, все в нашем тесте, на запросах, которые мы подготовили сами. Первая. Метка host пропускает после двоеточия ровно один пробел. Если пробелов два, метка встанет на второй пробел, а не на букву, хотя поиск имени пропускает все.
Вторая. Endhost считается до конца строки. Если за именем стоят пробелы или порт, они попадают в «имя», и endhost уезжает за них: на запросе с портом «двоеточие восемь ноль восемь ноль» он стоит после порта.
И третья. Для всех меток про имя в запросе после слова Host с двоеточием должно идти хотя бы три байта, считая пробел. Поиск имени справится и с двумя, а метка нет.
Проверь себя. На нашем запросе написали pos равно endhost. Сколько байт во второй части и чем она начинается?
Тридцать четыре: восемьдесят один минус сорок семь. Вторая часть начинается с конца строки: возврат каретки и перевод строки, потом заголовок User-Agent. В нашем тесте вышло именно так.
Итак, на запросе HTTP: method ноль, host тридцать два, sld тридцать шесть, midsld тридцать девять, endsld сорок три, endhost сорок семь. Midsld даёт то же место, что в приветствии, только число другое. Помните ловушки: host пропускает один пробел, endhost считается до конца строки, а после Host с двоеточием нужны три байта.
Второе справа слово: правило точки и середина слова
Остаётся самая интересная метка, midsld, середина второго справа слова. Начнём с самого слова. Вопрос сцены: как программа его находит?
В имени www точка example точка com слова разделены точками. Справа налево это com, example, www. Второе справа example. В программе его называют sld, от английского second-level domain, «домен второго уровня». По общим сведениям, доменом второго уровня строго называют example точка com целиком, но sld мы называем слово example, как в программе.
Идея простая, как у человека. Встаём в конец имени, идём влево до первой точки и запоминаем её. Идём влево дальше, до следующей точки или до начала имени. Слово между этими двумя местами и есть искомое.
В программе это делает «поиск слова номер эн справа», в коде FindNLD. Мы называем его просто поиском. Ему дают имя и номер слова справа, у нас два, а он отвечает, где слово начинается и сколько в нём байт.
!wiki-anim-video04-s07-find-word.svg
Теперь по клеткам. Имя состоит из пятнадцати байт, номера внутри имени с нуля. «www» занимает нулевой, первый и второй байты. Точка стоит на третьем. Слово example идёт с четвёртого по десятый. Вторая точка на одиннадцатом, а com с двенадцатого по четырнадцатый.
Первый указатель встаёт за конец имени, на пятнадцать, и шагает влево, пока не встретит точку: четырнадцать, тринадцать, двенадцать, одиннадцать. Стоп, это точка перед com.
Второй указатель стартует с десяти, на клетку левее, и тоже шагает влево: девять, восемь и так до трёх. Это точка перед example. Слово начинается на байт правее точки, на четвёртом. Длина: одиннадцать минус четыре, семь. Итог: начало четыре, длина семь. Все эти номера внутри имени, не в пакете.
!wiki-anim-video04-s07-add-to-packet.svg
Теперь в пакет. К номеру внутри имени прибавляется начало имени. В нашем приветствии оно на девяносто седьмом байте. Метка sld это начало слова: девяносто семь плюс четыре, сто один. Метка endsld это конец слова: сто один плюс семь, сто восемь.
А midsld это начало плюс длина, поделённая нацело на два. Деление нацело значит, что остаток отбрасывается: семь на два это три. Сто один плюс три, сто четыре.
Одно исключение. Если в слове одна буква, программа берёт начало плюс один, чтобы разрез встал после этой буквы.
Для запроса HTTP формулы те же, имя начинается на тридцать втором байте: sld тридцать шесть, endsld сорок три, midsld тридцать девять.
Посмотрим на сами байты приветствия. На сто четвёртом стоит буква «эм», а перед ней буква «а»: слово разрезано как «exa» и «mple». На сто первом байте слово начинается, на сто восьмом стоит точка перед com.
Разрез на сто четвёртом даёт первую часть в сто четыре байта, она кончается на «www точка exa». Вторая часть, сто сорок шесть байт, начинается с «mple точка com». Это те самые сто четыре и сто сорок шесть из выпуска «Письмо в сортировочном центре», и теперь мы знаем, откуда они.
Проверь себя. Имя www точка икс точка com начинается на тридцать втором байте. Чему равны sld, midsld и endsld?
Sld тридцать шесть: имя начинается на тридцать втором, потом «www» и точка. Endsld тридцать семь, слово из одной буквы. И midsld тоже тридцать семь: это то самое исключение, начало плюс один. Это счёт, запуска с таким именем у нас не было.
Итак, поиск находит слово по двум точкам: у нашего имени начало четыре и длина семь. Метки sld, endsld и midsld складывают эти числа с началом имени: для приветствия получается сто один, сто восемь и сто четыре. У слова из одной буквы midsld это начало плюс один.
Правило точки на необычных именах: co.uk, адрес IP, localhost
Что программа считает вторым справа словом у необычных имён? Ответы этой сцены дал «прогон функций»: мы взяли настоящие файлы разбора из программы, без изменений, дали им готовые строки и посмотрели, что они вернут. Это не запуск nfqws2, и в сеть ничего не уходит.
Игрушка, пример выдуманный. Курьер идёт по адресу с конца и считает точки, смысла слов он не понимает. Скажите ему «второе слово справа», и он назовёт его, даже если для человека это странный выбор. С именами сайтов так же.
Сначала напомним: поиск умеет искать слово любого номера справа. У нашего имени первое справа com, второе example, третье www, а четвёртого нет. Но метки sld, midsld и endsld всегда просят второе.
!wiki-anim-video04-s08-odd-names.svg
Имя example точка com, без www. Слово example начинается на нулевом байте имени, длина семь. Слева от него ничего нет, и это не мешает: поиск идёт до начала имени.
Имя st точка vk точка com. Второе справа слово vk, начало три, длина два.
Слово из одной буквы, как «икс» в имени www точка икс точка com, мы уже видели: у него midsld равна началу плюс один.
Имя «а» точка «бэ» точка co точка uk. Списка суффиксов вроде «co точка uk» в программе нет, по коду. Она просто берёт второе справа слово: co.
Адрес IP версии четыре в строке Host: четыре числа между точками. Для программы это тоже слова, и второе справа это третье по счёту число. Если в нём три цифры, midsld встанет после первой цифры. Запроса с таким адресом в нашей лаборатории не было, это прогон функций.
У имени localhost точек нет, и второго справа слова нет. Метки sld, midsld и endsld не находятся, разрез по ним молча пропадёт. Метки host и endhost с таким именем работают.
Ещё странность, тоже прогон функций. Если после com стоит лишняя точка, вторым справа оказывается com: слово после последней точки пустое, но оно считается первым справа, и счёт сдвигается на одно слово.
!wiki-anim-video04-s08-word-levels.svg
Проверь себя. Имя «а» точка «бэ» точка co точка uk. Внутри какого слова окажется разрез по midsld и где именно?
Внутри слова co, после его первой буквы. Midsld это начало слова плюс половина длины нацело: длина два, половина один. Это счёт по результату прогона; запуска с таким именем не было.
Проверь себя ещё раз. Строка Host: localhost, а в настройке «host, midsld», без пробела после запятой. Какие позиции останутся?
Одна: host. Midsld у имени без точек не находится, молча выпадает, и разрез будет один, перед именем. Это счёт по результатам прогона функций, запуска с таким запросом не было.
Определяет ли настоящее оборудование второе слово так же, мы не знаем. Мы описываем только правило zapret2.
Итак, правило одно: второе справа слово между точками. Списка суффиксов нет. У адреса IP это третье число, у слова из одной буквы midsld стоит после неё, а у имени без точек слова нет, и метки про него не находятся.
Метка плюс число, и как программа читает запись
К метке можно прибавить число. Вопрос сцены: что это даёт и как программа читает такую запись?
Игрушка, пример выдуманный. В строке «То, двоеточие, пробел, Анна» возьмём метку «перед именем» и прибавим один. Разрез сдвинется на байт вправо: первая буква имени останется слева, а остальные уйдут направо.
Запись «host плюс один» значит: взять метку host и сдвинуть разрез на один байт вправо. «Endhost минус один» это разрез перед последним байтом имени. Метка, знак, число, без пробелов.
В нашем тесте мы запускали настоящую программу с такими записями. «Host плюс один» дал девяносто восемь, а «endhost минус один» сто одиннадцать: это последний байт имени, и разрез стоит перед ним.
Ещё запуск: «host плюс три» и «endhost минус два». Журнал показал сто и сто десять, и на проводе ушли три пакета: сто байт, десять байт «точка, example, точка, цэ» и сто сорок байт.
!wiki-anim-video04-s09-offset-on-strip.svg
И «method плюс два» мы уже видели на запросе: два.
Остальные записи тоже запускали в нашем тесте. На приветствии: «host минус один» девяносто шесть, «midsld плюс один» сто пять, «sld плюс два» сто три.
На запросе: «host плюс один» тридцать три, «host минус один» тридцать один, «midsld плюс один» сорок, «sld плюс два» тридцать восемь.
Проверь себя. На запросе HTTP чему равна запись «endhost плюс два» и что стоит в этом месте?
Сорок девять: сорок семь плюс два. Это начало строки User-Agent, потому что два байта до неё занимают возврат каретки и перевод строки. Первая часть кончается вместе со строкой Host. Это счёт.
Теперь как программа читает такую запись. Сначала пробует прочитать всё как число. Получилось: это абсолютная позиция. Не получилось: она берёт слово до первого плюса или минуса, сверяет его с девятью именами и читает остаток как поправку. Нет остатка, поправка ноль.
Имя должно совпасть точно, регистр важен. В нашем тесте pos равно foo, имени нет в таблице, и программа сообщила об ошибке «плохой список меток»: приветствие ушло одним пакетом, двести пятьдесят байт, без разреза. По прогону функций, слово HOST большими буквами тоже отказ.
Зато число читается очень снисходительно: цифры, пока они идут, а хвост молча отбрасывается. Цифра пять и буква икс читается как пять. Два, точка, пять читается как два. А ноль, буква икс, один, ноль, шестнадцатеричная запись, читается как ноль: шестнадцатеричных чисел здесь не понимают.
В нашем тесте эти две опечатки стояли в списке, и журнал показал пять и ноль, а ошибки не было.
Ещё три правила разбора, прогон функций. «Host плюс» без числа значит «host плюс ноль». Пробелов в записи быть не должно: в «host запятая пробел midsld» после запятой стоит пробел, и весь список отвергается. И число должно помещаться в шестнадцатиразрядное целое со знаком: тридцать две тысячи семьсот шестьдесят семь можно, тридцать две тысячи семьсот шестьдесят восемь отказ.
!wiki-anim-video04-s09-marker-parsing.svg
Итак, к метке можно прибавить число, и разрез сдвинется. Имя читается точно, регистр важен, а число снисходительно: опечатка вроде «ноль, икс, один, ноль» тихо превращается в ноль, и никто об этом не скажет.
Списки меток: порядок, повторы, выпавшие метки
Метки собираются в список через запятую. Вопрос сцены: в каком порядке программа их применяет и что делает с повторами и с теми, что не нашлись?
Игрушка, пример выдуманный. Повар получает бумажку с заказом «суп, салат, суп, чай». Он вычёркивает то, чего нет в меню, склеивает одинаковые заказы и выдаёт по порядку меню. Список меток обрабатывается так же.
Список читается целиком. Каждая метка переводится в номер байта, те, что не нашлись, выбрасываются, остальные сортируются по возрастанию, повторы склеиваются. Поэтому порядок слов в настройке не важен.
Список принимают стратегии multisplit и multidisorder. Остальные стратегии с позициями берут одну метку или диапазон от метки до метки. И все они, по коду, работают только с пакетами TCP.
Размер средней части это разность двух соседних позиций. Возьмём список из четырёх: «два, host, midsld, endhost». Это числа два, девяносто семь, сто четыре и сто двенадцать.
Первая часть два байта, потому что позиция два. Вторая: девяносто семь минус два, девяносто пять.
Третья: сто четыре минус девяносто семь, семь. Четвёртая: сто двенадцать минус сто четыре, восемь. Последняя до конца: двести пятьдесят минус сто двенадцать, сто тридцать восемь. В нашем тесте на проводе были именно эти пять пакетов, вместе ровно двести пятьдесят байт.
!wiki-anim-video04-s10-part-sizes.svg
Теперь список с перестановкой и повторами: «midsld, два, midsld, host, два». В нашем тесте в журнале стояли два, девяносто семь и сто четыре: повторы склеились, и на проводе ушли четыре пакета, два байта, девяносто пять, семь и сто сорок шесть.
Прогон функций, ещё три списка. «Sniext, extlen» даёт восемьдесят шесть и девяносто два: extlen идёт первым, хотя написан вторым. «Endhost, host» даёт девяносто семь и сто двенадцать, порядок исправлен. «Host, host» даёт одно число, девяносто семь.
Метки, которые не нашлись, пропадают молча. «Два, пятьсот, минус один» даёт два и двести сорок девять: пятьсот за границей. А «midsld плюс двести» уходит за границу целиком, и не остаётся ничего.
В нашем тесте с «method, midsld» на приветствии метка method выпала, осталась одна midsld, и приветствие ушло двумя пакетами, сто четыре и сто сорок шесть. Ошибки нет.
А если не осталось ни одной позиции? Программа пишет в журнал «нет допустимых позиций» и пропускает пакет без изменений. В нашем тесте так вышло с «пятьсот, минус двести пятьдесят один, method»: все три не нашлись, и приветствие ушло одним пакетом, двести пятьдесят байт.
!wiki-anim-video04-s10-list-ladder.svg
Одна ошибка в записи ломает весь список. Пустое место между запятыми, например две запятые подряд, или неизвестное имя дают «плохой список меток». Пустая строка ошибкой не считается: это ноль меток. Это прогон функций.
Проверь себя. Список «endhost, host, host». Какие числа получатся и сколько частей уйдёт?
Девяносто семь и сто двенадцать: повтор склеился, порядок исправлен. Два разреза, три части: девяносто семь байт, пятнадцать байт имени, это сто двенадцать минус девяносто семь, и сто тридцать восемь. В нашем тесте вышло именно так.
Итак, список переводится метка за меткой, выпавшие метки пропадают, остальные сортируются и очищаются от повторов, а порядок в записи не важен. Если ничего не осталось, пакет идёт без изменений. Одна неверная метка ломает весь список.
Где метки не работают: чужой тип, QUIC, мало байт, склеенное приветствие
Теперь о том, где метки не работают. Вопрос сцены: что будет, если метка не подходит к пакету? Короткий ответ: ничего, и ошибки не будет.
Метки-слова понимают только два типа пакетов: приветствие TLS и запрос HTTP. Для любого другого типа работают только числа. Чужая метка не вызывает ошибку, она просто не находится. Вот зачем нужен ярлык с типом: он выбирает правила.
Это как ключ от чужой двери. Он не подошёл, дверь цела, она просто не открылась.
Прогон функций, настройка «host, method, два». Если функциям сказать, что наше приветствие это запрос HTTP, остаётся только два. Если запрос принять за приветствие или приветствие за пакет неизвестного типа, тоже только два. В настоящей программе тип выбирается по байтам, это опыт над функциями.
В нашем тесте то же на настоящем запуске: pos равно method на приветствии. Метка не нашлась, в журнале «нет допустимых позиций», и приветствие ушло целым, двести пятьдесят байт.
Теперь начальный пакет QUIC. По общим сведениям, QUIC это протокол поверх UDP, и в его начальном пакете приветствие зашифровано. В нашей записи такого пакета, тысяча двести байт, слов «www» и «google» нет вообще: имя под шифром.
Программа nfqws2 расшифровывает такой пакет своим кодом, и в нашем тесте она прочитала из него имя www точка google точка com. Но метки по этому имени не работают.
Часть программы, которая превращает метки в номера, назовём переводчиком меток. В нашем тесте мы дали ему на таком пакете двенадцать меток через пробную функцию из лаборатории: она только пишет в журнал, какие номера получились. Все слова не нашлись. Числа нашлись: ноль, два, сто и минус один, то есть последний байт, тысяча сто девяносто девятый.
Числа считаются по пакету целиком, а не по расшифрованному приветствию. Вывод: переводчик меток на QUIC отдаёт только числа. Но стратегии с позициями работают только с TCP, поэтому резать QUIC по позициям они не будут. Мы проверяли только сам переводчик.
!wiki-anim-video04-s11-type-and-quic.svg
Проверь себя. Переводчик меток получил на начальном пакете QUIC список «host, сто». Что он вернёт?
Одно число, сто: host не находится, сто находится. Так вёл себя наш запуск с пробной функцией. Резать по нему всё равно нечем: стратегии с позициями на UDP ничего не делают.
Теперь что будет, если данных мало. При обычных настройках неполное приветствие программа придерживает, и стратегия его не видит. Числа ниже верны, когда стратегия получила короткий кусок: в нашем тесте мы выключили склейку приветствия из нескольких пакетов и резали обрезки.
Меньше одиннадцати байт приветствие вообще не узнаётся. Ровно на одиннадцати узнаётся, но ни одно слово-метка не находится, находятся только числа.
С восьмидесяти восьми байт находится extlen: ему нужно только поле длины на восемьдесят шестом и восемьдесят седьмом байтах. И так до ста одиннадцати находится только он.
Со ста двенадцати байт добавляются host, sld, midsld, endsld и sniext: расширение с именем кончается на сто одиннадцатом байте и должно прийти целиком. Endhost ещё нет: он равен ста двенадцати, а позиция должна быть меньше размера. Он появляется со ста тринадцати.
Значит, метки появляются тремя ступенями: extlen, потом сразу пять, и последним endhost. «Узнать тип» и «найти имя» это два разных вопроса: одиннадцать байт против ста двенадцати.
!wiki-anim-video04-s11-short-data-steps.svg
Для запроса HTTP склейки нет: программа смотрит только на первый пакет. Самый короткий запрос, который она признаёт, это шестнадцать байт: «GET, косая черта, HTTP, косая черта, один, точка, один» в четырнадцать байт запросом не признаётся, а с концом строки признаётся. Это прогон функций.
И последнее о размере. Большое приветствие, тысяча четыреста восемьдесят четыре байта, в сети приходит несколькими пакетами. Программа склеивает его целиком, и позиции считаются по склеенным данным. В нашем тесте midsld на нём тоже сто четыре, а первые два пакета с данными были сто четыре байта и тысяча триста восемьдесят. Как программа склеивает, разбирает выпуск о сборке и повторном проигрывании.
Итак, метка не того типа и метка за границей молча выпадают, на QUIC переводчик меток понимает только числа, а при малом числе данных метки появляются тремя ступенями, и endhost последним. Позиции считаются по тем данным, что видит стратегия.
Три настоящих запуска: слово, список и ловушка
Теперь соберём всё на трёх настоящих запусках нашей лаборатории. В каждом смотрим три вещи: что написано в настройке, что программа записала в журнал и какие пакеты ушли на провод.
Первый запуск нам знаком: pos равно midsld. В журнале «позиции разреза: сто четыре». На проводе два пакета с данными: сто четыре байта и сто сорок шесть. Первый кончается на «www точка exa», второй начинается с «mple точка com». Сервер собрал из них те же двести пятьдесят байт.
На проводе у частей свои номера. Как в выпуске «Что видит наблюдатель», байты потока считаются с единицы: первая часть начинается с номера один, а вторая со ста пятого, сто четыре плюс один. По ним сервер складывает части в порядке.
Вся цепочка выпуска в одной строке: метка midsld, правило точки, сто один плюс три, сто четыре, разрез перед сто четвёртым байтом.
!wiki-anim-video04-s12-midsld-run.svg
Проверь себя перед вторым запуском. Список «host, midsld, endhost» на нашем приветствии. Сколько пакетов уйдёт на провод и какого размера?
Три разреза, четыре пакета. Позиции девяносто семь, сто четыре и сто двенадцать. Размеры: девяносто семь, потом сто четыре минус девяносто семь, семь, потом сто двенадцать минус сто четыре, восемь, и остаток, сто тридцать восемь. Это счёт.
Второй запуск, настоящий: все семь имён подряд, host, sld, midsld, endsld, endhost, sniext, extlen. Журнал записал семь чисел по возрастанию, и на проводе были восемь пакетов: восемьдесят шесть байт, шесть, пять, четыре, три, четыре, четыре и сто тридцать восемь. Вместе двести пятьдесят.
Посмотрим на середину. Четыре байта «www точка», три байта «exa», четыре байта «mple» и четыре байта «точка com». Имя сайта разложено по четырём пакетам, и в каждом свой кусочек.
А начало разложено по полям. В первом пакете восемьдесят шесть байт до поля длины расширений. Во втором шесть байт: поле длины расширений и заголовок расширения с именем. В третьем пять байт: длина списка, тип имени и длина имени, до первой буквы. Каждый разрез пришёлся на границу поля. Сервер получил двести пятьдесят байт, равные оригиналу.
!wiki-anim-video04-s12-seven-names-run.svg
Третий запуск, ловушка: pos равно ноль. Разрез перед самым первым байтом. Журнал говорит: «позиции разреза: ноль». Позиция есть. Но следующая строка: «нет допустимых позиций», и на провод ушёл один пакет, все двести пятьдесят байт, без изменений.
Почему так? Журнал печатается раньше, чем программа выбросит позицию, с которой разрез невозможен. Нулевой байт дал бы пустую первую часть, и такую позицию программа вычёркивает. Как именно, увидим в сцене про сдвиг на единицу.
А если рядом есть ещё метка, ноль ничему не мешает. «Ноль, midsld» тоже пишет в журнал «ноль, сто четыре», но отправляет две части, сто четыре и сто сорок шесть, без пустой первой. Так же ведёт себя «host минус девяносто семь»: это тоже ноль.
Урок ловушки: журнал показывает то, что программа прочитала, а не то, что она отправила. Читать нужно вместе с размерами пакетов на проводе.
!wiki-anim-video04-s12-zero-trap.svg
Итак, три запуска. Метка midsld даёт сто четыре и сто сорок шесть. Семь меток дают восемь пакетов, и по ним видны границы полей и слов имени. А число ноль в журнале стоит, но разреза нет.
В коде: переводчик метки и переводчик списка
Теперь коротко в код. Не весь, а два места, которые мы уже видели в работе. Вопрос сцены: где в программе метка превращается в номер и где из номеров собирается список?
Игрушка, пример выдуманный. Справочное бюро: вам дают слово и вид справочника, вы открываете нужный справочник и отвечаете номером страницы. Если такого слова в справочнике нет, вы говорите «не нашлось». Переводчик меток устроен так же.
Сначала одно пояснение. Стратегии вроде multisplit написаны на языке Lua: это скриптовый язык, и программа на Си вызывает его функции. Метки переводит не Lua, а Си: эти функции написаны на Си и живут в той же программе. Как Lua устроен внутри программы, разбирает выпуск «Lua внутри программы».
Первое место: переводчик метки, в коде ResolvePos; в сцене про QUIC мы звали его переводчиком меток. На входе данные пакета, их размер, тип нагрузки и сама метка. На выходе номер байта или особое значение «не нашлось»; в коде это минус один.
Внутри один выбор по типу. Для запроса HTTP зовётся разбор запроса. Для приветствия TLS зовётся разбор приветствия. Для любого другого типа зовётся функция, которая понимает только числа. Отсюда молчаливое «не нашлось» на чужом типе: ветки с ошибкой в этом месте просто нет.
Что происходит внутри разборов. Разбор приветствия находит имя ходьбой по длинам, разбор запроса находит строку Host. Дальше оба зовут одну общую функцию, «позицию по имени сайта», в коде HostPos. Host это начало имени плюс поправка, endhost это конец имени плюс поправка. Для sld, midsld и endsld она зовёт поиск второго слова справа, который мы разбирали. Поэтому формулы у приветствия и запроса одни и те же.
Любой из разборов в конце проверяет границы: номер не меньше нуля и меньше размера данных. Вот откуда в начале выпуска взялось «меньше размера». А отрицательное число у абсолютной метки сначала прибавляется к размеру: так считается от конца.
!wiki-anim-video04-s13-resolve-switch.svg
Второе место: переводчик списка, в коде ResolveMultiPos. Цикл по меткам: каждая идёт через переводчик метки, и результат записывается в массив. Если нашлось, счётчик растёт на единицу. Если нет, следующая метка затирает «не нашлось» на том же месте.
После цикла две строки. Первая сортирует массив по возрастанию. Вторая склеивает повторы.
Кто эти функции зовёт? Основная программа по своей инициативе метки не переводит и пакеты по меткам не режет. Их зовёт стратегия на Lua, через мост: она просит номера, а режет пакет и отправляет части сама. Переводит метки по-прежнему Си. Мост разберём в следующей сцене.
Проследим список «два, host, host, пятьсот» на нашем приветствии. Два находится, host даёт девяносто семь, второй host тоже. Пятьсот за границей и в массив не остаётся. Сортировка и склейка повторов: два и девяносто семь.
А в списке «method, midsld» на приветствии method уходит в функцию для чисел и не находится, остаётся одно число, сто четыре.
!wiki-anim-video04-s13-multi-loop.svg
Проверь себя. Метка host, а тип нагрузки unknown. Что вернёт переводчик метки?
«Не нашлось». Для этого типа работает только функция для чисел, а host для неё не число. Ошибки нет.
Итак, переводчик метки выбирает правила по типу пакета и в конце проверяет границы. Переводчик списка зовёт его для каждой метки, отбрасывает не нашедшиеся, сортирует и склеивает повторы. Метка не того типа или за границей молча пропадает.
В коде: плюс один на мосту в Lua, журнал и части
Остался сдвиг на единицу. Вопрос сцены: почему в журнале «позиции разреза» стоит сто четыре, а внутри стратегии то же место называется сто пять?
Две линейки. Си считает байты с нуля, а Lua, по общим сведениям, с единицы. Один и тот же байт у Си имеет номер сто четыре, у Lua сто пять.
Мост между ними стоит в Си, это функция привязки resolve_multi_pos. Она принимает три вещи: данные, имя типа нагрузки и строку меток. Потом зовёт переводчик списка.
Результат она кладёт в таблицу для Lua и к каждому числу прибавляет единицу. Наш список два, девяносто семь, сто четыре, сто двенадцать становится таким: три, девяносто восемь, сто пять, сто тринадцать.
Видимо, так сделано, чтобы части можно было брать обычной функцией Lua для кусков строки, у неё счёт идёт с единицы. Это наше предположение, в коде об этом не сказано.
Мост может выдать ошибку, но из multisplit достижима одна: плохой список меток. Мы видели её с pos равно foo.
!wiki-anim-video04-s14-two-rulers.svg
Стратегия multisplit в режиме отладки сначала печатает список в журнал, вычитая единицу обратно. Поэтому в журнале стоят числа Си: два, девяносто семь, сто четыре, сто двенадцать.
Потом она вычёркивает из списка число один. Это Lua-позиция один, то есть байт ноль: разрез перед самым первым байтом бессмыслен, первая часть была бы пустой. Вот почему pos равно ноль оставил в журнале ноль, а разрезать было нечего.
Если после вычёркивания список пуст, стратегия пишет «нет допустимых позиций» и ничего не отправляет: пакет идёт как есть.
Если не пуст, части берутся так, теперь в номерах Lua, с единицы. Первая идёт от первого байта до байта перед первой позицией, каждая следующая от своей позиции до байта перед следующей, последняя до конца. Частей на одну больше, чем разрезов.
Наш список Lua: три, девяносто восемь, сто пять, сто тринадцать. Первая часть от первого байта до второго, два байта. Вторая от третьего до девяносто седьмого, девяносто пять. Третья от девяносто восьмого до сто четвёртого, семь.
Четвёртая от сто пятого до сто двенадцатого, восемь. Последняя от сто тринадцатого до конца, сто тридцать восемь. Вместе двести пятьдесят, ровно размер данных. И на проводе в нашем тесте были именно эти пять пакетов.
Крайние значения. Один даёт Lua-позицию два, её не вычёркивают: в нашем тесте с pos равно один на провод ушли пакеты в один байт и в двести сорок девять. Ноль даёт Lua-позицию один, её вычёркивают: «ноль, midsld» остаётся с одним разрезом, сто четыре.
!wiki-anim-video04-s14-log-delete-parts.svg
Проверь себя. В журнале «ноль, сто четыре». Сколько частей уйдёт и какого размера?
Две: сто четыре и сто сорок шесть. Ноль вычеркнут как позиция один, остался один разрез. Так и вышло в нашем тесте.
Итак, мост прибавляет единицу к номерам Си, стратегия печатает их в журнал без единицы, вычёркивает позицию один и режет на части, которых на одну больше, чем разрезов. Нет позиций, значит нет разреза.
Итог выпуска: шпаргалка меток, границы знания и что дальше
Соберём выпуск в одну картину. Начиналось всё с вопроса: как сказать «режь здесь», если одно число на все пакеты не годится.
Первое: позиция. Это номер байта, перед которым делают разрез, счёт с нуля. Первая часть по длине равна позиции. Отрицательное число считается от конца, а позиция должна быть меньше размера данных.
Второе: метки. Их девять: abs, host, endhost, sld, midsld, endsld, method, extlen и sniext. Числа лежат на экране в таблице, а запомнить стоит порядок. На приветствии сначала extlen и sniext, потом host, потом три метки слова example и endhost последним. На запросе method первым, потом host, три метки слова и endhost. Midsld на приветствии сто четыре, на запросе тридцать девять.
!wiki-anim-video04-s15-cheatsheet-strips.svg
Третье: правила. Sld это второе справа слово между точками, midsld его середина: начало плюс половина длины нацело, а для слова из одной буквы начало плюс один. К метке можно прибавить число. Список сортируется, повторы склеиваются, не нашедшиеся метки выпадают молча, и если не осталось ничего, пакет идёт без разреза. Неверное имя даёт ошибку, а число читается снисходительно.
Четвёртое: границы. Метки-слова понимают только приветствие TLS и запрос HTTP, а на QUIC переводчик меток понимает только числа. Если стратегия получила мало данных, метки появляются тремя ступенями: extlen, потом сразу пять, и endhost последним. А в журнале число может стоять и без разреза, как с нулём.
Три запуска в одной строке: midsld дала сто четыре и сто сорок шесть, семь меток дали восемь пакетов, а ноль остался в журнале без разреза.
И выбор места важен. В нашей игрушке разрез по endhost оставил имя на прежнем месте, а разрез по host передвинул его во второй пакет.
Отсюда практический совет: проверяйте не только журнал, но и размеры пакетов на проводе. Журнал показывает прочитанное, провод показывает отправленное.
Теперь о границах знания. Всё сказанное это правила программы zapret2: мы читали их в коде и проверяли в нашей лаборатории, там, где об этом сказано. Ищет ли настоящее оборудование имя так же, читает ли оно заголовок Host, считает ли второе справа слово тем же способом, мы не знаем.
И напомним: в обоих наших приветствиях имя это первое расширение. По общим сведениям, у других клиентов порядок может быть другим, и тогда числа меняются, а ходьба остаётся той же.
Теперь мы умеем сказать «режь здесь». Что происходит дальше? Следующий выпуск про multisplit. Он берёт список позиций и строит из него пакеты на проводе: что копируется из исходного пакета, как меняются номера, в каком порядке части уходят и какие получаются промежутки между ними и как их собирает сервер. Всё, что мы разобрали сегодня, там заработает в деле.
!wiki-anim-video04-s15-runs-and-next.svg
Итак, метка позиции это слово или число, которое программа превращает в номер байта, а слово находит нужное место в любом пакете подходящего типа. Midsld на нашем приветствии даёт сто четыре и сто сорок шесть. Дальше посмотрим, как из позиций получаются пакеты.
См. также
- 03-chitaem-komandnuyu-stroku-zapret2 — предыдущее видео серии
- multisplit — как работает сама функция разреза
- payload — распознавание типов протоколов, от которого зависят относительные метки
- blob — как объявлять и передавать данные
[!quote] 🤖 Эти статьи открыты — можно обучать на них ИИ При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование доступно в Forgejo: исходник этой заметки · скачать весь репозиторий одним zip-архивом.