package decoder import ( "bytes" "encoding/binary" "fmt" "time" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/metric" ) // Directive is a Decode Directive, the basic building block of a decoder type Directive interface { // Execute performs the function of the decode directive. If DecodeContext is nil then the // ask is to check that a subsequent execution (with non nill DecodeContext) is expted to work. Execute(*bytes.Buffer, *DecodeContext) error } type IterOption struct { EOFTerminateIter bool RemainingToGreaterEqualOrTerminate uint32 } // ValueDirective is a decode directive that extracts some data from the packet, an integer or byte maybe, // which it then processes by using it, for example, as the counter for the number of iterations to perform // of downstream decode directives. // // A ValueDirective can be used to either Switch, Iter(ate), Encapsulate or Do mutually exclusively. type ValueDirective interface { Directive // Switch attaches a set of conditional decode directives downstream of this decode directive Switch(paths ...CaseValueDirective) ValueDirective // Iter attaches a single downstream decode directive that will be executed repeatedly according to the iteration count Iter(maxIterations uint32, dd Directive, iterOptions ...IterOption) ValueDirective // Encapsulated will form a new buffer of the encapsulated length and pass that buffer on to the downsstream decode directive Encapsulated(maxSize uint32, dd Directive) ValueDirective // Ref records this decode directive in the passed reference Ref(*interface{}) ValueDirective // Do attaches a Decode Operation - these are uses of the decoded information to perform work on, transform, write out etc. Do(ddo DirectiveOp) ValueDirective } type valueDirective struct { reference *valueDirective value interface{} noDecode bool cases []CaseValueDirective iter Directive maxIterations uint32 encapsulated Directive maxEncapsulation uint32 ops []DirectiveOp err error iterOption IterOption } func valueToString(in interface{}) string { switch v := in.(type) { case *uint16: return fmt.Sprintf("%d", *v) case uint16: return fmt.Sprintf("%d", v) case *uint32: return fmt.Sprintf("%d", *v) case uint32: return fmt.Sprintf("%d", v) default: return fmt.Sprintf("%v", in) } } func (dd *valueDirective) Execute(buffer *bytes.Buffer, dc *DecodeContext) error { if dd.reference == nil && !dd.noDecode { if e := binary.Read(buffer, binary.BigEndian, dd.value); e != nil { return e } } // Switch downstream? if dd.cases != nil && len(dd.cases) > 0 { for _, c := range dd.cases { if c.Equals(dd.value) { return c.Execute(buffer, dc) } } switch v := dd.value.(type) { case *uint32: return fmt.Errorf("(%T).Switch,unmatched case %d", v, *v) case *uint16: return fmt.Errorf("(%T).Switch,unmatched case %d", v, *v) default: return fmt.Errorf("(%T).Switch,unmatched case %v", dd.value, dd.value) } } // Iter downstream? if dd.iter != nil { fn := func(id interface{}) error { if dd.iterOption.RemainingToGreaterEqualOrTerminate > 0 && uint32(buffer.Len()) < dd.iterOption.RemainingToGreaterEqualOrTerminate { return nil } if dd.iterOption.EOFTerminateIter && buffer.Len() == 0 { return nil } if e := dd.iter.Execute(buffer, dc); e != nil { return e } return nil } switch v := dd.value.(type) { case *uint32: if *v > dd.maxIterations { return fmt.Errorf("iter exceeds configured max - value %d, limit %d", *v, dd.maxIterations) } for i := uint32(0); i < *v; i++ { if e := fn(i); e != nil { return e } } case *uint16: if *v > uint16(dd.maxIterations) { return fmt.Errorf("iter exceeds configured max - value %d, limit %d", *v, dd.maxIterations) } for i := uint16(0); i < *v; i++ { if e := fn(i); e != nil { return e } } default: // Can't actually get here if .Iter method check types (and it does) return fmt.Errorf("(%T).Iter, cannot iterator over this type", dd.value) } } // Encapsualted downstream> if dd.encapsulated != nil { switch v := dd.value.(type) { case *uint32: if *v > dd.maxEncapsulation { return fmt.Errorf("encap exceeds configured max - value %d, limit %d", *v, dd.maxEncapsulation) } return dd.encapsulated.Execute(bytes.NewBuffer(buffer.Next(int(*v))), dc) case *uint16: if *v > uint16(dd.maxEncapsulation) { return fmt.Errorf("encap exceeds configured max - value %d, limit %d", *v, dd.maxEncapsulation) } return dd.encapsulated.Execute(bytes.NewBuffer(buffer.Next(int(*v))), dc) } } // Perform the attached operations for _, op := range dd.ops { if err := op.process(dc, dd.value); err != nil { return err } } return nil } // panickIfNotBlackCanvas checks the state of this value directive to see if it is has // alrady been configured in a manner inconsistent with another configuration change func (dd *valueDirective) panickIfNotBlackCanvas(change string, checkDOs bool) { if dd.cases != nil { panic(fmt.Sprintf("already have switch cases assigned, cannot assign %s", change)) } if dd.iter != nil { panic(fmt.Sprintf("already have iter assigned, cannot assign %s", change)) } if dd.encapsulated != nil { panic(fmt.Sprintf("already have encap assigned, cannot assign %s @", change)) } if checkDOs && dd.ops != nil && len(dd.ops) > 0 { panic(fmt.Sprintf("already have do assigned, cannot assign %s", change)) } } func (dd *valueDirective) Switch(paths ...CaseValueDirective) ValueDirective { dd.panickIfNotBlackCanvas("new switch", true) dd.cases = paths return dd } func (dd *valueDirective) Iter(maxIterations uint32, iter Directive, iterOptions ...IterOption) ValueDirective { dd.panickIfNotBlackCanvas("new iter", true) switch dd.value.(type) { case *uint32: case *uint16: default: panic(fmt.Sprintf("cannot iterate a %T", dd.value)) } dd.iter = iter dd.maxIterations = maxIterations for _, io := range iterOptions { dd.iterOption = io } return dd } func (dd *valueDirective) Encapsulated(maxSize uint32, encapsulated Directive) ValueDirective { dd.panickIfNotBlackCanvas("new encapsulated", true) switch dd.value.(type) { case *uint32: case *uint16: default: panic(fmt.Sprintf("cannot encapsulated on a %T", dd.value)) } dd.encapsulated = encapsulated dd.maxEncapsulation = maxSize return dd } func (dd *valueDirective) Do(ddo DirectiveOp) ValueDirective { dd.panickIfNotBlackCanvas("new do", false) for { if ddo.prev() == nil { break } ddo = ddo.prev() } if err := ddo.process(nil, dd.value); err != nil { panic(fmt.Sprintf("directive operation %T cannot process %T - %s", ddo, dd.value, err)) } if dd.ops == nil { dd.ops = make([]DirectiveOp, 0, 5) } dd.ops = append(dd.ops, ddo) return dd } func (dd *valueDirective) Ref(ref *interface{}) ValueDirective { if *ref != nil { panic("ref already assigned, not overwritting") } *ref = dd return dd } // errorDirective a decode directive that reports an error type errorDirective struct { Directive } func (dd *errorDirective) Execute(buffer *bytes.Buffer, dc *DecodeContext) error { return fmt.Errorf("Error Directive") } // CaseValueDirective is a decode directive that also has a switch/case test type CaseValueDirective interface { Directive Equals(interface{}) bool } type caseValueDirective struct { caseValue interface{} isDefault bool equalsDd Directive } func (dd *caseValueDirective) Execute(buffer *bytes.Buffer, dc *DecodeContext) error { if dd.equalsDd == nil { return nil } return dd.equalsDd.Execute(buffer, dc) } func (dd *caseValueDirective) Equals(value interface{}) bool { if dd.isDefault { return true } switch ourV := dd.caseValue.(type) { case uint32: ov, ok := value.(*uint32) if ok { return ourV == *ov } case uint16: ov, ok := value.(*uint16) if ok { return ourV == *ov } case byte: ov, ok := value.([]byte) if ok { if len(ov) == 1 { return ourV == ov[0] } } } return false } // sequenceDirective is a decode directive that is a simple sequentially executed list of other decode directives type sequenceDirective struct { decoders []Directive } func (di *sequenceDirective) Execute(buffer *bytes.Buffer, dc *DecodeContext) error { for _, innerDD := range di.decoders { if err := innerDD.Execute(buffer, dc); err != nil { return err } } return nil } // openMetric a decode directive that opens the recording of new fields and tags type openMetric struct { name string } func (di *openMetric) Execute(buffer *bytes.Buffer, dc *DecodeContext) error { dc.openMetric(di.name) return nil } // closeMetric a decode directive that closes the current open metric type closeMetric struct { } func (di *closeMetric) Execute(buffer *bytes.Buffer, dc *DecodeContext) error { dc.closeMetric() return nil } // DecodeContext provides context for the decoding of a packet and primarily acts // as a repository for metrics that are collected during the packet decode process type DecodeContext struct { metrics []telegraf.Metric timeHasBeenSet bool // oreMetric is used to capture tags or fields that may be recored before a metric has been openned // these fields and tags are then copied into metrics that are then subsequently opened preMetric telegraf.Metric current telegraf.Metric nano int } func (dc *DecodeContext) openMetric(name string) { t := dc.preMetric.Time() if !dc.timeHasBeenSet { t = time.Now().Add(time.Duration(dc.nano)) } m, _ := metric.New(name, make(map[string]string), make(map[string]interface{}), t) dc.nano++ // make sure to copy any fields and tags that were capture prior to the metric being openned for t, v := range dc.preMetric.Tags() { m.AddTag(t, v) } for f, v := range dc.preMetric.Fields() { m.AddField(f, v) } dc.current = m } func (dc *DecodeContext) closeMetric() { if dc.current != nil { dc.metrics = append(dc.metrics, dc.current) } dc.current = nil } func (dc *DecodeContext) currentMetric() telegraf.Metric { if dc.current == nil { return dc.preMetric } return dc.current } // Decode initiates the decoding of the supplied buffer according to the root decode directive that is provided func (dc *DecodeContext) Decode(dd Directive, buffer *bytes.Buffer) error { return dd.Execute(buffer, dc) } // GetMetrics answers the metrics that have been collected during the packet decode func (dc *DecodeContext) GetMetrics() []telegraf.Metric { return dc.metrics } type notifyDirective struct { fn func() } func (nd *notifyDirective) Execute(_ *bytes.Buffer, dc *DecodeContext) error { if dc != nil { nd.fn() } return nil }