bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/snmp/asn1/common.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package asn1 6 7 import ( 8 "reflect" 9 "strconv" 10 "strings" 11 ) 12 13 // ASN.1 objects have metadata preceding them: 14 // the tag: the type of the object 15 // a flag denoting if this object is compound or not 16 // the class type: the namespace of the tag 17 // the length of the object, in bytes 18 19 // Here are some standard tags and classes 20 21 const ( 22 tagBoolean = 1 23 tagInteger = 2 24 tagBitString = 3 25 tagOctetString = 4 26 tagOID = 6 27 tagEnum = 10 28 tagUTF8String = 12 29 tagSequence = 16 30 tagSet = 17 31 tagPrintableString = 19 32 tagT61String = 20 33 tagIA5String = 22 34 tagUTCTime = 23 35 tagGeneralizedTime = 24 36 tagGeneralString = 27 37 ) 38 39 const ( 40 classUniversal = 0 41 classApplication = 1 42 classContextSpecific = 2 43 classPrivate = 3 44 ) 45 46 type tagAndLength struct { 47 class, tag, length int 48 isCompound bool 49 } 50 51 // ASN.1 has IMPLICIT and EXPLICIT tags, which can be translated as "instead 52 // of" and "in addition to". When not specified, every primitive type has a 53 // default tag in the UNIVERSAL class. 54 // 55 // For example: a BIT STRING is tagged [UNIVERSAL 3] by default (although ASN.1 56 // doesn't actually have a UNIVERSAL keyword). However, by saying [IMPLICIT 57 // CONTEXT-SPECIFIC 42], that means that the tag is replaced by another. 58 // 59 // On the other hand, if it said [EXPLICIT CONTEXT-SPECIFIC 10], then an 60 // /additional/ tag would wrap the default tag. This explicit tag will have the 61 // compound flag set. 62 // 63 // (This is used in order to remove ambiguity with optional elements.) 64 // 65 // You can layer EXPLICIT and IMPLICIT tags to an arbitrary depth, however we 66 // don't support that here. We support a single layer of EXPLICIT or IMPLICIT 67 // tagging with tag strings on the fields of a structure. 68 69 // fieldParameters is the parsed representation of tag string from a structure field. 70 type fieldParameters struct { 71 optional bool // true iff the field is OPTIONAL 72 explicit bool // true iff an EXPLICIT tag is in use. 73 application bool // true iff an APPLICATION tag is in use. 74 defaultValue *int64 // a default value for INTEGER typed fields (maybe nil). 75 tag *int // the EXPLICIT or IMPLICIT tag (maybe nil). 76 stringType int // the string tag to use when marshaling. 77 timeType int // the time tag to use when marshaling. 78 set bool // true iff this should be encoded as a SET 79 omitEmpty bool // true iff this should be omitted if empty when marshaling. 80 81 // Invariants: 82 // if explicit is set, tag is non-nil. 83 } 84 85 // Given a tag string with the format specified in the package comment, 86 // parseFieldParameters will parse it into a fieldParameters structure, 87 // ignoring unknown parts of the string. 88 func parseFieldParameters(str string) (ret fieldParameters) { 89 for _, part := range strings.Split(str, ",") { 90 switch { 91 case part == "optional": 92 ret.optional = true 93 case part == "explicit": 94 ret.explicit = true 95 if ret.tag == nil { 96 ret.tag = new(int) 97 } 98 case part == "generalized": 99 ret.timeType = tagGeneralizedTime 100 case part == "utc": 101 ret.timeType = tagUTCTime 102 case part == "ia5": 103 ret.stringType = tagIA5String 104 case part == "printable": 105 ret.stringType = tagPrintableString 106 case part == "utf8": 107 ret.stringType = tagUTF8String 108 case strings.HasPrefix(part, "default:"): 109 i, err := strconv.ParseInt(part[8:], 10, 64) 110 if err == nil { 111 ret.defaultValue = new(int64) 112 *ret.defaultValue = i 113 } 114 case strings.HasPrefix(part, "tag:"): 115 i, err := strconv.Atoi(part[4:]) 116 if err == nil { 117 ret.tag = new(int) 118 *ret.tag = i 119 } 120 case part == "set": 121 ret.set = true 122 case part == "application": 123 ret.application = true 124 if ret.tag == nil { 125 ret.tag = new(int) 126 } 127 case part == "omitempty": 128 ret.omitEmpty = true 129 } 130 } 131 return 132 } 133 134 // Given a reflected Go type, getUniversalType returns the default tag number 135 // and expected compound flag. 136 func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) { 137 switch t { 138 case objectIdentifierType: 139 return tagOID, false, true 140 case bitStringType: 141 return tagBitString, false, true 142 case timeType: 143 return tagUTCTime, false, true 144 case enumeratedType: 145 return tagEnum, false, true 146 case bigIntType: 147 return tagInteger, false, true 148 } 149 switch t.Kind() { 150 case reflect.Bool: 151 return tagBoolean, false, true 152 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 153 return tagInteger, false, true 154 case reflect.Struct: 155 return tagSequence, true, true 156 case reflect.Slice: 157 if t.Elem().Kind() == reflect.Uint8 { 158 return tagOctetString, false, true 159 } 160 if strings.HasSuffix(t.Name(), "SET") { 161 return tagSet, true, true 162 } 163 return tagSequence, true, true 164 case reflect.String: 165 return tagPrintableString, false, true 166 } 167 return 0, false, false 168 }