1*1c12ee1eSDan Willemsen// Copyright 2018 The Go Authors. All rights reserved. 2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style 3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file. 4*1c12ee1eSDan Willemsen 5*1c12ee1eSDan Willemsen// pbdump is a tool for decoding the wire format for protocol buffer messages. 6*1c12ee1eSDan Willemsenpackage main 7*1c12ee1eSDan Willemsen 8*1c12ee1eSDan Willemsenimport ( 9*1c12ee1eSDan Willemsen "bytes" 10*1c12ee1eSDan Willemsen "flag" 11*1c12ee1eSDan Willemsen "fmt" 12*1c12ee1eSDan Willemsen "io/ioutil" 13*1c12ee1eSDan Willemsen "log" 14*1c12ee1eSDan Willemsen "os" 15*1c12ee1eSDan Willemsen "path/filepath" 16*1c12ee1eSDan Willemsen "sort" 17*1c12ee1eSDan Willemsen "strconv" 18*1c12ee1eSDan Willemsen "strings" 19*1c12ee1eSDan Willemsen 20*1c12ee1eSDan Willemsen "google.golang.org/protobuf/encoding/protowire" 21*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/errors" 22*1c12ee1eSDan Willemsen "google.golang.org/protobuf/proto" 23*1c12ee1eSDan Willemsen "google.golang.org/protobuf/reflect/protodesc" 24*1c12ee1eSDan Willemsen "google.golang.org/protobuf/reflect/protoreflect" 25*1c12ee1eSDan Willemsen "google.golang.org/protobuf/testing/protopack" 26*1c12ee1eSDan Willemsen 27*1c12ee1eSDan Willemsen "google.golang.org/protobuf/types/descriptorpb" 28*1c12ee1eSDan Willemsen) 29*1c12ee1eSDan Willemsen 30*1c12ee1eSDan Willemsenfunc main() { 31*1c12ee1eSDan Willemsen log.SetFlags(0) 32*1c12ee1eSDan Willemsen log.SetOutput(os.Stderr) 33*1c12ee1eSDan Willemsen 34*1c12ee1eSDan Willemsen var fs fields 35*1c12ee1eSDan Willemsen var flagUsages []string 36*1c12ee1eSDan Willemsen flagVar := func(value flag.Value, name, usage string) { 37*1c12ee1eSDan Willemsen flagUsages = append(flagUsages, fmt.Sprintf(" -%-16v %v", name+" "+value.String(), usage)) 38*1c12ee1eSDan Willemsen flag.Var(value, name, usage) 39*1c12ee1eSDan Willemsen } 40*1c12ee1eSDan Willemsen flagBool := func(name, usage string) *bool { 41*1c12ee1eSDan Willemsen flagUsages = append(flagUsages, fmt.Sprintf(" -%-16v %v", name, usage)) 42*1c12ee1eSDan Willemsen return flag.Bool(name, false, usage) 43*1c12ee1eSDan Willemsen } 44*1c12ee1eSDan Willemsen flagVar(fieldsFlag{&fs, protoreflect.BoolKind}, "bools", "List of bool fields") 45*1c12ee1eSDan Willemsen flagVar(fieldsFlag{&fs, protoreflect.Int64Kind}, "ints", "List of int32 or int64 fields") 46*1c12ee1eSDan Willemsen flagVar(fieldsFlag{&fs, protoreflect.Sint64Kind}, "sints", "List of sint32 or sint64 fields") 47*1c12ee1eSDan Willemsen flagVar(fieldsFlag{&fs, protoreflect.Uint64Kind}, "uints", "List of enum, uint32, or uint64 fields") 48*1c12ee1eSDan Willemsen flagVar(fieldsFlag{&fs, protoreflect.Fixed32Kind}, "uint32s", "List of fixed32 fields") 49*1c12ee1eSDan Willemsen flagVar(fieldsFlag{&fs, protoreflect.Sfixed32Kind}, "int32s", "List of sfixed32 fields") 50*1c12ee1eSDan Willemsen flagVar(fieldsFlag{&fs, protoreflect.FloatKind}, "float32s", "List of float fields") 51*1c12ee1eSDan Willemsen flagVar(fieldsFlag{&fs, protoreflect.Fixed64Kind}, "uint64s", "List of fixed64 fields") 52*1c12ee1eSDan Willemsen flagVar(fieldsFlag{&fs, protoreflect.Sfixed64Kind}, "int64s", "List of sfixed64 fields") 53*1c12ee1eSDan Willemsen flagVar(fieldsFlag{&fs, protoreflect.DoubleKind}, "float64s", "List of double fields") 54*1c12ee1eSDan Willemsen flagVar(fieldsFlag{&fs, protoreflect.StringKind}, "strings", "List of string fields") 55*1c12ee1eSDan Willemsen flagVar(fieldsFlag{&fs, protoreflect.BytesKind}, "bytes", "List of bytes fields") 56*1c12ee1eSDan Willemsen flagVar(fieldsFlag{&fs, protoreflect.MessageKind}, "messages", "List of message fields") 57*1c12ee1eSDan Willemsen flagVar(fieldsFlag{&fs, protoreflect.GroupKind}, "groups", "List of group fields") 58*1c12ee1eSDan Willemsen printDesc := flagBool("print_descriptor", "Print the message descriptor") 59*1c12ee1eSDan Willemsen printSource := flagBool("print_source", "Print the output in valid Go syntax") 60*1c12ee1eSDan Willemsen flag.Usage = func() { 61*1c12ee1eSDan Willemsen fmt.Printf("Usage: %s [OPTIONS]... [INPUTS]...\n\n%s\n", filepath.Base(os.Args[0]), strings.Join(append([]string{ 62*1c12ee1eSDan Willemsen "Print structured representations of encoded protocol buffer messages.", 63*1c12ee1eSDan Willemsen "Since the protobuf wire format is not fully self-describing, type information", 64*1c12ee1eSDan Willemsen "about the proto message can be provided using flags (e.g., -messages).", 65*1c12ee1eSDan Willemsen "Each field list is a comma-separated list of field identifiers,", 66*1c12ee1eSDan Willemsen "where each field identifier is a dot-separated list of field numbers,", 67*1c12ee1eSDan Willemsen "identifying each field relative to the root message.", 68*1c12ee1eSDan Willemsen "", 69*1c12ee1eSDan Willemsen "For example, \"-messages 1,3,3.1 -float32s 1.2 -bools 3.1.2\" represents:", 70*1c12ee1eSDan Willemsen "", 71*1c12ee1eSDan Willemsen " message M {", 72*1c12ee1eSDan Willemsen " optional M1 f1 = 1; // -messages 1", 73*1c12ee1eSDan Willemsen " message M1 {", 74*1c12ee1eSDan Willemsen " repeated float f2 = 2; // -float32s 1.2", 75*1c12ee1eSDan Willemsen " }", 76*1c12ee1eSDan Willemsen " optional M3 f3 = 3; // -messages 3", 77*1c12ee1eSDan Willemsen " message M3 {", 78*1c12ee1eSDan Willemsen " optional M1 f1 = 1; // -messages 3.1", 79*1c12ee1eSDan Willemsen " message M1 {", 80*1c12ee1eSDan Willemsen " repeated bool f2 = 2; // -bools 3.1.2", 81*1c12ee1eSDan Willemsen " }", 82*1c12ee1eSDan Willemsen " }", 83*1c12ee1eSDan Willemsen " }", 84*1c12ee1eSDan Willemsen "", 85*1c12ee1eSDan Willemsen "Arbitrarily complex message schemas can be represented using these flags.", 86*1c12ee1eSDan Willemsen "Scalar field types are marked as repeated so that pbdump can decode", 87*1c12ee1eSDan Willemsen "the packed representations of such field types.", 88*1c12ee1eSDan Willemsen "", 89*1c12ee1eSDan Willemsen "If no inputs are specified, the wire data is read in from stdin, otherwise", 90*1c12ee1eSDan Willemsen "the contents of each specified input file is concatenated and", 91*1c12ee1eSDan Willemsen "treated as one large message.", 92*1c12ee1eSDan Willemsen "", 93*1c12ee1eSDan Willemsen "Options:", 94*1c12ee1eSDan Willemsen }, flagUsages...), "\n")) 95*1c12ee1eSDan Willemsen } 96*1c12ee1eSDan Willemsen flag.Parse() 97*1c12ee1eSDan Willemsen 98*1c12ee1eSDan Willemsen // Create message types. 99*1c12ee1eSDan Willemsen var desc protoreflect.MessageDescriptor 100*1c12ee1eSDan Willemsen if len(fs) > 0 { 101*1c12ee1eSDan Willemsen var err error 102*1c12ee1eSDan Willemsen desc, err = fs.Descriptor() 103*1c12ee1eSDan Willemsen if err != nil { 104*1c12ee1eSDan Willemsen log.Fatalf("Descriptor error: %v", err) 105*1c12ee1eSDan Willemsen } 106*1c12ee1eSDan Willemsen if *printDesc { 107*1c12ee1eSDan Willemsen fmt.Printf("%#v\n", desc) 108*1c12ee1eSDan Willemsen } 109*1c12ee1eSDan Willemsen } 110*1c12ee1eSDan Willemsen 111*1c12ee1eSDan Willemsen // Read message input. 112*1c12ee1eSDan Willemsen var buf []byte 113*1c12ee1eSDan Willemsen if flag.NArg() == 0 { 114*1c12ee1eSDan Willemsen b, err := ioutil.ReadAll(os.Stdin) 115*1c12ee1eSDan Willemsen if err != nil { 116*1c12ee1eSDan Willemsen log.Fatalf("ReadAll error: %v", err) 117*1c12ee1eSDan Willemsen } 118*1c12ee1eSDan Willemsen buf = b 119*1c12ee1eSDan Willemsen } 120*1c12ee1eSDan Willemsen for _, f := range flag.Args() { 121*1c12ee1eSDan Willemsen b, err := ioutil.ReadFile(f) 122*1c12ee1eSDan Willemsen if err != nil { 123*1c12ee1eSDan Willemsen log.Fatalf("ReadFile error: %v", err) 124*1c12ee1eSDan Willemsen } 125*1c12ee1eSDan Willemsen buf = append(buf, b...) 126*1c12ee1eSDan Willemsen } 127*1c12ee1eSDan Willemsen 128*1c12ee1eSDan Willemsen // Parse and print message structure. 129*1c12ee1eSDan Willemsen defer log.Printf("fatal input: %q", buf) // debug printout if panic occurs 130*1c12ee1eSDan Willemsen var m protopack.Message 131*1c12ee1eSDan Willemsen m.UnmarshalAbductive(buf, desc) 132*1c12ee1eSDan Willemsen if *printSource { 133*1c12ee1eSDan Willemsen fmt.Printf("%#v\n", m) 134*1c12ee1eSDan Willemsen } else { 135*1c12ee1eSDan Willemsen fmt.Printf("%+v\n", m) 136*1c12ee1eSDan Willemsen } 137*1c12ee1eSDan Willemsen if !bytes.Equal(buf, m.Marshal()) || len(buf) != m.Size() { 138*1c12ee1eSDan Willemsen log.Fatalf("roundtrip mismatch:\n\tgot: %d %x\n\twant: %d %x", m.Size(), m, len(buf), buf) 139*1c12ee1eSDan Willemsen } 140*1c12ee1eSDan Willemsen os.Exit(0) // exit cleanly, avoid debug printout 141*1c12ee1eSDan Willemsen} 142*1c12ee1eSDan Willemsen 143*1c12ee1eSDan Willemsen// fields is a tree of fields, keyed by a field number. 144*1c12ee1eSDan Willemsen// Fields representing messages or groups have sub-fields. 145*1c12ee1eSDan Willemsentype fields map[protowire.Number]*field 146*1c12ee1eSDan Willemsentype field struct { 147*1c12ee1eSDan Willemsen kind protoreflect.Kind 148*1c12ee1eSDan Willemsen sub fields // only for MessageKind or GroupKind 149*1c12ee1eSDan Willemsen} 150*1c12ee1eSDan Willemsen 151*1c12ee1eSDan Willemsen// Set parses s as a comma-separated list (see the help above for the format) 152*1c12ee1eSDan Willemsen// and treats each field identifier as the specified kind. 153*1c12ee1eSDan Willemsenfunc (fs *fields) Set(s string, k protoreflect.Kind) error { 154*1c12ee1eSDan Willemsen if *fs == nil { 155*1c12ee1eSDan Willemsen *fs = make(fields) 156*1c12ee1eSDan Willemsen } 157*1c12ee1eSDan Willemsen for _, s := range strings.Split(s, ",") { 158*1c12ee1eSDan Willemsen if err := fs.set("", strings.TrimSpace(s), k); err != nil { 159*1c12ee1eSDan Willemsen return err 160*1c12ee1eSDan Willemsen } 161*1c12ee1eSDan Willemsen } 162*1c12ee1eSDan Willemsen return nil 163*1c12ee1eSDan Willemsen} 164*1c12ee1eSDan Willemsenfunc (fs fields) set(prefix, s string, k protoreflect.Kind) error { 165*1c12ee1eSDan Willemsen if s == "" { 166*1c12ee1eSDan Willemsen return nil 167*1c12ee1eSDan Willemsen } 168*1c12ee1eSDan Willemsen 169*1c12ee1eSDan Willemsen // Parse next field number. 170*1c12ee1eSDan Willemsen i := strings.IndexByte(s, '.') 171*1c12ee1eSDan Willemsen if i < 0 { 172*1c12ee1eSDan Willemsen i = len(s) 173*1c12ee1eSDan Willemsen } 174*1c12ee1eSDan Willemsen prefix = strings.TrimPrefix(prefix+"."+s[:i], ".") 175*1c12ee1eSDan Willemsen n, _ := strconv.ParseInt(s[:i], 10, 32) 176*1c12ee1eSDan Willemsen num := protowire.Number(n) 177*1c12ee1eSDan Willemsen if num < protowire.MinValidNumber || protowire.MaxValidNumber < num { 178*1c12ee1eSDan Willemsen return errors.New("invalid field: %v", prefix) 179*1c12ee1eSDan Willemsen } 180*1c12ee1eSDan Willemsen s = strings.TrimPrefix(s[i:], ".") 181*1c12ee1eSDan Willemsen 182*1c12ee1eSDan Willemsen // Handle the current field. 183*1c12ee1eSDan Willemsen if fs[num] == nil { 184*1c12ee1eSDan Willemsen fs[num] = &field{0, make(fields)} 185*1c12ee1eSDan Willemsen } 186*1c12ee1eSDan Willemsen if len(s) == 0 { 187*1c12ee1eSDan Willemsen if fs[num].kind.IsValid() { 188*1c12ee1eSDan Willemsen return errors.New("field %v already set as %v type", prefix, fs[num].kind) 189*1c12ee1eSDan Willemsen } 190*1c12ee1eSDan Willemsen fs[num].kind = k 191*1c12ee1eSDan Willemsen } 192*1c12ee1eSDan Willemsen if err := fs[num].sub.set(prefix, s, k); err != nil { 193*1c12ee1eSDan Willemsen return err 194*1c12ee1eSDan Willemsen } 195*1c12ee1eSDan Willemsen 196*1c12ee1eSDan Willemsen // Verify that only messages or groups can have sub-fields. 197*1c12ee1eSDan Willemsen k2 := fs[num].kind 198*1c12ee1eSDan Willemsen if k2 > 0 && k2 != protoreflect.MessageKind && k2 != protoreflect.GroupKind && len(fs[num].sub) > 0 { 199*1c12ee1eSDan Willemsen return errors.New("field %v of %v type cannot have sub-fields", prefix, k2) 200*1c12ee1eSDan Willemsen } 201*1c12ee1eSDan Willemsen return nil 202*1c12ee1eSDan Willemsen} 203*1c12ee1eSDan Willemsen 204*1c12ee1eSDan Willemsen// Descriptor returns the field tree as a message descriptor. 205*1c12ee1eSDan Willemsenfunc (fs fields) Descriptor() (protoreflect.MessageDescriptor, error) { 206*1c12ee1eSDan Willemsen fd, err := protodesc.NewFile(&descriptorpb.FileDescriptorProto{ 207*1c12ee1eSDan Willemsen Name: proto.String("dump.proto"), 208*1c12ee1eSDan Willemsen Syntax: proto.String("proto2"), 209*1c12ee1eSDan Willemsen MessageType: []*descriptorpb.DescriptorProto{fs.messageDescriptor("X")}, 210*1c12ee1eSDan Willemsen }, nil) 211*1c12ee1eSDan Willemsen if err != nil { 212*1c12ee1eSDan Willemsen return nil, err 213*1c12ee1eSDan Willemsen } 214*1c12ee1eSDan Willemsen return fd.Messages().Get(0), nil 215*1c12ee1eSDan Willemsen} 216*1c12ee1eSDan Willemsenfunc (fs fields) messageDescriptor(name protoreflect.FullName) *descriptorpb.DescriptorProto { 217*1c12ee1eSDan Willemsen m := &descriptorpb.DescriptorProto{Name: proto.String(string(name.Name()))} 218*1c12ee1eSDan Willemsen for _, n := range fs.sortedNums() { 219*1c12ee1eSDan Willemsen k := fs[n].kind 220*1c12ee1eSDan Willemsen if !k.IsValid() { 221*1c12ee1eSDan Willemsen k = protoreflect.MessageKind 222*1c12ee1eSDan Willemsen } 223*1c12ee1eSDan Willemsen f := &descriptorpb.FieldDescriptorProto{ 224*1c12ee1eSDan Willemsen Name: proto.String(fmt.Sprintf("x%d", n)), 225*1c12ee1eSDan Willemsen Number: proto.Int32(int32(n)), 226*1c12ee1eSDan Willemsen Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 227*1c12ee1eSDan Willemsen Type: descriptorpb.FieldDescriptorProto_Type(k).Enum(), 228*1c12ee1eSDan Willemsen } 229*1c12ee1eSDan Willemsen switch k { 230*1c12ee1eSDan Willemsen case protoreflect.BoolKind, protoreflect.EnumKind, 231*1c12ee1eSDan Willemsen protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Uint32Kind, 232*1c12ee1eSDan Willemsen protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Uint64Kind, 233*1c12ee1eSDan Willemsen protoreflect.Sfixed32Kind, protoreflect.Fixed32Kind, protoreflect.FloatKind, 234*1c12ee1eSDan Willemsen protoreflect.Sfixed64Kind, protoreflect.Fixed64Kind, protoreflect.DoubleKind: 235*1c12ee1eSDan Willemsen f.Label = descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum() 236*1c12ee1eSDan Willemsen f.Options = &descriptorpb.FieldOptions{Packed: proto.Bool(true)} 237*1c12ee1eSDan Willemsen case protoreflect.MessageKind, protoreflect.GroupKind: 238*1c12ee1eSDan Willemsen s := name.Append(protoreflect.Name(fmt.Sprintf("X%d", n))) 239*1c12ee1eSDan Willemsen f.TypeName = proto.String(string("." + s)) 240*1c12ee1eSDan Willemsen m.NestedType = append(m.NestedType, fs[n].sub.messageDescriptor(s)) 241*1c12ee1eSDan Willemsen } 242*1c12ee1eSDan Willemsen m.Field = append(m.Field, f) 243*1c12ee1eSDan Willemsen } 244*1c12ee1eSDan Willemsen return m 245*1c12ee1eSDan Willemsen} 246*1c12ee1eSDan Willemsen 247*1c12ee1eSDan Willemsenfunc (fs fields) sortedNums() (ns []protowire.Number) { 248*1c12ee1eSDan Willemsen for n := range fs { 249*1c12ee1eSDan Willemsen ns = append(ns, n) 250*1c12ee1eSDan Willemsen } 251*1c12ee1eSDan Willemsen sort.Slice(ns, func(i, j int) bool { return ns[i] < ns[j] }) 252*1c12ee1eSDan Willemsen return ns 253*1c12ee1eSDan Willemsen} 254*1c12ee1eSDan Willemsen 255*1c12ee1eSDan Willemsen// fieldsFlag is an implementation of flag.Value that is keyed a specific kind. 256*1c12ee1eSDan Willemsentype fieldsFlag struct { 257*1c12ee1eSDan Willemsen f *fields 258*1c12ee1eSDan Willemsen k protoreflect.Kind 259*1c12ee1eSDan Willemsen} 260*1c12ee1eSDan Willemsen 261*1c12ee1eSDan Willemsenfunc (fs fieldsFlag) String() string { return "FIELDS" } 262*1c12ee1eSDan Willemsenfunc (fs fieldsFlag) Set(s string) error { return fs.f.Set(s, fs.k) } 263