UNIX: разработка сетевых приложений
Функция
дляsend_dns_queryпредставлена в листинге 29.16. Сравните ее с функциямиlibnetиsend_dns_queryдля символьных сокетов.udp_writeЛистинг 29.16. Функция send_dns_query, использующая libnet
//udpcksum/senddnsquery-libnet.c18 void19 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, "\001a\014root-servers\003net\000", 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: %s\n", 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: %s\n", libnet_geterror(l));64 }65 if (verbose)66 printf("sent: %d bytes of data\n", packet_size);67 }Формирование запроса DNSМы начинаем с формирования запроса DNS, которое выполняется так же, как в строках 25–30 листинга 29.8.25-32Затем мы вызываем функцию34-40, которая принимает поля пакета DNS в виде отдельных аргументов. Нам достаточно знать содержимое запроса, а упорядочением этого содержимого в заголовке пакета DNS занимается функция.libnet_build_dnsv4Заполнение заголовка UDP и подготовка к вычислению контрольной суммы UDPМы формируем заголовок UDP, вызывая функцию42-48. Поля заголовка UDP принимаются этой функцией также в виде отдельных аргументов. Если значение переданной контрольной суммы равно 0,libnet_build_udpавтоматически рассчитывает контрольную сумму.libnetЕсли пользователь запретил вычисление контрольной суммы, мы должны отключить эту функцию49-52явным образом.libnetЗаполнение заголовка IPОкончательное формирование пакета требует построения заголовка IPv4 вызовом53-65.libnet_build_ipv4ПРИМЕЧАНИЕБиблиотека libnet автоматически записывает поле ip_len в нужном порядке байтов. Это пример повышения переносимости программы благодаря использованию библиотек.
Отправка UDP-дейтаграммыМы вызываем функцию66-70для отправки подготовленной дейтаграммы в сеть.libnet_writeФункция
, использующаяsend_dns_query, состоит всего из 67 строк, тогда как в версии, работавшей с символьными сокетами, общая длина кода составила 96 строк, в которых было по крайней мере 2 трюка, связанных с переносимостью.libnet29.8. Резюме
Символьные сокеты предоставляют возможность записывать и считывать IP-дейтаграммы, которые могут быть не поняты ядром, а доступ к канальному уровню позволяет считывать и записывать кадры канального уровня любых типов (не только дейтаграммы IP). Программа
— это, вероятно, наиболее широко используемая программа, имеющая непосредственный доступ к канальному уровню.tcpdumpВ различных операционных системах применяются различные способы доступа к канальному уровню. Мы рассмотрели пакетный фильтр Беркли, DLPI SVR4 и пакетные сокеты Linux (
). Но у нас имеется возможность, не вникая в различия перечисленных способов, использовать находящуюся в свободном доступе переносимую библиотеку захвата пакетовSOCK_PACKET.libcap