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имеют значение 16. ЗначениеNGROUPSравно как минимум 1, а первым элементом массива является идентификатор группы.fc_ngroupsЭта информация всегда доступна через доменный сокет Unix, хотя отправителю часто приходится принимать дополнительные меры для обеспечения ее отправки вместе с данными, и получателю также приходится выполнять некоторые действия (например, устанавливать параметры сокета). В системе FreeBSD получатель может обойтись вызовом
с достаточно большим буфером для вспомогательных данных, чтобы туда поместились идентифицирующие данные (листинг 15.12). Однако отправитель обязан включить структуруrecvmsgпри отправке данных посредствомcmsgcred. Хотя включение структуры осуществляется отправителем, заполняется она ядром. Благодаря этому передача идентифицирующих данных через доменный сокет Unix является надежным способом проверки клиента.sendmsgПример
В качестве примера передачи идентифицирующих данных мы изменим наш потоковый доменный сервер Unix, так чтобы он запрашивал идентифицирующие данные клиента. В листинге 15.12 показана новая функция,
, аналогичная функцииread_cred, но возвращающая также структуруread, содержащую идентифицирующие данные отправителя.fcredЛистинг 15.12. Функция read_cred: чтение и возвращение идентифицирующих данных отправителя
//unixdomain/readcred.c1 #include "unp.h"2 #define CONTROL_LEN (sizeof(struct cmsghdr) + sizeof(struct cmsgcred))3 ssize_t4 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) всегда равно 1 или больше, нулевое значение указывает вызывающему процессу, что ядро не возвратило никаких идентифицирующих данных.cmcred_ngroupsФункция
для нашего эхо-сервера (см. листинг 15.3) остается неизменной. В листинге 15.13 показана новая версия функцииmain, полученная путем модификации листинга 5.2. Эта функция вызывается дочерним процессом после того, как родительский процесс принял новое клиентское соединение и вызвал функциюstr_echo.forkЛистинг 15.13. Функция str_echo, запрашивающая идентифицирующие данные клиента
//unixdomain/strecho.c1 #include "unp.h"2 ssize_t read_cred(int, void*, size_t, struct cmsgcred*);3 void4 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) {