-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patheti_server.py
executable file
·130 lines (105 loc) · 3.4 KB
/
eti_server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/env python3
# Dummy ETI demo server
#
#
# Requires at least Python 3.7.
#
# SPDX-FileCopyrightText: © 2021 Georg Sauthoff <mail@gms.tf>
# SPDX-License-Identifier: GPL-3.0-or-later
import argparse
import asyncio
import logging
import random
import struct
import sys
import eti.v13_0 as eti
from dressup import pformat
log = logging.getLogger(__name__)
len_st = struct.Struct('<I')
def set_seq_num(m, seq):
i = iter(m.__annotations__.items())
next(i)
zs = next(i)
header = m.__getattribute__(zs[0])
try:
header.__setattr__('MsgSeqNum', seq)
return seq + 1
except AttributeError:
# broadcast messages are out of sequence ...
return seq
def send_response(wstream, xs, seq, bs):
if not xs:
return seq
T, cond, zs = random.choice(xs)
if T is not None:
m = T()
seq = set_seq_num(m, seq)
m.update_length()
n = m.pack_into(bs)
log.info(f'Sending Response: {T} (n={n})')
wstream.write(memoryview(bs)[:n])
return send_response(wstream, zs, seq, bs)
def send_reject(wstream, text, seq, bs):
m = eti.Reject()
m.NRResponseHeaderME.MsgSeqNum = seq
m.SessionRejectReason = eti.SessionRejectReason.OTHER
m.VarText = text.encode()
m.VarTextLen = len(m.VarText)
m.update_length()
n = m.pack_into(bs)
log.info('Sending Reject')
wstream.write(memoryview(bs)[:n])
return seq + 1
async def serve_session(rstream, wstream):
seq = 1
buf = bytearray(1024)
global logon_count
lc = logon_count
logon_count += 1
try:
while True:
bs = await rstream.readexactly(4)
n = len_st.unpack(bs)[0]
rest = await rstream.readexactly(n - 4)
bs += rest
m = eti.unpack_from(bs)
m.rstrip()
log.info(f'Received: {pformat(m, width=45)}')
if lc == reject_nth_logon:
send_reject(wstream, 'You cannot logon until you have payed your bills!',
seq, buf)
await wstream.drain()
wstream.close()
await wstream.wait_closed()
log.info('rejected session, connection closed')
return
xs = eti.request2response[m.MessageHeaderIn.TemplateID]
seq = send_response(wstream, xs, seq, buf)
await wstream.drain()
except asyncio.IncompleteReadError:
log.info('Got EOF on read end')
async def server(host, port):
s = await asyncio.start_server(serve_session, host, port)
async with s:
await s.serve_forever()
def parse_args():
p = argparse.ArgumentParser(description='ETI example server')
p.add_argument('host', help='address to bind to')
p.add_argument('port', type=int, help='port to listen on')
p.add_argument('--reject-nth-logon', type=int, help='reject the nth logon (count start at 0)')
args = p.parse_args()
return args
def main():
args = parse_args()
global reject_nth_logon
reject_nth_logon = args.reject_nth_logon
global logon_count
logon_count = 0
logging.basicConfig(level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S',
format='%(asctime)s.%(msecs)03d [%(name)s] %(levelname).1s %(message)s')
asyncio.run(server(args.host, args.port))
if __name__ == '__main__':
try:
sys.exit(main())
except KeyboardInterrupt:
log.info('quitting due to SIGINT')