// Package ndr provides the ability to unmarshal NDR encoded byte steams into Go data structures
package ndr import ( ) // Struct tag values const ( TagConformant = "conformant" TagVarying = "varying" TagPointer = "pointer" TagPipe = "pipe" ) // Decoder unmarshals NDR byte stream data into a Go struct representation type Decoder struct { r *bufio.Reader // source of the data size int // initial size of bytes in buffer ch CommonHeader // NDR common header ph PrivateHeader // NDR private header conformantMax []uint32 // conformant max values that were moved to the beginning of the structure s interface{} // pointer to the structure being populated current []string // keeps track of the current field being populated } type deferedPtr struct { v reflect.Value tag reflect.StructTag } // NewDecoder creates a new instance of a NDR Decoder. func ( io.Reader) *Decoder { := new(Decoder) .r = bufio.NewReader() .r.Peek(int(commonHeaderBytes)) // For some reason an operation is needed on the buffer to initialise it so Buffered() != 0 .size = .r.Buffered() return } // Decode unmarshals the NDR encoded bytes into the pointer of a struct provided. func ( *Decoder) ( interface{}) error { .s = := .readCommonHeader() if != nil { return } = .readPrivateHeader() if != nil { return } _, = .r.Discard(4) //The next 4 bytes are an RPC unique pointer referent. We just skip these. if != nil { return Errorf("unable to process byte stream: %v", ) } return .process(, reflect.StructTag("")) } func ( *Decoder) ( interface{}, reflect.StructTag) error { // Scan for conformant fields as their max counts are moved to the beginning // http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagfcjh_37 := .scanConformantArrays(, ) if != nil { return } // Recursively fill the struct fields var []deferedPtr = .fill(, , &) if != nil { return Errorf("could not decode: %v", ) } // Read any deferred referents associated with pointers for , := range { = .(.v, .tag) if != nil { return fmt.Errorf("could not decode deferred referent: %v", ) } } return nil } // scanConformantArrays scans the structure for embedded conformant fields and captures the maximum element counts for // dimensions of the array that are moved to the beginning of the structure. func ( *Decoder) ( interface{}, reflect.StructTag) error { := .conformantScan(, ) if != nil { return fmt.Errorf("failed to scan for embedded conformant arrays: %v", ) } for := range .conformantMax { .conformantMax[], = .readUint32() if != nil { return fmt.Errorf("could not read preceding conformant max count index %d: %v", , ) } } return nil } // conformantScan inspects the structure's fields for whether they are conformant. func ( *Decoder) ( interface{}, reflect.StructTag) error { := parseTags() if .HasValue(TagPointer) { return nil } := getReflectValue() switch .Kind() { case reflect.Struct: for := 0; < .NumField(); ++ { := .(.Field(), .Type().Field().Tag) if != nil { return } } case reflect.String: if !.HasValue(TagConformant) { break } .conformantMax = append(.conformantMax, uint32(0)) case reflect.Slice: if !.HasValue(TagConformant) { break } , := sliceDimensions(.Type()) for := 0; < ; ++ { .conformantMax = append(.conformantMax, uint32(0)) } // For string arrays there is a common max for the strings within the array. if .Kind() == reflect.String { .conformantMax = append(.conformantMax, uint32(0)) } } return nil } func ( *Decoder) ( reflect.Value, reflect.StructTag, *[]deferedPtr) (bool, error) { // Pointer so defer filling the referent := parseTags() if .HasValue(TagPointer) { , := .readUint32() if != nil { return true, fmt.Errorf("could not read pointer: %v", ) } .delete(TagPointer) if != 0 { // if pointer is not zero add to the deferred items at end of stream * = append(*, deferedPtr{, .StructTag()}) } return true, nil } return false, nil } func getReflectValue( interface{}) ( reflect.Value) { if , := .(reflect.Value); { = } else { if reflect.ValueOf().Kind() == reflect.Ptr { = reflect.ValueOf().Elem() } } return } // fill populates fields with values from the NDR byte stream. func ( *Decoder) ( interface{}, reflect.StructTag, *[]deferedPtr) error { := getReflectValue() //// Pointer so defer filling the referent , := .isPointer(, , ) if != nil { return fmt.Errorf("could not process struct field(%s): %v", strings.Join(.current, "/"), ) } if { return nil } // Populate the value from the byte stream switch .Kind() { case reflect.Struct: .current = append(.current, .Type().Name()) //Track the current field being filled // in case struct is a union, track this and the selected union field for efficiency var reflect.Value var string // field to fill if struct is a union // Go through each field in the struct and recursively fill for := 0; < .NumField(); ++ { := .Type().Field().Name .current = append(.current, ) //Track the current field being filled //fmt.Fprintf(os.Stderr, "DEBUG Decoding: %s\n", strings.Join(dec.current, "/")) := .Type().Field().Tag := parseTags() // Union handling if !.IsValid() { // Is this field a union tag? = .isUnion(.Field(), ) } else { // What is the selected field value of the union if we don't already know if == "" { , = unionSelectedField(, ) if != nil { return fmt.Errorf("could not determine selected union value field for %s with discriminat"+ " tag %s: %v", .Type().Name(), , ) } } if .HasValue(TagUnionField) && != { // is a union and this field has not been selected so will skip it. .current = .current[:len(.current)-1] //This field has been skipped so remove it from the current field tracker continue } } // Check if field is a pointer if .Field().Type().Implements(reflect.TypeOf(new(RawBytes)).Elem()) && .Field().Type().Kind() == reflect.Slice && .Field().Type().Elem().Kind() == reflect.Uint8 { //field is for rawbytes , = addSizeToTag(, .Field(), ) if != nil { return fmt.Errorf("could not get rawbytes field(%s) size: %v", strings.Join(.current, "/"), ) } , := .isPointer(.Field(), , ) if != nil { return fmt.Errorf("could not process struct field(%s): %v", strings.Join(.current, "/"), ) } if ! { := .readRawBytes(.Field(), ) if != nil { return fmt.Errorf("could not fill raw bytes struct field(%s): %v", strings.Join(.current, "/"), ) } } } else { := .(.Field(), , ) if != nil { return fmt.Errorf("could not fill struct field(%s): %v", strings.Join(.current, "/"), ) } } .current = .current[:len(.current)-1] //This field has been filled so remove it from the current field tracker } .current = .current[:len(.current)-1] //This field has been filled so remove it from the current field tracker case reflect.Bool: , := .readBool() if != nil { return fmt.Errorf("could not fill %s: %v", .Type().Name(), ) } .Set(reflect.ValueOf()) case reflect.Uint8: , := .readUint8() if != nil { return fmt.Errorf("could not fill %s: %v", .Type().Name(), ) } .Set(reflect.ValueOf()) case reflect.Uint16: , := .readUint16() if != nil { return fmt.Errorf("could not fill %s: %v", .Type().Name(), ) } .Set(reflect.ValueOf()) case reflect.Uint32: , := .readUint32() if != nil { return fmt.Errorf("could not fill %s: %v", .Type().Name(), ) } .Set(reflect.ValueOf()) case reflect.Uint64: , := .readUint64() if != nil { return fmt.Errorf("could not fill %s: %v", .Type().Name(), ) } .Set(reflect.ValueOf()) case reflect.Int8: , := .readInt8() if != nil { return fmt.Errorf("could not fill %s: %v", .Type().Name(), ) } .Set(reflect.ValueOf()) case reflect.Int16: , := .readInt16() if != nil { return fmt.Errorf("could not fill %s: %v", .Type().Name(), ) } .Set(reflect.ValueOf()) case reflect.Int32: , := .readInt32() if != nil { return fmt.Errorf("could not fill %s: %v", .Type().Name(), ) } .Set(reflect.ValueOf()) case reflect.Int64: , := .readInt64() if != nil { return fmt.Errorf("could not fill %s: %v", .Type().Name(), ) } .Set(reflect.ValueOf()) case reflect.String: := parseTags() := .HasValue(TagConformant) // strings are always varying so this is assumed without an explicit tag var string var error if { , = .readConformantVaryingString() if != nil { return fmt.Errorf("could not fill with conformant varying string: %v", ) } } else { , = .readVaryingString() if != nil { return fmt.Errorf("could not fill with varying string: %v", ) } } .Set(reflect.ValueOf()) case reflect.Float32: , := .readFloat32() if != nil { return fmt.Errorf("could not fill %v: %v", .Type().Name(), ) } .Set(reflect.ValueOf()) case reflect.Float64: , := .readFloat64() if != nil { return fmt.Errorf("could not fill %v: %v", .Type().Name(), ) } .Set(reflect.ValueOf()) case reflect.Array: := .fillFixedArray(, , ) if != nil { return } case reflect.Slice: if .Type().Implements(reflect.TypeOf(new(RawBytes)).Elem()) && .Type().Elem().Kind() == reflect.Uint8 { //field is for rawbytes := .readRawBytes(, ) if != nil { return fmt.Errorf("could not fill raw bytes struct field(%s): %v", strings.Join(.current, "/"), ) } break } := parseTags() := .HasValue(TagConformant) := .HasValue(TagVarying) if .HasValue(TagPipe) { := .fillPipe(, ) if != nil { return } break } , := sliceDimensions(.Type()) if .Kind() == reflect.String && !.HasValue(subStringArrayValue) { // String array := .readStringsArray(, , ) if != nil { return } break } // varying is assumed as fixed arrays use the Go array type rather than slice if && { := .fillConformantVaryingArray(, , ) if != nil { return } } else if ! && { := .fillVaryingArray(, , ) if != nil { return } } else { //default to conformant and not varying := .fillConformantArray(, , ) if != nil { return } } default: return fmt.Errorf("unsupported type") } return nil } // readBytes returns a number of bytes from the NDR byte stream. func ( *Decoder) ( int) ([]byte, error) { //TODO make this take an int64 as input to allow for larger values on all systems? := make([]byte, , ) , := .r.Read() if != nil || != { return , fmt.Errorf("error reading bytes from stream: %v", ) } return , nil }