186 lines
4.8 KiB
Go
186 lines
4.8 KiB
Go
// Copyright 2018 The agentx authors
|
|
// Licensed under the LGPLv3 with static-linking exception.
|
|
// See LICENCE file for details.
|
|
|
|
package pdu
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/posteo/go-agentx/value"
|
|
)
|
|
|
|
// Variable defines the pdu varbind packet.
|
|
type Variable struct {
|
|
Type VariableType
|
|
Name ObjectIdentifier
|
|
Value interface{}
|
|
}
|
|
|
|
// Set sets the variable.
|
|
func (v *Variable) Set(oid value.OID, t VariableType, value interface{}) {
|
|
v.Name.SetIdentifier(oid)
|
|
v.Type = t
|
|
v.Value = value
|
|
}
|
|
|
|
// ByteSize returns the number of bytes, the binding would need in the encoded version.
|
|
func (v *Variable) ByteSize() int {
|
|
bytes, err := v.MarshalBinary()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return len(bytes)
|
|
}
|
|
|
|
// MarshalBinary returns the pdu packet as a slice of bytes.
|
|
func (v *Variable) MarshalBinary() ([]byte, error) {
|
|
buffer := &bytes.Buffer{}
|
|
|
|
binary.Write(buffer, binary.LittleEndian, &v.Type)
|
|
buffer.WriteByte(0x00)
|
|
buffer.WriteByte(0x00)
|
|
|
|
nameBytes, err := v.Name.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buffer.Write(nameBytes)
|
|
|
|
switch v.Type {
|
|
case VariableTypeInteger:
|
|
value := v.Value.(int32)
|
|
binary.Write(buffer, binary.LittleEndian, &value)
|
|
case VariableTypeOctetString:
|
|
octetString := &OctetString{Text: v.Value.(string)}
|
|
octetStringBytes, err := octetString.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buffer.Write(octetStringBytes)
|
|
case VariableTypeNull, VariableTypeNoSuchObject, VariableTypeNoSuchInstance, VariableTypeEndOfMIBView:
|
|
break
|
|
case VariableTypeObjectIdentifier:
|
|
targetOID, err := value.ParseOID(v.Value.(string))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
oi := &ObjectIdentifier{}
|
|
oi.SetIdentifier(targetOID)
|
|
oiBytes, err := oi.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buffer.Write(oiBytes)
|
|
case VariableTypeIPAddress:
|
|
ip := v.Value.(net.IP)
|
|
octetString := &OctetString{Text: string(ip)}
|
|
octetStringBytes, err := octetString.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buffer.Write(octetStringBytes)
|
|
case VariableTypeCounter32, VariableTypeGauge32:
|
|
value := v.Value.(uint32)
|
|
binary.Write(buffer, binary.LittleEndian, &value)
|
|
case VariableTypeTimeTicks:
|
|
value := uint32(v.Value.(time.Duration).Seconds() * 100)
|
|
binary.Write(buffer, binary.LittleEndian, &value)
|
|
case VariableTypeOpaque:
|
|
octetString := &OctetString{Text: string(v.Value.([]byte))}
|
|
octetStringBytes, err := octetString.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buffer.Write(octetStringBytes)
|
|
case VariableTypeCounter64:
|
|
value := v.Value.(uint64)
|
|
binary.Write(buffer, binary.LittleEndian, &value)
|
|
default:
|
|
return nil, fmt.Errorf("unhandled variable type %s", v.Type)
|
|
}
|
|
|
|
return buffer.Bytes(), nil
|
|
}
|
|
|
|
// UnmarshalBinary sets the packet structure from the provided slice of bytes.
|
|
func (v *Variable) UnmarshalBinary(data []byte) error {
|
|
buffer := bytes.NewBuffer(data)
|
|
|
|
if err := binary.Read(buffer, binary.LittleEndian, &v.Type); err != nil {
|
|
return err
|
|
}
|
|
offset := 4
|
|
|
|
if err := v.Name.UnmarshalBinary(data[offset:]); err != nil {
|
|
return err
|
|
}
|
|
offset += v.Name.ByteSize()
|
|
|
|
switch v.Type {
|
|
case VariableTypeInteger:
|
|
value := int32(0)
|
|
if err := binary.Read(buffer, binary.LittleEndian, &value); err != nil {
|
|
return err
|
|
}
|
|
v.Value = value
|
|
case VariableTypeOctetString:
|
|
octetString := &OctetString{}
|
|
if err := octetString.UnmarshalBinary(data[offset:]); err != nil {
|
|
return err
|
|
}
|
|
v.Value = octetString.Text
|
|
case VariableTypeNull, VariableTypeNoSuchObject, VariableTypeNoSuchInstance, VariableTypeEndOfMIBView:
|
|
v.Value = nil
|
|
case VariableTypeObjectIdentifier:
|
|
oid := &ObjectIdentifier{}
|
|
if err := oid.UnmarshalBinary(data[offset:]); err != nil {
|
|
return err
|
|
}
|
|
v.Value = oid.GetIdentifier()
|
|
case VariableTypeIPAddress:
|
|
octetString := &OctetString{}
|
|
if err := octetString.UnmarshalBinary(data[offset:]); err != nil {
|
|
return err
|
|
}
|
|
v.Value = net.IP(octetString.Text)
|
|
case VariableTypeCounter32, VariableTypeGauge32:
|
|
value := uint32(0)
|
|
if err := binary.Read(buffer, binary.LittleEndian, &value); err != nil {
|
|
return err
|
|
}
|
|
v.Value = value
|
|
case VariableTypeTimeTicks:
|
|
value := uint32(0)
|
|
if err := binary.Read(buffer, binary.LittleEndian, &value); err != nil {
|
|
return err
|
|
}
|
|
v.Value = time.Duration(value) * time.Second / 100
|
|
case VariableTypeOpaque:
|
|
octetString := &OctetString{}
|
|
if err := octetString.UnmarshalBinary(data[offset:]); err != nil {
|
|
return err
|
|
}
|
|
v.Value = []byte(octetString.Text)
|
|
case VariableTypeCounter64:
|
|
value := uint64(0)
|
|
if err := binary.Read(buffer, binary.LittleEndian, &value); err != nil {
|
|
return err
|
|
}
|
|
v.Value = value
|
|
default:
|
|
return fmt.Errorf("unhandled variable type %s", v.Type)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (v *Variable) String() string {
|
|
return fmt.Sprintf("(variable %s = %v)", v.Type, v.Value)
|
|
}
|