// Parses a given field, return the ASN.1 BER Type, its header length and the data func parseField(data []byte) (*RawBER, error) { log := l.GetDefaultLogger() var err error if len(data) == 0 { return nil, fmt.Errorf("Unable to parse BER: Data length 0") } ber := new(RawBER) ber.Type = Asn1BER(data[0]) // Parse Length length := data[1] // Check if this is padded or not if length > 0x80 { length = length - 0x80 log.Debug("Field length is padded to %d bytes\n", length) ber.DataLength = Uvarint(data[2 : 2+length]) log.Debug("Decoded final length: %d\n", ber.DataLength) ber.HeaderLength = 2 + uint64(length) } else { ber.HeaderLength = 2 ber.DataLength = uint64(length) } // Do sanity checks if ber.DataLength > uint64(len(data)) { return nil, fmt.Errorf("Unable to parse BER: provided data length is longer than actual data (%d vs %d)", ber.DataLength, len(data)) } ber.Data = data[ber.HeaderLength : ber.HeaderLength+ber.DataLength] ber.BERVariable, err = decodeValue(ber.Type, ber.Data) if err != nil { return nil, fmt.Errorf("Unable to decode value: %s\n", err.Error()) } return ber, nil }
func Unmarshal(packet []byte) (*SnmpPacket, error) { log := l.GetDefaultLogger() //var err error response := new(SnmpPacket) response.Variables = make([]SnmpPDU, 0, 5) // Start parsing the packet cursor := 0 // First bytes should be 0x30 if MessageType(packet[0]) == Sequence { // Parse packet length var length int // length of structure is spread over two bytes if packet[1] == 0x82 { length = int(packet[2])<<8 | int(packet[3]) length += 4 // account for header + length cursor += 4 } else { length = int(packet[1]) length += 2 // account for header + length cursor += 2 } if len(packet) == length { log.Debug("Packet sanity verified, we got all the bytes (%d)\n", length) // Parse SNMP Version rawVersion, count, err := parseRawField(packet[cursor:]) if err != nil { return nil, fmt.Errorf("Error parsing SNMP packet version: %s", err.Error()) } cursor += count if version, ok := rawVersion.(int); ok { response.Version = SnmpVersion(version) } // Parse community rawCommunity, count, err := parseRawField(packet[cursor:]) cursor += count if community, ok := rawCommunity.(string); ok { response.Community = community log.Debug("Parsed community %s\n", community) } // Parse SNMP packet type switch MessageType(packet[cursor]) { case GetResponse: log.Debug("SNMP Packet is get response\n") response.RequestType = GetResponse // Response length (dont really care what the length is) if packet[cursor+1] == 0x82 { cursor += 4 } else { cursor += 2 } log.Debug("Response length: %d\n", length) // Parse Request ID rawRequestId, count, err := parseRawField(packet[cursor:]) if err != nil { return nil, fmt.Errorf("Error parsing SNMP packet request ID: %s", err.Error()) } cursor += count if requestid, ok := rawRequestId.(int); ok { response.RequestID = uint8(requestid) } // Parse Error rawError, count, err := parseRawField(packet[cursor:]) if err != nil { return nil, fmt.Errorf("Error parsing SNMP packet error: %s", err.Error()) } cursor += count if errorNo, ok := rawError.(int); ok { response.Error = uint8(errorNo) } // Parse Error Index rawErrorIndex, count, err := parseRawField(packet[cursor:]) if err != nil { return nil, fmt.Errorf("Error parsing SNMP packet error index: %s", err.Error()) } cursor += count if errorindex, ok := rawErrorIndex.(int); ok { response.ErrorIndex = uint8(errorindex) } log.Debug("Request ID: %d Error: %d Error Index: %d\n", response.RequestID, response.Error, response.ErrorIndex) // Varbind list if packet[cursor] == 0x30 && packet[cursor+1] == 0x82 { cursor += 4 } else { cursor += 2 } // Loop & parse Varbinds for cursor < length { log.Debug("Parsing var bind response (Cursor at %d/%d)", cursor, length) if packet[cursor] == 0x30 && packet[cursor+1] == 0x82 { cursor += 4 log.Debug("Padded Varbind length\n") } else { cursor += 2 } // Parse OID rawOid, count, err := parseRawField(packet[cursor:]) cursor += count log.Debug("OID (%v) Field was %d bytes\n", rawOid, count) var pduLength int var paddedLength int = 0 if packet[cursor+1] == 0x81 { pduLength = int(packet[cursor+2]) paddedLength = 1 log.Debug("Padded Variable length\n") } else { pduLength = int(packet[cursor+1]) } log.Debug("PDU Value length: %d\n", pduLength) v, err := decodeValue(packet[cursor : cursor+pduLength]) if err != nil { return nil, fmt.Errorf("Error parsing PDU Value: %s", err.Error()) } if oid, ok := rawOid.([]int); ok { response.Variables = append(response.Variables, SnmpPDU{oidToString(oid), v.Type, v.Value}) } cursor += pduLength + paddedLength + 2 } } } else { return nil, fmt.Errorf("Error verifying packet sanity: Got %d Expected: %d\n", len(packet), length) } } else { return nil, fmt.Errorf("Invalid packet header\n") } return response, nil }
func Unmarshal(packet []byte) (*SnmpPacket, error) { log := l.GetDefaultLogger() log.Debug("Begin SNMP Packet unmarshal\n") //var err error response := new(SnmpPacket) response.Variables = make([]SnmpPDU, 0, 5) // Start parsing the packet var cursor uint64 = 0 // First bytes should be 0x30 if Asn1BER(packet[0]) == Sequence { // Parse packet length ber, err := parseField(packet) if err != nil { log.Error("Unable to parse packet header: %s\n", err.Error()) return nil, err } log.Debug("Packet sanity verified, we got all the bytes (%d)\n", ber.DataLength) cursor += ber.HeaderLength // Parse SNMP Version rawVersion, err := parseField(packet[cursor:]) if err != nil { return nil, fmt.Errorf("Error parsing SNMP packet version: %s", err.Error()) } cursor += rawVersion.DataLength + rawVersion.HeaderLength if version, ok := rawVersion.BERVariable.Value.(int); ok { response.Version = SnmpVersion(version) log.Debug("Parsed Version %d\n", version) } // Parse community rawCommunity, err := parseField(packet[cursor:]) if err != nil { log.Debug("Unable to parse Community Field: %s\n", err) } cursor += rawCommunity.DataLength + rawCommunity.HeaderLength if community, ok := rawCommunity.BERVariable.Value.(string); ok { response.Community = community log.Debug("Parsed community %s\n", community) } rawPDU, err := parseField(packet[cursor:]) if err != nil { log.Debug("Unable to parse SNMP PDU: %s\n", err.Error()) } response.RequestType = rawPDU.Type switch rawPDU.Type { default: log.Debug("Unsupported SNMP Packet Type %s\n", rawPDU.Type.String()) log.Debug("PDU Size is %d\n", rawPDU.DataLength) case GetRequest, GetResponse, GetBulkRequest: log.Debug("SNMP Packet is %s\n", rawPDU.Type.String()) log.Debug("PDU Size is %d\n", rawPDU.DataLength) cursor += rawPDU.HeaderLength // Parse Request ID rawRequestId, err := parseField(packet[cursor:]) if err != nil { return nil, err } cursor += rawRequestId.DataLength + rawRequestId.HeaderLength if requestid, ok := rawRequestId.BERVariable.Value.(int); ok { response.RequestID = uint8(requestid) log.Debug("Parsed Request ID: %d\n", requestid) } // Parse Error rawError, err := parseField(packet[cursor:]) if err != nil { return nil, err } cursor += rawError.DataLength + rawError.HeaderLength if errorNo, ok := rawError.BERVariable.Value.(int); ok { response.Error = uint8(errorNo) } // Parse Error Index rawErrorIndex, err := parseField(packet[cursor:]) if err != nil { return nil, err } cursor += rawErrorIndex.DataLength + rawErrorIndex.HeaderLength if errorindex, ok := rawErrorIndex.BERVariable.Value.(int); ok { response.ErrorIndex = uint8(errorindex) } log.Debug("Request ID: %d Error: %d Error Index: %d\n", response.RequestID, response.Error, response.ErrorIndex) rawResp, err := parseField(packet[cursor:]) if err != nil { return nil, err } cursor += rawResp.HeaderLength // Loop & parse Varbinds for cursor < uint64(len(packet)) { log.Debug("Parsing var bind response (Cursor at %d/%d)", cursor, len(packet)) rawVarbind, err := parseField(packet[cursor:]) if err != nil { return nil, err } cursor += rawVarbind.HeaderLength log.Debug("Varbind length: %d/%d\n", rawVarbind.HeaderLength, rawVarbind.DataLength) log.Debug("Parsing OID (Cursor at %d)\n", cursor) // Parse OID rawOid, err := parseField(packet[cursor:]) if err != nil { return nil, err } cursor += rawOid.HeaderLength + rawOid.DataLength log.Debug("OID (%v) Field was %d bytes\n", rawOid, rawOid.DataLength) rawValue, err := parseField(packet[cursor:]) if err != nil { return nil, err } cursor += rawValue.HeaderLength + rawValue.DataLength log.Debug("Value field was %d bytes\n", rawValue.DataLength) if oid, ok := rawOid.BERVariable.Value.([]int); ok { log.Debug("Varbind decoding success\n") response.Variables = append(response.Variables, SnmpPDU{oidToString(oid), rawValue.Type, rawValue.BERVariable.Value}) } } } } else { return nil, fmt.Errorf("Invalid packet header\n") } return response, nil }