Соединение отклонено при использовании абстрактных сокетов unix пространства имен

У меня есть странная проблема с сокетом unix (США) с использованием так называемых абстрактных пространств имен при использовании Python и «чистого» C (Python 3.x, но похоже, что 2.x имеет ту же проблему). «Нормальная» розетка работает как шарм. С «абстрактным» одним США мой код работает только тогда, когда я использую ту же «кодовую платформу» (C или Python).

Сначала я подумал, что это как-то связано с memset/str(n)cpy (см. Не удается подключиться к абстрактному сокету unix в python), но имхо это не тот случай.

Тестовая матрица (srv — сервер, cli — клиент):

  • 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
    • с + питон = ОК
  • srv + cli @ "abstract" unix sock:
    • python + c = FAIL [cli / strace output: ECONNREFUSED (Connection refused)]
    • c + python = FAIL [cli / подняло исключение: socket.error: [Errno 111] В соединении отказано]

/proc/net/unix / lsof или strace ничего необычного не показывают:

  • работающий "обычный" клиент сокета C:
    // ...
    socket(PF_LOCAL, SOCK_STREAM, 0) = 3
    connect(3, {sa_family=AF_LOCAL, sun_path=@"/var/tmp/sock.tmp"}, 110) = 0
    // ...

  • неправильное поведение "абстрактного" клиента сокета C:
    // ...
    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 или что...?

Суть с примерами кода для моей тестовой матрицы: https://gist.github.com/soutys/ffbe2e76a86835a9cc6b

Исходный код/образцы:

Обновление 2015-11-02

Подробнее о системе и компиляциях:

  • хост-система: Ubuntu 14.04.3 LTS (x64);
  • все тестовые файлы C, скомпилированные с gcc (пример: gcc -Wall -Wextra -pedantic -o abs_cli abs_cli.c);
  • все программы (как скомпилированные, так и встроенные на python) запускаются от имени пользователя без полномочий root;

person soutys    schedule 01.11.2015    source источник
comment
указанный код клиента 'C' не компилируется!! Включая первоисточники. среди прочего отсутствует заголовочный файл: #include <string.h> для системных функций: memset() и strncpy(). Всегда включайте все предупреждения при компиляции, а затем исправьте эти предупреждения. (для gcc при минимальном использовании: -Wall -Wextra -pedantic )   -  person user3629249    schedule 01.11.2015
comment
Кроме того, код C как в srv, так и в cli страдает именно от проблемы, описанной в связанном посте. Strncpy будет копировать только \0 null, независимо от того, что вы зададите как n (пока n > 0). Используйте memcpy().   -  person thuovila    schedule 02.11.2015
comment
@user3629249 user3629249 он компилируется с помощью gcc на моем x64 ubuntu как с -Wall, так и с -Wall -Wextra -pedantic ... хорошо - я добавил записи заголовка string.h для ясности.   -  person soutys    schedule 02.11.2015
comment
@thuovila, как вы можете видеть, в strncpy: strncpy(&addr.sun_path[1], socket_path, sizeof(addr.sun_path) - 2); есть сдвиг, который дает правильное смещение в структуре с нулями.   -  person soutys    schedule 02.11.2015
comment
Я могу не видеть. Его нет в версиях github.com/troydhanson/network/. blob/master/unixdomain/srv.c Я посмотрел по крайней мере. Кроме того, это не очень разумный способ сделать это. Просто используйте memcpy(), если в ваших данных есть нули.   -  person thuovila    schedule 03.11.2015
comment
@thuovila :D Я говорил о моем коде (в сущности)... Есть рабочее решение, но оно должно быть проверено (рассмотрено?) сначала кем-то из команды разработчиков Python.   -  person soutys    schedule 04.11.2015


Ответы (1)


При привязке сокета домена UNIX к абстрактному имени аргумент addrlen должен быть sizeof(struct sockaddr_un's sun_family) + количество символов в абстрактном имени + 1. «+1» — это нулевой байт перед абстрактным именем в sockaddr_un. путь_солнца. Давайте посмотрим на пример:

  • Сервер на С:

    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 = socket.socket(socket.AF_UNIX, ...)
    client.connect( "\0abstract_name" )
    

Использованная литература:

person Andrew Li    schedule 20.08.2017