kivy/oscpy

Blob size unpack_from with wrong unit

felixdollack opened this issue · 3 comments

Describe the bug
I am sending an OSC message containing a single blob of data:
/destn/1\x00\x00\x00\x00,b\x00\x00\x00\x00\x00\x08...
[28 bytes] /destn/1 b [8]0123456789ABCDEF

I am getting this result for both OSC data send from C and using Dart.

To Reproduce
Code example showing the issue is the example code from the readme:

from oscpy.server import OSCThreadServer
from time import sleep

osc = OSCThreadServer()
sock = osc.listen(address='0.0.0.0', port=9000, default=True)

@osc.address(b'/destn/*')
def callback(*values):
    print("got values: {}".format(values))

sleep(1000)
osc.stop()

Here the sending portion in C using tinyosc compiled with gcc *.c -Werror -std=c99 -O0 -g -o sendosc Note: main.c from tinyosc has to be removed or the content be replaced with below:

#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>

#include "tinyosc.h"

int main(int argc, char *argv[]) {
  char buffer[128];
  int len = 0;
  char blob[8] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};
  len = tosc_writeMessage(buffer, sizeof(buffer), "/destn/1", "b",
      sizeof(blob), blob);
  tosc_printOscBuffer(buffer, len);

  struct sockaddr_in client_addr;
  client_addr.sin_family = AF_INET;
  client_addr.sin_port = htons(9000);
  client_addr.sin_addr.s_addr = INADDR_ANY;
  const int fd_send = socket(AF_INET, SOCK_DGRAM, 0);
  if (fd_send < 0)
  {
    fprintf(stderr, "Error opening socket");
    return -1;
  }
  int response = sendto(
    fd_send, (void*) &buffer[0], len, 0,
    (struct sockaddr*)&client_addr, sizeof(client_addr)
  );
  if (response < 0) {
    fprintf(stderr, "Error in sendto()\n");
    return -1;
  }
  // close the UDP socket
  close(fd_send);
  return 0;
}

Expected behavior
Read the correct size of the blob and get a binary array same way it was written.
The same message works without any issue using e.g. python-osc.
blob = b'\x01#Eg\x89\xab\xcd\xef'

Logs/output

Exception in thread Thread-1:
Traceback (most recent call last):
  File "miniconda3/envs/v/lib/python3.9/threading.py", line 973, in _bootstrap_inner
    self.run()
  File "miniconda3/envs/v/lib/python3.9/threading.py", line 910, in run
    self._target(*self._args, **self._kwargs)
  File "miniconda3/envs/v/lib/python3.9/site-packages/oscpy/server.py", line 338, in _run_listener
    self._listen()
  File "miniconda3/envs/v/lib/python3.9/site-packages/oscpy/server.py", line 386, in _listen
    for address, tags, values, offset in read_packet(
  File "miniconda3/envs/v/lib/python3.9/site-packages/oscpy/parser.py", line 409, in read_packet
    read_message(
  File "miniconda3/envs/v/lib/python3.9/site-packages/oscpy/parser.py", line 309, in read_message
    value, off = parse(
  File "miniconda3/envs/v/lib/python3.9/site-packages/oscpy/parser.py", line 206, in parse
    return parser(
  File "miniconda3/envs/v/lib/python3.9/site-packages/oscpy/parser.py", line 96, in parse_blob
    data = unpack_from('>%iQ' % length, value, offset + size)
struct.error: unpack_from requires a buffer of at least 84 bytes for unpacking 64 bytes at offset 20 (actual buffer size is 28)
^CTraceback (most recent call last):
  File "Documents/code/udp_test/osc_example.py", line 11, in <module>
    sleep(1000)
KeyboardInterrupt

Platform (please complete the following information):

  • OS: OSX 12.5.1
  • Python 3.9
  • OSCpy 0.6.0 (installed with pip)

Additional context
Add any other context about the problem here.

When parsing the blob, why do you use an integer (>%iQ) instead of a char (>%cQ)?

From osc.parser:

def parse_blob(value, offset=0, **kwargs):
    """Return a blob from offset in value."""
    size = calcsize('>i')
    length = unpack_from('>i', value, offset)[0]
    data = unpack_from('>%iQ' % length, value, offset + size)
    return data, padded(length, 8)

The specification says:

OSC-blob
An int32 size count, followed by that many 8-bit bytes of arbitrary binary data, followed by 0-3 additional zero bytes to make the total number of bits a multiple of 32.

I think the line where the data is unpacked should be something like:

data = unpack_from('>%ic' % length value, offset + size)

Seems like a bug indeed, i didn’t put a lot of attention in the project since i don’t need it currently, but i’ll try to have a look at it, of course, if someone else has time before that, PR welcome.

Solved by PR #74 and commit 696e5b9.