0

I have a strange problem with unix socket (US) using so-called abstract namespaces when using Python and "pure" C (Python 3.x but looks like 2.x have the same problem). "Normal" socket works like a charm. With "abstract" one US my code works only when I'm using same "code platform" (C or Python).

First I thought it has something to do with memset / str(n)cpy (see Can not connect to an abstract unix socket in python) but imho it's not that case.

Test matrix (srv - server, cli - client):

  • srv + cli @ "abstract" unix sock:
    • python + python = OK
    • c + c = OK
  • srv + cli @ "normal" unix sock:
    • python + python = OK
    • c + c = OK
  • srv + cli @ "normal" unix sock:
    • python + c = OK
    • c + python = OK
  • srv + cli @ "abstract" unix sock:
    • python + c = FAIL [cli / strace output: ECONNREFUSED (Connection refused)]
    • c + python = FAIL [cli / raised exception: socket.error: [Errno 111] Connection refused]

/proc/net/unix / lsof or strace show nothing unusual:

  • working "normal" socket C client:
    // ...
    socket(PF_LOCAL, SOCK_STREAM, 0) = 3
    connect(3, {sa_family=AF_LOCAL, sun_path=@"/var/tmp/sock.tmp"}, 110) = 0
    // ...

  • misbehaving "abstract" socket C client:
    // ...
    socket(PF_LOCAL, SOCK_STREAM, 0) = 3
    connect(3, {sa_family=AF_LOCAL, sun_path=@"/var/tmp/sock.tmp"}, 110) = -1 ECONNREFUSED (Connection refused)
    // ...

Python bug or what...?

Gist with code samples for my test matrix: https://gist.github.com/soutys/ffbe2e76a86835a9cc6b

Original code / samples:

Update 2015-11-02

More info about system and compilations:

  • host system: Ubuntu 14.04.3 LTS (x64);
  • all C test files compiled with gcc (example: gcc -Wall -Wextra -pedantic -o abs_cli abs_cli.c);
  • all programs (both compiled and built-in python) run as non-root user;
Community
  • 1
  • 1
soutys
  • 1
  • 3
  • the referenced 'C' client code, does not compile!! Including the original sources. amongst other things it is missing the header file: `#include ` for the system functions: `memset()` and `strncpy()`. Always enable all warnings when compiling, then fix those warnings. (for gcc, at a minimum use: `-Wall -Wextra -pedantic` ) – user3629249 Nov 01 '15 at 17:53
  • Also, the C code in both srv and cli does suffer from exactly the issue described in the linked post. The strncpy will only copy the \0 null, no matter what you give it as n (as long as n > 0). Use memcpy(). – thuovila Nov 02 '15 at 09:18
  • @user3629249 it compiles with gcc on my x64 ubuntu both with `-Wall` and `-Wall -Wextra -pedantic`... ok - I added string.h header entries for clarity. – soutys Nov 02 '15 at 14:50
  • @thuovila as you may see there's a shift in strncpy: `strncpy(&addr.sun_path[1], socket_path, sizeof(addr.sun_path) - 2);` which gives proper "offset" in zeros-padded struct. – soutys Nov 02 '15 at 14:51
  • I may not see. It is not present in the versions of https://github.com/troydhanson/network/blob/master/unixdomain/srv.c I have looked at at least. Also, that is not a very sensible way to do it. Just use memcpy() if you have nulls in your data. – thuovila Nov 03 '15 at 14:55
  • @thuovila :D I was talking about **my** code (at gist)... There's a working solution but it needs to be verified (reviewed?) first by someone from python dev-team. – soutys Nov 04 '15 at 19:07

1 Answers1

0

When binding a UNIX domain socket to an abstract name, the addrlen argument should be sizeof(struct sockaddr_un's sun_family) + number of characters in abstract name + 1. The "+1" is for the null byte in front of the abstract name in sockaddr_un's sun_path. Let's look at an example:

  • Server in C:

    int sockfd;
    struct sockaddr_un addr;
    /* create socket, set addr.sun_family, set addr.sun_path to 
       null byte followed by abstract_name */
    bind(sockfd, (struct sockaddr *)&addr, sizeof(addr.sun_family) +
         strlen(abstract_name) + 1);
    
  • Client in Python:

    client = socket.socket(socket.AF_UNIX, ...)
    client.connect( "\0abstract_name" )
    

References: