KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программное обеспечение » Уильям Стивенс - UNIX: разработка сетевых приложений

Уильям Стивенс - UNIX: разработка сетевых приложений

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Уильям Стивенс, "UNIX: разработка сетевых приложений" бесплатно, без регистрации.
Перейти на страницу:

В листинге 15.11 показана последняя функция, write_fd, вызывающая функцию sendmsg для отправки дескриптора (и, возможно, еще каких-либо данных, которые мы не используем) через доменный сокет Unix.

Листинг 15.11. Функция write_fd: передача дескриптора при помощи вызова функции sendmsg

//lib/write_fd.c

 1 #include "unp.h"


 2 ssize_t

 3 write_fd(int fd, void *ptr, size_t nbytes, int sendfd)

 4 {

 5  struct msghdr msg;

 6  struct iovec iov[1];


 7 #ifdef HAVE_MSGHDR_MSG_CONTROL

 8  union {

 9   struct cmsghdr cm;

10   char control[CMSG_SPACE(sizeof(int))];

11  } control_un;

12  struct cmsghdr *cmptr;


13  msg.msg_control = control_un.control;

14  msg.msg_controllen = sizeof(control_un.control);


15  cmptr = CMSG_FIRSTHDR(&msg);

16  cmptr->cmsg_len = CMSG_LEN(sizeof(int));

17  cmptr->cmsg_level = SOL_SOCKET;

18  cmptr->cmsg_type = SCM_RIGHTS;

19  *((int*)CMSG_DATA(cmptr)) = sendfd;

20 #else

21  msg.msg_accrights = (caddr_t)&sendfd;

22  msg.msg_accrightslen = sizeof(int);

23 #endif


24  msg.msg_name = NULL;

25  msg.msg_namelen = 0;


26  iov[0].iov_base = ptr;

27  iov[0].iov_len = nbytes;

28  msg.msg_iov = iov;

29  msg.msg_iovlen = 1;


30  return (sendmsg(fd, &msg, 0));

31 }

Как и в случае функции read_fg, эта функция обрабатывает либо вспомогательные данные, либо права доступа, которые предшествовали вспомогательным данным в более ранних реализациях. В любом случае инициализируется структура msghdr и затем вызывается функция sendmsg.

В разделе 28.7 мы приводим пример передачи дескриптора, в котором участвуют неродственные (unrelated) процессы, а в разделе 30.9 — пример, где задействованы родственные процессы. В них мы будем использовать функции read_fd и write_fd, которые только что описали.

15.8. Получение информации об отправителе

На рис. 14.4 мы показали другой тип информации, передаваемой через доменный сокет Unix в виде вспомогательных данных: информацию об отправителе, которая передается с помощью структуры cmsgcred, определяемой путем включения заголовочного файла <sys/socket.h>. Упаковка и формат данных зависят от операционной системы. Такая возможность появилась только в 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 = %dn", cred.cmcred_pid);

16    printf("real user ID = %dn", cred.cmcred_uid);

17    printf("real group ID = %dn", cred.cmcred_gid);

18    printf("effective user ID = %dn", 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)

Если мы запустим сервер в одном окне, а клиент в другом, то для сервера после однократного выполнения клиента получим представленный ниже вывод.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*