gosnmp
/
marshal.go
1390 строк · 39.7 Кб
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
5package gosnmp
6
7import (
8"bytes"
9"context"
10"encoding/asn1"
11"encoding/binary"
12"errors"
13"fmt"
14"io"
15"net"
16"runtime"
17"strings"
18"sync/atomic"
19"time"
20)
21
22//
23// Remaining globals and definitions located here.
24// See http://www.rane.com/note161.html for a succint description of the SNMP
25// protocol.
26//
27
28// SnmpVersion 1, 2c and 3 implemented
29type SnmpVersion uint8
30
31// SnmpVersion 1, 2c and 3 implemented
32const (
33Version1 SnmpVersion = 0x0
34Version2c SnmpVersion = 0x1
35Version3 SnmpVersion = 0x3
36)
37
38// SnmpPacket struct represents the entire SNMP Message or Sequence at the
39// application layer.
40type SnmpPacket struct {
41Version SnmpVersion
42MsgFlags SnmpV3MsgFlags
43SecurityModel SnmpV3SecurityModel
44SecurityParameters SnmpV3SecurityParameters // interface
45ContextEngineID string
46ContextName string
47Community string
48PDUType PDUType
49MsgID uint32
50RequestID uint32
51MsgMaxSize uint32
52Error SNMPError
53ErrorIndex uint8
54NonRepeaters uint8
55MaxRepetitions uint32
56Variables []SnmpPDU
57Logger Logger
58
59// v1 traps have a very different format from v2c and v3 traps.
60//
61// These fields are set via the SnmpTrap parameter to SendTrap().
62SnmpTrap
63}
64
65// SnmpTrap is used to define a SNMP trap, and is passed into SendTrap
66type SnmpTrap struct {
67Variables []SnmpPDU
68
69// If true, the trap is an InformRequest, not a trap. This has no effect on
70// v1 traps, as Inform is not part of the v1 protocol.
71IsInform bool
72
73// These fields are required for SNMPV1 Trap Headers
74Enterprise string
75AgentAddress string
76GenericTrap int
77SpecificTrap int
78Timestamp uint
79}
80
81// VarBind struct represents an SNMP Varbind.
82type VarBind struct {
83Name asn1.ObjectIdentifier
84Value asn1.RawValue
85}
86
87// PDUType describes which SNMP Protocol Data Unit is being sent.
88type PDUType byte
89
90// The currently supported PDUType's
91const (
92Sequence PDUType = 0x30
93GetRequest PDUType = 0xa0
94GetNextRequest PDUType = 0xa1
95GetResponse PDUType = 0xa2
96SetRequest PDUType = 0xa3
97Trap PDUType = 0xa4 // v1
98GetBulkRequest PDUType = 0xa5
99InformRequest PDUType = 0xa6
100SNMPv2Trap PDUType = 0xa7 // v2c, v3
101Report PDUType = 0xa8 // v3
102)
103
104//go:generate stringer -type=PDUType
105
106// SNMPv3: User-based Security Model Report PDUs and
107// error types as per https://tools.ietf.org/html/rfc3414
108const (
109usmStatsUnsupportedSecLevels = ".1.3.6.1.6.3.15.1.1.1.0"
110usmStatsNotInTimeWindows = ".1.3.6.1.6.3.15.1.1.2.0"
111usmStatsUnknownUserNames = ".1.3.6.1.6.3.15.1.1.3.0"
112usmStatsUnknownEngineIDs = ".1.3.6.1.6.3.15.1.1.4.0"
113usmStatsWrongDigests = ".1.3.6.1.6.3.15.1.1.5.0"
114usmStatsDecryptionErrors = ".1.3.6.1.6.3.15.1.1.6.0"
115snmpUnknownSecurityModels = ".1.3.6.1.6.3.11.2.1.1.0"
116snmpInvalidMsgs = ".1.3.6.1.6.3.11.2.1.2.0"
117snmpUnknownPDUHandlers = ".1.3.6.1.6.3.11.2.1.3.0"
118)
119
120var (
121ErrDecryption = errors.New("decryption error")
122ErrInvalidMsgs = errors.New("invalid messages")
123ErrNotInTimeWindow = errors.New("not in time window")
124ErrUnknownEngineID = errors.New("unknown engine id")
125ErrUnknownPDUHandlers = errors.New("unknown pdu handlers")
126ErrUnknownReportPDU = errors.New("unknown report pdu")
127ErrUnknownSecurityLevel = errors.New("unknown security level")
128ErrUnknownSecurityModels = errors.New("unknown security models")
129ErrUnknownUsername = errors.New("unknown username")
130ErrWrongDigest = errors.New("wrong digest")
131)
132
133const rxBufSize = 65535 // max size of IPv4 & IPv6 packet
134
135// Logger is an interface used for debugging. Both Print and
136// Printf have the same interfaces as Package Log in the std library. The
137// Logger interface is small to give you flexibility in how you do
138// your debugging.
139//
140
141// Logger
142// For verbose logging to stdout:
143// gosnmp_logger = NewLogger(log.New(os.Stdout, "", 0))
144type LoggerInterface interface {
145Print(v ...interface{})
146Printf(format string, v ...interface{})
147}
148
149type Logger struct {
150logger LoggerInterface
151}
152
153func NewLogger(logger LoggerInterface) Logger {
154return Logger{
155logger: logger,
156}
157}
158
159func (packet *SnmpPacket) SafeString() string {
160sp := ""
161if packet.SecurityParameters != nil {
162sp = packet.SecurityParameters.SafeString()
163}
164return fmt.Sprintf("Version:%s, MsgFlags:%s, SecurityModel:%s, SecurityParameters:%s, ContextEngineID:%s, ContextName:%s, Community:%s, PDUType:%s, MsgID:%d, RequestID:%d, MsgMaxSize:%d, Error:%s, ErrorIndex:%d, NonRepeaters:%d, MaxRepetitions:%d, Variables:%v",
165packet.Version,
166packet.MsgFlags,
167packet.SecurityModel,
168sp,
169packet.ContextEngineID,
170packet.ContextName,
171packet.Community,
172packet.PDUType,
173packet.MsgID,
174packet.RequestID,
175packet.MsgMaxSize,
176packet.Error,
177packet.ErrorIndex,
178packet.NonRepeaters,
179packet.MaxRepetitions,
180packet.Variables,
181)
182}
183
184// GoSNMP
185// send/receive one snmp request
186func (x *GoSNMP) sendOneRequest(packetOut *SnmpPacket,
187wait bool) (result *SnmpPacket, err error) {
188allReqIDs := make([]uint32, 0, x.Retries+1)
189// allMsgIDs := make([]uint32, 0, x.Retries+1) // unused
190
191timeout := x.Timeout
192withContextDeadline := false
193for retries := 0; ; retries++ {
194if retries > 0 {
195if x.OnRetry != nil {
196x.OnRetry(x)
197}
198
199x.Logger.Printf("Retry number %d. Last error was: %v", retries, err)
200if withContextDeadline && strings.Contains(err.Error(), "timeout") {
201err = context.DeadlineExceeded
202break
203}
204if retries > x.Retries {
205if strings.Contains(err.Error(), "timeout") {
206err = fmt.Errorf("request timeout (after %d retries)", retries-1)
207}
208break
209}
210if x.ExponentialTimeout {
211// https://www.webnms.com/snmp/help/snmpapi/snmpv3/v1/timeout.html
212timeout *= 2
213}
214withContextDeadline = false
215}
216err = nil
217
218if x.Context.Err() != nil {
219return nil, x.Context.Err()
220}
221
222reqDeadline := time.Now().Add(timeout)
223if contextDeadline, ok := x.Context.Deadline(); ok {
224if contextDeadline.Before(reqDeadline) {
225reqDeadline = contextDeadline
226withContextDeadline = true
227}
228}
229
230err = x.Conn.SetDeadline(reqDeadline)
231if err != nil {
232return nil, err
233}
234
235// Request ID is an atomic counter that wraps to 0 at max int32.
236reqID := (atomic.AddUint32(&(x.requestID), 1) & 0x7FFFFFFF)
237allReqIDs = append(allReqIDs, reqID)
238
239packetOut.RequestID = reqID
240
241if x.Version == Version3 {
242msgID := (atomic.AddUint32(&(x.msgID), 1) & 0x7FFFFFFF)
243
244// allMsgIDs = append(allMsgIDs, msgID) // unused
245
246packetOut.MsgID = msgID
247
248err = x.initPacket(packetOut)
249if err != nil {
250break
251}
252}
253if x.Version == Version3 {
254packetOut.SecurityParameters.Log()
255}
256
257var outBuf []byte
258outBuf, err = packetOut.marshalMsg()
259if err != nil {
260// Don't retry - not going to get any better!
261err = fmt.Errorf("marshal: %w", err)
262break
263}
264
265if x.PreSend != nil {
266x.PreSend(x)
267}
268x.Logger.Printf("SENDING PACKET: %s", packetOut.SafeString())
269// If using UDP and unconnected socket, send packet directly to stored address.
270if uconn, ok := x.Conn.(net.PacketConn); ok && x.uaddr != nil {
271_, err = uconn.WriteTo(outBuf, x.uaddr)
272} else {
273_, err = x.Conn.Write(outBuf)
274}
275if err != nil {
276continue
277}
278if x.OnSent != nil {
279x.OnSent(x)
280}
281
282// all sends wait for the return packet, except for SNMPv2Trap
283if !wait {
284return &SnmpPacket{}, nil
285}
286
287waitingResponse:
288for {
289x.Logger.Print("WAITING RESPONSE...")
290// Receive response and try receiving again on any decoding error.
291// Let the deadline abort us if we don't receive a valid response.
292
293var resp []byte
294resp, err = x.receive()
295if err == io.EOF && strings.HasPrefix(x.Transport, tcp) {
296// EOF on TCP: reconnect and retry. Do not count
297// as retry as socket was broken
298x.Logger.Printf("ERROR: EOF. Performing reconnect")
299err = x.netConnect()
300if err != nil {
301return nil, err
302}
303retries--
304break
305} else if err != nil {
306// receive error. retrying won't help. abort
307break
308}
309if x.OnRecv != nil {
310x.OnRecv(x)
311}
312x.Logger.Printf("GET RESPONSE OK: %+v", resp)
313result = new(SnmpPacket)
314result.Logger = x.Logger
315
316result.MsgFlags = packetOut.MsgFlags
317if packetOut.SecurityParameters != nil {
318result.SecurityParameters = packetOut.SecurityParameters.Copy()
319}
320
321var cursor int
322cursor, err = x.unmarshalHeader(resp, result)
323if err != nil {
324x.Logger.Printf("ERROR on unmarshall header: %s", err)
325break
326}
327
328if x.Version == Version3 {
329useResponseSecurityParameters := false
330if usp, ok := x.SecurityParameters.(*UsmSecurityParameters); ok {
331if usp.AuthoritativeEngineID == "" {
332useResponseSecurityParameters = true
333}
334}
335err = x.testAuthentication(resp, result, useResponseSecurityParameters)
336if err != nil {
337x.Logger.Printf("ERROR on Test Authentication on v3: %s", err)
338break
339}
340resp, cursor, err = x.decryptPacket(resp, cursor, result)
341if err != nil {
342x.Logger.Printf("ERROR on decryptPacket on v3: %s", err)
343break
344}
345}
346
347err = x.unmarshalPayload(resp, cursor, result)
348if err != nil {
349x.Logger.Printf("ERROR on UnmarshalPayload on v3: %s", err)
350break
351}
352if result.Error == NoError && len(result.Variables) < 1 {
353x.Logger.Printf("ERROR on UnmarshalPayload on v3: Empty result")
354break
355}
356
357// While Report PDU was defined by RFC 1905 as part of SNMPv2, it was never
358// used until SNMPv3. Report PDU's allow a SNMP engine to tell another SNMP
359// engine that an error was detected while processing an SNMP message.
360//
361// The format for a Report PDU is
362// -----------------------------------
363// | 0xA8 | reqid | 0 | 0 | varbinds |
364// -----------------------------------
365// where:
366// - PDU type 0xA8 indicates a Report PDU.
367// - reqid is either:
368// The request identifier of the message that triggered the report
369// or zero if the request identifier cannot be extracted.
370// - The variable bindings will contain a single object identifier and its value
371//
372// usmStatsNotInTimeWindows and usmStatsUnknownEngineIDs are recoverable errors
373// and will be retransmitted, for others we return the result with an error.
374if result.Version == Version3 && result.PDUType == Report && len(result.Variables) == 1 {
375switch result.Variables[0].Name {
376case usmStatsUnsupportedSecLevels:
377return result, ErrUnknownSecurityLevel
378case usmStatsNotInTimeWindows:
379break waitingResponse
380case usmStatsUnknownUserNames:
381return result, ErrUnknownUsername
382case usmStatsUnknownEngineIDs:
383break waitingResponse
384case usmStatsWrongDigests:
385return result, ErrWrongDigest
386case usmStatsDecryptionErrors:
387return result, ErrDecryption
388case snmpUnknownSecurityModels:
389return result, ErrUnknownSecurityModels
390case snmpInvalidMsgs:
391return result, ErrInvalidMsgs
392case snmpUnknownPDUHandlers:
393return result, ErrUnknownPDUHandlers
394default:
395return result, ErrUnknownReportPDU
396}
397}
398
399validID := false
400for _, id := range allReqIDs {
401if id == result.RequestID {
402validID = true
403}
404}
405if result.RequestID == 0 {
406validID = true
407}
408if !validID {
409x.Logger.Print("ERROR out of order")
410continue
411}
412
413break
414}
415if err != nil {
416continue
417}
418
419if x.OnFinish != nil {
420x.OnFinish(x)
421}
422// Success!
423return result, nil
424}
425
426// Return last error
427return nil, err
428}
429
430// generic "sender" that negotiate any version of snmp request
431//
432// all sends wait for the return packet, except for SNMPv2Trap
433func (x *GoSNMP) send(packetOut *SnmpPacket, wait bool) (result *SnmpPacket, err error) {
434defer func() {
435if e := recover(); e != nil {
436var buf = make([]byte, 8192)
437runtime.Stack(buf, true)
438
439err = fmt.Errorf("recover: %v Stack:%v", e, string(buf))
440}
441}()
442
443if x.Conn == nil {
444return nil, fmt.Errorf("&GoSNMP.Conn is missing. Provide a connection or use Connect()")
445}
446
447if x.Retries < 0 {
448x.Retries = 0
449}
450x.Logger.Print("SEND INIT")
451if packetOut.Version == Version3 {
452x.Logger.Print("SEND INIT NEGOTIATE SECURITY PARAMS")
453if err = x.negotiateInitialSecurityParameters(packetOut); err != nil {
454return &SnmpPacket{}, err
455}
456x.Logger.Print("SEND END NEGOTIATE SECURITY PARAMS")
457}
458
459// perform request
460result, err = x.sendOneRequest(packetOut, wait)
461if err != nil {
462x.Logger.Printf("SEND Error on the first Request Error: %s", err)
463return result, err
464}
465
466if result.Version == Version3 {
467x.Logger.Printf("SEND STORE SECURITY PARAMS from result: %s", result.SecurityParameters.SafeString())
468err = x.storeSecurityParameters(result)
469
470if result.PDUType == Report && len(result.Variables) == 1 {
471switch result.Variables[0].Name {
472case usmStatsNotInTimeWindows:
473x.Logger.Print("WARNING detected out-of-time-window ERROR")
474if err = x.updatePktSecurityParameters(packetOut); err != nil {
475x.Logger.Printf("ERROR updatePktSecurityParameters error: %s", err)
476return nil, err
477}
478// retransmit with updated auth engine params
479result, err = x.sendOneRequest(packetOut, wait)
480if err != nil {
481x.Logger.Printf("ERROR out-of-time-window retransmit error: %s", err)
482return result, ErrNotInTimeWindow
483}
484
485case usmStatsUnknownEngineIDs:
486x.Logger.Print("WARNING detected unknown engine id ERROR")
487if err = x.updatePktSecurityParameters(packetOut); err != nil {
488x.Logger.Printf("ERROR updatePktSecurityParameters error: %s", err)
489return nil, err
490}
491// retransmit with updated engine id
492result, err = x.sendOneRequest(packetOut, wait)
493if err != nil {
494x.Logger.Printf("ERROR unknown engine id retransmit error: %s", err)
495return result, ErrUnknownEngineID
496}
497}
498}
499}
500return result, err
501}
502
503// -- Marshalling Logic --------------------------------------------------------
504
505// MarshalMsg marshalls a snmp packet, ready for sending across the wire
506func (packet *SnmpPacket) MarshalMsg() ([]byte, error) {
507return packet.marshalMsg()
508}
509
510// marshal an SNMP message
511func (packet *SnmpPacket) marshalMsg() ([]byte, error) {
512var err error
513buf := new(bytes.Buffer)
514
515// version
516buf.Write([]byte{2, 1, byte(packet.Version)})
517
518if packet.Version == Version3 {
519buf, err = packet.marshalV3(buf)
520if err != nil {
521return nil, err
522}
523} else {
524// community
525buf.Write([]byte{4, uint8(len(packet.Community))})
526buf.WriteString(packet.Community)
527// pdu
528pdu, err2 := packet.marshalPDU()
529if err2 != nil {
530return nil, err2
531}
532buf.Write(pdu)
533}
534
535// build up resulting msg - sequence, length then the tail (buf)
536msg := new(bytes.Buffer)
537msg.WriteByte(byte(Sequence))
538
539bufLengthBytes, err2 := marshalLength(buf.Len())
540if err2 != nil {
541return nil, err2
542}
543msg.Write(bufLengthBytes)
544_, err = buf.WriteTo(msg)
545if err != nil {
546return nil, err
547}
548
549authenticatedMessage, err := packet.authenticate(msg.Bytes())
550if err != nil {
551return nil, err
552}
553
554return authenticatedMessage, nil
555}
556
557func (packet *SnmpPacket) marshalSNMPV1TrapHeader() ([]byte, error) {
558buf := new(bytes.Buffer)
559
560// marshal OID
561oidBytes, err := marshalObjectIdentifier(packet.Enterprise)
562if err != nil {
563return nil, fmt.Errorf("unable to marshal OID: %w", err)
564}
565buf.Write([]byte{byte(ObjectIdentifier), byte(len(oidBytes))})
566buf.Write(oidBytes)
567
568// marshal AgentAddress (ip address)
569ip := net.ParseIP(packet.AgentAddress)
570ipAddressBytes := ipv4toBytes(ip)
571buf.Write([]byte{byte(IPAddress), byte(len(ipAddressBytes))})
572buf.Write(ipAddressBytes)
573
574// marshal GenericTrap. Could just cast GenericTrap to a single byte as IDs greater than 6 are unknown,
575// but do it properly. See issue 182.
576var genericTrapBytes []byte
577genericTrapBytes, err = marshalInt32(packet.GenericTrap)
578if err != nil {
579return nil, fmt.Errorf("unable to marshal SNMPv1 GenericTrap: %w", err)
580}
581buf.Write([]byte{byte(Integer), byte(len(genericTrapBytes))})
582buf.Write(genericTrapBytes)
583
584// marshal SpecificTrap
585var specificTrapBytes []byte
586specificTrapBytes, err = marshalInt32(packet.SpecificTrap)
587if err != nil {
588return nil, fmt.Errorf("unable to marshal SNMPv1 SpecificTrap: %w", err)
589}
590buf.Write([]byte{byte(Integer), byte(len(specificTrapBytes))})
591buf.Write(specificTrapBytes)
592
593// marshal timeTicks
594timeTickBytes, err := marshalUint32(packet.Timestamp)
595if err != nil {
596return nil, fmt.Errorf("unable to Timestamp: %w", err)
597}
598buf.Write([]byte{byte(TimeTicks), byte(len(timeTickBytes))})
599buf.Write(timeTickBytes)
600
601return buf.Bytes(), nil
602}
603
604// marshal a PDU
605func (packet *SnmpPacket) marshalPDU() ([]byte, error) {
606buf := new(bytes.Buffer)
607
608switch packet.PDUType {
609case GetBulkRequest:
610// requestid
611err := shrinkAndWriteUint(buf, int(packet.RequestID))
612if err != nil {
613return nil, err
614}
615
616// non repeaters
617nonRepeaters, err := marshalUint32(packet.NonRepeaters)
618if err != nil {
619return nil, fmt.Errorf("marshalPDU: unable to marshal NonRepeaters to uint32: %w", err)
620}
621
622buf.Write([]byte{2, byte(len(nonRepeaters))})
623if err = binary.Write(buf, binary.BigEndian, nonRepeaters); err != nil {
624return nil, fmt.Errorf("marshalPDU: unable to marshal NonRepeaters: %w", err)
625}
626
627// max repetitions
628maxRepetitions, err := marshalUint32(packet.MaxRepetitions)
629if err != nil {
630return nil, fmt.Errorf("marshalPDU: unable to marshal maxRepetitions to uint32: %w", err)
631}
632
633buf.Write([]byte{2, byte(len(maxRepetitions))})
634if err = binary.Write(buf, binary.BigEndian, maxRepetitions); err != nil {
635return nil, fmt.Errorf("marshalPDU: unable to marshal maxRepetitions: %w", err)
636}
637
638case Trap:
639// write SNMP V1 Trap Header fields
640snmpV1TrapHeader, err := packet.marshalSNMPV1TrapHeader()
641if err != nil {
642return nil, err
643}
644
645buf.Write(snmpV1TrapHeader)
646
647default:
648// requestid
649err := shrinkAndWriteUint(buf, int(packet.RequestID))
650if err != nil {
651return nil, err
652}
653
654// error status
655errorStatus, err := marshalUint32(packet.Error)
656if err != nil {
657return nil, fmt.Errorf("marshalPDU: unable to marshal errorStatus to uint32: %w", err)
658}
659
660buf.Write([]byte{2, byte(len(errorStatus))})
661if err = binary.Write(buf, binary.BigEndian, errorStatus); err != nil {
662return nil, fmt.Errorf("marshalPDU: unable to marshal errorStatus: %w", err)
663}
664
665// error index
666errorIndex, err := marshalUint32(packet.ErrorIndex)
667if err != nil {
668return nil, fmt.Errorf("marshalPDU: unable to marshal errorIndex to uint32: %w", err)
669}
670
671buf.Write([]byte{2, byte(len(errorIndex))})
672if err = binary.Write(buf, binary.BigEndian, errorIndex); err != nil {
673return nil, fmt.Errorf("marshalPDU: unable to marshal errorIndex: %w", err)
674}
675}
676
677// build varbind list
678vbl, err := packet.marshalVBL()
679if err != nil {
680return nil, fmt.Errorf("marshalPDU: unable to marshal varbind list: %w", err)
681}
682buf.Write(vbl)
683
684// build up resulting pdu
685pdu := new(bytes.Buffer)
686// calculate pdu length
687bufLengthBytes, err := marshalLength(buf.Len())
688if err != nil {
689return nil, fmt.Errorf("marshalPDU: unable to marshal pdu length: %w", err)
690}
691// write request type
692pdu.WriteByte(byte(packet.PDUType))
693// write pdu length
694pdu.Write(bufLengthBytes)
695// write the tail (buf)
696if _, err = buf.WriteTo(pdu); err != nil {
697return nil, fmt.Errorf("marshalPDU: unable to marshal pdu: %w", err)
698}
699
700return pdu.Bytes(), nil
701}
702
703// marshal a varbind list
704func (packet *SnmpPacket) marshalVBL() ([]byte, error) {
705vblBuf := new(bytes.Buffer)
706for _, pdu := range packet.Variables {
707pdu := pdu
708vb, err := marshalVarbind(&pdu)
709if err != nil {
710return nil, err
711}
712vblBuf.Write(vb)
713}
714
715vblBytes := vblBuf.Bytes()
716vblLengthBytes, err := marshalLength(len(vblBytes))
717if err != nil {
718return nil, err
719}
720
721// FIX does bytes.Buffer give better performance than byte slices?
722result := []byte{byte(Sequence)}
723result = append(result, vblLengthBytes...)
724result = append(result, vblBytes...)
725return result, nil
726}
727
728// marshal a varbind
729func marshalVarbind(pdu *SnmpPDU) ([]byte, error) {
730oid, err := marshalObjectIdentifier(pdu.Name)
731if err != nil {
732return nil, err
733}
734pduBuf := new(bytes.Buffer)
735tmpBuf := new(bytes.Buffer)
736
737// Marshal the PDU type into the appropriate BER
738switch pdu.Type {
739case Null:
740ltmp, err2 := marshalLength(len(oid))
741if err2 != nil {
742return nil, err2
743}
744tmpBuf.Write([]byte{byte(ObjectIdentifier)})
745tmpBuf.Write(ltmp)
746tmpBuf.Write(oid)
747tmpBuf.Write([]byte{byte(Null), byte(EndOfContents)})
748
749ltmp, err2 = marshalLength(tmpBuf.Len())
750if err2 != nil {
751return nil, err2
752}
753pduBuf.Write([]byte{byte(Sequence)})
754pduBuf.Write(ltmp)
755_, err2 = tmpBuf.WriteTo(pduBuf)
756if err2 != nil {
757return nil, err2
758}
759
760case Integer:
761// Oid
762tmpBuf.Write([]byte{byte(ObjectIdentifier), byte(len(oid))})
763tmpBuf.Write(oid)
764
765// Number
766var intBytes []byte
767switch value := pdu.Value.(type) {
768case byte:
769intBytes = []byte{byte(pdu.Value.(int))}
770case int:
771if intBytes, err = marshalInt32(value); err != nil {
772return nil, fmt.Errorf("error mashalling PDU Integer: %w", err)
773}
774default:
775return nil, fmt.Errorf("unable to marshal PDU Integer; not byte or int")
776}
777tmpBuf.Write([]byte{byte(Integer), byte(len(intBytes))})
778tmpBuf.Write(intBytes)
779
780// Sequence, length of oid + integer, then oid/integer data
781pduBuf.WriteByte(byte(Sequence))
782pduBuf.WriteByte(byte(len(oid) + len(intBytes) + 4))
783pduBuf.Write(tmpBuf.Bytes())
784
785case Counter32, Gauge32, TimeTicks, Uinteger32:
786// Oid
787tmpBuf.Write([]byte{byte(ObjectIdentifier), byte(len(oid))})
788tmpBuf.Write(oid)
789
790// Number
791var intBytes []byte
792switch value := pdu.Value.(type) {
793case uint32:
794if intBytes, err = marshalUint32(value); err != nil {
795return nil, fmt.Errorf("error marshalling PDU Uinteger32 type from uint32: %w", err)
796}
797case uint:
798if intBytes, err = marshalUint32(value); err != nil {
799return nil, fmt.Errorf("error marshalling PDU Uinteger32 type from uint: %w", err)
800}
801default:
802return nil, fmt.Errorf("unable to marshal pdu.Type %v; unknown pdu.Value %v[type=%T]", pdu.Type, pdu.Value, pdu.Value)
803}
804tmpBuf.Write([]byte{byte(pdu.Type), byte(len(intBytes))})
805tmpBuf.Write(intBytes)
806
807// Sequence, length of oid + integer, then oid/integer data
808pduBuf.WriteByte(byte(Sequence))
809pduBuf.WriteByte(byte(len(oid) + len(intBytes) + 4))
810pduBuf.Write(tmpBuf.Bytes())
811
812case OctetString, BitString, Opaque:
813// Oid
814tmpBuf.Write([]byte{byte(ObjectIdentifier), byte(len(oid))})
815tmpBuf.Write(oid)
816
817// OctetString
818var octetStringBytes []byte
819switch value := pdu.Value.(type) {
820case []byte:
821octetStringBytes = value
822case string:
823octetStringBytes = []byte(value)
824default:
825return nil, fmt.Errorf("unable to marshal PDU OctetString; not []byte or string")
826}
827
828var length []byte
829length, err = marshalLength(len(octetStringBytes))
830if err != nil {
831return nil, fmt.Errorf("unable to marshal PDU length: %w", err)
832}
833tmpBuf.WriteByte(byte(pdu.Type))
834tmpBuf.Write(length)
835tmpBuf.Write(octetStringBytes)
836
837tmpBytes := tmpBuf.Bytes()
838
839length, err = marshalLength(len(tmpBytes))
840if err != nil {
841return nil, fmt.Errorf("unable to marshal PDU data length: %w", err)
842}
843// Sequence, length of oid + octetstring, then oid/octetstring data
844pduBuf.WriteByte(byte(Sequence))
845
846pduBuf.Write(length)
847pduBuf.Write(tmpBytes)
848
849case ObjectIdentifier:
850// Oid
851tmpBuf.Write([]byte{byte(ObjectIdentifier), byte(len(oid))})
852tmpBuf.Write(oid)
853value := pdu.Value.(string)
854oidBytes, err := marshalObjectIdentifier(value)
855if err != nil {
856return nil, fmt.Errorf("error marshalling ObjectIdentifier: %w", err)
857}
858
859// Oid data
860var length []byte
861length, err = marshalLength(len(oidBytes))
862if err != nil {
863return nil, fmt.Errorf("error marshalling ObjectIdentifier length: %w", err)
864}
865tmpBuf.WriteByte(byte(pdu.Type))
866tmpBuf.Write(length)
867tmpBuf.Write(oidBytes)
868
869tmpBytes := tmpBuf.Bytes()
870length, err = marshalLength(len(tmpBytes))
871if err != nil {
872return nil, fmt.Errorf("error marshalling ObjectIdentifier data length: %w", err)
873}
874// Sequence, length of oid + oid, then oid/oid data
875pduBuf.WriteByte(byte(Sequence))
876pduBuf.Write(length)
877pduBuf.Write(tmpBytes)
878
879case IPAddress:
880// Oid
881tmpBuf.Write([]byte{byte(ObjectIdentifier), byte(len(oid))})
882tmpBuf.Write(oid)
883// OctetString
884var ipAddressBytes []byte
885switch value := pdu.Value.(type) {
886case []byte:
887ipAddressBytes = value
888case string:
889ip := net.ParseIP(value)
890ipAddressBytes = ipv4toBytes(ip)
891default:
892return nil, fmt.Errorf("unable to marshal PDU IPAddress; not []byte or string")
893}
894tmpBuf.Write([]byte{byte(IPAddress), byte(len(ipAddressBytes))})
895tmpBuf.Write(ipAddressBytes)
896// Sequence, length of oid + octetstring, then oid/octetstring data
897pduBuf.WriteByte(byte(Sequence))
898pduBuf.WriteByte(byte(len(oid) + len(ipAddressBytes) + 4))
899pduBuf.Write(tmpBuf.Bytes())
900
901case OpaqueFloat, OpaqueDouble:
902converters := map[Asn1BER]func(interface{}) ([]byte, error){
903OpaqueFloat: marshalFloat32,
904OpaqueDouble: marshalFloat64,
905}
906
907intBuf := new(bytes.Buffer)
908intBuf.WriteByte(byte(AsnExtensionTag))
909intBuf.WriteByte(byte(pdu.Type))
910intBytes, err := converters[pdu.Type](pdu.Value)
911if err != nil {
912return nil, fmt.Errorf("error converting PDU value type %v to %v: %w", pdu.Value, pdu.Type, err)
913}
914intLength, err := marshalLength(len(intBytes))
915if err != nil {
916return nil, fmt.Errorf("error marshalling Float type length: %w", err)
917}
918intBuf.Write(intLength)
919intBuf.Write(intBytes)
920
921opaqueLength, err := marshalLength(len(intBuf.Bytes()))
922if err != nil {
923return nil, fmt.Errorf("error marshalling Float type length: %w", err)
924}
925tmpBuf.Write([]byte{byte(ObjectIdentifier), byte(len(oid))})
926tmpBuf.Write(oid)
927tmpBuf.WriteByte(byte(Opaque))
928tmpBuf.Write(opaqueLength)
929tmpBuf.Write(intBuf.Bytes())
930
931length, err := marshalLength(len(tmpBuf.Bytes()))
932if err != nil {
933return nil, fmt.Errorf("error marshalling Float type length: %w", err)
934}
935
936// Sequence, length of oid + oid, then oid/oid data
937pduBuf.WriteByte(byte(Sequence))
938pduBuf.Write(length)
939pduBuf.Write(tmpBuf.Bytes())
940
941case Counter64:
942tmpBuf.Write([]byte{byte(ObjectIdentifier), byte(len(oid))})
943tmpBuf.Write(oid)
944tmpBuf.WriteByte(byte(pdu.Type))
945intBytes := marshalUint64(pdu.Value)
946tmpBuf.WriteByte(byte(len(intBytes)))
947tmpBuf.Write(intBytes)
948tmpBytes := tmpBuf.Bytes()
949length, err := marshalLength(len(tmpBytes))
950if err != nil {
951return nil, fmt.Errorf("error marshalling Float type length: %w", err)
952}
953// Sequence, length of oid + oid, then oid/oid data
954pduBuf.WriteByte(byte(Sequence))
955pduBuf.Write(length)
956pduBuf.Write(tmpBytes)
957
958case NoSuchInstance, NoSuchObject, EndOfMibView:
959tmpBuf.Write([]byte{byte(ObjectIdentifier), byte(len(oid))})
960tmpBuf.Write(oid)
961tmpBuf.WriteByte(byte(pdu.Type))
962tmpBuf.WriteByte(byte(EndOfContents))
963tmpBytes := tmpBuf.Bytes()
964length, err := marshalLength(len(tmpBytes))
965if err != nil {
966return nil, fmt.Errorf("error marshalling Null type data length: %w", err)
967}
968// Sequence, length of oid + oid, then oid/oid data
969pduBuf.WriteByte(byte(Sequence))
970pduBuf.Write(length)
971pduBuf.Write(tmpBytes)
972
973default:
974return nil, fmt.Errorf("unable to marshal PDU: unknown BER type %q", pdu.Type)
975}
976
977return pduBuf.Bytes(), nil
978}
979
980// -- Unmarshalling Logic ------------------------------------------------------
981
982func (x *GoSNMP) unmarshalVersionFromHeader(packet []byte, response *SnmpPacket) (SnmpVersion, int, error) {
983if len(packet) < 2 {
984return 0, 0, fmt.Errorf("cannot unmarshal empty packet")
985}
986if response == nil {
987return 0, 0, fmt.Errorf("cannot unmarshal response into nil packet reference")
988}
989
990response.Variables = make([]SnmpPDU, 0, 5)
991
992// Start parsing the packet
993cursor := 0
994
995// First bytes should be 0x30
996if PDUType(packet[0]) != Sequence {
997return 0, 0, fmt.Errorf("invalid packet header")
998}
999
1000length, cursor, err := parseLength(packet)
1001if err != nil {
1002return 0, 0, err
1003}
1004if len(packet) != length {
1005return 0, 0, fmt.Errorf("error verifying packet sanity: Got %d Expected: %d", len(packet), length)
1006}
1007x.Logger.Printf("Packet sanity verified, we got all the bytes (%d)", length)
1008
1009// Parse SNMP Version
1010rawVersion, count, err := parseRawField(x.Logger, packet[cursor:], "version")
1011if err != nil {
1012return 0, 0, fmt.Errorf("error parsing SNMP packet version: %w", err)
1013}
1014
1015cursor += count
1016if cursor >= len(packet) {
1017return 0, 0, fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor)
1018}
1019
1020if version, ok := rawVersion.(int); ok {
1021x.Logger.Printf("Parsed version %d", version)
1022return SnmpVersion(version), cursor, nil
1023}
1024return 0, cursor, err
1025}
1026
1027func (x *GoSNMP) unmarshalHeader(packet []byte, response *SnmpPacket) (int, error) {
1028version, cursor, err := x.unmarshalVersionFromHeader(packet, response)
1029if err != nil {
1030return 0, err
1031}
1032response.Version = version
1033
1034if response.Version == Version3 {
1035oldcursor := cursor
1036cursor, err = x.unmarshalV3Header(packet, cursor, response)
1037if err != nil {
1038return 0, err
1039}
1040x.Logger.Printf("UnmarshalV3Header done. [with SecurityParameters]. Header Size %d. Last 4 Bytes=[%v]", cursor-oldcursor, packet[cursor-4:cursor])
1041} else {
1042// Parse community
1043rawCommunity, count, err := parseRawField(x.Logger, packet[cursor:], "community")
1044if err != nil {
1045return 0, fmt.Errorf("error parsing community string: %w", err)
1046}
1047cursor += count
1048if cursor > len(packet) {
1049return 0, fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor)
1050}
1051
1052if community, ok := rawCommunity.(string); ok {
1053response.Community = community
1054x.Logger.Printf("Parsed community %s", community)
1055}
1056}
1057return cursor, nil
1058}
1059
1060func (x *GoSNMP) unmarshalPayload(packet []byte, cursor int, response *SnmpPacket) error {
1061if len(packet) == 0 {
1062return errors.New("cannot unmarshal nil or empty payload packet")
1063}
1064if cursor >= len(packet) {
1065return fmt.Errorf("cannot unmarshal payload, packet length %d cursor %d", len(packet), cursor)
1066}
1067if response == nil {
1068return errors.New("cannot unmarshal payload response into nil packet reference")
1069}
1070
1071// Parse SNMP packet type
1072requestType := PDUType(packet[cursor])
1073x.Logger.Printf("UnmarshalPayload Meet PDUType %#x. Offset %v", requestType, cursor)
1074switch requestType {
1075// known, supported types
1076case GetResponse, GetNextRequest, GetBulkRequest, Report, SNMPv2Trap, GetRequest, SetRequest, InformRequest:
1077response.PDUType = requestType
1078if err := x.unmarshalResponse(packet[cursor:], response); err != nil {
1079return fmt.Errorf("error in unmarshalResponse: %w", err)
1080}
1081// If it's an InformRequest, mark the trap.
1082response.IsInform = (requestType == InformRequest)
1083case Trap:
1084response.PDUType = requestType
1085if err := x.unmarshalTrapV1(packet[cursor:], response); err != nil {
1086return fmt.Errorf("error in unmarshalTrapV1: %w", err)
1087}
1088default:
1089x.Logger.Printf("UnmarshalPayload Meet Unknown PDUType %#x. Offset %v", requestType, cursor)
1090return fmt.Errorf("unknown PDUType %#x", requestType)
1091}
1092return nil
1093}
1094
1095func (x *GoSNMP) unmarshalResponse(packet []byte, response *SnmpPacket) error {
1096cursor := 0
1097
1098getResponseLength, cursor, err := parseLength(packet)
1099if err != nil {
1100return err
1101}
1102if len(packet) != getResponseLength {
1103return fmt.Errorf("error verifying Response sanity: Got %d Expected: %d", len(packet), getResponseLength)
1104}
1105x.Logger.Printf("getResponseLength: %d", getResponseLength)
1106
1107// Parse Request-ID
1108rawRequestID, count, err := parseRawField(x.Logger, packet[cursor:], "request id")
1109if err != nil {
1110return fmt.Errorf("error parsing SNMP packet request ID: %w", err)
1111}
1112cursor += count
1113if cursor > len(packet) {
1114return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor)
1115}
1116
1117if requestid, ok := rawRequestID.(int); ok {
1118response.RequestID = uint32(requestid)
1119x.Logger.Printf("requestID: %d", response.RequestID)
1120}
1121
1122if response.PDUType == GetBulkRequest {
1123// Parse Non Repeaters
1124rawNonRepeaters, count, err := parseRawField(x.Logger, packet[cursor:], "non repeaters")
1125if err != nil {
1126return fmt.Errorf("error parsing SNMP packet non repeaters: %w", err)
1127}
1128cursor += count
1129if cursor > len(packet) {
1130return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor)
1131}
1132
1133if nonRepeaters, ok := rawNonRepeaters.(int); ok {
1134response.NonRepeaters = uint8(nonRepeaters)
1135}
1136
1137// Parse Max Repetitions
1138rawMaxRepetitions, count, err := parseRawField(x.Logger, packet[cursor:], "max repetitions")
1139if err != nil {
1140return fmt.Errorf("error parsing SNMP packet max repetitions: %w", err)
1141}
1142cursor += count
1143if cursor > len(packet) {
1144return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor)
1145}
1146
1147if maxRepetitions, ok := rawMaxRepetitions.(int); ok {
1148response.MaxRepetitions = uint32(maxRepetitions & 0x7FFFFFFF)
1149}
1150} else {
1151// Parse Error-Status
1152rawError, count, err := parseRawField(x.Logger, packet[cursor:], "error-status")
1153if err != nil {
1154return fmt.Errorf("error parsing SNMP packet error: %w", err)
1155}
1156cursor += count
1157if cursor > len(packet) {
1158return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor)
1159}
1160
1161if errorStatus, ok := rawError.(int); ok {
1162response.Error = SNMPError(errorStatus)
1163x.Logger.Printf("errorStatus: %d", uint8(errorStatus))
1164}
1165
1166// Parse Error-Index
1167rawErrorIndex, count, err := parseRawField(x.Logger, packet[cursor:], "error index")
1168if err != nil {
1169return fmt.Errorf("error parsing SNMP packet error index: %w", err)
1170}
1171cursor += count
1172if cursor > len(packet) {
1173return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor)
1174}
1175
1176if errorindex, ok := rawErrorIndex.(int); ok {
1177response.ErrorIndex = uint8(errorindex)
1178x.Logger.Printf("error-index: %d", uint8(errorindex))
1179}
1180}
1181
1182return x.unmarshalVBL(packet[cursor:], response)
1183}
1184
1185func (x *GoSNMP) unmarshalTrapV1(packet []byte, response *SnmpPacket) error {
1186cursor := 0
1187
1188getResponseLength, cursor, err := parseLength(packet)
1189if err != nil {
1190return err
1191}
1192if len(packet) != getResponseLength {
1193return fmt.Errorf("error verifying Response sanity: Got %d Expected: %d", len(packet), getResponseLength)
1194}
1195x.Logger.Printf("getResponseLength: %d", getResponseLength)
1196
1197// Parse Enterprise
1198rawEnterprise, count, err := parseRawField(x.Logger, packet[cursor:], "enterprise")
1199if err != nil {
1200return fmt.Errorf("error parsing SNMP packet error: %w", err)
1201}
1202
1203cursor += count
1204if cursor > len(packet) {
1205return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor)
1206}
1207
1208if Enterprise, ok := rawEnterprise.(string); ok {
1209response.Enterprise = Enterprise
1210x.Logger.Printf("Enterprise: %+v", Enterprise)
1211}
1212
1213// Parse AgentAddress
1214rawAgentAddress, count, err := parseRawField(x.Logger, packet[cursor:], "agent-address")
1215if err != nil {
1216return fmt.Errorf("error parsing SNMP packet error: %w", err)
1217}
1218cursor += count
1219if cursor > len(packet) {
1220return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor)
1221}
1222
1223if AgentAddress, ok := rawAgentAddress.(string); ok {
1224response.AgentAddress = AgentAddress
1225x.Logger.Printf("AgentAddress: %s", AgentAddress)
1226}
1227
1228// Parse GenericTrap
1229rawGenericTrap, count, err := parseRawField(x.Logger, packet[cursor:], "generic-trap")
1230if err != nil {
1231return fmt.Errorf("error parsing SNMP packet error: %w", err)
1232}
1233cursor += count
1234if cursor > len(packet) {
1235return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor)
1236}
1237
1238if GenericTrap, ok := rawGenericTrap.(int); ok {
1239response.GenericTrap = GenericTrap
1240x.Logger.Printf("GenericTrap: %d", GenericTrap)
1241}
1242
1243// Parse SpecificTrap
1244rawSpecificTrap, count, err := parseRawField(x.Logger, packet[cursor:], "specific-trap")
1245if err != nil {
1246return fmt.Errorf("error parsing SNMP packet error: %w", err)
1247}
1248cursor += count
1249if cursor > len(packet) {
1250return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor)
1251}
1252
1253if SpecificTrap, ok := rawSpecificTrap.(int); ok {
1254response.SpecificTrap = SpecificTrap
1255x.Logger.Printf("SpecificTrap: %d", SpecificTrap)
1256}
1257
1258// Parse TimeStamp
1259rawTimestamp, count, err := parseRawField(x.Logger, packet[cursor:], "time-stamp")
1260if err != nil {
1261return fmt.Errorf("error parsing SNMP packet error: %w", err)
1262}
1263cursor += count
1264if cursor > len(packet) {
1265return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor)
1266}
1267
1268if Timestamp, ok := rawTimestamp.(uint); ok {
1269response.Timestamp = Timestamp
1270x.Logger.Printf("Timestamp: %d", Timestamp)
1271}
1272
1273return x.unmarshalVBL(packet[cursor:], response)
1274}
1275
1276// unmarshal a Varbind list
1277func (x *GoSNMP) unmarshalVBL(packet []byte, response *SnmpPacket) error {
1278var cursor, cursorInc int
1279var vblLength int
1280
1281if len(packet) == 0 || cursor > len(packet) {
1282return fmt.Errorf("truncated packet when unmarshalling a VBL, got length %d cursor %d", len(packet), cursor)
1283}
1284
1285if packet[cursor] != 0x30 {
1286return fmt.Errorf("expected a sequence when unmarshalling a VBL, got %x", packet[cursor])
1287}
1288
1289vblLength, cursor, err := parseLength(packet)
1290if err != nil {
1291return err
1292}
1293if vblLength == 0 || vblLength > len(packet) {
1294return fmt.Errorf("truncated packet when unmarshalling a VBL, packet length %d cursor %d", len(packet), cursor)
1295}
1296
1297if len(packet) != vblLength {
1298return fmt.Errorf("error verifying: packet length %d vbl length %d", len(packet), vblLength)
1299}
1300x.Logger.Printf("vblLength: %d", vblLength)
1301
1302// check for an empty response
1303if vblLength == 2 && packet[1] == 0x00 {
1304return nil
1305}
1306
1307// Loop & parse Varbinds
1308for cursor < vblLength {
1309if packet[cursor] != 0x30 {
1310return fmt.Errorf("expected a sequence when unmarshalling a VB, got %x", packet[cursor])
1311}
1312
1313_, cursorInc, err = parseLength(packet[cursor:])
1314if err != nil {
1315return err
1316}
1317cursor += cursorInc
1318if cursor > len(packet) {
1319return fmt.Errorf("error parsing OID Value: packet %d cursor %d", len(packet), cursor)
1320}
1321
1322// Parse OID
1323rawOid, oidLength, err := parseRawField(x.Logger, packet[cursor:], "OID")
1324if err != nil {
1325return fmt.Errorf("error parsing OID Value: %w", err)
1326}
1327cursor += oidLength
1328if cursor > len(packet) {
1329return fmt.Errorf("error parsing OID Value: truncated, packet length %d cursor %d", len(packet), cursor)
1330}
1331oid, ok := rawOid.(string)
1332if !ok {
1333return fmt.Errorf("unable to type assert rawOid |%v| to string", rawOid)
1334}
1335x.Logger.Printf("OID: %s", oid)
1336// Parse Value
1337var decodedVal variable
1338if err = x.decodeValue(packet[cursor:], &decodedVal); err != nil {
1339return fmt.Errorf("error decoding value: %w", err)
1340}
1341
1342valueLength, _, err := parseLength(packet[cursor:])
1343if err != nil {
1344return err
1345}
1346cursor += valueLength
1347if cursor > len(packet) {
1348return fmt.Errorf("error decoding OID Value: truncated, packet length %d cursor %d", len(packet), cursor)
1349}
1350
1351response.Variables = append(response.Variables, SnmpPDU{Name: oid, Type: decodedVal.Type, Value: decodedVal.Value})
1352}
1353return nil
1354}
1355
1356// receive response from network and read into a byte array
1357func (x *GoSNMP) receive() ([]byte, error) {
1358var n int
1359var err error
1360// If we are using UDP and unconnected socket, read the packet and
1361// disregard the source address.
1362if uconn, ok := x.Conn.(net.PacketConn); ok {
1363n, _, err = uconn.ReadFrom(x.rxBuf[:])
1364} else {
1365n, err = x.Conn.Read(x.rxBuf[:])
1366}
1367if err == io.EOF {
1368return nil, err
1369} else if err != nil {
1370return nil, fmt.Errorf("error reading from socket: %w", err)
1371}
1372
1373if n == rxBufSize {
1374// This should never happen unless we're using something like a unix domain socket.
1375return nil, fmt.Errorf("response buffer too small")
1376}
1377
1378resp := make([]byte, n)
1379copy(resp, x.rxBuf[:n])
1380return resp, nil
1381}
1382
1383func shrinkAndWriteUint(buf io.Writer, in int) error {
1384out, err := asn1.Marshal(in)
1385if err != nil {
1386return err
1387}
1388_, err = buf.Write(out)
1389return err
1390}
1391