Категории
Самые читаемые
PochitayKnigi » Компьютеры и Интернет » Программное обеспечение » UNIX: разработка сетевых приложений - Уильям Стивенс

UNIX: разработка сетевых приложений - Уильям Стивенс

Читать онлайн UNIX: разработка сетевых приложений - Уильям Стивенс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 220 221 222 223 224 225 226 227 228 ... 263
Перейти на страницу:

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, "01a14root-servers03net00", 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 datan", nbytes);

36 }

Инициализация указателя на буфер

11-12 В буфере buf имеется место для 20-байтового заголовка IP, 8-байтового заголовка UDP и еще 100 байт для пользовательских данных. Указатель ptr установлен на первый байт пользовательских данных.

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

13-24 Для понимания деталей устройства дейтаграммы UDP требуется понимание формата сообщения DNS. Эту информацию можно найти в разделе 14.3 [111]. Мы присваиваем полю идентификации значение 1234, сбрасываем флаги, задаем количество запросов — 1, а затем обнуляем количество записей ресурсов (RR, resource records), получаемых в ответ, количество RR, определяющих полномочия, и количество дополнительных RR.

25-30 Затем мы формируем простой запрос, который располагается после заголовка: запрос типа А IP-адреса узла a.root-servers.net. Это доменное имя занимает 20 байт и состоит из 4 фрагментов: однобайтовая часть a, 12-байтовая часть root-servers, 3-байтовая часть net и корневая часть, длина которой занимает 0 байт. Тип запроса 1 (так называемый запрос типа А), и класс запроса также 1.

Запись дейтаграммы UDP

31-32 Это сообщение состоит из 36 байт пользовательских данных (восемь 2-байтовых полей и 20-байтовое доменное имя). Мы вызываем нашу функцию udp_write для формирования заголовков UDP и IP и последующей записи дейтаграммы UDP в наш символьный сокет.

В листинге 29.9 показана функция open_output, работающая с символьными сокетами.

Листинг 29.9. Функция open_output: подготовка символьного сокета

 2 int rawfd; /* символьный сокет */

 3 void

 4 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_HDRINCL. Это позволяет нам формировать IP-дейтаграммы целиком, включая заголовок IP.

В листинге 29.10 показана наша функция udp_write, которая формирует заголовки IP и UDP, а затем записывает дейтаграмму в символьный сокет.

Листинг 29.10. Функция udp_write: формирование заголовков UDP и IP и запись дейтаграммы IP в символьный сокет

//udpcksum/udpwrite.c

19 void

20 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;

33  ui->ui_src.s_addr = ((struct sockaddr_in*)local)->sin_addr.s_addr;

34  ui->ui_dst.s_addr = ((struct sockaddr_in*)dest)->sin_addr.s_addr;

35  ui->ui_sport = ((struct sockaddr_in*)local)->sin_port;

36  ui->ui_dport = ((struct sockaddr_in*)dest)->sin_port;

37  ui->ui_ulen = ui->ui_len;

38  if (zerosum == 0) {

39 #if 1 /* заменить на if 0 для Solaris 2.x. x < 6 */

40   if ((ui->ui_sum = m_cksum((u_int16_t*)in, userlen)) == 0)

41   ui->ui_sum = 0xffff;

42 #else

43   ui->ui_sum = ui->ui_len;

44 #endif

45  }

46  /* заполнение оставшейся части IP-заголовка */

47  /* функция p_output() вычисляет и сохраняет контрольную сумму IP */

48  ip->ip_v = IPVERSION;

49  ip->ip_hl = sizeof(struct ip) >> 2;

50  ip->ip_tos = 0;

51 #if defined(linux) || defined(__OpenBSD__)

52  ip->ip_len = htons(userlen); /* сетевой порядок байтов */

53 #else

54  ip->ip_len = userlen; /* порядок байтов узла */

55 #endif

56  ip->ip_id = 0; /* это пусть устанавливает уровень IP */

57  ip->ip_off = 0; /* смещение флагов, флаги MF и DF */

58  ip->ip_ttl = TTL_OUT;

59  Sendto(rawfd, buf, userlen, 0, dest, destlen);

60 }

Инициализация указателей на заголовки пакетов

24-26 Указатель ip указывает на начало заголовка IP (структуру ip), а указатель ui указывает на то же место, но структура udpiphdr является объединением заголовков IP и UDP.

Обнуление заголовка

27 Мы явным образом записываем в заголовок нули, чтобы предотвратить учет случайного мусора, который мог остаться в буфере, при вычислении контрольной суммы.

Обновление значений длины

28-31 Переменная ui_len — это длина дейтаграммы UDP: количество байтов пользовательских данных плюс размер заголовка UDP (8 байт). Переменная userlen (количество байтов пользовательских данных, которые следуют за заголовком UDP) увеличивается на 28 (20 байт на заголовок IP и 8 байт на заголовок UDP), для того чтобы соответствовать настоящему размеру дейтаграммы IP.

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

32-45 При вычислении контрольной суммы UDP учитывается не только заголовок и данные UDP, но и поля заголовка IP. Эти дополнительные поля заголовка IP образуют то, что называется псевдозаголовком (pseudoheader). Включение псевдозаголовка обеспечивает дополнительную проверку на то, что если значение контрольной суммы верно, то дейтаграмма была доставлена на правильный узел и с правильным кодом протокола. В указанных строках располагаются операторы инициализации полей в IP-заголовке, формирующих псевдозаголовок. Данный фрагмент кода несколько запутан, но его объяснение приводится в разделе 23.6 [128]. Конечным результатом является запись контрольной суммы UDP в поле ui_sum, если не установлен флаг zerosum (что соответствует наличию аргумента командной строки -0).

Если при вычислении контрольной суммы получается 0, вместо него записывается значение 0xffff. В обратном коде эти числа совпадают, но протокол UDP устанавливает контрольную сумму в нуль, чтобы обозначить, что она вовсе не была вычислена. Обратите внимание, что в листинге 28.10 мы не проверяем, равно ли значение контрольной суммы нулю: дело в том, что в случае ICMPv4 нулевое значение контрольной суммы не означает ее отсутствия.

ПРИМЕЧАНИЕ

Следует отметить, что в Solaris 2.x, где x<6, в случаях, когда дейтаграммы UDP или сегменты TCP отправляются с символьного сокета при установленном параметре IP_HDRINCL, возникает ошибка. Контрольную сумму вычисляет ядро, а мы должны установить поле ui_sum равным длине дейтаграммы UDP.

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

36-49 Поскольку мы установили параметр сокета IP_HDRINCL, нам следует заполнить большую часть полей в заголовке IP. (В разделе 28.3 обсуждается запись в символьный сокет при включенном параметре IP_HDRINCL.) Мы присваиваем полю идентификации нуль (ip_id), что указывает IP на необходимость задания значения этого поля. IP также вычисляет контрольную сумму IP, а функция sendto записывает дейтаграмму IP.

ПРИМЕЧАНИЕ

Обратите внимание, что поле ip_len может иметь либо сетевой порядок байтов, либо порядок байтов узла. Это типичная проблема с совместимостью, возникающая при использовании символьных сокетов.

1 ... 220 221 222 223 224 225 226 227 228 ... 263
Перейти на страницу:
Тут вы можете бесплатно читать книгу UNIX: разработка сетевых приложений - Уильям Стивенс.
Комментарии