GNServer 0.0.0.0.47__tar.gz → 0.0.0.0.49__tar.gz
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.
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/GNServer/GNServer/_app.py +31 -7
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/GNServer/GNServer/_client.py +23 -13
- gnserver-0.0.0.0.49/GNServer/GNServer/models.py +69 -0
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/GNServer/GNServer.egg-info/PKG-INFO +1 -1
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/GNServer/GNServer.egg-info/SOURCES.txt +1 -0
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/PKG-INFO +1 -1
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/setup.py +1 -1
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/GNServer/GNServer/__init__.py +0 -0
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/GNServer/GNServer/_cors_resolver.py +0 -0
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/GNServer/GNServer/_crt.py +0 -0
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/GNServer/GNServer/_func_params_validation.py +0 -0
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/GNServer/GNServer/_routes.py +0 -0
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/GNServer/GNServer/_template_resolver.py +0 -0
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/GNServer/GNServer.egg-info/dependency_links.txt +0 -0
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/GNServer/GNServer.egg-info/requires.txt +0 -0
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/GNServer/GNServer.egg-info/top_level.txt +0 -0
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/GNServer/LICENSE +0 -0
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/GNServer/mmbConfig.json +0 -0
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/LICENSE +0 -0
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/MANIFEST.in +0 -0
- {gnserver-0.0.0.0.47 → gnserver-0.0.0.0.49}/setup.cfg +0 -0
@@ -7,7 +7,7 @@ import asyncio
|
|
7
7
|
import inspect
|
8
8
|
import traceback
|
9
9
|
import datetime
|
10
|
-
from typing import Any, Callable, Dict, List, Optional, Tuple, Union, AsyncGenerator
|
10
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union, AsyncGenerator, Awaitable
|
11
11
|
from aioquic.asyncio.server import serve
|
12
12
|
from aioquic.asyncio.protocol import QuicConnectionProtocol
|
13
13
|
from aioquic.quic.configuration import QuicConfiguration
|
@@ -24,6 +24,7 @@ from ._cors_resolver import resolve_cors
|
|
24
24
|
|
25
25
|
from ._routes import Route, _compile_path, _ensure_async, _convert_value
|
26
26
|
|
27
|
+
from .models import KDCObject
|
27
28
|
|
28
29
|
|
29
30
|
try:
|
@@ -120,6 +121,13 @@ class App:
|
|
120
121
|
|
121
122
|
self.domain: str = None # type: ignore
|
122
123
|
|
124
|
+
self.__allowed_modes = (1, 2, 4)
|
125
|
+
|
126
|
+
self._kdc: Optional[KDCObject] = None
|
127
|
+
|
128
|
+
def setKDC(self, kdc: KDCObject):
|
129
|
+
self._kdc = kdc
|
130
|
+
|
123
131
|
def route(self, method: str, path: str, cors: Optional[CORSObject] = None):
|
124
132
|
if path == '/':
|
125
133
|
path = ''
|
@@ -158,7 +166,7 @@ class App:
|
|
158
166
|
|
159
167
|
|
160
168
|
def addEventListener(self, name: str):
|
161
|
-
def decorator(fn: Callable[[
|
169
|
+
def decorator(fn: Callable[[Callable[[dict | None], Awaitable[Any]]], None]):
|
162
170
|
events = self._events.get(name, [])
|
163
171
|
events.append({
|
164
172
|
'func': fn,
|
@@ -323,13 +331,21 @@ class App:
|
|
323
331
|
# получаем длинну пакета
|
324
332
|
mode, stream, lenght = GNRequest.type(buf)
|
325
333
|
|
326
|
-
if mode not in
|
327
|
-
logger.debug(f'Пакет отклонен: mode пакета {mode}. Разрешен 1, 2,
|
334
|
+
if mode not in self._api._App__allowed_modes: # не наш пакет # type: ignore
|
335
|
+
logger.debug(f'Пакет отклонен: mode пакета {mode}. Разрешен 1, 2, 4')
|
328
336
|
return
|
329
337
|
|
330
338
|
if not stream: # если не стрим, то ждем конец quic стрима и запускаем обработку ответа
|
331
339
|
if event.end_stream:
|
332
|
-
|
340
|
+
|
341
|
+
if self._api._kdc is not None:
|
342
|
+
buf = self._api._kdc.decode(buf)
|
343
|
+
|
344
|
+
if buf is not None:
|
345
|
+
request = GNRequest.deserialize(buf, mode)
|
346
|
+
else:
|
347
|
+
raise Exception('Не удалось расшифровать от KDC')
|
348
|
+
|
333
349
|
request.stream_id = event.stream_id # type: ignore
|
334
350
|
asyncio.create_task(self._handle_request(request, mode))
|
335
351
|
logger.debug(f'Отправлена задача разрешения пакета {request} route -> {request.route}')
|
@@ -350,6 +366,9 @@ class App:
|
|
350
366
|
del buf[:lenght]
|
351
367
|
|
352
368
|
# формируем запрос
|
369
|
+
|
370
|
+
if self._api._kdc is not None:
|
371
|
+
data = self._api._kdc.decode(data)
|
353
372
|
request = GNRequest.deserialize(data, mode)
|
354
373
|
|
355
374
|
logger.debug(request, f'event.stream_id -> {event.stream_id}')
|
@@ -411,7 +430,7 @@ class App:
|
|
411
430
|
await self.sendResponse(request, response, mode)
|
412
431
|
logger.debug(f'Отправлен на сервер ответ -> {response.command} {response.payload if response.payload and len(str(response.payload)) < 200 else ''}')
|
413
432
|
except Exception as e:
|
414
|
-
if isinstance(e,
|
433
|
+
if isinstance(e, GNRequest):
|
415
434
|
await self.sendResponse(request, e, mode)
|
416
435
|
else:
|
417
436
|
logger.error('GNServer: error\n' + traceback.format_exc())
|
@@ -422,9 +441,14 @@ class App:
|
|
422
441
|
|
423
442
|
async def sendResponse(self, request: GNRequest, response: GNResponse, mode: int, end_stream: bool = True):
|
424
443
|
await response.assembly()
|
444
|
+
|
445
|
+
blob = response.serialize(mode)
|
446
|
+
|
425
447
|
|
448
|
+
if self._api._kdc is not None:
|
449
|
+
blob = self._api._kdc.encode(blob)
|
426
450
|
|
427
|
-
self._quic.send_stream_data(request.stream_id,
|
451
|
+
self._quic.send_stream_data(request.stream_id, blob, end_stream=end_stream) # type: ignore
|
428
452
|
self.transmit()
|
429
453
|
|
430
454
|
def run(
|
@@ -84,7 +84,7 @@ from KeyisBTools.ranges.positionRange import in_range
|
|
84
84
|
from gnobjects.net.objects import GNRequest, GNResponse, Url
|
85
85
|
|
86
86
|
from ._crt import crt_client
|
87
|
-
|
87
|
+
from .models import KDCObject
|
88
88
|
|
89
89
|
|
90
90
|
_log_levels: dict[str, int] = {
|
@@ -124,10 +124,6 @@ def devLog(request: GNRequest, point: str, level: int, log: str):
|
|
124
124
|
|
125
125
|
|
126
126
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
127
|
async def chain_async(first_item, rest: AsyncIterable) -> AsyncGenerator:
|
132
128
|
yield first_item
|
133
129
|
async for x in rest:
|
@@ -151,6 +147,8 @@ class AsyncClient:
|
|
151
147
|
|
152
148
|
self.__domain = domain
|
153
149
|
|
150
|
+
self._kdc: Optional[KDCObject] = None
|
151
|
+
|
154
152
|
if server_key:
|
155
153
|
if isinstance(server_key, bytes):
|
156
154
|
self.__server_key = server_key
|
@@ -162,6 +160,8 @@ class AsyncClient:
|
|
162
160
|
else:
|
163
161
|
self.__server_key = None
|
164
162
|
|
163
|
+
def setKDC(self, kdc: KDCObject):
|
164
|
+
self._kdc = kdc
|
165
165
|
|
166
166
|
def addRequestCallback(self, callback: Callable, name: str):
|
167
167
|
self.__request_callbacks[name] = callback
|
@@ -195,7 +195,7 @@ class AsyncClient:
|
|
195
195
|
else:
|
196
196
|
return c
|
197
197
|
|
198
|
-
c = QuicClient()
|
198
|
+
c = QuicClient(self)
|
199
199
|
c.status = 'connecting'
|
200
200
|
self._active_connections[domain] = c
|
201
201
|
|
@@ -252,7 +252,7 @@ class AsyncClient:
|
|
252
252
|
request = await self._resolve_requests_transport(request)
|
253
253
|
try:
|
254
254
|
c = await self.connect(request, restart_connection, reconnect_wait, keep_alive=keep_alive)
|
255
|
-
except
|
255
|
+
except BaseException as e:
|
256
256
|
if isinstance(e, GNResponse):
|
257
257
|
return e
|
258
258
|
else:
|
@@ -606,15 +606,26 @@ class RawQuicClient(QuicConnectionProtocol):
|
|
606
606
|
async def request(self, request: Union[GNRequest, AsyncGenerator[GNRequest, Any]]):
|
607
607
|
if isinstance(request, GNRequest):
|
608
608
|
blob = request.serialize(2)
|
609
|
+
|
610
|
+
if self.quicClient._client._kdc is not None:
|
611
|
+
blob = self.quicClient._client._kdc.encode(request.url.hostname, blob)
|
612
|
+
|
609
613
|
sid = self._quic.get_next_available_stream_id()
|
610
|
-
devLog(request, '1.3.1', 10, f'Sending request on stream {sid}')
|
611
614
|
self._enqueue(sid, blob, True, False)
|
612
615
|
self._schedule_flush()
|
613
616
|
|
614
617
|
|
615
618
|
fut = asyncio.get_running_loop().create_future()
|
616
619
|
self._inflight[sid] = fut
|
617
|
-
|
620
|
+
data = await fut
|
621
|
+
|
622
|
+
if self.quicClient._client._kdc is not None:
|
623
|
+
data = self.quicClient._client._kdc.decode(request.url.hostname, data)
|
624
|
+
|
625
|
+
if data is not None:
|
626
|
+
return GNResponse.deserialize(data, 2)
|
627
|
+
else:
|
628
|
+
return GNResponse('gn:client:0')
|
618
629
|
|
619
630
|
else:
|
620
631
|
sid = self._quic.get_next_available_stream_id()
|
@@ -692,7 +703,8 @@ class RawQuicClient(QuicConnectionProtocol):
|
|
692
703
|
class QuicClient:
|
693
704
|
"""Обёртка‑фасад над RawQuicClient."""
|
694
705
|
|
695
|
-
def __init__(self):
|
706
|
+
def __init__(self, Client: AsyncClient):
|
707
|
+
self._client = Client
|
696
708
|
self._quik_core: Optional[RawQuicClient] = None
|
697
709
|
self._client_cm = None
|
698
710
|
self._disconnect_signal = None
|
@@ -768,9 +780,7 @@ class QuicClient:
|
|
768
780
|
raise RuntimeError("Not connected")
|
769
781
|
|
770
782
|
resp = await self._quik_core.request(request)
|
771
|
-
|
772
|
-
devLog(request, '1.4.1', 10, f'Response received: {r}')
|
773
|
-
return r
|
783
|
+
return resp
|
774
784
|
|
775
785
|
async def asyncRequestStream(self, request: Union[GNRequest, AsyncGenerator[GNRequest, Any]]) -> AsyncGenerator[GNResponse, None]:
|
776
786
|
|
@@ -0,0 +1,69 @@
|
|
1
|
+
|
2
|
+
from typing import List, Optional, Dict
|
3
|
+
from KeyisBTools.cryptography.sign import s2
|
4
|
+
from KeyisBTools.cryptography import m1
|
5
|
+
|
6
|
+
from ._client import AsyncClient
|
7
|
+
from ._app import GNRequest, GNResponse
|
8
|
+
from gnobjects.net.objects import Url
|
9
|
+
|
10
|
+
from KeyisBTools.cryptography.bytes import hash3
|
11
|
+
|
12
|
+
class KDCObject:
|
13
|
+
def __init__(self, domain: str, kdc_domain: str, kdc_key: bytes, requested_domains: List[str]):
|
14
|
+
self._domain = domain
|
15
|
+
self._kdc_domain = kdc_domain
|
16
|
+
self._kdc_key = kdc_key
|
17
|
+
self._requested_domains = requested_domains
|
18
|
+
|
19
|
+
self._client = AsyncClient(domain)
|
20
|
+
|
21
|
+
self._servers_keys: Optional[Dict[str, bytes]] = None
|
22
|
+
self._servers_keys_hash_domain: Dict[bytes, str] = {}
|
23
|
+
self._servers_keys_domain_hash: Dict[str, bytes] = {}
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
async def init(self):
|
29
|
+
payload = self._requested_domains
|
30
|
+
r = await self._client.request(GNRequest('GET', Url(f'gn://{self._kdc_domain}/api/sys/server/keys'), payload=payload))
|
31
|
+
|
32
|
+
if not r.command.ok:
|
33
|
+
raise r
|
34
|
+
|
35
|
+
self._servers_keys: Dict[str, bytes] = r.payload
|
36
|
+
|
37
|
+
for domain in self._servers_keys.keys():
|
38
|
+
h = hash3(domain.encode())
|
39
|
+
self._servers_keys_hash_domain[h] = domain
|
40
|
+
self._servers_keys_domain_hash[domain] = h
|
41
|
+
|
42
|
+
|
43
|
+
self._servers_keys[self._kdc_domain] = self._kdc_key
|
44
|
+
h = hash3(self._kdc_domain.encode())
|
45
|
+
self._servers_keys_hash_domain[h] = self._kdc_domain
|
46
|
+
self._servers_keys_domain_hash[self._kdc_domain] = h
|
47
|
+
|
48
|
+
|
49
|
+
def encode(self, domain: str, request: bytes):
|
50
|
+
if domain not in self._servers_keys:
|
51
|
+
return request
|
52
|
+
|
53
|
+
sig = s2.sign(self._kdc_key)
|
54
|
+
data = m1.encrypt(self._domain.encode(), sig, request, self._kdc_key)
|
55
|
+
return sig + self._servers_keys_domain_hash[domain] + data
|
56
|
+
|
57
|
+
def decode(self, response: bytes):
|
58
|
+
if len(response) < 164+64:
|
59
|
+
return response
|
60
|
+
|
61
|
+
sig, domain_h, data = response[:164], response[164:164+64], response[164+64:]
|
62
|
+
|
63
|
+
if domain_h not in self._servers_keys_hash_domain:
|
64
|
+
return response
|
65
|
+
|
66
|
+
key = self._servers_keys[self._servers_keys_hash_domain[domain_h]]
|
67
|
+
if not s2.verify(key, sig):
|
68
|
+
return None
|
69
|
+
return m1.decrypt(self._domain.encode(), sig, data, key)
|
@@ -11,6 +11,7 @@ GNServer/GNServer/_crt.py
|
|
11
11
|
GNServer/GNServer/_func_params_validation.py
|
12
12
|
GNServer/GNServer/_routes.py
|
13
13
|
GNServer/GNServer/_template_resolver.py
|
14
|
+
GNServer/GNServer/models.py
|
14
15
|
GNServer/GNServer.egg-info/PKG-INFO
|
15
16
|
GNServer/GNServer.egg-info/SOURCES.txt
|
16
17
|
GNServer/GNServer.egg-info/dependency_links.txt
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|