31 goto clienterr;
32 }
33 }
34 Write(unixfd, "1", 1); /* сообщение клиенту об успехе */
35 Close(recvfd); /* работа с UDP-сокетом клиента завершена */
36 return(--nready);
37 clienterr:
38 Write(unixfd, "0", 1); /* сообщение клиенту об ошибке */
39 clientdone:
40 Close(unixfd);
41 if (recvfd >= 0)
42 Close(recvfd);
43 FD_CLR(unixfd, &allset);
44 client[i].connfd = -1;
45 return(--nready);
46 }
Получение номера порта, связанного с сокетом UDP
21-25
Вызывается функция
getsockname
, так что демон может получить номер порта, связанного с сокетом. Поскольку неизвестно, каков размер буфера, необходимого для размещения структуры адреса сокета, мы используем структуру
sockaddr_storage
, которая достаточно велика для структуры адреса сокета любого поддерживаемого системой типа и обеспечивает нужное выравнивание.
26-33
Семейство адресов сокета вместе с номером порта сохраняется в структуре
client
. Если номер порта равен нулю, мы вызываем функцию
sock_bind_wild
для связывания универсального адреса и динамически назначаемого порта с сокетом, но, как отмечалось ранее, такой подход не работает в реализациях SVR4.
Сообщение клиенту об успехе
34
Один байт, содержащий символ
"1"
, отправляется обратно клиенту.
Закрытие UDP-сокета клиента
35
Заканчиваем работу с UDP-сокетом клиента и закрываем его с помощью функции
close
. Дескриптор был переслан нам клиентом и, таким образом, является копией; следовательно, UDP-сокет все еще открыт на стороне клиента.
Обработка ошибок и завершение работы клиента
37-45
Если происходит ошибка, клиент получает нулевой байт. Когда клиент завершается, наша часть доменного сокета Unix закрывается, и соответствующий дескриптор удаляется из набора дескрипторов для функции
select
. Полю
connfd
структуры
client
присваивается значение -1, что является указанием на ее освобождение.
Функция
readable_v4
вызывается, когда символьный сокет ICMPv4 открыт для чтения. Первая часть данной функции приведена в листинге 28.29. Этот код аналогичен коду для ICMPv4, приведенному ранее в листингах 28.6 и 28.15.
Листинг 28.29. Обработка полученных дейтаграмм ICMPv4, первая часть
//icmpd/readable_v4.c
1 #include "icmpd.h"
2 #include <netinet/in_systm.h>
3 #include <netinet/ip.h>
4 #include <netinet/ip_icmp.h>
5 #include <netinet/udp.h>
6 int
7 readable_v4(void)
8 {
9 int i, hlen1, hlen2, icmplen, sport;
10 char buf[MAXLINE];
11 char srcstr[INET_ADDRSTRLEN], dststr[INET_ADDRSTRLEN];
12 ssize_t n;
13 socklen_t len;
14 struct ip *ip, *hip;
15 struct icmp *icmp;
16 struct udphdr *udp;
17 struct sockaddr_in from, dest;
18 struct icmpd_err icmpd_err;
19 len = sizeof(from);
20 n = Recvfrom(fd4, buf, MAXLINE, 0, (SA*)&from, &len);
21 printf("%d bytes ICMPv4 from %s:", n, Sock_ntop_host((SA*)&from, len));
22 ip = (struct ip*)buf; /* начало IP-заголовка */
23 hlen1 = ip->ip_hl << 2; /* длина IP-заголовка */
24 icmp = (struct icmp*)(buf + hlen1); /* начало ICMP-заголовка */
25 if ((icmplen = n - hlen1) < 8)
26 err_quit("icmplen (%d) < 8", icmplen);
27 printf(" type = %d, code = %d\n", icmp->icmp_type, icmp->icmp_code);
Функция выводит некоторую информацию о каждом получаемом сообщении ICMP. Это было сделано для отладки при разработке демона, и вывод управляется аргументом командной строки.
В листинге 28.30 приведена вторая часть функции
readable_v4
.
Листинг 28.30. Обработка полученных дейтаграмм ICMPv4, вторая часть
//icmpd/readable_v4.c
28 if (icmp->icmp_type == ICMP_UNREACH ||
29 icmp->icmp_type ==ICMP_TIMXCEED ||
30 icmp->icmp_type == ICMP_SOURCEQUENCH) {
31 if (icmplen < 8 + 20 + 8)
32 err_quit("icmplen (%d) < 8 + 20 + 8, icmplen);
33 hip = (struct ip*)(buf + hlen1 + 8);
34 hlen2 = hip->ip_hl << 2;
35 printf("\tsrcip = %s, dstip = %s, proto = %d\n",
36 Inet_ntop(AF_INET, &hip->ip_src, srcstr, sizeof(srcstr)),
37 Inet_ntop(AF_INET, &hip->ip_dst, dststr, sizeof(dststr)),
38 hip->ip_p);