gosnmp
/
helper.go
876 строк · 24.0 Кб
1// Copyright 2012 The GoSNMP Authors. All rights reserved. Use of this
2// source code is governed by a BSD-style license that can be found in the
3// LICENSE file.
4
5// Copyright 2009 The Go Authors. All rights reserved.
6// Use of this source code is governed by a BSD-style
7// license that can be found in the LICENSE file.
8
9package gosnmp
10
11import (
12"bytes"
13"encoding/binary"
14"errors"
15"fmt"
16"io"
17"log"
18"math"
19"net"
20"os"
21"strconv"
22)
23
24// variable struct is used by decodeValue()
25type variable struct {
26Value interface{}
27Type Asn1BER
28}
29
30// helper error modes
31var (
32ErrBase128IntegerTooLarge = errors.New("base 128 integer too large")
33ErrBase128IntegerTruncated = errors.New("base 128 integer truncated")
34ErrFloatBufferTooShort = errors.New("float buffer too short")
35ErrFloatTooLarge = errors.New("float too large")
36ErrIntegerTooLarge = errors.New("integer too large")
37ErrInvalidOidLength = errors.New("invalid OID length")
38ErrInvalidPacketLength = errors.New("invalid packet length")
39ErrZeroByteBuffer = errors.New("zero byte buffer")
40ErrZeroLenInteger = errors.New("zero length integer")
41)
42
43// -- helper functions (mostly) in alphabetical order --------------------------
44
45// Check makes checking errors easy, so they actually get a minimal check
46func (x *GoSNMP) Check(err error) {
47if err != nil {
48x.Logger.Printf("Check: %v\n", err)
49os.Exit(1)
50}
51}
52
53// Check makes checking errors easy, so they actually get a minimal check
54func (packet *SnmpPacket) Check(err error) {
55if err != nil {
56packet.Logger.Printf("Check: %v\n", err)
57os.Exit(1)
58}
59}
60
61// Check makes checking errors easy, so they actually get a minimal check
62func Check(err error) {
63if err != nil {
64log.Fatalf("Check: %v\n", err)
65}
66}
67
68func (x *GoSNMP) decodeValue(data []byte, retVal *variable) error {
69if len(data) == 0 {
70return ErrZeroByteBuffer
71}
72
73switch Asn1BER(data[0]) {
74case Integer, Uinteger32:
75// 0x02. signed
76x.Logger.Printf("decodeValue: type is %s", Asn1BER(data[0]).String())
77length, cursor, err := parseLength(data)
78if err != nil {
79return err
80}
81// check for truncated packets
82if length > len(data) {
83return fmt.Errorf("bytes: % x err: truncated (data %d length %d)", data, len(data), length)
84}
85
86var ret int
87if ret, err = parseInt(data[cursor:length]); err != nil {
88x.Logger.Printf("%v:", err)
89return fmt.Errorf("bytes: % x err: %w", data, err)
90}
91retVal.Type = Asn1BER(data[0])
92switch Asn1BER(data[0]) {
93case Uinteger32:
94retVal.Value = uint32(ret)
95default:
96retVal.Value = ret
97}
98
99case OctetString:
100// 0x04
101x.Logger.Print("decodeValue: type is OctetString")
102length, cursor, err := parseLength(data)
103if err != nil {
104return err
105}
106// check for truncated packet and throw an error
107if length > len(data) {
108return fmt.Errorf("bytes: % x err: truncated (data %d length %d)", data, len(data), length)
109}
110
111retVal.Type = OctetString
112retVal.Value = data[cursor:length]
113case Null:
114// 0x05
115x.Logger.Print("decodeValue: type is Null")
116retVal.Type = Null
117retVal.Value = nil
118case ObjectIdentifier:
119// 0x06
120x.Logger.Print("decodeValue: type is ObjectIdentifier")
121rawOid, _, err := parseRawField(x.Logger, data, "OID")
122if err != nil {
123return fmt.Errorf("error parsing OID Value: %w", err)
124}
125oid, ok := rawOid.(string)
126if !ok {
127return fmt.Errorf("unable to type assert rawOid |%v| to string", rawOid)
128}
129retVal.Type = ObjectIdentifier
130retVal.Value = oid
131case IPAddress:
132// 0x40
133x.Logger.Print("decodeValue: type is IPAddress")
134retVal.Type = IPAddress
135if len(data) < 2 {
136return fmt.Errorf("not enough data for ipv4 address: %x", data)
137}
138
139switch data[1] {
140case 0: // real life, buggy devices returning bad data
141retVal.Value = nil
142return nil
143case 4: // IPv4
144if len(data) < 6 {
145return fmt.Errorf("not enough data for ipv4 address: %x", data)
146}
147retVal.Value = net.IPv4(data[2], data[3], data[4], data[5]).String()
148case 16: // IPv6
149if len(data) < 18 {
150return fmt.Errorf("not enough data for ipv6 address: %x", data)
151}
152d := make(net.IP, 16)
153copy(d, data[2:17])
154retVal.Value = d.String()
155default:
156return fmt.Errorf("got ipaddress len %d, expected 4 or 16", data[1])
157}
158case Counter32:
159// 0x41. unsigned
160x.Logger.Print("decodeValue: type is Counter32")
161length, cursor, err := parseLength(data)
162if err != nil {
163return err
164}
165if length > len(data) {
166return fmt.Errorf("not enough data for Counter32 %x (data %d length %d)", data, len(data), length)
167}
168
169ret, err := parseUint(data[cursor:length])
170if err != nil {
171x.Logger.Printf("decodeValue: err is %v", err)
172break
173}
174retVal.Type = Counter32
175retVal.Value = ret
176case Gauge32:
177// 0x42. unsigned
178x.Logger.Print("decodeValue: type is Gauge32")
179length, cursor, err := parseLength(data)
180if err != nil {
181return err
182}
183if length > len(data) {
184return fmt.Errorf("not enough data for Gauge32 %x (data %d length %d)", data, len(data), length)
185}
186
187ret, err := parseUint(data[cursor:length])
188if err != nil {
189x.Logger.Printf("decodeValue: err is %v", err)
190break
191}
192retVal.Type = Gauge32
193retVal.Value = ret
194case TimeTicks:
195// 0x43
196x.Logger.Print("decodeValue: type is TimeTicks")
197length, cursor, err := parseLength(data)
198if err != nil {
199return err
200}
201if length > len(data) {
202return fmt.Errorf("not enough data for TimeTicks %x (data %d length %d)", data, len(data), length)
203}
204
205ret, err := parseUint32(data[cursor:length])
206if err != nil {
207x.Logger.Printf("decodeValue: err is %v", err)
208break
209}
210retVal.Type = TimeTicks
211retVal.Value = ret
212case Opaque:
213// 0x44
214x.Logger.Print("decodeValue: type is Opaque")
215length, cursor, err := parseLength(data)
216if err != nil {
217return err
218}
219if length > len(data) {
220return fmt.Errorf("not enough data for Opaque %x (data %d length %d)", data, len(data), length)
221}
222return parseOpaque(x.Logger, data[cursor:length], retVal)
223case Counter64:
224// 0x46
225x.Logger.Print("decodeValue: type is Counter64")
226length, cursor, err := parseLength(data)
227if err != nil {
228return err
229}
230if length > len(data) {
231return fmt.Errorf("not enough data for Counter64 %x (data %d length %d)", data, len(data), length)
232}
233ret, err := parseUint64(data[cursor:length])
234if err != nil {
235x.Logger.Printf("decodeValue: err is %v", err)
236break
237}
238retVal.Type = Counter64
239retVal.Value = ret
240case NoSuchObject:
241// 0x80
242x.Logger.Print("decodeValue: type is NoSuchObject")
243retVal.Type = NoSuchObject
244retVal.Value = nil
245case NoSuchInstance:
246// 0x81
247x.Logger.Print("decodeValue: type is NoSuchInstance")
248retVal.Type = NoSuchInstance
249retVal.Value = nil
250case EndOfMibView:
251// 0x82
252x.Logger.Print("decodeValue: type is EndOfMibView")
253retVal.Type = EndOfMibView
254retVal.Value = nil
255default:
256x.Logger.Printf("decodeValue: type %x isn't implemented", data[0])
257retVal.Type = UnknownType
258retVal.Value = nil
259}
260x.Logger.Printf("decodeValue: value is %#v", retVal.Value)
261return nil
262}
263
264func marshalBase128Int(out io.ByteWriter, n int64) (err error) {
265if n == 0 {
266err = out.WriteByte(0)
267return
268}
269
270l := 0
271for i := n; i > 0; i >>= 7 {
272l++
273}
274
275for i := l - 1; i >= 0; i-- {
276o := byte(n >> uint(i*7))
277o &= 0x7f
278if i != 0 {
279o |= 0x80
280}
281err = out.WriteByte(o)
282if err != nil {
283return
284}
285}
286
287return nil
288}
289
290/*
291snmp Integer32 and INTEGER:
292-2^31 and 2^31-1 inclusive (-2147483648 to 2147483647 decimal)
293(FYI https://groups.google.com/forum/#!topic/comp.protocols.snmp/1xaAMzCe_hE)
294
295versus:
296
297snmp Counter32, Gauge32, TimeTicks, Unsigned32: (below)
298non-negative integer, maximum value of 2^32-1 (4294967295 decimal)
299*/
300
301// marshalInt32 builds a byte representation of a signed 32 bit int in BigEndian form
302// ie -2^31 and 2^31-1 inclusive (-2147483648 to 2147483647 decimal)
303func marshalInt32(value int) ([]byte, error) {
304if value < math.MinInt32 || value > math.MaxInt32 {
305return nil, fmt.Errorf("unable to marshal: %d overflows int32", value)
306}
307const mask1 uint32 = 0xFFFFFF80
308const mask2 uint32 = 0xFFFF8000
309const mask3 uint32 = 0xFF800000
310const mask4 uint32 = 0x80000000
311// ITU-T Rec. X.690 (2002) 8.3.2
312// If the contents octets of an integer value encoding consist of more than
313// one octet, then the bits of the first octet and bit 8 of the second octet:
314// a) shall not all be ones; and
315// b) shall not all be zero
316// These rules ensure that an integer value is always encoded in the smallest
317// possible number of octets.
318val := uint32(value)
319switch {
320case val&mask1 == 0 || val&mask1 == mask1:
321return []byte{byte(val)}, nil
322case val&mask2 == 0 || val&mask2 == mask2:
323return []byte{byte(val >> 8), byte(val)}, nil
324case val&mask3 == 0 || val&mask3 == mask3:
325return []byte{byte(val >> 16), byte(val >> 8), byte(val)}, nil
326default:
327return []byte{byte(val >> 24), byte(val >> 16), byte(val >> 8), byte(val)}, nil
328}
329}
330
331func marshalUint64(v interface{}) []byte {
332bs := make([]byte, 8)
333source := v.(uint64)
334binary.BigEndian.PutUint64(bs, source) // will panic on failure
335// truncate leading zeros. Cleaner technique?
336return bytes.TrimLeft(bs, "\x00")
337}
338
339// Counter32, Gauge32, TimeTicks, Unsigned32, SNMPError
340func marshalUint32(v interface{}) ([]byte, error) {
341var source uint32
342switch val := v.(type) {
343case uint32:
344source = val
345case uint:
346source = uint32(val)
347case uint8:
348source = uint32(val)
349case SNMPError:
350source = uint32(val)
351// We could do others here, but coercing from anything else is dangerous.
352// Even uint could be 64 bits, though in practice nothing we work with is.
353default:
354return nil, fmt.Errorf("unable to marshal %T to uint32", v)
355}
356buf := make([]byte, 4)
357binary.BigEndian.PutUint32(buf, source)
358var i int
359for i = 0; i < 3; i++ {
360if buf[i] != 0 {
361break
362}
363}
364buf = buf[i:]
365// if the highest bit in buf is set and x is not negative - prepend a byte to make it positive
366if len(buf) > 0 && buf[0]&0x80 > 0 {
367buf = append([]byte{0}, buf...)
368}
369return buf, nil
370}
371
372func marshalFloat32(v interface{}) ([]byte, error) {
373source := v.(float32)
374out := bytes.NewBuffer(nil)
375err := binary.Write(out, binary.BigEndian, source)
376return out.Bytes(), err
377}
378
379func marshalFloat64(v interface{}) ([]byte, error) {
380source := v.(float64)
381out := bytes.NewBuffer(nil)
382err := binary.Write(out, binary.BigEndian, source)
383return out.Bytes(), err
384}
385
386// marshalLength builds a byte representation of length
387//
388// http://luca.ntop.org/Teaching/Appunti/asn1.html
389//
390// Length octets. There are two forms: short (for lengths between 0 and 127),
391// and long definite (for lengths between 0 and 2^1008 -1).
392//
393// - Short form. One octet. Bit 8 has value "0" and bits 7-1 give the length.
394// - Long form. Two to 127 octets. Bit 8 of first octet has value "1" and bits
395// 7-1 give the number of additional length octets. Second and following
396// octets give the length, base 256, most significant digit first.
397func marshalLength(length int) ([]byte, error) {
398// more convenient to pass length as int than uint64. Therefore check < 0
399if length < 0 {
400return nil, fmt.Errorf("length must be greater than zero")
401} else if length < 127 {
402return []byte{byte(length)}, nil
403}
404
405buf := new(bytes.Buffer)
406err := binary.Write(buf, binary.BigEndian, uint64(length))
407if err != nil {
408return nil, err
409}
410bufBytes := buf.Bytes()
411
412// strip leading zeros
413for idx, octect := range bufBytes {
414if octect != 00 {
415bufBytes = bufBytes[idx:]
416break
417}
418}
419
420header := []byte{byte(128 | len(bufBytes))}
421return append(header, bufBytes...), nil
422}
423
424func marshalObjectIdentifier(oid string) ([]byte, error) {
425out := new(bytes.Buffer)
426oidLength := len(oid)
427oidBase := 0
428var err error
429i := 0
430for j := 0; j < oidLength; {
431if oid[j] == '.' {
432j++
433continue
434}
435var val int64
436for j < oidLength && oid[j] != '.' {
437ch := int64(oid[j] - '0')
438if ch > 9 {
439return []byte{}, fmt.Errorf("unable to marshal OID: Invalid object identifier")
440}
441val *= 10
442val += ch
443j++
444}
445switch i {
446case 0:
447if val > 6 {
448return []byte{}, fmt.Errorf("unable to marshal OID: Invalid object identifier")
449}
450oidBase = int(val * 40)
451case 1:
452if val >= 40 {
453return []byte{}, fmt.Errorf("unable to marshal OID: Invalid object identifier")
454}
455oidBase += int(val)
456err = out.WriteByte(byte(oidBase))
457if err != nil {
458return []byte{}, fmt.Errorf("unable to marshal OID: Invalid object identifier")
459}
460
461default:
462if val > MaxObjectSubIdentifierValue {
463return []byte{}, fmt.Errorf("unable to marshal OID: Value out of range")
464}
465err = marshalBase128Int(out, val)
466if err != nil {
467return []byte{}, fmt.Errorf("unable to marshal OID: Invalid object identifier")
468}
469}
470i++
471}
472if i < 2 || i > 128 {
473return []byte{}, fmt.Errorf("unable to marshal OID: Invalid object identifier")
474}
475
476return out.Bytes(), nil
477}
478
479// TODO no tests
480func ipv4toBytes(ip net.IP) []byte {
481return []byte(ip)[12:]
482}
483
484// parseOpaque parses a Opaque encoded data
485// Known data-types is OpaqueDouble and OpaqueFloat
486// Other data decoded as binary Opaque data
487// TODO: add OpaqueCounter64 (0x76), OpaqueInteger64 (0x80), OpaqueUinteger64 (0x81)
488func parseOpaque(logger Logger, data []byte, retVal *variable) error {
489if len(data) == 0 {
490return ErrZeroByteBuffer
491}
492if len(data) > 2 && data[0] == AsnExtensionTag {
493switch Asn1BER(data[1]) {
494case OpaqueDouble:
495// 0x79
496data = data[1:]
497logger.Print("decodeValue: type is OpaqueDouble")
498length, cursor, err := parseLength(data)
499if err != nil {
500return err
501}
502if length > len(data) {
503return fmt.Errorf("not enough data for OpaqueDouble %x (data %d length %d)", data, len(data), length)
504}
505retVal.Type = OpaqueDouble
506retVal.Value, err = parseFloat64(data[cursor:length])
507if err != nil {
508return err
509}
510case OpaqueFloat:
511// 0x78
512data = data[1:]
513logger.Print("decodeValue: type is OpaqueFloat")
514length, cursor, err := parseLength(data)
515if err != nil {
516return err
517}
518if length > len(data) {
519return fmt.Errorf("not enough data for OpaqueFloat %x (data %d length %d)", data, len(data), length)
520}
521if cursor > length {
522return fmt.Errorf("invalid cursor position for OpaqueFloat %x (data %d length %d cursor %d)", data, len(data), length, cursor)
523}
524retVal.Type = OpaqueFloat
525retVal.Value, err = parseFloat32(data[cursor:length])
526if err != nil {
527return err
528}
529default:
530logger.Print("decodeValue: type is Opaque")
531retVal.Type = Opaque
532retVal.Value = data[0:]
533}
534} else {
535logger.Print("decodeValue: type is Opaque")
536retVal.Type = Opaque
537retVal.Value = data[0:]
538}
539return nil
540}
541
542// parseBase128Int parses a base-128 encoded int from the given offset in the
543// given byte slice. It returns the value and the new offset.
544func parseBase128Int(bytes []byte, initOffset int) (int64, int, error) {
545var ret int64
546var offset = initOffset
547for shifted := 0; offset < len(bytes); shifted++ {
548if shifted > 4 {
549return 0, 0, ErrBase128IntegerTooLarge
550}
551ret <<= 7
552b := bytes[offset]
553ret |= int64(b & 0x7f)
554offset++
555if b&0x80 == 0 {
556return ret, offset, nil
557}
558}
559return 0, 0, ErrBase128IntegerTruncated
560}
561
562// parseInt64 treats the given bytes as a big-endian, signed integer and
563// returns the result.
564func parseInt64(bytes []byte) (int64, error) {
565switch {
566case len(bytes) == 0:
567// X.690 8.3.1: Encoding of an integer value:
568// The encoding of an integer value shall be primitive.
569// The contents octets shall consist of one or more octets.
570return 0, ErrZeroLenInteger
571case len(bytes) > 8:
572// We'll overflow an int64 in this case.
573return 0, ErrIntegerTooLarge
574}
575var ret int64
576for bytesRead := 0; bytesRead < len(bytes); bytesRead++ {
577ret <<= 8
578ret |= int64(bytes[bytesRead])
579}
580// Shift up and down in order to sign extend the result.
581ret <<= 64 - uint8(len(bytes))*8
582ret >>= 64 - uint8(len(bytes))*8
583return ret, nil
584}
585
586// parseInt treats the given bytes as a big-endian, signed integer and returns
587// the result.
588func parseInt(bytes []byte) (int, error) {
589ret64, err := parseInt64(bytes)
590if err != nil {
591return 0, err
592}
593if ret64 != int64(int(ret64)) {
594return 0, ErrIntegerTooLarge
595}
596return int(ret64), nil
597}
598
599// parseLength parses and calculates an snmp packet length
600// and returns an error when invalid data is detected
601//
602// http://luca.ntop.org/Teaching/Appunti/asn1.html
603//
604// Length octets. There are two forms: short (for lengths between 0 and 127),
605// and long definite (for lengths between 0 and 2^1008 -1).
606//
607// - Short form. One octet. Bit 8 has value "0" and bits 7-1 give the length.
608// - Long form. Two to 127 octets. Bit 8 of first octet has value "1" and bits
609// 7-1 give the number of additional length octets. Second and following
610// octets give the length, base 256, most significant digit first.
611func parseLength(bytes []byte) (int, int, error) {
612var cursor, length int
613switch {
614case len(bytes) <= 2:
615// handle null octet strings ie "0x04 0x00"
616cursor = len(bytes)
617length = len(bytes)
618case int(bytes[1]) <= 127:
619length = int(bytes[1])
620length += 2
621cursor += 2
622default:
623numOctets := int(bytes[1]) & 127
624for i := 0; i < numOctets; i++ {
625length <<= 8
626if len(bytes) < 2+i+1 {
627// Invalid data detected, return an error
628return 0, 0, ErrInvalidPacketLength
629}
630length += int(bytes[2+i])
631if length < 0 {
632// Invalid length due to overflow, return an error
633return 0, 0, ErrInvalidPacketLength
634}
635}
636length += 2 + numOctets
637cursor += 2 + numOctets
638}
639if length < 0 {
640// Invalid data detected, return an error
641return 0, 0, ErrInvalidPacketLength
642}
643return length, cursor, nil
644}
645
646// parseObjectIdentifier parses an OBJECT IDENTIFIER from the given bytes and
647// returns it. An object identifier is a sequence of variable length integers
648// that are assigned in a hierarchy.
649func parseObjectIdentifier(src []byte) (string, error) {
650if len(src) == 0 {
651return "", ErrInvalidOidLength
652}
653
654out := new(bytes.Buffer)
655
656out.WriteByte('.')
657out.WriteString(strconv.FormatInt(int64(int(src[0])/40), 10))
658out.WriteByte('.')
659out.WriteString(strconv.FormatInt(int64(int(src[0])%40), 10))
660
661var v int64
662var err error
663for offset := 1; offset < len(src); {
664out.WriteByte('.')
665v, offset, err = parseBase128Int(src, offset)
666if err != nil {
667return "", err
668}
669out.WriteString(strconv.FormatInt(v, 10))
670}
671return out.String(), nil
672}
673
674func parseRawField(logger Logger, data []byte, msg string) (interface{}, int, error) {
675if len(data) == 0 {
676return nil, 0, fmt.Errorf("empty data passed to parseRawField")
677}
678logger.Printf("parseRawField: %s", msg)
679switch Asn1BER(data[0]) {
680case Integer:
681length, cursor, err := parseLength(data)
682if err != nil {
683return nil, 0, err
684}
685if length > len(data) {
686return nil, 0, fmt.Errorf("not enough data for Integer (%d vs %d): %x", length, len(data), data)
687}
688if cursor > length {
689return nil, 0, fmt.Errorf("invalid cursor position for Integer %x (data %d length %d cursor %d)", data, len(data), length, cursor)
690}
691i, err := parseInt(data[cursor:length])
692if err != nil {
693return nil, 0, fmt.Errorf("unable to parse raw INTEGER: %x err: %w", data, err)
694}
695return i, length, nil
696case OctetString:
697length, cursor, err := parseLength(data)
698if err != nil {
699return nil, 0, err
700}
701if length > len(data) {
702return nil, 0, fmt.Errorf("not enough data for OctetString (%d vs %d): %x", length, len(data), data)
703}
704if cursor > length {
705return nil, 0, fmt.Errorf("invalid cursor position for OctetString %x (data %d length %d cursor %d)", data, len(data), length, cursor)
706}
707return string(data[cursor:length]), length, nil
708case ObjectIdentifier:
709length, cursor, err := parseLength(data)
710if err != nil {
711return nil, 0, err
712}
713if length > len(data) {
714return nil, 0, fmt.Errorf("not enough data for OID (%d vs %d): %x", length, len(data), data)
715}
716if cursor > length {
717return nil, 0, fmt.Errorf("invalid cursor position for OID %x (data %d length %d cursor %d)", data, len(data), length, cursor)
718}
719oid, err := parseObjectIdentifier(data[cursor:length])
720return oid, length, err
721case IPAddress:
722length, _, err := parseLength(data)
723if err != nil {
724return nil, 0, err
725}
726if len(data) < 2 {
727return nil, 0, fmt.Errorf("not enough data for ipv4 address: %x", data)
728}
729
730switch data[1] {
731case 0: // real life, buggy devices returning bad data
732return nil, length, nil
733case 4: // IPv4
734if len(data) < 6 {
735return nil, 0, fmt.Errorf("not enough data for ipv4 address: %x", data)
736}
737return net.IPv4(data[2], data[3], data[4], data[5]).String(), length, nil
738default:
739return nil, 0, fmt.Errorf("got ipaddress len %d, expected 4", data[1])
740}
741case TimeTicks:
742length, cursor, err := parseLength(data)
743if err != nil {
744return nil, 0, err
745}
746if length > len(data) {
747return nil, 0, fmt.Errorf("not enough data for TimeTicks (%d vs %d): %x", length, len(data), data)
748}
749if cursor > length {
750return nil, 0, fmt.Errorf("invalid cursor position for TimeTicks %x (data %d length %d cursor %d)", data, len(data), length, cursor)
751}
752ret, err := parseUint(data[cursor:length])
753if err != nil {
754return nil, 0, fmt.Errorf("error in parseUint: %w", err)
755}
756return ret, length, nil
757}
758
759return nil, 0, fmt.Errorf("unknown field type: %x", data[0])
760}
761
762// parseUint64 treats the given bytes as a big-endian, unsigned integer and returns
763// the result.
764func parseUint64(bytes []byte) (uint64, error) {
765var ret uint64
766if len(bytes) > 9 || (len(bytes) > 8 && bytes[0] != 0x0) {
767// We'll overflow a uint64 in this case.
768return 0, ErrIntegerTooLarge
769}
770for bytesRead := 0; bytesRead < len(bytes); bytesRead++ {
771ret <<= 8
772ret |= uint64(bytes[bytesRead])
773}
774return ret, nil
775}
776
777// parseUint32 treats the given bytes as a big-endian, signed integer and returns
778// the result.
779func parseUint32(bytes []byte) (uint32, error) {
780ret, err := parseUint(bytes)
781if err != nil {
782return 0, err
783}
784return uint32(ret), nil
785}
786
787// parseUint treats the given bytes as a big-endian, signed integer and returns
788// the result.
789func parseUint(bytes []byte) (uint, error) {
790ret64, err := parseUint64(bytes)
791if err != nil {
792return 0, err
793}
794if ret64 != uint64(uint(ret64)) {
795return 0, ErrIntegerTooLarge
796}
797return uint(ret64), nil
798}
799
800func parseFloat32(bytes []byte) (float32, error) {
801if len(bytes) > 4 {
802// We'll overflow a uint64 in this case.
803return 0, ErrFloatTooLarge
804}
805if len(bytes) < 4 {
806// We'll cause a panic in binary.BigEndian.Uint32() in this case
807return 0, ErrFloatBufferTooShort
808}
809return math.Float32frombits(binary.BigEndian.Uint32(bytes)), nil
810}
811
812func parseFloat64(bytes []byte) (float64, error) {
813if len(bytes) > 8 {
814// We'll overflow a uint64 in this case.
815return 0, ErrFloatTooLarge
816}
817if len(bytes) < 8 {
818// We'll cause a panic in binary.BigEndian.Uint64() in this case
819return 0, ErrFloatBufferTooShort
820}
821return math.Float64frombits(binary.BigEndian.Uint64(bytes)), nil
822}
823
824// -- Bit String ---------------------------------------------------------------
825
826// BitStringValue is the structure to use when you want an ASN.1 BIT STRING type. A
827// bit string is padded up to the nearest byte in memory and the number of
828// valid bits is recorded. Padding bits will be zero.
829type BitStringValue struct {
830Bytes []byte // bits packed into bytes.
831BitLength int // length in bits.
832}
833
834// At returns the bit at the given index. If the index is out of range it
835// returns false.
836func (b BitStringValue) At(i int) int {
837if i < 0 || i >= b.BitLength {
838return 0
839}
840x := i / 8
841y := 7 - uint(i%8)
842return int(b.Bytes[x]>>y) & 1
843}
844
845// RightAlign returns a slice where the padding bits are at the beginning. The
846// slice may share memory with the BitString.
847func (b BitStringValue) RightAlign() []byte {
848shift := uint(8 - (b.BitLength % 8))
849if shift == 8 || len(b.Bytes) == 0 {
850return b.Bytes
851}
852
853a := make([]byte, len(b.Bytes))
854a[0] = b.Bytes[0] >> shift
855for i := 1; i < len(b.Bytes); i++ {
856a[i] = b.Bytes[i-1] << (8 - shift)
857a[i] |= b.Bytes[i] >> shift
858}
859
860return a
861}
862
863// -- SnmpVersion --------------------------------------------------------------
864
865func (s SnmpVersion) String() string {
866switch s {
867case Version1:
868return "1"
869case Version2c:
870return "2c"
871case Version3:
872return "3"
873default:
874return "3"
875}
876}
877