added the functional/API improvements made by Dusan, without the styling improvements (to reduce diff)

master
Luis Alvarez 4 years ago
parent d5abb19d8c
commit 0e8a8204bb

1
.gitignore vendored

@ -6,3 +6,4 @@ htmlcov
.idea
.autoenv
.venv
.vscode

@ -1,3 +1,4 @@
import logging
import sys
import struct
import socket
@ -8,12 +9,13 @@ from levin.constants import *
from levin.exceptions import BadArgumentException
from levin.ctypes import *
log = logging.getLogger()
class Bucket:
def __init__(self):
self.signature = LEVIN_SIGNATURE
if self.signature != LEVIN_SIGNATURE:
raise BadArgumentException('Bender\'s nightmare')
raise BadArgumentException("Bender's nightmare")
self.cb = None
self.return_data = None
@ -54,9 +56,11 @@ class Bucket:
bucket.protocol_version = LEVIN_PROTOCOL_VER_1
@staticmethod
def create_handshake_request(my_port: int = 0, network_id: bytes = None,
peer_id: bytes = b'\x41\x41\x41\x41\x41\x41\x41\x41',
verbose=False):
def create_handshake_request(
my_port: int = 0,
network_id: bytes = None,
peer_id: bytes = b"\x41\x41\x41\x41\x41\x41\x41\x41",
):
"""
Helper function to create a handshake request. Does not require
parameters but you can use them to impersonate a legit node.
@ -66,18 +70,32 @@ class Bucket:
:param verbose:
:return:
"""
handshake_section = Section.handshake_request(peer_id=peer_id, network_id=network_id, my_port=my_port)
bucket = Bucket.create_request(1001, section=handshake_section)
if verbose:
print(">> created packet \'%s\'" % P2P_COMMANDS[bucket.command])
header = bucket.header()
handshake_section = Section.handshake_request(
peer_id=peer_id, network_id=network_id, my_port=my_port
)
bucket = Bucket.create_request(
P2P_COMMAND_HANDSHAKE.value, section=handshake_section
)
log.debug(">> created packet '%s'" % P2P_COMMANDS[bucket.command])
header = bucket.header()
body = bucket.payload()
return bucket
@staticmethod
def create_stat_info_request(
peer_id: bytes = b"\x41\x41\x41\x41\x41\x41\x41\x41",
):
stat_info_section = Section.stat_info_request(peer_id=peer_id)
log.debug(stat_info_section.entries["proof_of_trust"].entries.keys())
bucket = Bucket.create_request(
P2P_COMMAND_REQUEST_STAT_INFO.value, section=stat_info_section
)
log.debug(">> created packet '%s'" % P2P_COMMANDS[bucket.command])
return bucket
@classmethod
def from_buffer(cls, signature: c_uint64, sock: socket.socket, verbose: bool = False):
def from_buffer(cls, signature: c_uint64, sock: socket.socket):
if isinstance(signature, bytes):
signature = c_uint64(signature)
# if isinstance(buffer, bytes):
@ -111,21 +129,19 @@ class Bucket:
bucket.payload_section = None
if verbose:
print("<< received packet \'%s\'" % P2P_COMMANDS[bucket.command])
log.debug("<< received packet '%s'" % P2P_COMMANDS[bucket.command])
from levin.reader import LevinReader
bucket.payload_section = LevinReader(BytesIO(bucket.payload)).read_payload()
if verbose:
print("<< parsed packet \'%s\'" % P2P_COMMANDS[bucket.command])
log.debug("<< parsed packet '%s'" % P2P_COMMANDS[bucket.command])
return bucket
def header(self):
return b''.join((
return b"".join((
bytes(LEVIN_SIGNATURE),
bytes(self.cb),
b'\x01' if self.return_data else b'\x00',
b"\x01" if self.return_data else b"\x00",
bytes(self.command),
bytes(self.return_code),
bytes(self.flags),

@ -20,12 +20,18 @@ P2P_COMMANDS_POOL_BASE = c_uint32(1000)
P2P_COMMAND_HANDSHAKE = c_uint32(P2P_COMMANDS_POOL_BASE + 1) # carries payload
P2P_COMMAND_TIMED_SYNC = c_uint32(P2P_COMMANDS_POOL_BASE + 2) # carries payload
P2P_COMMAND_PING = c_uint32(P2P_COMMANDS_POOL_BASE + 3)
P2P_COMMAND_REQUEST_STAT_INFO = c_uint32(P2P_COMMANDS_POOL_BASE + 4)
P2P_COMMAND_REQUEST_NETWORK_STATE = c_uint32(P2P_COMMANDS_POOL_BASE + 5)
P2P_COMMAND_REQUEST_PEER_ID = c_uint32(P2P_COMMANDS_POOL_BASE + 6)
P2P_COMMAND_REQUEST_SUPPORT_FLAGS = c_uint32(P2P_COMMANDS_POOL_BASE + 7)
P2P_COMMANDS = {
P2P_COMMAND_HANDSHAKE: 'handshake',
P2P_COMMAND_TIMED_SYNC: 'timed_sync',
P2P_COMMAND_PING: 'ping',
P2P_COMMAND_REQUEST_SUPPORT_FLAGS: 'support_flags'
P2P_COMMAND_HANDSHAKE: "handshake",
P2P_COMMAND_TIMED_SYNC: "timed_sync",
P2P_COMMAND_PING: "ping",
P2P_COMMAND_REQUEST_STAT_INFO: "stat_info",
P2P_COMMAND_REQUEST_NETWORK_STATE: "network_state",
P2P_COMMAND_REQUEST_PEER_ID: "peer_id",
P2P_COMMAND_REQUEST_SUPPORT_FLAGS: "support_flags",
}
PORTABLE_STORAGE_SIGNATUREA = c_uint32(0x01011101)

@ -4,10 +4,19 @@ import ipaddress
from io import BytesIO
from datetime import datetime
from ctypes import (
c_ushort as _c_ushort, c_ubyte as _c_ubyte, c_uint8 as _c_uint8,
c_uint16 as _c_uint16, c_uint32 as _c_uint32, c_uint64 as _c_uint64,
c_short as _c_short, c_byte as _c_byte, c_int8 as _c_int8, c_int16 as _c_int16,
c_int32 as _c_int32, c_int64 as _c_int64)
c_ushort as _c_ushort,
c_ubyte as _c_ubyte,
c_uint8 as _c_uint8,
c_uint16 as _c_uint16,
c_uint32 as _c_uint32,
c_uint64 as _c_uint64,
c_short as _c_short,
c_byte as _c_byte,
c_int8 as _c_int8,
c_int16 as _c_int16,
c_int32 as _c_int32,
c_int64 as _c_int64,
)
from levin.exceptions import BadArgumentException
@ -18,7 +27,9 @@ class _CType:
self.endian = endian
@classmethod
def from_buffer(cls, buffer, endian: str = 'little', padding: bytes = None):
def from_buffer(
cls, buffer, endian: str = "little", padding: bytes = None
):
if isinstance(buffer, BytesIO):
buffer = buffer.read(cls.NBYTES)
elif isinstance(buffer, socket.socket):
@ -26,23 +37,23 @@ class _CType:
if len(buffer) < cls.NBYTES:
if not padding:
_bytes = 'bytes' if cls.NBYTES > 1 else 'byte'
_bytes = "bytes" if cls.NBYTES > 1 else "byte"
raise BadArgumentException("requires a buffer of %d %s, received %d" % (cls.NBYTES, _bytes, len(buffer)))
func_padding = bytes.ljust if endian == 'little' else bytes.rjust
func_padding = bytes.ljust if endian == "little" else bytes.rjust
buffer = func_padding(buffer, cls.NBYTES, padding)
_endian = '<' if endian == 'little' else '>'
type_struct = cls.TYPE_STRUCT if hasattr(cls, 'TYPE_STRUCT') else cls.TYPE._type_
value = struct.unpack('%s%s' % (_endian, type_struct), buffer)[0]
_endian = "<" if endian == "little" else ">"
type_struct = cls.TYPE_STRUCT if hasattr(cls, "TYPE_STRUCT") else cls.TYPE._type_
value = struct.unpack("%s%s" % (_endian, type_struct), buffer)[0]
return cls(value, endian)
def to_bytes(self):
if isinstance(self.value, (int, bool)):
type_struct = self.TYPE_STRUCT if hasattr(self, 'TYPE_STRUCT') else self.TYPE._type_
return struct.pack('%s%s' % ('<' if self.endian == 'little' else '>', type_struct), self.value)
type_struct = self.TYPE_STRUCT if hasattr(self, "TYPE_STRUCT") else self.TYPE._type_
return struct.pack("%s%s" % ("<" if self.endian == "little" else ">", type_struct), self.value)
elif isinstance(self.value, bytes):
if self.endian == 'little':
if self.endian == "little":
return self.value[::-1]
return self.value
@ -51,7 +62,7 @@ class _CType:
try:
self.value.to_bytes(self.NBYTES, byteorder=self.endian, signed=self.SIGNED)
except OverflowError as e:
raise OverflowError("Value \'%d\' does not fit in %s." % (self.value, self.__class__.__name__))
raise OverflowError("Value '%d' does not fit in %s." % (self.value, self.__class__.__name__))
def __len__(self):
if isinstance(self.value, bytes):
@ -172,15 +183,15 @@ class _CType:
class _IntType(_CType):
def __init__(self, value, endian='little'):
def __init__(self, value, endian="little"):
super(_IntType, self).__init__(value, endian)
self._overflows()
def __repr__(self):
_signed = 'unsigned' if not self.SIGNED else 'signed'
_bytes = 'bytes' if self.NBYTES > 1 else 'byte'
_signed = "unsigned" if not self.SIGNED else "signed"
_bytes = "bytes" if self.NBYTES > 1 else "byte"
_cls_name = self.__class__.__name__
return '\'%d\' - %s - %s %d %s' % (self.value, _cls_name, _signed, self.NBYTES, _bytes)
return "'%d' - %s - %s %d %s" % (self.value, _cls_name, _signed, self.NBYTES, _bytes)
class c_int16(_IntType):
@ -188,7 +199,7 @@ class c_int16(_IntType):
NBYTES = 2
SIGNED = True
def __init__(self, value, endian='little'):
def __init__(self, value, endian="little"):
super(c_int16, self).__init__(value, endian)
@ -197,7 +208,7 @@ class c_uint16(_IntType):
NBYTES = 2
SIGNED = False
def __init__(self, value, endian='little'):
def __init__(self, value, endian="little"):
super(c_uint16, self).__init__(value, endian)
@ -206,7 +217,7 @@ class c_int32(_IntType):
NBYTES = 4
SIGNED = True
def __init__(self, value, endian='little'):
def __init__(self, value, endian="little"):
super(c_int32, self).__init__(value, endian)
@ -215,7 +226,7 @@ class c_uint32(_IntType):
NBYTES = 4
SIGNED = False
def __init__(self, value, endian='little'):
def __init__(self, value, endian="little"):
super(c_uint32, self).__init__(value, endian)
@property
@ -225,11 +236,11 @@ class c_uint32(_IntType):
class c_int64(_IntType):
TYPE = _c_uint64
TYPE_STRUCT = 'q'
TYPE_STRUCT = "q"
NBYTES = 8
SIGNED = True
def __init__(self, value, endian='little'):
def __init__(self, value, endian="little"):
super(c_int64, self).__init__(value, endian)
@property
@ -239,11 +250,11 @@ class c_int64(_IntType):
class c_uint64(_IntType):
TYPE = _c_uint64
TYPE_STRUCT = 'Q'
TYPE_STRUCT = "Q"
NBYTES = 8
SIGNED = False
def __init__(self, value, endian='little'):
def __init__(self, value, endian="little"):
super(c_uint64, self).__init__(value, endian)
@property
@ -256,12 +267,12 @@ class c_byte(_IntType):
NBYTES = 1
SIGNED = True
def __init__(self, value, endian='little'):
def __init__(self, value, endian="little"):
super(c_byte, self).__init__(value, endian)
class c_bytes(_CType):
def __init__(self, value, endian='little'):
def __init__(self, value, endian="little"):
super(c_bytes, self).__init__(value, endian)
@ -270,12 +281,12 @@ class c_ubyte(_IntType):
NBYTES = 1
SIGNED = False
def __init__(self, value, endian='little'):
def __init__(self, value, endian="little"):
super(c_ubyte, self).__init__(value, endian)
class c_string(_CType):
ENCODING = 'ascii'
ENCODING = "ascii"
def __init__(self, value):
super(c_string, self).__init__(value)
@ -286,10 +297,10 @@ class c_string(_CType):
class c_bool(_CType):
TYPE_STRUCT = '?'
TYPE_STRUCT = "?"
NBYTES = 1
def __init__(self, value, endian=None):
if value not in [True, False]:
raise BadArgumentException('bool')
raise BadArgumentException("bool")
super(c_bool, self).__init__(value)

@ -43,7 +43,7 @@ class LevinReader:
def read_section_name(self) -> str:
len_name = c_ubyte.from_buffer(self.buffer)
name = self.buffer.read(len_name.value)
return name.decode('ascii')
return name.decode("ascii")
def load_storage_entry(self):
_type = c_ubyte.from_buffer(self.buffer)
@ -125,7 +125,7 @@ class LevinReader:
elif size_mask == PORTABLE_RAW_SIZE_MARK_INT64:
v = rshift(self.read_rest(b, 7), 2)
else:
raise IOError('invalid var_int')
raise IOError("invalid var_int")
return v
def read_rest(self, first_byte: int, _bytes: int):

@ -55,6 +55,25 @@ class Section:
section.add("support_flags", P2P_SUPPORT_FLAGS)
return section
@classmethod
def stat_info_request(cls, peer_id: bytes = None):
if not peer_id:
peer_id = random.getrandbits(64)
signature = bytes.fromhex(
"418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e3"
)
section = cls()
proof_of_trust = Section()
proof_of_trust.add("peer_id", c_uint64(peer_id))
proof_of_trust.add("time", c_uint64(int(time())))
proof_of_trust.add("sign", c_string(signature))
section.add("proof_of_trust", proof_of_trust)
return section
def __bytes__(self):
from levin.writer import LevinWriter

@ -7,7 +7,7 @@ from io import BytesIO
def ip2int(addr):
return struct.unpack("!I", socket.inetaton(addr))[0]
return struct.unpack("!I", socket.inet_aton(addr))[0]
def int2ip(addr):

@ -23,7 +23,7 @@ class LevinWriter:
def put_section(self, section: Section):
self.write_var_in(len(section))
for k, v in section.entries.items():
_k = k.encode('ascii')
_k = k.encode("ascii")
self.write(bytes(c_ubyte(len(_k))))
self.write(_k)
self.serialized_write(v)

Loading…
Cancel
Save