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

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

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

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 214 215 216 217 218 219 220 221 222 ... 263
Перейти на страницу:

21-25 Вызывается функция getsockname, так что демон может получить номер порта, связанного с сокетом. Поскольку неизвестно, каков размер буфера, необходимого для размещения структуры адреса сокета, мы используем структуру sockaddr_storage, которая достаточно велика для структуры адреса сокета любого поддерживаемого системой типа и обеспечивает нужное выравнивание.

26-33 Семейство адресов сокета вместе с номером порта сохраняется в структуре client. Если номер порта равен нулю, мы вызываем функцию sock_bind_wild для связывания универсального адреса и динамически назначаемого порта с сокетом, но, как отмечалось ранее, такой подход не работает в реализациях SVR4.

Сообщение клиенту об успехе

34 Один байт, содержащий символ "1", отправляется обратно клиенту.

Закрытие UDP-сокета клиента

35 Заканчиваем работу с UDP-сокетом клиента и закрываем его с помощью функции close. Дескриптор был переслан нам клиентом и, таким образом, является копией; следовательно, UDP-сокет все еще открыт на стороне клиента.

Обработка ошибок и завершение работы клиента

37-45 Если происходит ошибка, клиент получает нулевой байт. Когда клиент завершается, наша часть доменного сокета Unix закрывается, и соответствующий дескриптор удаляется из набора дескрипторов для функции select. Полю connfd структуры client присваивается значение -1, что является указанием на ее освобождение.

Функция readable_v4 вызывается, когда символьный сокет ICMPv4 открыт для чтения. Первая часть данной функции приведена в листинге 28.29. Этот код аналогичен коду для ICMPv4, приведенному ранее в листингах 28.6 и 28.15.

Листинг 28.29. Обработка полученных дейтаграмм ICMPv4, первая часть

