Skip to content
4 changes: 2 additions & 2 deletions debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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)))
}
Expand Down
45 changes: 44 additions & 1 deletion dictionary/dictionary.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{")
Expand Down Expand Up @@ -213,7 +244,7 @@ func (a *Attribute) GoString() string {
type Value struct {
Attribute string
Name string
Number int
Number uint
}

type Vendor struct {
Expand All @@ -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
Expand Down
49 changes: 34 additions & 15 deletions dictionary/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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]
}
Expand All @@ -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,
Expand All @@ -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{
Expand Down Expand Up @@ -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?

Expand Down Expand Up @@ -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 '.':
Expand Down Expand Up @@ -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,
Expand All @@ -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
}
Expand All @@ -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],
}
Expand Down
5 changes: 5 additions & 0 deletions dictionary/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ func TestParser(t *testing.T) {
Name: "Half",
Number: 2,
},
{
Attribute: "Mode",
Name: "Quarter",
Number: 4,
},
},
}

Expand Down
4 changes: 4 additions & 0 deletions dictionary/testdata/simple.dictionary
Original file line number Diff line number Diff line change
@@ -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]
2 changes: 1 addition & 1 deletion dictionarygen/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, `)`)
}
Expand Down
2 changes: 1 addition & 1 deletion dictionarygen/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, `)`)
}
Expand Down