Skip to content

Commit

Permalink
support nested attributes and values in packet and dictionary
Browse files Browse the repository at this point in the history
  • Loading branch information
Pat-pGuo committed Dec 11, 2024
1 parent 22a9981 commit f4bc7b7
Show file tree
Hide file tree
Showing 10 changed files with 475 additions and 180 deletions.
2 changes: 1 addition & 1 deletion example/auth_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def test_auth1():
else:
reply = future.result()

if reply.code == AccessAccept:
if reply.number == AccessAccept:
print("Access accepted")
else:
print("Access denied")
Expand Down
8 changes: 1 addition & 7 deletions pyrad/datatypes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ def parse(self, dictionary: 'Dictionary', string: str,
"""

@abstractmethod
def get_value(self, dictionary: 'Dictionary', code: tuple[int, ...],
attribute: 'Attribute', packet: bytes,
offset: int) -> (tuple[((int, ...), bytes|dict), ...], int):
def get_value(self, attribute: 'Attribute', packet: bytes, offset: int) -> (tuple[((int, ...), bytes | dict), ...], int):
"""
gets encapsulated value
Expand All @@ -83,10 +81,6 @@ def get_value(self, dictionary: 'Dictionary', code: tuple[int, ...],
tuple of (key, value) pairs, a single bytestring or dict will be
returned.
:param dictionary: RADIUS dictionary
:type dictionary: pyrad.dictionary.Dictionary class
:param code: full OID of current attribute
:type code: tuple(int)
:param attribute: dictionary attribute
:type attribute: pyrad.dictionary.Attribute class
:param packet: entire packet bytestring
Expand Down
4 changes: 2 additions & 2 deletions pyrad/datatypes/leaf.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ def decode(self, raw: bytes, *args, **kwargs) -> any:
:return: python data structure
"""

def get_value(self, dictionary, code, attribute, packet, offset):
def get_value(self, attribute, packet, offset):
_, attr_len = struct.unpack('!BB', packet[offset:offset + 2])[0:2]
return ((code, packet[offset + 2:offset + attr_len]),), attr_len
return packet[offset + 2:offset + attr_len], attr_len

class AscendBinary(AbstractLeaf):
"""
Expand Down
48 changes: 20 additions & 28 deletions pyrad/datatypes/structural.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ def __init__(self):
def encode(self, attribute, decoded, *args, **kwargs):
encoding = b''
for key, value in decoded.items():
encoding += attribute.sub_attributes[key].encode(value, )
encoding += attribute.children[key].encode(value, )

if len(encoding) + 2 > 255:
raise ValueError('TLV length too long for one packet')

return (struct.pack('!B', attribute.code)
return (struct.pack('!B', attribute.number)
+ struct.pack('!B', len(encoding) + 2)
+ encoding)

def get_value(self, dictionary, code, attribute: 'Attribute', packet, offset):
def get_value(self, attribute: 'Attribute', packet, offset):
sub_attrs = {}

_, outer_len = struct.unpack('!BB', packet[offset:offset + 2])[0:2]
Expand All @@ -56,17 +56,15 @@ def get_value(self, dictionary, code, attribute: 'Attribute', packet, offset):
if sub_len < 3:
raise ValueError('TLV length field too small')

# future work will allow nested TLVs and structures. for now, TLVs
# must contain leaf attributes. As such, we can just extract the
# value from the packet
value = packet[cursor + 2:cursor + sub_len]
sub_attrs.setdefault(sub_type, []).append(value)
cursor += sub_len
return ((code, sub_attrs),), outer_len
sub_value, sub_offset = attribute[sub_type].get_value(packet, cursor)
sub_attrs.setdefault(sub_type, []).append(sub_value)

cursor += sub_offset
return sub_attrs, outer_len

def print(self, attribute, decoded, *args, **kwargs):
sub_attr_strings = [sub_attr.print()
for sub_attr in attribute.sub_attributes]
for sub_attr in attribute.children]
return f"{attribute.name} = {{ {', '.join(sub_attr_strings)} }}"

def parse(self, dictionary, string, *args, **kwargs):
Expand All @@ -86,44 +84,38 @@ def encode(self, attribute, decoded, *args, **kwargs):
encoding = b''

for key, value in decoded.items():
encoding += attribute.sub_attributes[key].encode(value, )
encoding += attribute.children[key].encode(value, )

return (struct.pack('!B', attribute.code)
return (struct.pack('!B', attribute.number)
+ struct.pack('!B', len(encoding) + 4)
+ struct.pack('!L', attribute.vendor)
+ encoding)

def get_value(self, dictionary, code, attribute, packet, offset):
def get_value(self, attribute: 'Attribute', packet, offset):
values = {}

# currently, a list of (code, value) pair is returned. with the v4
# update, a single (nested) object will be returned
values = []
# values = []

(_, length) = struct.unpack('!BB', packet[offset:offset + 2])
if length < 8:
return ((26, packet[offset + 2:offset + length]),), length
return {packet[offset + 2:offset + length]: {}}, length

vendor = struct.unpack('!L', packet[offset + 2:offset + 6])
vendor = struct.unpack('!L', packet[offset + 2:offset + 6])[0]

cursor = offset + 6
while cursor < offset + length:
(sub_type, _) = struct.unpack('!BB', packet[cursor:cursor + 2])

# first, using the vendor ID and sub attribute type, get the name
# of the sub attribute. then, using the name, get the Attribute
# object to call .get_value(...)
sub_attr_name = dictionary.attrindex.GetBackward(vendor + (sub_type,))
sub_attr = dictionary.attributes[sub_attr_name]

(sub_value, sub_offset) = sub_attr.get_value(dictionary, (vendor + (sub_type,)), packet, cursor)

values += sub_value
values[sub_type], sub_offset = attribute[vendor][sub_type].get_value(packet, cursor)
cursor += sub_offset

return values, length
return {vendor: values}, length

def print(self, attribute, decoded, *args, **kwargs):
sub_attr_strings = [sub_attr.print()
for sub_attr in attribute.sub_attributes]
for sub_attr in attribute.children]
return f"Vendor-Specific = {{ {attribute.vendor} = {{ {', '.join(sub_attr_strings)} }}"

def parse(self, dictionary, string, *args, **kwargs):
Expand Down
Loading

0 comments on commit f4bc7b7

Please sign in to comment.