Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
В разделе 28.7 мы приводим пример передачи дескриптора, в котором участвуют неродственные (unrelated) процессы, а в разделе 30.9 — пример, где задействованы родственные процессы. В них мы будем использовать функции read_fd
и write_fd
, которые только что описали.
15.8. Получение информации об отправителе
На рис. 14.4 мы показали другой тип информации, передаваемой через доменный сокет Unix в виде вспомогательных данных: информацию об отправителе, которая передается с помощью структуры cmsgcred
, определяемой путем включения заголовочного файла . Упаковка и формат данных зависят от операционной системы. Такая возможность появилась только в BSD/OS 2.1. Мы описываем FreeBSD, а прочие варианты Unix во многом подобны ей (проблема обычно состоит в выборе структуры, которую следует использовать для передачи данных). Рассказ об этой возможности мы считаем необходимым, поскольку это важное, хотя и простое дополнение доменных протоколов Unix. Когда клиент и сервер связываются с помощью этих протоколов, серверу часто бывает необходим способ точно узнать, кто является клиентом, чтобы убедиться, что клиент имеет право запрашивать определенный сервис.
struct fcred {
uid_t fc_ruid; /* действующий идентификатор пользователя */
gid_t fc_rgid; /* действующий групповой идентификатор */
char fc_login[MAXLOGNAME]; /* имя setlogin() */
uid_t fc_uid; /* идентификатор пользователя */
short fc_ngroups; /* количество групп */
gid_t fc_groups[NGROUPS]; /* дополнительные групповые идентификаторы */
};
#define fc_gid fc_groups[0] /* групповой идентификатор */
Обычно MAXLONGNAME
и NGROUPS
имеют значение 16. Значение fc_ngroups
равно как минимум 1, а первым элементом массива является идентификатор группы.
Эта информация всегда доступна через доменный сокет Unix, хотя отправителю часто приходится принимать дополнительные меры для обеспечения ее отправки вместе с данными, и получателю также приходится выполнять некоторые действия (например, устанавливать параметры сокета). В системе FreeBSD получатель может обойтись вызовом recvmsg
с достаточно большим буфером для вспомогательных данных, чтобы туда поместились идентифицирующие данные (листинг 15.12). Однако отправитель обязан включить структуру cmsgcred
при отправке данных посредством sendmsg
. Хотя включение структуры осуществляется отправителем, заполняется она ядром. Благодаря этому передача идентифицирующих данных через доменный сокет Unix является надежным способом проверки клиента.
Пример
В качестве примера передачи идентифицирующих данных мы изменим наш потоковый доменный сервер Unix, так чтобы он запрашивал идентифицирующие данные клиента. В листинге 15.12 показана новая функция, read_cred
, аналогичная функции read
, но возвращающая также структуру fcred
, содержащую идентифицирующие данные отправителя.
Листинг 15.12. Функция read_cred: чтение и возвращение идентифицирующих данных отправителя
//unixdomain/readcred.c
1 #include "unp.h"
2 #define CONTROL_LEN (sizeof(struct cmsghdr) + sizeof(struct cmsgcred))
3 ssize_t
4 read_cred(int fd, void *ptr, size_t nbytes, struct cmsgcred *cmsgcredptr)
5 {
6 struct msghdr msg;
7 struct iovec iov[1];
8 char control[CONTROL_LEN];
9 int n;
10 msg.msg_name = NULL;
11 msg.msg_namelen = 0;
12 iov[0].iov_base = ptr;
13 iov[0].iov_len = nbytes;
14 msg.msg_iov = iov;
15 msg.msg_iovlen = 1;
16 msg.msg_control = control;
17 msg.msg_controllen = sizeof(control);
18 msg.msg_flags = 0;
19 if ((n = recvmsg(fd, &msg, 0)) < 0)
20 return(n);
21 cmsgcredptr->cmcred_ngroups = 0; /* идентифицирующие данные не получены */
22 if (cmsgcredptr && msg.msg_controllen > 0) {
23 struct cmsghdr *cmptr = (struct cmsghdr*)control;
24 if (cmptr->cmsg_len < CONTROL_LEN)
25 err_quit("control length = %d", cmptr->cmsg_len);
26 if (cmptr->cmsg_level != SOL_SOCKET)
27 err_quit("control level != SOL_SOCKET");
28 if (cmptr->cmsg_type != SCM_CREDS)
29 err_quit("control type != SCM_CREDS");
30 memcpy(cmsgcredptr, CMSG_DATA(cmptr), sizeof(struct cmsgcred));
31 }
32 return(n);
33 }
3-4
Первые три аргумента идентичны аргументам функции read
, а четвертый аргумент — это указатель на структуру cmsgcred
, которая будет заполнена.
22-31
Если данные были переданы, проверяются длина, уровень и тип вспомогательных данных, и результирующая структура копируется обратно вызывающему процессу. Если никаких идентифицирующих данных не было передано, мы обнуляем структуру. Поскольку число групп ( cmcred_ngroups
) всегда равно 1 или больше, нулевое значение указывает вызывающему процессу, что ядро не возвратило никаких идентифицирующих данных.
Функция main
для нашего эхо-сервера (см. листинг 15.3) остается неизменной. В листинге 15.13 показана новая версия функции str_echo
, полученная путем модификации листинга 5.2. Эта функция вызывается дочерним процессом после того, как родительский процесс принял новое клиентское соединение и вызвал функцию fork
.
Листинг 15.13. Функция str_echo, запрашивающая идентифицирующие данные клиента
//unixdomain/strecho.c
1 #include "unp.h"
2 ssize_t read_cred(int, void*, size_t, struct cmsgcred*);
3 void
4 str_echo(int sockfd)
5 {
6 ssize_t n;
7 int i;
8 char buf[MAXLINE];
9 struct cmsgcred cred;
10 again:
11 while ((n = read_cred(sockfd, buf, MAXLINE, &cred)) > 0) {
12 if (cred.cmcred_ngroups == 0) {
13 printf("(no credentials returned)\n");
14 } else {
15 printf("PID of sender = %d\n", cred.cmcred_pid);
16 printf("real user ID = %d\n", cred.cmcred_uid);
17 printf("real group ID = %d\n", cred.cmcred_gid);
18 printf("effective user ID = %d\n", cred.cmcred_euid);
19 printf("%d groups:", cred.cmcred_ngroups - 1);
20 for (i = 1; i < cred.cmcred_ngroups; i++)
21 printf(" %d", cred.cmcred_groups[i]);
22 printf("\n");
23 }
24 Writen(sockfd, buf, n);
25 }
26 if (n < 0 && errno == EINTR)
27 goto again;
28 else if (n < 0)
29 err_sys("str_echo: read error");
30 }
11-23
Если идентифицирующие данные возвращаются, они выводятся.
24-25
Оставшаяся часть цикла не меняется. Этот код считывает строки от клиента и затем отправляет их обратно клиенту.
Наш клиент, представленный в листинге 15.4, остается практически неизменным. Мы добавляем передачу пустой структуры cmsgcred
при вызове sendmsg
, которая заполняется ядром.
Перед запуском клиента определим свои личные данные командой id
:
freebsd % id
uid=1007(andy) gid=1007(andy) groups=1007(andy), 0(wheel)
Если мы запустим сервер в одном окне, а клиент в другом, то для сервера после однократного выполнения клиента получим представленный ниже вывод.
freebsd % unixstrserv02
PID of sender = 26881
real user ID = 1007
real group ID = 1007
effective user ID = 1007
2 groups: 1007 0
Информация выводится только после отправки клиентом данных серверу. Мы видим, что сведения соответствуют тем, которые были получены командой id
.
Интервал:
Закладка: