P1sec / pysctp

SCTP stack for Python
http://www.p1sec.com
165 stars 67 forks source link

Fix / sctp_send: add support of bytearray #40

Closed p1-ra closed 2 years ago

p1-ra commented 2 years ago

Hi,

Actually, the wrapper arround _sctp.sctp_send_msg only accept readonly bytes.

On my side I'm using python asyncio from an existing sctp socket. The library will time to time call the socket send method with a mutable bytearray instead of an immutable bytes as parameter, which will trigger an error:

  File "/usr/local/lib/python3.10/dist-packages/pysctp-0.7.1-py3.10-linux-x86_64.egg/sctp.py", line 1178, in sctp_send
    return _sctp.sctp_send_msg(self._sk.fileno(), msg, to, ppid, flags, stream, timetolive, context)
TypeError: argument 2 must be read-only bytes-like object, not bytearray

It's seems common for python to accept either a bytearray or bytes and looks to not ~fully be a bug on asyncio side, python socket.py write's method description says:

        """Write the given bytes or bytearray object *b* to the socket
        and return the number of bytes written.  This can be less than
        len(b) if not all data could be written.  If the socket is
        non-blocking and no bytes could be written None is returned.
        """

Proposed fix here, always enforce the conversion to read only bytes

p1-ra commented 2 years ago

Hi again,

Rather than loosing some perf by always forcing the conversion of a potential mutable bytearray to an immutable bytes (for everyone), I'm using a workaround on my side, I'm injecting a new method on the pysctp socket's instance in charge of that, bellow the code if anyone have the same need:

import types
(...)

def stcp_send_mutable(self: sctpsocket, msg: Union[bytes, bytearray], flags: int=0, to: Tuple[str, int]=("",0), ppid: Optional[int]=None, stream: Optional[int]=None, timetolive: Optional[int]=None, context: int=0,
                        record_file_prefix: str ="RECORD_sctp_traffic", datalogging: bool = False) -> bytes:

    return self.sctp_send(bytes(msg), to=to, ppid=ppid, flags=flags, stream=stream, timetolive=timetolive, context=context,
                   record_file_prefix=record_file_prefix, datalogging=datalogging)

 (...)

 data = bytearray(b'0000')
 my_sctp_socket.stcp_send_mutable = types.MethodType(stcp_send_mutable, my_sctp_socket) # type: ignore
 my_sctp_socket.stcp_send_mutable(data)

In my actual use case, with asyncio, I'm actually overriding the send method btw:

my_sctp_socket.send = types.MethodType(stcp_send_mutable, my_sctp_socket) # type: ignore

I'm closing the PR.