1. Си / Говнокод #20274

    −46

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    7. 7
    case DEBUG_VAR__SIMPLE_TYPE_BOOL:
        b = (bool *)(item->ptr);
        if (0 != strncmp(var->var_s, "0", 2) && 0 != strncmp(var->var_s, "1", 2)) {
            ERROR("Value of %s is not boolean (%s)", name, var->var_s);
            return false;
        }
        *b = (var->var_s[0] == '0') ? 0 : 1;

    Все, что вам нужно знать о формате Protobuf.

    Предыстория: нужно используя Протобаф переслать значение которое может быть либо строкой, либо числом, либо логической переменной.

    Варианты решения:
    1. Создать message с опциональными полями - аналог сишного union.
    2. Придумать формат в котором в одно поле будет записываться тип пересылаемого значения, а в другое - неведомая херня, которую потом динамически проверять.

    Запостил: wvxvw, 26 Июня 2016

    Комментарии (77) RSS

    • Годно))
      Ответить
    • Говноптимизация
      case DEBUG_VAR__SIMPLE_TYPE_BOOL:
          b = (bool *)(item->ptr);
          if (0 != strncmp(var->var_s, "0", 2) && !(*b = (0 == strncmp(var->var_s, "1", 2)))) {
              ERROR("Value of %s is not boolean (%s)", name, var->var_s);
              return false;
          }
      Ответить
    • Так это код, сгенерированный protoc, или что? Каким был proto-файл?
      Ответить
      • А, вроде понял.
        Шаблон tagged string?
        Ответить
        • Не, это рукотворное творчество. В месадже это поле описано как строка.
          Ответить
          • Надо было в эту строку месаджпак засовывать, чего уж там.
            Ответить
    • А почему не https://developers.google.com/protocol-buffers/docs/proto#oneof?
      Ответить
      • О, ну иле это. Я, если честно не знаком с сишной частью генерации. Может сишный кодгенератор такое не умеет. А может, как всегда, наоптимизировали и понадеялись на то, что если скомпилировалось, то типы обязательно совпадут, а потом поняли, что ну его нахер.
        Конкретно с этим сервером работают еще клиент на Питоне, и вот я сейчас ваяю на Го. Возможно есть какие-то проблемы на стороне Питона, которые не позволяют сделать one-of / optional. После охуительной поддержки и продуманости трифтовских реализаций я уже ничему не удивляюсь.

        На правах истроической справки: когда-то давно, когда ЯСОН все еще был только в проекте, прогрессивное человечество спорило о лучшем формате передачи данных в интернете. АМФ все еще был вполне реальной альтернативой. Разница между АМФ и Протобаф принципиально в том, что АМФ содержит метаданные которые позволяют из бинарника восстановить информацию в большинстве случаев без наличия исходников. Протобаф - посчитали что на метаданных можно сэкономить (типичная логика си/сиплюсплюс). А еще они сэкономили на записи длины сообщения. Зато теперь, если формат даже капельку поменялся - можно днями его разгадывать.
        Ответить
        • А ASN.1 DER тогда разве не было?
          Ответить
          • АСН - в том же направлении, но он очень упрощенный / неоптимизированый.
            Например, типичные оптимизации которые можно сделать в бинарном формате: использовать специальную технику кодирования интов / даблов которая, при условии, что используются только младшие байты будет использовать меньше места (микрооптимизация), или вместо того, чтобы записывать повторяющиеся элементы столько раз, сколько они повторяются, второй и все последующие разы записывать ссылку (рассчитывая на то, что парсер будет создавать ссылки таким же образом как и генератор).
            Таким образом АМФ, например может даже выигрывать у Протобаф если нужно посылать много однотипных данных не смотря на то, что посылает метаданные вместе с данными.
            Ответить
        • Если формат капельку поменялся, то твоя клиентская либа стухла, потому что ты уже не знаешь то ли это поле, или просто так совпало. Метаданные должны быть снаружи.

          Как же заебал этот ламерский пиздеж, ты небось еще и на рест дрочишь, и соап ненавидишь. И си сюда приплел ни к месту. Причем тут си? Выключай уже бредогенератор свой.
          Ответить
          • Хз, ты или читать не умеешь, или просто тупой. Попробуй, чтоли, прочитай еще раз.
            Ответить
            • А я вот с ним согласен. Если схема данных известна, то дублировать ее в сообщении нафиг не надо.
              Ответить
              • Этот подход заведомо корректен только в случае "написал, прошил, закопал и забыл".
                Ответить
                • Так версию схемы нужно в hello-сообщении гонять.
                  Ответить
                  • Так можно и схему целиком гонять, не?
                    Ответить
                    • Если старый клиент в новую схему не умеет, нахуй она ему тогда?
                      Сервер должен уметь во все поддерживаемые версии схемы: клиент запрашивает нужную версию, если она ещё поддерживается, то начинается общение, иначе его посылают.
                      Ответить
                    • Можно, но не нужно. Только канал зря забивает. Снаут про версионирование правильно говорит.
                      Ответить
                      • Вообще, у нас на самом деле есть перетаскивание схемы с сервера на клиент. Но только один раз, когда клиент видит, что в серванте изменился софт. Дальше он её у себя хранит. Этим мы добиваемся, что одну железку можно обновлять отдельно от другой. При этом изменение мажорной версии работает примерно так, как я описал выше.
                        Ответить
                    • В протоколе, который я в данный момент делаю, схема занимает порядка сотни мегабайт. Do the math.
                      Ответить
                      • Это где такая жесть возникает?
                        Ответить
                        • Никакой жести, просто один из центральных сетевых узлов, общающийся на трёх стеках протоколов, с десятками разных аппликейшнов, управляющий траффиком трёх разных поколений сетей.
                          Обобщённый интерфейс управления всем этим.
                          Ответить
                          • На трех стеках? У вас где то не айпи?
                            Ответить
                            • SS7 + SIGTRAN + IP
                              Ответить
                              • > SS7 + SIGTRAN + IP

                                Пахнуло Швецией и Эриксоном.
                                Ответить
                                • Есть, правда, ещё Хуавей с Ноклой, но Кашицин меня ожидаемо задеанонил.
                                  Ответить
                                  • > Кашицин

                                    Почему все пишут мою фамилию неправильно?
                                    цын же по правилам
                                    Ответить
                                    • По каким правилам? "Цыган на цыпочках цыпленку цыкнул цыц" - кашицинов в списке нет.
                                      Ответить
                                      • > По каким правилам?

                                        Т.е. ты считаешь, что на цы только одно правило?

                                        Т.е. нужно писать лисицин хвост и царицины глаза?

                                        Гугли суффиксы притяжательных прилагательных.

                                        http://rus.stackexchange.com/questions/115/Окончание-фамилий
                                        Ответить
                                    • Вот, кстати, наглядный пример, почему "метаданные" Олега -- говно. Я допустил грамматическую ошибку в посте, но пакет всё равно был доставлен по нужному адресу, сервер его правильно интерпретировал и запостил к моему унижению. А если бы вместо годной инкапсуляции была метанопитушня, одна ошибка в данных форшмачила бы всё, начиная с канального уровня.
                                      Ответить
                                      • > "метаданные" Олега

                                        Давайте не будем называть его Олегом, я всё время путаю с Олегом Киселёвым.
                                        Ответить
                                      • Метанопетушня - это типо когда много пердят не в унисон?
                                        Ответить
                                    • Летчиц ли-си-цын (С)
                                      Ответить
              • ОК, а теперь ты смотриш в tcpdump выхлоп и судорожно пытаешься понять где "что-то пошло не так", без метаданных - это занятие на недели, а может и месяцы, если код сервера/клиента не доступен, или не понятен, или, в нем возможно есть ошибка. С метадатой - максимум на пару часов.

                И не путайте метаданные со схемой: метаданные нужны для того, чтобы убедится в том, что данные были записаны и приняты в соответствии с форматом передачи данных. Схема нужна для того, чтобы описать логику на уровень выше - там где только подмножество допустимых данных можно обработать.

                Если пересылать данные без метаданных вообще - то прочитать их вообще нет возможности (т.как никто не знает когда данные закончатся), т.е. как минимум нужна длина (это пример минимальных метаданных).
                Но на единицу длины мы имеем факториал единиц вореций пересылаемой информации. Т.е. зная длину мы все равно с вероятностью 1-n/n! можем получить мусор. Если мы, например, добавим еще какие-нибудь метаданные, например, выравнивание, то мы уменьшим процент мусора, и т.д.

                Другой пример: апи без метаданных становится доступен злоумышленнику - тут же просто раздолье для ДДОС. С метаданными это на много сложнее.
                Ответить
                • > (т.как никто не знает когда данные закончатся)
                  Это ты про фрейминг так витиевато говоришь? Ответ: не все транспортные протоколы одинаково уёбищны.
                  Ответить
                  • Транспортные - это TCP/UDP? Где в TCP границы в пользовательских данных?
                    Ответить
                    • А TCP не нужен, родной. Есть же SCTP.
                      Но вообще, про фрейминг это была самая очевидная догадка, так-то я вообще не врубился в тот пост.
                      Ответить
                      • Я тебе не родной, а про SCTP слышу в первый раз.
                        Ответить
                        • - Здравствуйте, я пидар и я безграмотный неосилятор.
                          - Здра-а-а-а-вствуй, пидар!
                          Ответить
                        • Ты случайно не министр связи РФ? Он тоже про половину протоколов не знает, поэтому когда суёт нос в Интернет, что-нибудь отваливается.
                          Ответить
                          • Министр связи и не должен знать протоколы, для этого у него есть спецы. Они их мало где знают но балуются блокировками только в недостранах вроде твоей.

                            Как там ваш пиздотп натуется, кстати?
                            Ответить
                            • ну да, а министр финансов не должен знать что такое "валовой продукт", а министр здравоохранения не должен знать чем бактерии от вирусов отличаются, а министр обороны не должен знать кто главнее -- полковник или майор

                              а премьер министр вообще нихуя знать не должен
                              Ответить
                              • Министр связи не должен знать каждый протокол, а вот во что выльется для отрасли тот или иной закон - да. Впрочем, про страну вечного стекломоя сказать ничего не могу.
                                Ответить
                                • > во что выльется для отрасли тот или иной закон
                                  А если он сам не знает - должен попросить спецов, чтобы объяснили, чем всё это закончится... Ну это в теории.

                                  Ибо тут они и на мнение опсосов, яндекса и мейлрушечки из принципа положили большой хуй.
                                  Ответить
                                  • Такое впечатление, что у вас скоро на все IT положат большой хуй. Будете с украхой разве что меряться.

                                    Собственно, народ-гной, народ-пидар, народ-хуесос большего и не заслуживает.
                                    Ответить
                  • Не, транспорт это все-таки уровнем ниже.
                    Для того, чтобы сделать это более конкретным.

                    Вот у нас есть сообщение: { a: 1, b: "x", c: true }.
                    1. Вариант без метаданных: записываем 4 байта int32, 2 байта на строку (с нуль-байтом в конце) и 1 байт на булеан.
                    1.1. Сервер решил по какой либо причение прислать еще значение 42 после c: true - что делать клиенту? Выбрасывать? Сигналить ошибку? Хранить в надежде на то, что это начало следующего сообщения?
                    1.2. Сервер забыл, что строки заканчиваются нуль байтом, и записал всего один байт, но за ним шел false, которй тоже представлен нуль байтом. Но т.как сообщения были в массиве, одно за другим, клиент ничего не сказал ему, смолчал, но продолжил работу. Аквалангисты - это хорошо.

                    2. Если у нас есть минимальные метаданные: длина:
                    2.1. Аналогично с первым, но мы то знаем, что сервер ошибся.
                    2.2. Все равно жопа.
                    Ответить
                    • > 1.1. Сервер решил по какой либо причение прислать еще значение 42
                      > после c: true - что делать клиенту? Выбрасывать? Сигналить ошибку?
                      > Хранить в надежде на то, что это начало следующего сообщения?
                      Это, блядь, и называется фрейминг. Иногда его можно позаимствовать из низлежащего транспортного протокола, иногда сначала определяется фрейминг-протокол на уровне аппликейшна, в который затем инкапсулируется всё остальное. Иногда в ход идут твои "метаданные", если уж так хочется смешать мух с котлетами.
                      Ответить
                      • Какая разница на каком уровне это определяется, если это все равно метаданные? "мета" значит "о чем-то": данные которые не представляют саму пересылаемую информацию, а вспомогательную информацию (такую как длина) и есть информация "о пересылаемых данных", т.е. метаданные.

                        Хз. мне просто слове фрейминг не знакомо в этом контексте.
                        Ответить
                        • > Хз. мне просто слове фрейминг не знакомо в этом контексте.
                          Ну так по-моему неудивительно, что разработчик протоколов, который не знает таких базовых вещей, будет дебажить протоколы неделями. И да, не думаю, что дальше стоит обсуждать эту метапитушню.
                          Кстати, для protobuf есть модуль для вайршарка, правда 3rd-party, т.к. правильно заметили, что protobuf -- это сериализатор, ограниченно годный для создания именно протоколов.
                          Ответить
                          • Ну так этот плагин без схемы не работает, о чем собственно и речь. О не может показать содержание произвольного соединения.
                            Ответить
                            • У вас там исходники проебали? Или ты хэккер, жалующийся, что тебе их не дали? То, что ты хочешь, называется реверсинг протокола, а не дебаггинг.
                              Ответить
                              • Смысл же в удобстве. Можно и зубы через нос почистить, но есть нюанс.
                                Ответить
                                • Чистить зубы через нос -- это вся метушня твоя.
                                  Ответить
                • Тогда я не знаю, что ты вообще имеешь ввиду. Слишком общие слова. Если что, протобуф не про "никто не знает когда данные закончатся", а про сериализацию. Посмотри на grpc. Там данные пересылают в протобуфе, но за всю вот эту чухню, которую ты расписал, отвечает http/2.
                  Еще раз: формат данных и rpc - это ортогональные сущности.
                  Ответить
                  • А HTML это не HTTP, спасибо, друг, ты открыл мне глаза.
                    Вод представь что ты десереализируешь данные - откуда ты знаешь, когда они закончатся?
                    Ответить
                    • Мы все еще про протобуф говорим? Вот как-то реализация протобуфа справляется с этой задачей.
                      Ответить
                      • Так не справляется, жеж. Ну сколько можно это объяснять. За нее это нужно делать.
                        Ответить
                        • Ох блядь. Мы кругами уже ходим.
                          Ответить
                          • Щас он пожалуется, что в JPEG-картинках нет описания алгоритма розжатия, а в ELF-бинарниках -- полной спецификации x86. Метаноинформации недоложили!
                            Ответить
                        • Если твоему десериализатору нужно сообщить размер сообщения, значит бери этот размер из транспорта. Например, шли свои мессаджи по хттп и смотри в Content-Length.
                          Ответить
                          • Да, а вот преставляешь, что именно так и есть. Ходим кругами, а все потому что:
                            если мы не записываем длину в формате X, а вместо этого записываем длину в формате Y, который включает в себя формат X, тогда нам нужно узнать длину формата Y, и тут возникает проблема: может быть стоит не записывать длину в формате Y, а создать дополнительный формат Z, где мы запишем длину Y - экономия на лицо.
                            Ответить
                • Про тисипидамп тоже не понял, кстати. Какая разница, есть "метаданные" или нет? В любом случае берешь и парсишь в соответствии с ожидаемым форматом. Если что-то не сходится - разбираешься. Где там занятие на недели? Скрипт на питоне напиши, он быстро справится.
                  Ответить
                  • ОК, tcpdump, при наличии метаданных - можно написать утилиту, которая парсит содержание любой пересылки использующей заданый формат данных. Без метаданных - такую утилиту прийдется писать для каждого сервера - поновой.
                    Ответить
                    • Напиши generic утилиту, которая будет парсить в соответствии с заданной схемой. А лучше модуль для вайршарка.
                      Ответить
                      • См. выше, СНауТ уже предлагал. Это в принципе не возможно.
                        Ответить
                    • Вот тут двачну. Особо весело будет если не указывается даже версия протокола.
                      Ответить
          • Хуесто, а если сервак тебе не принадлежит?
            Ответить
    • if (*(short *)var->var_s & ~1 != '0')
        ERROR("Value of %s is not boolean (%s)", name, var->var_s);
      Ответить
      • Приоритет & ниже, чем !=, хотя об этом канпелятор предупредит скорее всего. Но что когда sizeof(short) окажется больше 2? Лудше сразу на века:
        /* proto.h */
        #define KO *(short *)
        #define K0 "0"
        #define KYD "\xfe"
        #define KYDAX "\xff\0"
        #define TAX "\0\0"

        /* supre.c */
        if ((KO s - KO K0) & KO KYD KYDAX TAX TAX)
            ERROR("...");

        Лишь бы short не перевалил за 8 байтов и '0' оставался непосредственно перед '1'.
        Ответить

    Добавить комментарий