Необработанный декодер для формата protobufs
Я хотел бы найти способ преобразования двоичного сообщения protobuf в человекообразное описание содержащихся данных без использования .proto файлов.
Фон состоит в том, что у меня есть сообщение .proto, что он отклоняется парсером на Android, но не совсем понятно почему. Я мог пройти через сообщение, но это довольно утомительно.
Я пробовал protoc --decode_raw
, но он просто выдает ошибку "Не удалось разобрать вход". Я google надеясь/ожидая, что кто-то сделал бы полезную веб-утилиту, которая могла бы это сделать, но не нашла ничего очевидного.
Я просто надеюсь получить какой-то результат, например:
field 1: varint: 128
field 4: string: "foo"
Любые указатели в правильном направлении будут приветствоваться!
Ответы
Ответ 1
Вы можете попробовать заставить его через плагин wirehark, или, возможно, вы могли бы заимствовать часть "читателя" некоторых из реализаций (я знаю, как это сделать на С#, например, но я сомневаюсь, что это то, что вы имели в виду).
Однако будьте осторожны - строка в буферах протокола на самом деле не означает "строка" - это может быть:
- строка UTF-8
- необработанный BLOB произвольных данных
- под-сообщение
- "упакованный" массив
- (возможно, что-то еще забываю)
Ответ 2
Для посты:
Буфер протокола Google инструменты имеют возможность декодировать необработанные буферы.
Просто отправьте ему неизвестный буфер и передайте флаг --decode_raw
$ cat has_no_proto.buff | protoc --decode_raw
2 {
2: "Error retrieving information from server. [RH-02]"
}
Итак, здесь сообщение с полем 2 установлено на встроенное сообщение, которое, в свою очередь,
его второе поле задано строкой, в которой говорилось, что я разозлился с Google Play.
Информация о типе не определена (похоже, она попытается отобразить все двоичные данные в виде строк, но ваше требование для различия varint/string/submessage выполнено).
Ответ 3
Если у вас есть двоичный файл, содержащий (несколько?) длину, префиксные сообщения protobuf, protoc ‒‒decode_raw < file
не может проанализировать его из-за префиксов длины. Простым способом является разделение файла на его последовательные сообщения, а затем преобразование каждого из них с помощью protoc
.
Мой прием:
var fs = File.OpenRead(filename));
var buffer = new byte[4096];
int size;
for (int part = 1; Serializer.TryReadLengthPrefix(fs, PrefixStyle.Base128, out size); part++) {
long startPosition = fs.Position;
using (var writer = File.OpenWrite(string.Format("{0}[{1}].pb", filename, part))) {
for (int bytesToRead = size; bytesToRead > 0; ) {
int bytesRead = fs.Read(buffer, 0, Math.Min(bytesToRead, buffer.Length));
bytesToRead -= bytesRead;
if (bytesRead <= 0) // End of file.
break;
writer.Write(buffer, 0, bytesRead);
}
}
}
Ответ 4
Как указано в Ответ Мишель де Рюйтер, возможно, что ваше сообщение protobuf имеет length-prefix. Предполагая, что это так, этот ответ должен помочь.
(ПРИМЕЧАНИЕ. Для большинства приведенных ниже команд я предполагаю, что ваше сообщение protobuf хранится в файле с именем input
.)
protoc --decode_raw
+ dd
для одного сообщения:
Если это просто одно сообщение, тогда вы действительно можете использовать protoc --decode_raw
, но сначала нужно снять заголовок с префиксом длины. Предполагая, что заголовок имеет длину 4 байта, вы можете использовать dd
, чтобы отключить заголовок input
, а затем подать вывод в protoc
.
dd bs=1 skip=4 if=input 2>/dev/null | protoc --decode_raw
protoc-decode-lenprefix --decode_raw
для одного сообщения:
Я также написал script, который автоматически обрабатывает разделение заголовков:
protoc-decode-lenprefix --decode_raw < input
Этот script является просто оболочкой поверх protoc --decode_raw
, но обрабатывает разбор заголовка длины и префикса, а затем вызывает protoc
.
Теперь этот script не очень полезен в этом случае, потому что мы можем просто использовать трюк dd
выше, чтобы отключить заголовок. Однако, скажем, у нас есть поток данных (например, файл или поток TCP), содержащий несколько сообщений, которые заключены в заголовки с префиксом длины....
protoc-decode-lenprefix --decode_raw
для потока сообщений:
Вместо одного сообщения protobuf во входном файле, скажем, input
содержит несколько сообщений protobuf, которые создаются заголовками префикса длины. В этом случае невозможно просто использовать трюк dd
, потому что вам нужно действительно прочитать содержимое заголовка префикса длины, чтобы определить, как долго будет длиться последующее сообщение в потоке, и, следовательно, сколько байтов впереди следующего заголовка + сообщение лежит. Поэтому вместо того, чтобы беспокоиться обо всем этом, вы можете просто снова использовать protoc-decode-lenprefix
:
protoc-decode-lenprefix --decode_raw < input
protoc-decode-lenprefix --decode ... foo.proto
для потока сообщений
Этот script также может использоваться для полного декодирования сообщений с префиксом длины (вместо их "необработанного декодирования" ). Предполагается, что у вас есть доступ к файлам .proto
, которые определяют сообщение protobuf, так же как и обернутая команда protoc
. Синтаксис вызова идентичен protoc --decode
. Например, используя трюк dd
с protoc --decode
, а вход будет Mesos task.info, синтаксис выглядит следующим образом:
dd bs=1 skip=4 if=task.info 2>/dev/null | \
protoc --decode mesos.internal.Task \
-I MESOS_CODE/src -I MESOS_CODE/include \
MESOS_CODE/src/messages/messages.proto
И при идентичном использовании параметров protoc-decode-lenprefix
cat task.info | \
protoc-decode-lenprefix --decode mesos.internal.Task \
-I MESOS_CODE/src -I MESOS_CODE/include \
MESOS_CODE/src/messages/messages.proto