//icmpd/readable_v4.c

 1 #include "icmpd.h"

 2 #include <netinet/in_systm.h>

 3 #include <netinet/ip.h>

 4 #include <netinet/ip_icmp.h>

 5 #include <netinet/udp.h>

 6 int

 7 readable_v4(void)

 8 {

 9  int i, hlen1, hlen2, icmplen, sport;

10  char buf[MAXLINE];

11  char srcstr[INET_ADDRSTRLEN], dststr[INET_ADDRSTRLEN];

12  ssize_t n;

13  socklen_t len;

14  struct ip *ip, *hip;

15  struct icmp *icmp;

16  struct udphdr *udp;

17  struct sockaddr_in from, dest;

18  struct icmpd_err icmpd_err;

19  len = sizeof(from);

20  n = Recvfrom(fd4, buf, MAXLINE, 0, (SA*)&from, &len);

21  printf("%d bytes ICMPv4 from %s:", n, Sock_ntop_host((SA*)&from, len));

22  ip = (struct ip*)buf; /* начало IP-заголовка */

23  hlen1 = ip->ip_hl << 2; /* длина IP-заголовка */

24  icmp = (struct icmp*)(buf + hlen1); /* начало ICMP-заголовка */

25  if ((icmplen = n - hlen1) < 8)

26   err_quit("icmplen (%d) < 8", icmplen);

27  printf(" type = %d, code = %dn", icmp->icmp_type, icmp->icmp_code);

Функция выводит некоторую информацию о каждом получаемом сообщении ICMP. Это было сделано для отладки при разработке демона, и вывод управляется аргументом командной строки.

В листинге 28.30 приведена вторая часть функции readable_v4.

Листинг 28.30. Обработка полученных дейтаграмм ICMPv4, вторая часть

//icmpd/readable_v4.c

28  if (icmp->icmp_type == ICMP_UNREACH ||

29   icmp->icmp_type ==ICMP_TIMXCEED ||

30   icmp->icmp_type == ICMP_SOURCEQUENCH) {

31   if (icmplen < 8 + 20 + 8)

32    err_quit("icmplen (%d) < 8 + 20 + 8, icmplen);

33   hip = (struct ip*)(buf + hlen1 + 8);

34   hlen2 = hip->ip_hl << 2;

35   printf("tsrcip = %s, dstip = %s, proto = %dn",

36    Inet_ntop(AF_INET, &hip->ip_src, srcstr, sizeof(srcstr)),

37    Inet_ntop(AF_INET, &hip->ip_dst, dststr, sizeof(dststr)),

38    hip->ip_p);

39   if (hip->ip_p == IPPROTO_UDP) {

40    udp = (struct udphdr*)(buf + hlen1 + 8 + hlen2);

41    sport = udp->uh_sport;

42    /* поиск доменного сокета клиента, отправка заголовка */

43    for (i = 0; i <= maxi; i++) {

44     if (client[i].connfd >= 0 &&

45      client[i].family == AF_INET &&

46      client[i].lport == sport) {

47      bzero(&dest, sizeof(dest));

48      dest.sin_family = AF_INET;

49 #ifdef HAVE_SOCKADDR_SA_LEN

50      dest.sin_len = sizeof(dest);

51 #endif

52      memcpy(&dest.sin_addr, &hip->ip_dst,

53       sizeof(struct in_addr));

54      dest.sin_port = udp->uh_dport;

55      icmpd_err.icmpd_type = icmp->icmp_type;

56      icmpd_err.icmpd_code = icmp->icmp_code;

57      icmpd_err.icmpd_len = sizeof(struct sockaddr_in);

58      memcpy(&icmpd_err.icmpd_dest, &dest, sizeof(dest));

59      /* преобразование кода и типа ICMP в значение errno */

60      icmpd_err.icmpd_errno = EHOSTUNREACH; /* по умолчанию */

61      if (icmp->icmp_type == ICMP_UNREACH) {

62       if (icmp->icmp_code == ICMP_UNREACH_PORT)

63        icmpd_err.icmpd_errno = ECONNREFUSED;

64       else if (icmp->icmp_code == ICMP_UNREACH_NEEDFRAG)

65        icmpd_err.icmpd_errno = EMSGSIZE;

66      }

67      Write(client[i].connfd, &icmpd_err, sizeof(icmpd_err));

68     }

69    }

70   }

71  }

72  return(--nready);

73 }

Проверка типа сообщения, уведомление приложения

29-31 ICMP-сообщения, которые посылаются приложениям, — это сообщения о недоступности порта, превышении времени и завершении клиента (см. табл. 28.1).

Проверка ошибки UDP, поиск клиента

34-42 Указатель hip указывает на IP-заголовок, который возвращается сразу после заголовка ICMP. Это IP-заголовок дейтаграммы, вызвавшей ICMP-ошибку. Мы убеждаемся, что эта IP-дейтаграмма является UDP-дейтаграммой, а затем извлекаем номер UDP-порта из UDP-заголовка, следующего за IP-заголовком.

43-55 По всем структурам client осуществляется поиск подходящего семейства адресов и порта. Если соответствие найдено, строится структура адреса сокета IPv4, которая содержит IP-адрес получателя и порт из UDP-дейтаграммы, вызвавшей ошибку.

Построение структуры icmpd_err

56-70 Строится структура icmpd_err, посылаемая клиенту через доменный сокет Unix. Тип и код сообщения ICMP сначала отображаются в значение errno, как показано в табл. 28.1.

Ошибки ICMPv6 обрабатываются функцией readable_v6, первая часть которой приведена в листинге 28.31. Обработка ошибок ICMPv6 аналогична коду, приведенному в листингах 28.7 и 28.16.

Листинг 28.31. Обработка полученной дейтаграммы ICMPv6, первая часть

//icmpd/readable_v6.c

 1 #include "icmpd.h"

 2 #include <netinet/in_systm.h>

 3 #include <netinet/ip.h>

 4 #include <netinet/ip_icmp.h>

 5 #include <netinet/udp.h>

 6 #ifdef IPV6

 7 #include <netinet/ip6.h>

 8 #include <netinet/icmp6.h>

 9 #endif

10 int

11 readable_v6(void)

12 {

13 #ifdef IPV6

14  int i, hlen2, icmp6len, sport;

15  char buf[MAXLINE];

16  char srcstr[INET6_ADDRSTRLEN], dststr[INET6_ADDRSTRLEN];

17  ssize_t n;

18  socklen_t len;

19  struct ip6_hdr *ip6, *hip6;

20  struct icmp6_hdr *icmp6;

21  struct udphdr *udp;

22  struct sockaddr_in6 from, dest;

23  struct icmpd_err icmpd_err;

24  len = sizeof(from);

25  n = Recvfrom(fd6, buf, MAXLINE, 0, (SA*)&from, &len);

26  printf("%d bytes ICMPv6 from %s:", n, Sock_ntop_host((SA*)&from, len));

27  icmp6 = (struct icmp6_hdr*)buf; /* начало заголовка ICMPv6 */

28  if ((icmp6len = n) < 8)

29   err_quit("icmp6len (%d) < 8", icmp6len);

30  printf(" type = %d, code = %dn", icmp6->icmp6_type, icmp6->icmp6_code);

Вторая часть функции readable_v6 приведена в листинге 28.32. Код аналогичен приведенному в листинге 28.30: мы проверяем тип ICMP-ошибки, убеждаемся, что дейтаграмма, вызвавшая ошибку, является UDP-дейтаграммой, а затем строим структуру icmpd_err, которую отсылаем клиенту.

Листинг 28.32. Обработка полученной дейтаграммы ICMPv6, вторая часть

//icmpd/readable_v6.c

31  if (icmp6->icmp6_type == ICMP6_DST_UNREACH ||

32   icmp6->icmp6_type == ICMP6_PACKET_TOO_BIG ||

33   icmp6->icmp6_type == ICMP6_TIME_EXCEEDED) {

34   if (icmp6len < 8+8)

35    err_quit("icmp6len (%d) < 8 + 8", icmp6len);

36   hip6 = (struct ip6_hdr*)(buf + 8);

37   hlen2 = sizeof(struct ip6_hdr);

38   printf("tsrcip = %s, dstip = %s, next hdr = %dn",

39    Inet_ntop(AF_INET6, &hip6->ip6_src, srcstr, sizeof(srcstr)),

40    Inet_ntop(AF_INET6, &hip6->ip6_dst, dststr, sizeof(dststr)),

41    hip6->ip6_nxt);

42   if (hip6->ip6_nxt == IPPROTO_UDP) {

43    udp = (struct udphdr*)(buf + 8 + hlen2);

44    sport = udp->uh_sport;

45    /* поиск доменного сокета клиента, отправка заголовков */

46    for (i = 0; i <= maxi; i++) {

47     if (client[i].connfd >= 0 &&

48      client[i].family == AF_INET6 &&

1 ... 214 215 216 217 218 219 220 221 222 ... 263
Перейти на страницу:
Тут вы можете бесплатно читать книгу UNIX: разработка сетевых приложений - Уильям Стивенс.
Комментарии