diff --git a/debug/debug.go b/debug/debug.go index 7a756ae..24c1386 100644 --- a/debug/debug.go +++ b/debug/debug.go @@ -92,7 +92,7 @@ func dumpAttrs(w io.Writer, c *Config, p *radius.Packet) { case dictionary.AttributeInteger: switch len(attr) { case 4: - intVal := int(binary.BigEndian.Uint32(attr)) + intVal := uint(binary.BigEndian.Uint32(attr)) if dictAttr != nil { var matchedNames []string for _, value := range dictionary.ValuesByAttribute(searchValues, dictAttr.Name) { @@ -106,7 +106,7 @@ func dumpAttrs(w io.Writer, c *Config, p *radius.Packet) { break } } - attrStr = strconv.Itoa(intVal) + attrStr = strconv.FormatUint(uint64(intVal), 10) case 8: attrStr = strconv.Itoa(int(binary.BigEndian.Uint64(attr))) } diff --git a/dictionary/dictionary.go b/dictionary/dictionary.go index 057d77b..e7fd7bc 100644 --- a/dictionary/dictionary.go +++ b/dictionary/dictionary.go @@ -184,6 +184,37 @@ func (a *Attribute) Equals(o *Attribute) bool { return true } +func normalizedType(t AttributeType) AttributeType { + if t == AttributeString { + return AttributeOctets + } + + return t +} + +func (a *Attribute) MostlyEquals(o *Attribute) bool { + if a == o { + return true + } + if a == nil || o == nil { + return false + } + + if a.Name != o.Name || !a.OID.Equals(o.OID) { + return false + } + + if normalizedType(a.Type) != normalizedType(o.Type) { + return false + } + + if a.FlagEncrypt != o.FlagEncrypt || a.FlagHasTag != o.FlagHasTag || a.FlagConcat != o.FlagConcat { + return false + } + + return true +} + func (a *Attribute) GoString() string { var b bytes.Buffer b.WriteString("&dictionary.Attribute{") @@ -213,7 +244,7 @@ func (a *Attribute) GoString() string { type Value struct { Attribute string Name string - Number int + Number uint } type Vendor struct { @@ -227,6 +258,18 @@ type Vendor struct { Values []*Value } +func (a *Vendor) Equals(b *Vendor) bool { + if a == b { + return true + } + + if a == nil || b == nil { + return false + } + + return a.Name == b.Name && a.Number == a.Number +} + func (v *Vendor) GetTypeOctets() int { if v.TypeOctets == nil { return 1 diff --git a/dictionary/parser.go b/dictionary/parser.go index d25b5de..622d019 100644 --- a/dictionary/parser.go +++ b/dictionary/parser.go @@ -40,7 +40,8 @@ type Parser struct { // IgnoreIdenticalAttributes specifies whether identical attributes are // ignored, rather than a parse error being emitted. - IgnoreIdenticalAttributes bool + IgnoreIdenticalAttributes bool + IgnoreUnknownAttributeType bool } func (p *Parser) Parse(f File) (*Dictionary, error) { @@ -62,6 +63,7 @@ func (p *Parser) parse(dict *Dictionary, parsedFiles map[string]struct{}, f File lineNo := 1 for ; s.Scan(); lineNo++ { line := s.Text() + line = strings.TrimLeft(line, " ") if idx := strings.IndexByte(line, '#'); idx >= 0 { line = line[:idx] } @@ -74,6 +76,12 @@ func (p *Parser) parse(dict *Dictionary, parsedFiles map[string]struct{}, f File case (len(fields) == 4 || len(fields) == 5) && fields[0] == "ATTRIBUTE": attr, err := p.parseAttribute(fields) if err != nil { + switch err.(type) { + case *UnknownAttributeTypeError: + if p.IgnoreUnknownAttributeType { + continue + } + } return &ParseError{ Inner: err, File: f, @@ -88,7 +96,7 @@ func (p *Parser) parse(dict *Dictionary, parsedFiles map[string]struct{}, f File existing = AttributeByName(vendorBlock.Attributes, attr.Name) } if existing != nil { - if p.IgnoreIdenticalAttributes && attr.Equals(existing) { + if p.IgnoreIdenticalAttributes && attr.MostlyEquals(existing) { break } return &ParseError{ @@ -134,18 +142,10 @@ func (p *Parser) parse(dict *Dictionary, parsedFiles map[string]struct{}, f File } } - if existing := vendorByNameOrNumber(dict.Vendors, vendor.Name, vendor.Number); existing != nil { - return &ParseError{ - Inner: &DuplicateVendorError{ - Vendor: vendor, - }, - File: f, - Line: lineNo, - } + if existing := vendorByNameOrNumber(dict.Vendors, vendor.Name, vendor.Number); existing == nil || !vendor.Equals(existing) { + dict.Vendors = append(dict.Vendors, vendor) } - dict.Vendors = append(dict.Vendors, vendor) - case len(fields) == 2 && fields[0] == "BEGIN-VENDOR": // TODO: support RFC 6929 extended VSA? @@ -276,6 +276,16 @@ func (p *Parser) ParseFile(filename string) (*Dictionary, error) { func parseOID(s string) OID { var o OID + if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') { + num, err := strconv.ParseInt(s[2:], 16, 32) + if err != nil { + return nil + } + + o = append(o, int(num)) + return o + } + for i, ch := range s { switch ch { case '.': @@ -405,6 +415,8 @@ func (p *Parser) parseAttribute(f []string) (*Attribute, error) { Valid: true, Bool: true, } + case f == "virtual": + continue default: return nil, &UnknownAttributeFlagError{ Flag: f, @@ -424,11 +436,18 @@ func (p *Parser) parseValue(f []string) (*Value, error) { Name: f[2], } - number, err := strconv.ParseInt(f[3], 10, 32) + numStr := f[3] + base := 10 + if len(numStr) > 2 && (numStr[0] == '0' && (numStr[1] == 'x' || numStr[1] == 'X')) { + base = 16 + numStr = numStr[2:] + } + + number, err := strconv.ParseUint(numStr, base, 32) if err != nil { return nil, err } - value.Number = int(number) + value.Number = uint(number) return value, nil } @@ -450,7 +469,7 @@ func (p *Parser) parseVendor(f []string) (*Vendor, error) { // "format=t,l" // t ∈ [1, 2, 4] // l ∈ [0, 1, 2] - if !strings.HasPrefix(f[3], "format=") || len(f[3]) != 10 || f[3][8] != ',' || (f[3][7] != '1' && f[3][7] != '2' && f[3][7] != '4') || (f[3][9] < '0' && f[3][9] > '2') { + if !strings.HasPrefix(f[3], "format=") || len(f[3]) < 10 || f[3][8] != ',' || (f[3][7] != '1' && f[3][7] != '2' && f[3][7] != '4') || (f[3][9] < '0' || f[3][9] > '2') { return nil, &InvalidVendorFormatError{ Format: f[3], } diff --git a/dictionary/parser_test.go b/dictionary/parser_test.go index 58717a2..eff8415 100644 --- a/dictionary/parser_test.go +++ b/dictionary/parser_test.go @@ -80,6 +80,11 @@ func TestParser(t *testing.T) { Name: "Half", Number: 2, }, + { + Attribute: "Mode", + Name: "Quarter", + Number: 4, + }, }, } diff --git a/dictionary/testdata/simple.dictionary b/dictionary/testdata/simple.dictionary index 01b82c6..37f4229 100644 --- a/dictionary/testdata/simple.dictionary +++ b/dictionary/testdata/simple.dictionary @@ -1,8 +1,12 @@ ATTRIBUTE User-Name 1 string ATTRIBUTE User-Password 2 octets encrypt=1 +#Skip comment + #Skip comment with spaces in front + ATTRIBUTE Mode 127 integer VALUE Mode Full 1 VALUE Mode Half 2 +VALUE Mode Quarter 0x4 ATTRIBUTE ARAP-Challenge-Response 84 octets[8] diff --git a/dictionarygen/attributes.go b/dictionarygen/attributes.go index 0d27a9d..44b52e5 100644 --- a/dictionarygen/attributes.go +++ b/dictionarygen/attributes.go @@ -873,7 +873,7 @@ func (g *Generator) genAttributeInteger(w io.Writer, attr *dictionary.Attribute, p(w, `const (`) for _, value := range values { valueIdent := identifier(value.Name) - p(w, ` `, ident, `_Value_`, valueIdent, ` `, ident, ` = `, strconv.Itoa(value.Number)) + p(w, ` `, ident, `_Value_`, valueIdent, ` `, ident, ` = `, strconv.FormatUint(uint64(value.Number), 10)) } p(w, `)`) } diff --git a/dictionarygen/generator.go b/dictionarygen/generator.go index 11b8955..c7658b1 100644 --- a/dictionarygen/generator.go +++ b/dictionarygen/generator.go @@ -292,7 +292,7 @@ func (g *Generator) Generate(dict *dictionary.Dictionary) ([]byte, error) { for _, value := range exAttr.Values { attrIdent := identifier(value.Attribute) valueIdent := identifier(value.Name) - p(&w, ` `, attrIdent, `_Value_`, valueIdent, ` `, attrIdent, ` = `, strconv.Itoa(value.Number)) + p(&w, ` `, attrIdent, `_Value_`, valueIdent, ` `, attrIdent, ` = `, strconv.FormatUint(uint64(value.Number), 10)) } p(&w, `)`) }