28 pid = getpid 0xffff; /* поле идентификатора ICMP имеет размер 16 бит */
29 Signal(SIGALRM, sig_alrm);
30 ai = Host_serv(host, NULL, 0, 0);
31 h = Sock_ntop_host(ai-ai_addr, ai-ai_addrlen);
32 printf("PING %s (%s): %d data bytes\n",
33 ai-ai_canonname ? ai-ai_canonname : h, h, datalen);
34 /* инициализация в соответствии с протоколом */
35 if (ai-ai_family == AF_INET) {
36 pr = proto_v4;
37 #ifdef IPV6
38 } else if (ai-ai_family == AF_INET6) {
39 pr = proto_v6;
40 if (IN6_IS_ADDR_V4MAPPED((((struct sockaddr_in6*)
41 ai-ai_addr)-sin6_addr)))
42 err_quit("cannot ping IPv4-mapped IPv6 address");
43 #endif
44 } else
45 err_quit("unknown address family %d", ai-ai_family);
46 pr-sasend = ai-ai_addr;
47 pr-sarecv = Calloc(1, ai-ai_addrlen);
48 pr-salen = ai-ai_addrlen;
49 readloop;
50 exit(0);
51 }
2-7
Определяется структура
proto
для IPv4 и IPv6. Указатели структуры адреса сокета инициализируются как нулевые, поскольку еще не известно, какая из версий будет использоваться — IPv4 или IPv6.
8
Устанавливается количество дополнительных данных (56 байт), которые будут посылаться с эхо-запросом ICMP. При этом полная IPv4-дейтаграмма будет иметь размер 84 байта (20 байт на IPv4-заголовок и 8 байт на ICMP-заголовок), а IPv6-дейтаграмма будет иметь длину 104 байта. Все данные, посылаемые с эхо- запросом, должны быть возвращены в эхо-ответе. Время отправки эхо-запроса будет сохраняться в первых 8 байтах области данных, а затем, при получении эхо- ответа, будет использоваться для вычисления и вывода времени RTT.
15-24
Единственный параметр командной строки, поддерживаемый в нашей версии, это параметр
-v
, в результате использования которого большинство ICMP-сообщений будут выводиться на консоль. (Мы не выводим эхо-ответы, принадлежащие другой запущенной копии программы
ping
.) Для сигнала
SIGALRM
устанавливается обработчик, и мы увидим, что этот сигнал генерируется один раз в секунду и вызывает отправку эхо-запросов ICMP.
31-48
Строка, содержащая имя узла или IP-адрес, является обязательным аргументом и обрабатывается функцией
host_serv
. Возвращаемая структура
addrinfo
содержит семейство протоколов — либо
AF_INET
, либо
AF_INET6
. Глобальный указатель pr устанавливается на требуемую в конкретной ситуации структуру
proto
. Также с помощью вызова функции
IN6_IS_ADDR_V4MAPPED
мы убеждаемся, что адрес IPv6 на самом деле не является адресом IPv4, преобразованным к виду IPv6, поскольку даже если возвращаемый адрес является адресом IPv6, узлу будет отправлен пакет IPv4. (Если такая ситуация возникнет, можно переключиться и использовать IPv4.) Структура адреса сокета, уже размещенная в памяти с помощью функции
getaddrinfo
, используется для отправки, а другая структура адреса сокета того же размера размещается в памяти для получения.
Обработка ответов осуществляется функцией
readlоор
, представленной в листинге 28.4.
Листинг 28.4. Функция readloop
//ping/readlоор.c
1 #include "ping.h"
2 void
3 readloop(void)
4 {
5 int size;
6 char recvbuf[BUFSIZE];
7 char controlbuf[BUFSIZE];
8 struct msghdr msg;
9 struct iovec iov;
10 ssize_t n;
11 struct timeval tval;
12 sockfd = Socket(pr-sasend-sa_family, SOCK_RAW, pr-icmpproto);
13 setuid(getuid); /* права привилегированного пользователя
больше не нужны */
14 if (pr-finit)
15 (*pr-finit);
16 size = 60 * 1024; /* setsockopt может завершиться с ошибкой */
17 setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, size, sizeof(size));