UNIX: разработка сетевых приложений
Часть 337 из 399 Информация о книге
Листинг 29.8. Функция send_dns_query: отправка запроса UDP на сервер DNS
//udpcksum/senddnsquery-raw.c6 void7 send_dns_query(void)8 {9 size_t nbytes;10 char *buf, *ptr;11 buf = Malloc(sizeof(struct udpiphdr) + 100);12 ptr = buf + sizeof(struct udpiphdr); /* место для заголовков IP и UDP */13 *((uint16_t*)ptr) = htons(1234); /* идентификатор */14 ptr += 2;15 *((uint16_t*)ptr) = htons(0x0100); /* флаги */16 ptr += 2;17 *((uint16_t*)ptr) = htons(1); /* количество запросов */18 ptr += 2;19 *((uint16_t*)ptr) = 0; /* количество записей в ответе */20 ptr += 2;21 *((uint16_t*)ptr) = 0; /* количество авторитетных записей */22 ptr += 2;23 *((uint16_t*)ptr) = 0; /* количество дополнительных записей */24 ptr += 2;25 memcpy(ptr, "\001a\014root-servers\003net\000", 20);26 ptr += 20;27 *((uint16_t*)ptr) = htons(1); /* тип запроса = А */28 ptr += 2;29 *((uint16_t*)ptr) = htons(1); /* класс запроса = 1 (IP-адрес) */30 ptr += 2;31 nbytes = (ptr - buf) - sizeof(struct udpiphdr);32 udp_write(buf, mbytes),33 if (verbose)35 printf("sent: %d bytes of data\n", nbytes);36 }Инициализация указателя на буферВ буфере11-12имеется место для 20-байтового заголовка IP, 8-байтового заголовка UDP и еще 100 байт для пользовательских данных. Указательbufустановлен на первый байт пользовательских данных.ptrФормирование запроса DNSДля понимания деталей устройства дейтаграммы UDP требуется понимание формата сообщения DNS. Эту информацию можно найти в разделе 14.3 [111]. Мы присваиваем полю идентификации значение 1234, сбрасываем флаги, задаем количество запросов — 1, а затем обнуляем количество записей ресурсов (RR, resource records), получаемых в ответ, количество RR, определяющих полномочия, и количество дополнительных RR.13-24Затем мы формируем простой запрос, который располагается после заголовка: запрос типа А IP-адреса узла25-30. Это доменное имя занимает 20 байт и состоит из 4 фрагментов: однобайтовая частьa.root-servers.net, 12-байтовая частьa, 3-байтовая частьroot-serversи корневая часть, длина которой занимает 0 байт. Тип запроса 1 (так называемый запрос типа А), и класс запроса также 1.netЗапись дейтаграммы UDPЭто сообщение состоит из 36 байт пользовательских данных (восемь 2-байтовых полей и 20-байтовое доменное имя). Мы вызываем нашу функцию31-32для формирования заголовков UDP и IP и последующей записи дейтаграммы UDP в наш символьный сокет.udp_writeВ листинге 29.9 показана функция
, работающая с символьными сокетами.open_outputЛистинг 29.9. Функция open_output: подготовка символьного сокета
2 int rawfd; /* символьный сокет */3 void4 open_output(void)5 {6 int on=1;7 /*8 * Для отправки IP-дейтаграмм нужен символьный сокет9 * Для его создания нужны права привилегированного пользователя.10 * Кроме того, необходимо указать параметр сокета IP_HDRINCL.11 */12 rawfd = Socket(dest->sa_family, SOCK_RAW, 0);13 Setsockopt(rawfd, IPPROTO_IP, IP_HDRINCL, &on., sizeof(on));14 }Объявление дескриптора символьного сокетаМы объявляем глобальную переменную, в которой будет храниться дескриптор символьного сокета.2Создание сокета и установка IP_HDRINCLМы создаем символьный сокет и включаем параметр сокета7-13. Это позволяет нам формировать IP-дейтаграммы целиком, включая заголовок IP.IP_HDRINCLВ листинге 29.10 показана наша функция
, которая формирует заголовки IP и UDP, а затем записывает дейтаграмму в символьный сокет.udp_writeЛистинг 29.10. Функция udp_write: формирование заголовков UDP и IP и запись дейтаграммы IP в символьный сокет
//udpcksum/udpwrite.c19 void20 udp_write(char *buf, int userlen)21 {22 struct udpiphdr *ui;23 struct ip *ip;24 /* заполнение заголовка и вычисление контрольной суммы */25 ip = (struct ip*)buf;26 ui = (struct udpiphdr*)buf;27 bzero(ui, sizeof(*ui));28 /* добавляем 8 к длине псевдозаголовка */29 ui->ui_len = htons((uint16_t)(sizeof(struct udphdr) + userlen));30 /* добавление 28 к длине IP-дейтаграммы */31 userlen += sizeof(struct udpiphdr);32 ui->ui_pr = IPPROTO_UDP;