pyrad 2.3__py3-none-any.whl → 2.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- docs/Makefile +20 -0
- docs/make.bat +36 -0
- docs/source/_static/logo.png +0 -0
- docs/source/api/client.rst +10 -0
- docs/source/api/dictionary.rst +10 -0
- docs/source/api/host.rst +7 -0
- docs/source/api/packet.rst +48 -0
- docs/source/api/proxy.rst +7 -0
- docs/source/api/server.rst +13 -0
- docs/source/conf.py +158 -0
- docs/source/index.rst +75 -0
- example/acct.py +41 -0
- example/auth.py +37 -0
- example/auth_async.py +164 -0
- example/client-coa.py +61 -0
- example/coa.py +40 -0
- example/dictionary +405 -0
- example/dictionary.freeradius +91 -0
- example/pyrad.log +0 -0
- example/server.py +68 -0
- example/server_async.py +117 -0
- example/status.py +26 -0
- pyrad/__init__.py +3 -3
- pyrad/client.py +54 -9
- pyrad/client_async.py +22 -14
- pyrad/dictfile.py +2 -5
- pyrad/dictionary.py +12 -1
- pyrad/host.py +1 -1
- pyrad/packet.py +208 -133
- pyrad/proxy.py +2 -2
- pyrad/server.py +3 -7
- pyrad/server_async.py +4 -5
- pyrad/tests/__init__.py +2 -2
- pyrad/tests/mock.py +5 -1
- pyrad/tests/{testBidict.py → test_bidict.py} +2 -2
- pyrad/tests/{testClient.py → test_client.py} +28 -30
- pyrad/tests/{testDictionary.py → test_dictionary.py} +38 -21
- pyrad/tests/{testHost.py → test_host.py} +10 -10
- pyrad/tests/test_packet.py +679 -0
- pyrad/tests/{testProxy.py → test_proxy.py} +11 -11
- pyrad/tests/{testServer.py → test_server.py} +35 -33
- pyrad/tests/test_tools.py +126 -0
- pyrad/tools.py +254 -158
- {pyrad-2.3.dist-info → pyrad-2.5.0.dist-info}/METADATA +44 -20
- pyrad-2.5.0.dist-info/RECORD +51 -0
- {pyrad-2.3.dist-info → pyrad-2.5.0.dist-info}/WHEEL +1 -1
- {pyrad-2.3.dist-info → pyrad-2.5.0.dist-info/licenses}/LICENSE.txt +2 -1
- pyrad-2.5.0.dist-info/top_level.txt +3 -0
- pyrad/tests/testPacket.py +0 -530
- pyrad/tests/testTools.py +0 -122
- pyrad-2.3.dist-info/RECORD +0 -29
- pyrad-2.3.dist-info/top_level.txt +0 -1
- {pyrad-2.3.dist-info → pyrad-2.5.0.dist-info}/zip-safe +0 -0
example/server_async.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import traceback
|
|
7
|
+
from pyrad.dictionary import Dictionary
|
|
8
|
+
from pyrad.server_async import ServerAsync
|
|
9
|
+
from pyrad.packet import AccessAccept
|
|
10
|
+
from pyrad.server import RemoteHost
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
import uvloop
|
|
14
|
+
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
|
15
|
+
except:
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
logging.basicConfig(level="DEBUG",
|
|
19
|
+
format="%(asctime)s [%(levelname)-8s] %(message)s")
|
|
20
|
+
|
|
21
|
+
class FakeServer(ServerAsync):
|
|
22
|
+
|
|
23
|
+
def __init__(self, loop, dictionary):
|
|
24
|
+
|
|
25
|
+
ServerAsync.__init__(self, loop=loop, dictionary=dictionary,
|
|
26
|
+
enable_pkt_verify=True, debug=True)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def handle_auth_packet(self, protocol, pkt, addr):
|
|
30
|
+
|
|
31
|
+
print("Received an authentication request with id ", pkt.id)
|
|
32
|
+
print('Authenticator ', pkt.authenticator.hex())
|
|
33
|
+
print('Secret ', pkt.secret)
|
|
34
|
+
print("Attributes: ")
|
|
35
|
+
for attr in pkt.keys():
|
|
36
|
+
print("%s: %s" % (attr, pkt[attr]))
|
|
37
|
+
|
|
38
|
+
reply = self.CreateReplyPacket(pkt, **{
|
|
39
|
+
"Service-Type": "Framed-User",
|
|
40
|
+
"Framed-IP-Address": '192.168.0.1',
|
|
41
|
+
"Framed-IPv6-Prefix": "fc66::1/64"
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
reply.code = AccessAccept
|
|
45
|
+
protocol.send_response(reply, addr)
|
|
46
|
+
|
|
47
|
+
def handle_acct_packet(self, protocol, pkt, addr):
|
|
48
|
+
|
|
49
|
+
print("Received an accounting request")
|
|
50
|
+
print("Attributes: ")
|
|
51
|
+
for attr in pkt.keys():
|
|
52
|
+
print("%s: %s" % (attr, pkt[attr]))
|
|
53
|
+
|
|
54
|
+
reply = self.CreateReplyPacket(pkt)
|
|
55
|
+
protocol.send_response(reply, addr)
|
|
56
|
+
|
|
57
|
+
def handle_coa_packet(self, protocol, pkt, addr):
|
|
58
|
+
|
|
59
|
+
print("Received an coa request")
|
|
60
|
+
print("Attributes: ")
|
|
61
|
+
for attr in pkt.keys():
|
|
62
|
+
print("%s: %s" % (attr, pkt[attr]))
|
|
63
|
+
|
|
64
|
+
reply = self.CreateReplyPacket(pkt)
|
|
65
|
+
protocol.send_response(reply, addr)
|
|
66
|
+
|
|
67
|
+
def handle_disconnect_packet(self, protocol, pkt, addr):
|
|
68
|
+
|
|
69
|
+
print("Received an disconnect request")
|
|
70
|
+
print("Attributes: ")
|
|
71
|
+
for attr in pkt.keys():
|
|
72
|
+
print("%s: %s" % (attr, pkt[attr]))
|
|
73
|
+
|
|
74
|
+
reply = self.CreateReplyPacket(pkt)
|
|
75
|
+
# COA NAK
|
|
76
|
+
reply.code = 45
|
|
77
|
+
protocol.send_response(reply, addr)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if __name__ == '__main__':
|
|
81
|
+
|
|
82
|
+
# create server and read dictionary
|
|
83
|
+
loop = asyncio.get_event_loop()
|
|
84
|
+
server = FakeServer(loop=loop, dictionary=Dictionary('dictionary'))
|
|
85
|
+
|
|
86
|
+
# add clients (address, secret, name)
|
|
87
|
+
server.hosts["127.0.0.1"] = RemoteHost("127.0.0.1",
|
|
88
|
+
b"Kah3choteereethiejeimaeziecumi",
|
|
89
|
+
"localhost")
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
|
|
93
|
+
# Initialize transports
|
|
94
|
+
loop.run_until_complete(
|
|
95
|
+
asyncio.ensure_future(
|
|
96
|
+
server.initialize_transports(enable_auth=True,
|
|
97
|
+
enable_acct=True,
|
|
98
|
+
enable_coa=True)))
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
# start server
|
|
102
|
+
loop.run_forever()
|
|
103
|
+
except KeyboardInterrupt as k:
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
# Close transports
|
|
107
|
+
loop.run_until_complete(asyncio.ensure_future(
|
|
108
|
+
server.deinitialize_transports()))
|
|
109
|
+
|
|
110
|
+
except Exception as exc:
|
|
111
|
+
print('Error: ', exc)
|
|
112
|
+
print('\n'.join(traceback.format_exc().splitlines()))
|
|
113
|
+
# Close transports
|
|
114
|
+
loop.run_until_complete(asyncio.ensure_future(
|
|
115
|
+
server.deinitialize_transports()))
|
|
116
|
+
|
|
117
|
+
loop.close()
|
example/status.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
from pyrad.client import Client
|
|
3
|
+
from pyrad.dictionary import Dictionary
|
|
4
|
+
import socket
|
|
5
|
+
import sys
|
|
6
|
+
import pyrad.packet
|
|
7
|
+
|
|
8
|
+
srv = Client(server="localhost", authport=18121, secret=b"test", dict=Dictionary("dictionary"))
|
|
9
|
+
|
|
10
|
+
req = srv.CreateAuthPacket(code=pyrad.packet.StatusServer)
|
|
11
|
+
req["FreeRADIUS-Statistics-Type"] = "All"
|
|
12
|
+
req.add_message_authenticator()
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
print("Sending FreeRADIUS status request")
|
|
16
|
+
reply = srv.SendPacket(req)
|
|
17
|
+
except pyrad.client.Timeout:
|
|
18
|
+
print("RADIUS server does not reply")
|
|
19
|
+
sys.exit(1)
|
|
20
|
+
except socket.error as error:
|
|
21
|
+
print("Network error: " + error[1])
|
|
22
|
+
sys.exit(1)
|
|
23
|
+
|
|
24
|
+
print("Attributes returned by server:")
|
|
25
|
+
for i in reply.keys():
|
|
26
|
+
print("%s: %s" % (i, reply[i]))
|
pyrad/__init__.py
CHANGED
|
@@ -38,9 +38,9 @@ This package contains four modules:
|
|
|
38
38
|
|
|
39
39
|
__docformat__ = 'epytext en'
|
|
40
40
|
|
|
41
|
-
__author__ = 'Christian Giese <
|
|
41
|
+
__author__ = 'Christian Giese <gic@gicnet.de>, Istvan Ruzman <istvan@ruzman.eu> and Stefan Lieberth <stefan@lieberth.net>'
|
|
42
42
|
__url__ = 'http://pyrad.readthedocs.io/en/latest/?badge=latest'
|
|
43
|
-
__copyright__ = 'Copyright 2002-
|
|
44
|
-
__version__ = '2.
|
|
43
|
+
__copyright__ = 'Copyright 2002-2026 Wichert Akkerman, Christian Giese, Istvan Ruzman and Stefan Lieberth. All rights reserved.'
|
|
44
|
+
__version__ = '2.5.0'
|
|
45
45
|
|
|
46
46
|
__all__ = ['client', 'dictionary', 'packet', 'server', 'tools', 'dictfile']
|
pyrad/client.py
CHANGED
|
@@ -4,13 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
__docformat__ = "epytext en"
|
|
6
6
|
|
|
7
|
+
import hashlib
|
|
7
8
|
import select
|
|
8
9
|
import socket
|
|
9
10
|
import time
|
|
11
|
+
import struct
|
|
10
12
|
import six
|
|
11
13
|
from pyrad import host
|
|
12
14
|
from pyrad import packet
|
|
13
15
|
|
|
16
|
+
EAP_CODE_REQUEST = 1
|
|
17
|
+
EAP_CODE_RESPONSE = 2
|
|
18
|
+
EAP_TYPE_IDENTITY = 1
|
|
19
|
+
|
|
14
20
|
|
|
15
21
|
class Timeout(Exception):
|
|
16
22
|
"""Simple exception class which is raised when a timeout occurs
|
|
@@ -26,11 +32,10 @@ class Client(host.Host):
|
|
|
26
32
|
:ivar retries: number of times to retry sending a RADIUS request
|
|
27
33
|
:type retries: integer
|
|
28
34
|
:ivar timeout: number of seconds to wait for an answer
|
|
29
|
-
:type timeout:
|
|
35
|
+
:type timeout: float
|
|
30
36
|
"""
|
|
31
37
|
def __init__(self, server, authport=1812, acctport=1813,
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
coaport=3799, secret=six.b(''), dict=None, retries=3, timeout=5, enforce_ma=False):
|
|
34
39
|
"""Constructor.
|
|
35
40
|
|
|
36
41
|
:param server: hostname or IP address of RADIUS server
|
|
@@ -45,14 +50,17 @@ class Client(host.Host):
|
|
|
45
50
|
:type secret: string
|
|
46
51
|
:param dict: RADIUS dictionary
|
|
47
52
|
:type dict: pyrad.dictionary.Dictionary
|
|
53
|
+
:param enforce_ma: Enforce usage and check of Message-Authenticator
|
|
54
|
+
:type enforce_ma: boolean
|
|
48
55
|
"""
|
|
49
56
|
host.Host.__init__(self, authport, acctport, coaport, dict)
|
|
50
57
|
|
|
51
58
|
self.server = server
|
|
52
59
|
self.secret = secret
|
|
53
60
|
self._socket = None
|
|
54
|
-
self.retries =
|
|
55
|
-
self.timeout =
|
|
61
|
+
self.retries = retries
|
|
62
|
+
self.timeout = timeout
|
|
63
|
+
self.enforce_ma = enforce_ma
|
|
56
64
|
self._poll = select.poll()
|
|
57
65
|
|
|
58
66
|
def bind(self, addr):
|
|
@@ -69,12 +77,12 @@ class Client(host.Host):
|
|
|
69
77
|
|
|
70
78
|
def _SocketOpen(self):
|
|
71
79
|
try:
|
|
72
|
-
family = socket.getaddrinfo(self.server,
|
|
73
|
-
except:
|
|
80
|
+
family = socket.getaddrinfo(self.server, 80)[0][0]
|
|
81
|
+
except Exception:
|
|
74
82
|
family = socket.AF_INET
|
|
75
83
|
if not self._socket:
|
|
76
84
|
self._socket = socket.socket(family,
|
|
77
|
-
|
|
85
|
+
socket.SOCK_DGRAM)
|
|
78
86
|
self._socket.setsockopt(socket.SOL_SOCKET,
|
|
79
87
|
socket.SO_REUSEADDR, 1)
|
|
80
88
|
self._poll.register(self._socket, select.POLLIN)
|
|
@@ -95,6 +103,9 @@ class Client(host.Host):
|
|
|
95
103
|
:return: a new empty packet instance
|
|
96
104
|
:rtype: pyrad.packet.AuthPacket
|
|
97
105
|
"""
|
|
106
|
+
if self.enforce_ma:
|
|
107
|
+
return host.Host.CreateAuthPacket(self, secret=self.secret,
|
|
108
|
+
message_authenticator=True, **args)
|
|
98
109
|
return host.Host.CreateAuthPacket(self, secret=self.secret, **args)
|
|
99
110
|
|
|
100
111
|
def CreateAcctPacket(self, **args):
|
|
@@ -159,6 +170,8 @@ class Client(host.Host):
|
|
|
159
170
|
try:
|
|
160
171
|
reply = pkt.CreateReply(packet=rawreply)
|
|
161
172
|
if pkt.VerifyReply(reply, rawreply):
|
|
173
|
+
if hasattr(pkt, 'authenticator'):
|
|
174
|
+
reply.request_authenticator = pkt.authenticator
|
|
162
175
|
return reply
|
|
163
176
|
except packet.PacketError:
|
|
164
177
|
pass
|
|
@@ -177,7 +190,39 @@ class Client(host.Host):
|
|
|
177
190
|
:raise Timeout: RADIUS server does not reply
|
|
178
191
|
"""
|
|
179
192
|
if isinstance(pkt, packet.AuthPacket):
|
|
180
|
-
|
|
193
|
+
if pkt.auth_type == 'eap-md5':
|
|
194
|
+
# Creating EAP-Identity
|
|
195
|
+
password = pkt[2][0] if 2 in pkt else pkt[1][0]
|
|
196
|
+
pkt[79] = [struct.pack('!BBHB%ds' % len(password),
|
|
197
|
+
EAP_CODE_RESPONSE,
|
|
198
|
+
packet.CurrentID,
|
|
199
|
+
len(password) + 5,
|
|
200
|
+
EAP_TYPE_IDENTITY,
|
|
201
|
+
password)]
|
|
202
|
+
reply = self._SendPacket(pkt, self.authport)
|
|
203
|
+
if (
|
|
204
|
+
reply
|
|
205
|
+
and reply.code == packet.AccessChallenge
|
|
206
|
+
and pkt.auth_type == 'eap-md5'
|
|
207
|
+
):
|
|
208
|
+
# Got an Access-Challenge
|
|
209
|
+
eap_code, eap_id, eap_size, eap_type, eap_md5 = struct.unpack(
|
|
210
|
+
'!BBHB%ds' % (len(reply[79][0]) - 5), reply[79][0]
|
|
211
|
+
)
|
|
212
|
+
# Sending back an EAP-Type-MD5-Challenge
|
|
213
|
+
# Thank god for http://www.secdev.org/python/eapy.py
|
|
214
|
+
client_pw = pkt[2][0] if 2 in pkt else pkt[1][0]
|
|
215
|
+
md5_challenge = hashlib.md5(
|
|
216
|
+
struct.pack('!B', eap_id) + client_pw + eap_md5[1:]
|
|
217
|
+
).digest()
|
|
218
|
+
pkt[79] = [
|
|
219
|
+
struct.pack('!BBHBB', 2, eap_id, len(md5_challenge) + 6,
|
|
220
|
+
4, len(md5_challenge)) + md5_challenge
|
|
221
|
+
]
|
|
222
|
+
# Copy over Challenge-State
|
|
223
|
+
pkt[24] = reply[24]
|
|
224
|
+
reply = self._SendPacket(pkt, self.authport)
|
|
225
|
+
return reply
|
|
181
226
|
elif isinstance(pkt, packet.CoAPacket):
|
|
182
227
|
return self._SendPacket(pkt, self.coaport)
|
|
183
228
|
else:
|
pyrad/client_async.py
CHANGED
|
@@ -6,7 +6,6 @@ __docformat__ = "epytext en"
|
|
|
6
6
|
|
|
7
7
|
from datetime import datetime
|
|
8
8
|
import asyncio
|
|
9
|
-
import six
|
|
10
9
|
import logging
|
|
11
10
|
import random
|
|
12
11
|
|
|
@@ -92,12 +91,10 @@ class DatagramProtocolClient(asyncio.Protocol):
|
|
|
92
91
|
def connection_made(self, transport):
|
|
93
92
|
self.transport = transport
|
|
94
93
|
socket = transport.get_extra_info('socket')
|
|
95
|
-
self.logger.info(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
socket.getsockname()[1]
|
|
100
|
-
)
|
|
94
|
+
self.logger.info('[%s:%d] Transport created with binding in %s:%d',
|
|
95
|
+
self.server, self.port,
|
|
96
|
+
socket.getsockname()[0],
|
|
97
|
+
socket.getsockname()[1])
|
|
101
98
|
|
|
102
99
|
pre_loop = asyncio.get_event_loop()
|
|
103
100
|
asyncio.set_event_loop(loop=self.client.loop)
|
|
@@ -121,21 +118,21 @@ class DatagramProtocolClient(asyncio.Protocol):
|
|
|
121
118
|
try:
|
|
122
119
|
reply = Packet(packet=data, dict=self.client.dict)
|
|
123
120
|
|
|
124
|
-
if reply and reply.id in self.pending_requests:
|
|
121
|
+
if reply.code and reply.id in self.pending_requests:
|
|
125
122
|
req = self.pending_requests[reply.id]
|
|
126
123
|
packet = req['packet']
|
|
127
124
|
|
|
128
125
|
reply.dict = packet.dict
|
|
129
126
|
reply.secret = packet.secret
|
|
130
127
|
|
|
131
|
-
if packet.VerifyReply(reply, data):
|
|
128
|
+
if packet.VerifyReply(reply, data, enforce_ma=self.client.enforce_ma):
|
|
132
129
|
req['future'].set_result(reply)
|
|
133
130
|
# Remove request for map
|
|
134
131
|
del self.pending_requests[reply.id]
|
|
135
132
|
else:
|
|
136
|
-
self.logger.warn('[%s:%d] Ignore invalid reply for id %d
|
|
133
|
+
self.logger.warn('[%s:%d] Ignore invalid reply for id %d: %s', self.server, self.port, reply.id, data)
|
|
137
134
|
else:
|
|
138
|
-
self.logger.warn('[%s:%d] Ignore invalid reply: %
|
|
135
|
+
self.logger.warn('[%s:%d] Ignore invalid reply: %s', self.server, self.port, data)
|
|
139
136
|
|
|
140
137
|
except Exception as exc:
|
|
141
138
|
self.logger.error('[%s:%d] Error on decode packet: %s', self.server, self.port, exc)
|
|
@@ -175,9 +172,9 @@ class ClientAsync:
|
|
|
175
172
|
"""
|
|
176
173
|
# noinspection PyShadowingBuiltins
|
|
177
174
|
def __init__(self, server, auth_port=1812, acct_port=1813,
|
|
178
|
-
coa_port=3799, secret=
|
|
175
|
+
coa_port=3799, secret=b'', dict=None,
|
|
179
176
|
loop=None, retries=3, timeout=30,
|
|
180
|
-
logger_name='pyrad'):
|
|
177
|
+
logger_name='pyrad', enforce_ma=False):
|
|
181
178
|
|
|
182
179
|
"""Constructor.
|
|
183
180
|
|
|
@@ -216,6 +213,7 @@ class ClientAsync:
|
|
|
216
213
|
|
|
217
214
|
self.protocol_coa = None
|
|
218
215
|
self.coa_port = coa_port
|
|
216
|
+
self.enforce_ma = enforce_ma
|
|
219
217
|
|
|
220
218
|
async def initialize_transports(self, enable_acct=False,
|
|
221
219
|
enable_auth=False, enable_coa=False,
|
|
@@ -325,6 +323,11 @@ class ClientAsync:
|
|
|
325
323
|
"""
|
|
326
324
|
if not self.protocol_auth:
|
|
327
325
|
raise Exception('Transport not initialized')
|
|
326
|
+
if self.enforce_ma:
|
|
327
|
+
return AuthPacket(dict=self.dict,
|
|
328
|
+
id=self.protocol_auth.create_id(),
|
|
329
|
+
secret=self.secret,
|
|
330
|
+
message_authenticator=True, **args)
|
|
328
331
|
|
|
329
332
|
return AuthPacket(dict=self.dict,
|
|
330
333
|
id=self.protocol_auth.create_id(),
|
|
@@ -360,7 +363,7 @@ class ClientAsync:
|
|
|
360
363
|
:rtype: pyrad.packet.Packet
|
|
361
364
|
"""
|
|
362
365
|
|
|
363
|
-
if not self.
|
|
366
|
+
if not self.protocol_coa:
|
|
364
367
|
raise Exception('Transport not initialized')
|
|
365
368
|
|
|
366
369
|
return CoAPacket(id=self.protocol_coa.create_id(),
|
|
@@ -398,9 +401,14 @@ class ClientAsync:
|
|
|
398
401
|
if not self.protocol_acct:
|
|
399
402
|
raise Exception('Transport not initialized')
|
|
400
403
|
|
|
404
|
+
self.protocol_acct.send_packet(pkt, ans)
|
|
405
|
+
|
|
401
406
|
elif isinstance(pkt, CoAPacket):
|
|
402
407
|
if not self.protocol_coa:
|
|
403
408
|
raise Exception('Transport not initialized')
|
|
409
|
+
|
|
410
|
+
self.protocol_coa.send_packet(pkt, ans)
|
|
411
|
+
|
|
404
412
|
else:
|
|
405
413
|
raise Exception('Unsupported packet')
|
|
406
414
|
|
pyrad/dictfile.py
CHANGED
|
@@ -9,7 +9,6 @@ RADIUS $INCLUDE directives behind the scene.
|
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
import os
|
|
12
|
-
import six
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
class _Node(object):
|
|
@@ -54,10 +53,8 @@ class DictFile(object):
|
|
|
54
53
|
self.__ReadNode(fil)
|
|
55
54
|
|
|
56
55
|
def __ReadNode(self, fil):
|
|
57
|
-
node = None
|
|
58
56
|
parentdir = self.__CurDir()
|
|
59
|
-
if isinstance(fil,
|
|
60
|
-
fname = None
|
|
57
|
+
if isinstance(fil, str):
|
|
61
58
|
if os.path.isabs(fil):
|
|
62
59
|
fname = fil
|
|
63
60
|
else:
|
|
@@ -105,7 +102,7 @@ class DictFile(object):
|
|
|
105
102
|
def __next__(self):
|
|
106
103
|
while self.stack:
|
|
107
104
|
line = self.stack[-1].Next()
|
|
108
|
-
if line
|
|
105
|
+
if line is None:
|
|
109
106
|
self.stack.pop()
|
|
110
107
|
else:
|
|
111
108
|
inc = self.__GetInclude(line)
|
pyrad/dictionary.py
CHANGED
|
@@ -75,7 +75,6 @@ from pyrad import bidict
|
|
|
75
75
|
from pyrad import tools
|
|
76
76
|
from pyrad import dictfile
|
|
77
77
|
from copy import copy
|
|
78
|
-
import logging
|
|
79
78
|
|
|
80
79
|
__docformat__ = 'epytext en'
|
|
81
80
|
|
|
@@ -220,6 +219,18 @@ class Dictionary(object):
|
|
|
220
219
|
(attribute, code, datatype) = tokens[1:4]
|
|
221
220
|
|
|
222
221
|
codes = code.split('.')
|
|
222
|
+
|
|
223
|
+
# Codes can be sent as hex, or octal or decimal string representations.
|
|
224
|
+
tmp = []
|
|
225
|
+
for c in codes:
|
|
226
|
+
if c.startswith('0x'):
|
|
227
|
+
tmp.append(int(c, 16))
|
|
228
|
+
elif c.startswith('0o'):
|
|
229
|
+
tmp.append(int(c, 8))
|
|
230
|
+
else:
|
|
231
|
+
tmp.append(int(c, 10))
|
|
232
|
+
codes = tmp
|
|
233
|
+
|
|
223
234
|
is_sub_attribute = (len(codes) > 1)
|
|
224
235
|
if len(codes) == 2:
|
|
225
236
|
code = int(codes[1])
|
pyrad/host.py
CHANGED
|
@@ -57,7 +57,7 @@ class Host(object):
|
|
|
57
57
|
|
|
58
58
|
def CreateAcctPacket(self, **args):
|
|
59
59
|
"""Create a new accounting RADIUS packet.
|
|
60
|
-
This utility function creates a new
|
|
60
|
+
This utility function creates a new accounting RADIUS packet
|
|
61
61
|
which can be used to communicate with the RADIUS server this
|
|
62
62
|
client talks to. This is initializing the new packet with the
|
|
63
63
|
dictionary and secret used for the client.
|