В стандарте SUSv3 не уточняется размер поля sun_path. В ранних реализациях системы BSD для него применялись 108 и 104 байта, а в одной современной системе (HP-UX 11) действует значение 92. Портируемые приложения должны быть созданы из расчета наименьшего размера, используя при записи в это поле вызов snprintf() или strncpy(), чтобы избежать переполнения буфера.
Для привязки сокета UNIX-домена к адресу нужно инициализировать структуру sockaddr_un, передать (приведенный) указатель на нее аргументу addr вызова bind() и указать addrlen в качестве размера этой структуры, как показано в листинге 53.1.
Листинг 53.1. Привязка сокета домена UNIX
const char *SOCKNAME = "/tmp/mysock";
int sfd;
struct sockaddr_un addr;
sfd = socket(AF_UNIX, SOCK_STREAM, 0); /* Создаем сокет */
if (sfd == -1)
errExit("socket");
memset(&addr, 0, sizeof(struct sockaddr_un)); /* Очищаем структуру */
addr.sun_family = AF_UNIX; /* Адрес домена UNIX */
strncpy(addr.sun_path, SOCKNAME, sizeof(addr.sun_path) — 1);
if (bind(sfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1)
errExit("bind");
Применение вызова memset() в листинге 53.1 гарантирует, что все поля структуры равны 0 (данным аспектом пользуется последующий вызов strncpy(), финальный аргумент которого на единицу меньше, чем размер поля sun_path; благодаря этому в конце данного поля всегда будет находиться нулевой символ). Обнуление всей структуры целиком с помощью вызова memset() вместо инициализации отдельных полей гарантирует, что ни одно поле, даже если оно нестандартное и предоставляется только текущей реализацией, не будет пропущено.
У вызова memset() есть альтернатива из мира BSD, функция bzero(), тоже обнуляющая содержимое структуры. В стандарте SUSv3 она упоминается в связке с функцией bcopy() (которая является аналогом memmove()); обе эти функции считаются устаревшими, а вместо них рекомендуется использовать вызовы memset() и memmove(). Функции bzero() и bcopy() не вошли в стандарт SUSv4.
Привязывая сокет домена UNIX, вызов bind() создает запись в файловой системе (следовательно, каталог, являющийся частью пути к сокету, должен быть доступен для чтения и записи). Владелец файла определяется на основе стандартных правил (см. подраздел 15.3.1). Сам файл помечается как сокет. Если применить к нему вызов stat(), то в поле st_mode возвращенной структуры stat (точнее, в той его части, которая хранит тип файла) будет указано значение S_IFSOCK (см. раздел 15.1). При вводе команды ls — l в первом столбце будет значиться тип s, указывающий на сокет домена UNIX, а команда ls — F добавляет к пути сокета знак равенства (=).
Сокеты домена UNIX идентифицируются с помощью путей, однако операции ввода/вывода, которые к ним применяются, не затрагивают исходное устройство.
Необходимо отметить несколько моментов, касающихся привязки сокетов UNIX-домена.
• Сокет нельзя привязать к существующему пути (вызов bind() завершится ошибкой EADDRINUSE).
• Обычно сокет привязывают к полному пути для фиксации его местоположения в файловой системе. Использование относительных путей допустимо, но не рекомендуется, поскольку подразумевается, что клиент, который хочет подключиться к сокету, знает текущий каталог приложения, выполнившего вызов bind().
• Сокет можно привязать только к одному пути; и наоборот — путь может быть привязан только к одному сокету.
• Сокет нельзя открыть с помощью вызова open().
• Когда сокет больше не нужен, его запись в файловой системе (путь) можно удалить, используя такие вызовы, как unlink() или remove() (обычно так и следует делать).
В большинстве примеров, которые здесь приводятся, сокеты домена UNIX привязываются к файлам в каталоге /tmp, поскольку он присутствует в любой системе и является доступным для записи. Это облегчает запуск программ и избавляет от необходимости изменять пути к сокетам.
Однако имейте в виду, что в большинстве случаев данный выбор не самый лучший. Как уже отмечалось в разделе 38.7, создание файлов в публичных каталогах, доступных для записи (таких как /tmp) может стать причиной различного рода уязвимостей. Например, создавая в каталоге /tmp файл, имя которого совпадает с сокетом какого-то приложения, мы фактически осуществляем простую DoS-атаку. Реальные приложения должны привязывать свои сокеты домена UNIX к абсолютным путям на основе достаточно защищенных каталогов.
В данном разделе представлено простое клиент-серверное приложение, использующее потоковые сокеты в домене UNIX. Клиентская программа (листинг 53.4) устанавливает соединение с сервером и передает ему данные со своего стандартного ввода. Серверная программа (листинг 53.3) принимает клиентские соединения и направляет все данные, посланные клиентами, в стандартный вывод. Это простой пример