Одно из отличий вызова socketpair() от выполнения аналогичной операции вручную заключается в том, что сокеты не привязываются ни к какому адресу. Это позволяет избежать целого ряда уязвимостей в безопасности, поскольку другие процессы таких сокетов не видят.
Так называемое
• можно не беспокоиться о возможных конфликтах с существующими файлами;
• после завершения работы с сокетом не нужно удалять соответствующий файл. Абстрактное имя автоматически исчезает вместе с закрытием сокета;
• нет нужды создавать для сокета отдельный файл. Это может пригодиться в среде chroot или при отсутствии прав на запись в файловую систему.
Чтобы создать абстрактную привязку, нужно сделать первый байт поля sun_path нулевым (\0). Это отличает абстрактные имена сокетов от традиционных, принятых в домене UNIX и состоящих из одного или несколько ненулевых байтов, в конце которых находится нулевой символ. Остальные байты поля sun_path определяют абстрактное имя сокета. Данное имя не предусматривает завершающего символа, поэтому учитывается каждый байт.
Создание абстрактной привязки для сокета демонстрируется в листинге 53.8.
Листинг 53.8. Создание абстрактной привязки для сокета
#include
#include
#include "tlpi_hdr.h"
int
main(int argc, char *argv[])
{
int sockfd;
struct sockaddr_un addr;
char *str;
memset(&addr, 0, sizeof(struct sockaddr_un));
/* Очищаем структуру с адресом */
addr.sun_family = AF_UNIX; /* Адрес домена UNIX */
/* Бит addr.sun_path[0] уже был обнулен вызовом memset() */
strncpy(&addr.sun_path[1], "xyz", sizeof(addr.sun_path) — 2);
/* Абстрактное имя "xyz", за которым следуют нулевые байты */
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd == -1)
errExit("socket");
if (bind(sockfd, (struct sockaddr *) &addr,
sizeof(sa_family_t) + strlen(str) + 1) == -1)
errExit("bind");
sleep(60);
exit(EXIT_SUCCESS);
}
Идентификация абстрактных имен сокетов с помощью начального нулевого байта может иметь необычные последствия. Представьте, что переменная name указывает на строку нулевой длины; попытаемся привязать сокет домена UNIX к полю sun_path, инициализированному следующим образом:
strncpy(addr.sun_path, name, sizeof(addr.sun_path) — 1);
В Linux таким манером мы непреднамеренно создадим для сокета абстрактную привязку. Этот код можно считать ошибочным. В других реализациях UNIX последующий вызов bind() завершится ошибкой.
Сокеты UNIX-домена позволяют приложениям взаимодействовать на одном и том же компьютере. Эти сокеты могут быть потоковыми и датаграммными.
Сокеты домена UNIX идентифицируются по имени в файловой системе. Доступ к сокету может регулироваться с помощью прав доступа к соответствующему файлу.
Системный вызов socketpair() создает пару сокетов домена UNIX, соединенных между собой. Это позволяет избежать сразу нескольких системных вызовов для операций создания, привязки и подключения. В своем использовании такие сокеты обычно похожи на именованный канал: создав пару сокетов, процесс генерирует поток, который наследует ссылающиеся на них дескрипторы.
Абстрактное пространство имен сокетов (доступное только в Linux) позволяет привязывать сокет домена UNIX к имени, не имеющему отношения к файловой системе.
Ознакомьтесь с источниками, приведенными в разделе 55.14.
53.1. В разделе 53.3 отмечалось, что датаграммные сокеты домена UNIX являются надежными. Напишите программу, которая показывает следующее: отправитель блокируется, если шлет сообщения быстрее, чем получатель может их прочитать, и остается заблокированным, пока получатель читает отложенные датаграммы.
53.2. Перепишите программы us_xfr_sv.c (см. листинг 53.3) и us_xfr_cl.c (см. листинг 53.4) с помощью абстрактного пространства имен сокетов (см. раздел 53.6).
53.3. Заново реализуйте клиент-серверное приложение для генерирования числовых последовательностей (см. раздел 44.8), задействуя потоковые сокеты домена UNIX.
53.4. Представьте: мы создали два датаграммных сокета в домене UNIX, привязанных к путям /somepath/a и /somepath/b, и соединили их друг с другом. Что произойдет, если мы создадим третий датаграммный сокет и попытаемся с его помощью передать сообщение сокету /somepath/a (с помощью вызова sendto())? Напишите программу, которая отвечает на этот вопрос. По возможности проверьте, как будет вести себя данная программа в других UNIX-системах.
54. Сокеты: основы сетей TCP/IP