|
@@ -0,0 +1,302 @@
|
|
|
|
|
+// Copyright 2018 The Go Authors. All rights reserved.
|
|
|
|
|
+// Use of this source code is governed by a BSD-style
|
|
|
|
|
+// license that can be found in the LICENSE file.
|
|
|
|
|
+
|
|
|
|
|
+// pbdump is a tool for decoding the wire format for protocol buffer messages.
|
|
|
|
|
+package main
|
|
|
|
|
+
|
|
|
|
|
+import (
|
|
|
|
|
+ "bytes"
|
|
|
|
|
+ "flag"
|
|
|
|
|
+ "fmt"
|
|
|
|
|
+ "io/ioutil"
|
|
|
|
|
+ "log"
|
|
|
|
|
+ "os"
|
|
|
|
|
+ "path/filepath"
|
|
|
|
|
+ "sort"
|
|
|
|
|
+ "strconv"
|
|
|
|
|
+ "strings"
|
|
|
|
|
+
|
|
|
|
|
+ "google.golang.org/proto/internal/encoding/pack"
|
|
|
|
|
+ "google.golang.org/proto/internal/encoding/wire"
|
|
|
|
|
+ "google.golang.org/proto/reflect/protoreflect"
|
|
|
|
|
+ "google.golang.org/proto/reflect/prototype"
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+func main() {
|
|
|
|
|
+ log.SetFlags(0)
|
|
|
|
|
+ log.SetOutput(os.Stderr)
|
|
|
|
|
+
|
|
|
|
|
+ var fs fields
|
|
|
|
|
+ flag.Var((*boolFields)(&fs), "bools", "List of bool fields")
|
|
|
|
|
+ flag.Var((*intFields)(&fs), "ints", "List of int32 or int64 fields")
|
|
|
|
|
+ flag.Var((*sintFields)(&fs), "sints", "List of sint32 or sint64 fields")
|
|
|
|
|
+ flag.Var((*uintFields)(&fs), "uints", "List of enum, uint32, or uint64 fields")
|
|
|
|
|
+ flag.Var((*uint32Fields)(&fs), "uint32s", "List of fixed32 fields")
|
|
|
|
|
+ flag.Var((*int32Fields)(&fs), "int32s", "List of sfixed32 fields")
|
|
|
|
|
+ flag.Var((*float32Fields)(&fs), "float32s", "List of float fields")
|
|
|
|
|
+ flag.Var((*uint64Fields)(&fs), "uint64s", "List of fixed64 fields")
|
|
|
|
|
+ flag.Var((*int64Fields)(&fs), "int64s", "List of sfixed64 fields")
|
|
|
|
|
+ flag.Var((*float64Fields)(&fs), "float64s", "List of double fields")
|
|
|
|
|
+ flag.Var((*stringFields)(&fs), "strings", "List of string fields")
|
|
|
|
|
+ flag.Var((*bytesFields)(&fs), "bytes", "List of bytes fields")
|
|
|
|
|
+ flag.Var((*messageFields)(&fs), "messages", "List of message fields")
|
|
|
|
|
+ flag.Var((*groupFields)(&fs), "groups", "List of group fields")
|
|
|
|
|
+ printDesc := flag.Bool("print_descriptor", false, "Print the message descriptor")
|
|
|
|
|
+ printSource := flag.Bool("print_source", false, "Print the output in valid Go syntax")
|
|
|
|
|
+ flag.Usage = func() {
|
|
|
|
|
+ log.Printf("Usage: %s [OPTIONS]... [INPUTS]...\n\n%s\n", filepath.Base(os.Args[0]), strings.Join([]string{
|
|
|
|
|
+ "Print structured representations of encoded protocol buffer messages.",
|
|
|
|
|
+ "Since the protobuf wire format is not fully self-describing, type information",
|
|
|
|
|
+ "about the proto message can be provided using flags (e.g., -messages).",
|
|
|
|
|
+ "Each field list is a comma-separated list of field identifiers,",
|
|
|
|
|
+ "where each field identifier is a dot-separated list of field numbers,",
|
|
|
|
|
+ "identifying each field relative to the root message.",
|
|
|
|
|
+ "",
|
|
|
|
|
+ "For example, \"-messages 1,3,3.1 -float32s 1.2 -bools 3.1.2\" represents:",
|
|
|
|
|
+ "",
|
|
|
|
|
+ " message M {",
|
|
|
|
|
+ " optional M1 f1 = 1; // -messages 1",
|
|
|
|
|
+ " message M1 {",
|
|
|
|
|
+ " repeated float f2 = 2; // -float32s 1.2",
|
|
|
|
|
+ " }",
|
|
|
|
|
+ " optional M3 f3 = 3; // -messages 3",
|
|
|
|
|
+ " message M3 {",
|
|
|
|
|
+ " optional M1 f1 = 1; // -messages 3.1",
|
|
|
|
|
+ " message M1 {",
|
|
|
|
|
+ " repeated bool f2 = 2; // -bools 3.1.2",
|
|
|
|
|
+ " }",
|
|
|
|
|
+ " }",
|
|
|
|
|
+ " }",
|
|
|
|
|
+ "",
|
|
|
|
|
+ "Arbitrarily complex message schemas can be represented using these flags.",
|
|
|
|
|
+ "Scalar field types are marked as repeated so that pbdump can decode",
|
|
|
|
|
+ "the packed representations of such field types.",
|
|
|
|
|
+ "",
|
|
|
|
|
+ "If no inputs are specified, the wire data is read in from stdin, otherwise",
|
|
|
|
|
+ "the contents of each specified input file is concatenated and",
|
|
|
|
|
+ "treated as one large message.",
|
|
|
|
|
+ "",
|
|
|
|
|
+ "Options:",
|
|
|
|
|
+ " -bools fields " + flag.Lookup("bools").Usage,
|
|
|
|
|
+ " -ints fields " + flag.Lookup("ints").Usage,
|
|
|
|
|
+ " -sints fields " + flag.Lookup("sints").Usage,
|
|
|
|
|
+ " -uints fields " + flag.Lookup("uints").Usage,
|
|
|
|
|
+ " -int32s fields " + flag.Lookup("int32s").Usage,
|
|
|
|
|
+ " -int64s fields " + flag.Lookup("int64s").Usage,
|
|
|
|
|
+ " -uint32s fields " + flag.Lookup("uint32s").Usage,
|
|
|
|
|
+ " -uint64s fields " + flag.Lookup("uint64s").Usage,
|
|
|
|
|
+ " -float32s fields " + flag.Lookup("float32s").Usage,
|
|
|
|
|
+ " -float64s fields " + flag.Lookup("float64s").Usage,
|
|
|
|
|
+ " -strings fields " + flag.Lookup("strings").Usage,
|
|
|
|
|
+ " -bytes fields " + flag.Lookup("bytes").Usage,
|
|
|
|
|
+ " -messages fields " + flag.Lookup("messages").Usage,
|
|
|
|
|
+ " -groups fields " + flag.Lookup("groups").Usage,
|
|
|
|
|
+ " -print_descriptor " + flag.Lookup("print_descriptor").Usage,
|
|
|
|
|
+ " -print_source " + flag.Lookup("print_source").Usage,
|
|
|
|
|
+ }, "\n"))
|
|
|
|
|
+ }
|
|
|
|
|
+ flag.Parse()
|
|
|
|
|
+
|
|
|
|
|
+ // Create message types.
|
|
|
|
|
+ var desc protoreflect.MessageDescriptor
|
|
|
|
|
+ if len(fs) > 0 {
|
|
|
|
|
+ var err error
|
|
|
|
|
+ desc, err = fs.Descriptor()
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ log.Fatalf("Descriptor error: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if *printDesc {
|
|
|
|
|
+ log.Printf("%#v\n", desc)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Read message input.
|
|
|
|
|
+ var buf []byte
|
|
|
|
|
+ if flag.NArg() == 0 {
|
|
|
|
|
+ b, err := ioutil.ReadAll(os.Stdin)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ log.Fatalf("ReadAll error: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ buf = b
|
|
|
|
|
+ }
|
|
|
|
|
+ for _, f := range flag.Args() {
|
|
|
|
|
+ b, err := ioutil.ReadFile(f)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ log.Fatalf("ReadFile error: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ buf = append(buf, b...)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Parse and print message structure.
|
|
|
|
|
+ defer log.Printf("fatal input: %q", buf) // debug printout if panic occurs
|
|
|
|
|
+ var m pack.Message
|
|
|
|
|
+ m.UnmarshalDescriptor(buf, desc)
|
|
|
|
|
+ if *printSource {
|
|
|
|
|
+ fmt.Fprintf(os.Stdout, "%#v\n", m)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ fmt.Fprintf(os.Stdout, "%+v\n", m)
|
|
|
|
|
+ }
|
|
|
|
|
+ if !bytes.Equal(buf, m.Marshal()) || len(buf) != m.Size() {
|
|
|
|
|
+ log.Fatalf("roundtrip mismatch:\n\tgot: %d %x\n\twant: %d %x", m.Size(), m, len(buf), buf)
|
|
|
|
|
+ }
|
|
|
|
|
+ os.Exit(0) // exit cleanly, avoid debug printout
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// fields is a tree of fields, keyed by a field number.
|
|
|
|
|
+// Fields representing messages or groups have sub-fields.
|
|
|
|
|
+type fields map[wire.Number]*field
|
|
|
|
|
+type field struct {
|
|
|
|
|
+ kind protoreflect.Kind
|
|
|
|
|
+ sub fields // only for MessageKind or GroupKind
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Set parses s as a comma-separated list (see the help above for the format)
|
|
|
|
|
+// and treats each field identifier as the specified kind.
|
|
|
|
|
+func (fs *fields) Set(s string, k protoreflect.Kind) error {
|
|
|
|
|
+ if *fs == nil {
|
|
|
|
|
+ *fs = make(fields)
|
|
|
|
|
+ }
|
|
|
|
|
+ for _, s := range strings.Split(s, ",") {
|
|
|
|
|
+ if err := fs.set("", strings.TrimSpace(s), k); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+func (fs fields) set(prefix, s string, k protoreflect.Kind) error {
|
|
|
|
|
+ if s == "" {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Parse next field number.
|
|
|
|
|
+ i := strings.IndexByte(s, '.')
|
|
|
|
|
+ if i < 0 {
|
|
|
|
|
+ i = len(s)
|
|
|
|
|
+ }
|
|
|
|
|
+ prefix = strings.TrimPrefix(prefix+"."+s[:i], ".")
|
|
|
|
|
+ n, _ := strconv.ParseInt(s[:i], 10, 32)
|
|
|
|
|
+ num := wire.Number(n)
|
|
|
|
|
+ if num < wire.MinValidNumber || wire.MaxValidNumber < num {
|
|
|
|
|
+ return fmt.Errorf("invalid field: %v", prefix)
|
|
|
|
|
+ }
|
|
|
|
|
+ s = strings.TrimPrefix(s[i:], ".")
|
|
|
|
|
+
|
|
|
|
|
+ // Handle the current field.
|
|
|
|
|
+ if fs[num] == nil {
|
|
|
|
|
+ fs[num] = &field{0, make(fields)}
|
|
|
|
|
+ }
|
|
|
|
|
+ if len(s) == 0 {
|
|
|
|
|
+ if fs[num].kind.IsValid() {
|
|
|
|
|
+ return fmt.Errorf("field %v already set as %v type", prefix, fs[num].kind)
|
|
|
|
|
+ }
|
|
|
|
|
+ fs[num].kind = k
|
|
|
|
|
+ }
|
|
|
|
|
+ if err := fs[num].sub.set(prefix, s, k); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Verify that only messages or groups can have sub-fields.
|
|
|
|
|
+ k2 := fs[num].kind
|
|
|
|
|
+ if k2 > 0 && k2 != protoreflect.MessageKind && k2 != protoreflect.GroupKind && len(fs[num].sub) > 0 {
|
|
|
|
|
+ return fmt.Errorf("field %v of %v type cannot have sub-fields", prefix, k2)
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Descriptor returns the field tree as a message descriptor.
|
|
|
|
|
+func (fs fields) Descriptor() (protoreflect.MessageDescriptor, error) {
|
|
|
|
|
+ ftyp, err := prototype.NewFile(&prototype.File{
|
|
|
|
|
+ Syntax: protoreflect.Proto2,
|
|
|
|
|
+ Messages: []prototype.Message{fs.messageDescriptor("M")},
|
|
|
|
|
+ })
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+ return ftyp.Messages().Get(0), nil
|
|
|
|
|
+}
|
|
|
|
|
+func (fs fields) messageDescriptor(name protoreflect.FullName) prototype.Message {
|
|
|
|
|
+ m := prototype.Message{Name: name.Name()}
|
|
|
|
|
+ for _, n := range fs.sortedNums() {
|
|
|
|
|
+ f := prototype.Field{
|
|
|
|
|
+ Name: protoreflect.Name(fmt.Sprintf("f%d", n)),
|
|
|
|
|
+ Number: n,
|
|
|
|
|
+ Cardinality: protoreflect.Optional,
|
|
|
|
|
+ Kind: fs[n].kind,
|
|
|
|
|
+ }
|
|
|
|
|
+ if !f.Kind.IsValid() {
|
|
|
|
|
+ f.Kind = protoreflect.MessageKind
|
|
|
|
|
+ }
|
|
|
|
|
+ switch f.Kind {
|
|
|
|
|
+ case protoreflect.BoolKind, protoreflect.EnumKind,
|
|
|
|
|
+ protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Uint32Kind,
|
|
|
|
|
+ protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Uint64Kind,
|
|
|
|
|
+ protoreflect.Sfixed32Kind, protoreflect.Fixed32Kind, protoreflect.FloatKind,
|
|
|
|
|
+ protoreflect.Sfixed64Kind, protoreflect.Fixed64Kind, protoreflect.DoubleKind:
|
|
|
|
|
+ f.Cardinality = protoreflect.Repeated
|
|
|
|
|
+ f.IsPacked = true
|
|
|
|
|
+ case protoreflect.MessageKind, protoreflect.GroupKind:
|
|
|
|
|
+ s := name.Append(protoreflect.Name(fmt.Sprintf("M%d", n)))
|
|
|
|
|
+ f.MessageType = prototype.PlaceholderMessage(s)
|
|
|
|
|
+ m.Messages = append(m.Messages, fs[n].sub.messageDescriptor(s))
|
|
|
|
|
+ }
|
|
|
|
|
+ m.Fields = append(m.Fields, f)
|
|
|
|
|
+ }
|
|
|
|
|
+ return m
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (fs fields) sortedNums() (ns []wire.Number) {
|
|
|
|
|
+ for n := range fs {
|
|
|
|
|
+ ns = append(ns, n)
|
|
|
|
|
+ }
|
|
|
|
|
+ sort.Slice(ns, func(i, j int) bool { return ns[i] < ns[j] })
|
|
|
|
|
+ return ns
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+type (
|
|
|
|
|
+ boolFields fields
|
|
|
|
|
+ intFields fields
|
|
|
|
|
+ sintFields fields
|
|
|
|
|
+ uintFields fields
|
|
|
|
|
+ uint32Fields fields
|
|
|
|
|
+ int32Fields fields
|
|
|
|
|
+ float32Fields fields
|
|
|
|
|
+ uint64Fields fields
|
|
|
|
|
+ int64Fields fields
|
|
|
|
|
+ float64Fields fields
|
|
|
|
|
+ stringFields fields
|
|
|
|
|
+ bytesFields fields
|
|
|
|
|
+ messageFields fields
|
|
|
|
|
+ groupFields fields
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+// String and Set implement flag.Value.
|
|
|
|
|
+// The String method is not implemented since the flag helper never prints it.
|
|
|
|
|
+func (p *boolFields) String() string { return "not implemented" }
|
|
|
|
|
+func (p *intFields) String() string { return "not implemented" }
|
|
|
|
|
+func (p *sintFields) String() string { return "not implemented" }
|
|
|
|
|
+func (p *uintFields) String() string { return "not implemented" }
|
|
|
|
|
+func (p *uint32Fields) String() string { return "not implemented" }
|
|
|
|
|
+func (p *int32Fields) String() string { return "not implemented" }
|
|
|
|
|
+func (p *float32Fields) String() string { return "not implemented" }
|
|
|
|
|
+func (p *uint64Fields) String() string { return "not implemented" }
|
|
|
|
|
+func (p *int64Fields) String() string { return "not implemented" }
|
|
|
|
|
+func (p *float64Fields) String() string { return "not implemented" }
|
|
|
|
|
+func (p *stringFields) String() string { return "not implemented" }
|
|
|
|
|
+func (p *bytesFields) String() string { return "not implemented" }
|
|
|
|
|
+func (p *messageFields) String() string { return "not implemented" }
|
|
|
|
|
+func (p *groupFields) String() string { return "not implemented" }
|
|
|
|
|
+func (p *boolFields) Set(s string) error { return (*fields)(p).Set(s, protoreflect.BoolKind) }
|
|
|
|
|
+func (p *intFields) Set(s string) error { return (*fields)(p).Set(s, protoreflect.Int64Kind) }
|
|
|
|
|
+func (p *sintFields) Set(s string) error { return (*fields)(p).Set(s, protoreflect.Sint64Kind) }
|
|
|
|
|
+func (p *uintFields) Set(s string) error { return (*fields)(p).Set(s, protoreflect.Uint64Kind) }
|
|
|
|
|
+func (p *uint32Fields) Set(s string) error { return (*fields)(p).Set(s, protoreflect.Fixed32Kind) }
|
|
|
|
|
+func (p *int32Fields) Set(s string) error { return (*fields)(p).Set(s, protoreflect.Sfixed32Kind) }
|
|
|
|
|
+func (p *float32Fields) Set(s string) error { return (*fields)(p).Set(s, protoreflect.FloatKind) }
|
|
|
|
|
+func (p *uint64Fields) Set(s string) error { return (*fields)(p).Set(s, protoreflect.Fixed64Kind) }
|
|
|
|
|
+func (p *int64Fields) Set(s string) error { return (*fields)(p).Set(s, protoreflect.Sfixed64Kind) }
|
|
|
|
|
+func (p *float64Fields) Set(s string) error { return (*fields)(p).Set(s, protoreflect.DoubleKind) }
|
|
|
|
|
+func (p *stringFields) Set(s string) error { return (*fields)(p).Set(s, protoreflect.StringKind) }
|
|
|
|
|
+func (p *bytesFields) Set(s string) error { return (*fields)(p).Set(s, protoreflect.BytesKind) }
|
|
|
|
|
+func (p *messageFields) Set(s string) error { return (*fields)(p).Set(s, protoreflect.MessageKind) }
|
|
|
|
|
+func (p *groupFields) Set(s string) error { return (*fields)(p).Set(s, protoreflect.GroupKind) }
|