Книга: UNIX: разработка сетевых приложений

Функции libnet

Функции libnet

В этом разделе приводятся альтернативные версии функций open_output и send_dns_query, в которых вместо символьных сокетов используются функции библиотеки libnet. Библиотека libnet берет на себя заботу о множестве деталей, в частности, устраняет проблемы с переносимостью, связанные с вычислением контрольных сумм и порядком байтов в заголовке, о которых мы говорили выше. Функция open output представлена в листинге 29.15.

Листинг 29.15. Функция open_output, использующая libnet

//udpcksum/senddnsquery-libnet.c
 7 static libnet_t *l; /* дескриптор libnet */
 8 void
 9 open_output(void)
10 {
11  char errbuf[LIBNET_ERRBUF_SIZE];
12  /* инициализация libnet с символьным сокетом IPv4 */
13  l = libnet_init(LIBNET_RAW4, NULL, errbuf);
14  if (l == NULL) {
15   err_quit("Can't initialize libnet: %s", errbuf);
16  }
17 }

Объявление дескриптора libnet

7 В библиотеке libnet используется непрозрачный тип libnet_t. Функция libnet_init возвращает указатель на этот тип, который затем передается другим функциям libnet для обращения к конкретному сокету. В этом смысле данный тип аналогичен дескрипторам сокетов и устройств pcap.

Инициализация libnet

12-16 Мы вызываем функцию libnet_init, запрашивая открытие символьного сокета IPv4. Для этого в качестве первого аргумента указывается константа LIBNET_RAW4. В случае возникновения ошибки функция возвращает текст сообщения в аргументе errbuf, который мы распечатываем.

Функция send_dns_query для libnet представлена в листинге 29.16. Сравните ее с функциями send_dns_query и udp_write для символьных сокетов.

Листинг 29.16. Функция send_dns_query, использующая libnet

//udpcksum/senddnsquery-libnet.c
18 void
19 send_dns_query(void)
20 {
21  char qbuf[24], *ptr;
22  u_int16_t one;
23  int packet_size = LIBNET_UDP_H + LIBNET_DNSV4_H + 24;
24  static libnet_ptag_t ip_tag, udp_tag, dns_tag;
25  /* построение запроса внутри UDP-пакета */
26  ptr = qbuf;
27  memcpy(ptr, "01a14root-servers03net00", 20);
28  ptr += 20;
29  one = htons(1);
30  memcpy(ptr, &one, 2); /* тип запроса = A */
31  ptr += 2;
32  memcpy(ptr, &one, 2); /* класс запроса = 1 (IP-адрес) */
33  /* формирование пакета DNS */
34  dns_tag = libnet_build_dnsv4(
35   1234 /* идентификатор */,
36   0x0100 /* флаги: рекурсия разрешена */,
37   1 /* кол-во запросов */, 0 /* кол-во записей в ответе */,
38   0 /* кол-во авторитетных записей */, 0 /* кол-во дополнительных */,
39   qbuf /* запрос */,
40   24 /* длина запроса */, 1, dns_tag);
41  /* формирование заголовка UDP */
42  udp_tag = libnet_build_udp(
43   ((struct sockaddr_in*)local)->
44    sin_port /* порт отправителя */,
45   ((struct sockaddr_in*)dest)->
46    sin_port /* порт получателя */,
47   packet_size /* длина */, 0 /* контрольная сумма */,
48   NULL /* полезные данные */, 0 /* длина полезн. данных */, l, udp_tag);
49  /* Так как мы установили контр. сумму равной нулю, libnet автоматически */
50  /* рассчитает контр. сумму UDP. Эту функцию можно отключить. */
51  if (zerosum)
52   if (libnet_toggle_checksum(l, udp_tag, LIBNET_OFF) < 0)
53    err_quit("turning off checksums: %sn", libnet_geterror(l));
54  /* формирование IP-заголовка */
55  ip_tag = libnet_build_ipv4(packet_size + LIBNET_IPV4_H /* длина */,
56   0 /* tos */, 0 /* IP ID */, 0 /* фрагмент*/,
57   TTL_OUT /* ttl */, IPPROTO_UDP /* протокол */,
58   0 /* контр. сумма */,
59   ((struct sockaddr_in*)local)->sin_addr.s_addr /* отправитель */,
60   ((struct sockaddr_in*)dest)->sin_addr.s_addr /* получатель */,
61   NULL /* полезные данные */, 0 /* длина полезн. данных */, l, ip_tag);
62  if (libnet_write(l) < 0) {
63   err_quit("libnet_write: %sn", libnet_geterror(l));
64  }
65  if (verbose)
66   printf("sent: %d bytes of datan", packet_size);
67  }

Формирование запроса DNS

25-32 Мы начинаем с формирования запроса DNS, которое выполняется так же, как в строках 25–30 листинга 29.8.

34-40 Затем мы вызываем функцию libnet_build_dnsv4, которая принимает поля пакета DNS в виде отдельных аргументов. Нам достаточно знать содержимое запроса, а упорядочением этого содержимого в заголовке пакета DNS занимается функция.

Заполнение заголовка UDP и подготовка к вычислению контрольной суммы UDP

42-48 Мы формируем заголовок UDP, вызывая функцию libnet_build_udp. Поля заголовка UDP принимаются этой функцией также в виде отдельных аргументов. Если значение переданной контрольной суммы равно 0, libnet автоматически рассчитывает контрольную сумму.

49-52 Если пользователь запретил вычисление контрольной суммы, мы должны отключить эту функцию libnet явным образом.

Заполнение заголовка IP

53-65 Окончательное формирование пакета требует построения заголовка IPv4 вызовом libnet_build_ipv4.

ПРИМЕЧАНИЕ

Библиотека libnet автоматически записывает поле ip_len в нужном порядке байтов. Это пример повышения переносимости программы благодаря использованию библиотек.

Отправка UDP-дейтаграммы

66-70 Мы вызываем функцию libnet_write для отправки подготовленной дейтаграммы в сеть.

Функция send_dns_query, использующая libnet, состоит всего из 67 строк, тогда как в версии, работавшей с символьными сокетами, общая длина кода составила 96 строк, в которых было по крайней мере 2 трюка, связанных с переносимостью.

Оглавление книги

Оглавление статьи/книги

Генерация: 1.722. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз