This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
"""Strict parser for the bounded Phase-1.0DQ inventory stream."""
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
import struct
|
||||
|
||||
MAGIC=b"CHI10DQ1";FRAME=struct.Struct("<8sIIiiIIQ24s");ENTRY=struct.Struct("<256sIIIIQQq24s")
|
||||
HEADER=1;TERMINAL=2;ERROR=3;MAX_ENTRIES=256;MAX_WIRE=64+MAX_ENTRIES*320+64
|
||||
class ProtocolError(ValueError):pass
|
||||
@dataclass(frozen=True)
|
||||
class InventoryEntry:name:str;file_type:int;mode:int;size:int;inode:int;mtime:int
|
||||
def _frame(raw:bytes):
|
||||
if len(raw)!=FRAME.size:raise ProtocolError("frame size")
|
||||
value=FRAME.unpack(raw)
|
||||
if value[0]!=MAGIC or value[1]!=1 or value[8]!=bytes(24):raise ProtocolError("frame identity")
|
||||
return value
|
||||
def parse_stream(raw:bytes)->tuple[InventoryEntry,...]:
|
||||
if not isinstance(raw,bytes) or not 128<=len(raw)<=MAX_WIRE or (len(raw)-128)%ENTRY.size:raise ProtocolError("stream length")
|
||||
first=_frame(raw[:64]);kind,status,saved,count,reserved,transferred=first[2:8]
|
||||
if kind==ERROR:raise ProtocolError(f"target error {status}:{saved}")
|
||||
if kind!=HEADER or status or saved or count or reserved or transferred:raise ProtocolError("header invalid")
|
||||
entries=[]
|
||||
for offset in range(64,len(raw)-64,ENTRY.size):
|
||||
name_raw,name_length,file_type,mode,reserved,size,inode,mtime,padding=ENTRY.unpack(raw[offset:offset+ENTRY.size])
|
||||
if not 0<name_length<256 or reserved or padding!=bytes(24) or any(name_raw[name_length:]):raise ProtocolError("entry encoding")
|
||||
try:name=name_raw[:name_length].decode("utf-8")
|
||||
except UnicodeDecodeError as exc:raise ProtocolError("entry utf8") from exc
|
||||
if name in {".",".."} or "/" in name or "\\" in name or "\x00" in name:raise ProtocolError("entry name")
|
||||
entries.append(InventoryEntry(name,file_type,mode,size,inode,mtime))
|
||||
terminal=_frame(raw[-64:]);kind,status,saved,count,reserved,transferred=terminal[2:8]
|
||||
if kind!=TERMINAL or status or saved or reserved or count!=len(entries) or transferred!=len(entries)*ENTRY.size:raise ProtocolError("terminal invalid")
|
||||
return tuple(entries)
|
||||
Reference in New Issue
Block a user