Читайте книги онлайн на Bookidrom.ru! Бесплатные книги в одном клике

Читать онлайн «UNIX: разработка сетевых приложений». Страница 89

Автор Уильям Стивенс

Функция gethostbyname отличается от других функций сокетов, описанных нами, тем, что она не задает значение переменной errno, когда происходит ошибка. Вместо этого она присваивает глобальной целочисленной переменной h_errno одну из следующих констант, определяемых в заголовке <netdb.h>:

■ HOST_NOT_FOUND;

■ TRY_AGAIN;

■ NO_RECOVERY;

■ NO_DATA (идентично NO_ADDRESS).

Ошибка NO_DATA означает, что заданное имя действительно, но у него нет записи типа А. Примером может служить имя узла, имеющего только запись типа MX.

Самые современные распознаватели предоставляют функцию hstrerror, которая в качестве единственного аргумента получает значение h_errno и возвращает указатель типа const char* на описание ошибки. Некоторые примеры строк, возвращаемых этой функцией, мы увидим в следующем примере.

Пример

В листинге 11.1[1] показана простая программа, вызывающая функцию gethostbyname для любого числа аргументов командной строки и выводящая всю возвращаемую информацию.

Листинг 11.1. Вызов функции и вывод возвращаемой информации

//names/hostent.c

 1 #include "unp.h"


 2 int

 3 main(int argc, char **argv)

 4 {

 5  char *ptr, **pptr;

 6  char str[INET_ADDRSTRLEN];

 7  struct hostent *hptr;


 8  while (--argc > 0) {

 9   ptr = *++argv;

10   if ((hptr = gethostbyname(ptr)) == NULL) {

11    err_msg("gethostbyname error for host, %s: %s",

12     ptr, hstrerror(h_errno));

13    continue;

14   }

15   printf("official hostname: %s\n", hptr->h_name);


16   for (pptr = hptr->h_aliases; *pptr != NULL; pptr++)

17    printf("\talias: %s\n", *pptr);


18   switch (hptr->h_addrtype) {

19   case AF_INET:

20    pptr = hptr->h_addr_list;

21    for (; *pptr != NULL; pptr++)

22     printf("\taddress: %s\n",

23    Inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str)));

24    break;


25   default:

26    err_ret("unknown address type");

27    break;

28   }

29  }

30  exit(0);

31 }

8-14 Функция gethostbyname вызывается для каждого аргумента командной строки.

15-17 Выводится каноническое имя узла, за которым идет список альтернативных имен.

18-24 Переменная pptr указывает на массив указателей на индивидуальные адреса. Для каждого адреса мы вызываем функцию inet_ntop и выводим возвращаемую строку.

Сначала мы выполняем программу с именем нашего узла aix, у которого имеется только один адрес IPv4:

freebsd % hostent aix

official hostname: aix.unpbook.com

         address:  192.168 42.2

Обратите внимание, что официальное имя узла — это FQDN. Кроме того, хотя у узла имеется адрес IPv6, возвращается только адрес IPv4. Следующим будет веб-сервер с несколькими адресами IPv4:

solaris % hostent cnn.com

official hostname: cnn.com

         address: 64.236.16.20

         address: 64.236.16.52

         address: 64.236 16.84

         address: 64.236.16.116

         address: 64.236.24.4

         address: 64.236.24.12

         address: 64.236.24.20

         address: 64.236.24.28

Далее идет имя, представленное в разделе 11.2 как имя с записью типа CNAME:

solaris % hostent www

official hostname: linux.unpbook.com

         alias: www.unpbook.com

         address: 206.168.112.219

Как мы и предполагали, официальное имя узла отличается от нашего аргумента командной строки.

Чтобы увидеть строки ошибок, возвращаемые функцией hstrerror, мы сначала задаем несуществующее имя узла, а затем имя, имеющее только запись типа MX:

solaris % hostent nosuchname.invalid

gethostbyname error for host: nosuchname.invalid: Unknown host


solaris % hostent uunet.uu.net

gethostbyname error for host: uunet.uu.net: No address associated with name

11.4 Функция gethostbyaddr

Функция gethostbyaddr получает в качестве аргумента двоичный IP-адрес и пытается найти имя узла, соответствующее этому адресу. Ее действие обратно действию функции gethostbyname.

#include <netdb.h>


struct hostent *gethostbyaddr(const char *addr, size_t len, int family);

Возвращает: непустой указатель в случае успешного выполнения, -1 в случае ошибки

Эта функция возвращает указатель на ту же структуру hostent, которую мы описывали при рассмотрении функции gethostbyname. Обычно в этой структуре нас интересует поле h_name, каноническое имя узла.

Аргумент addr не относится к типу char*, но в действительности это указатель на структуру in_addr, содержащую адрес IPv4. Поле len — это длина структуры: 4 для адресов IPv4. Аргумент family будет иметь значение AF_INET.

В терминах DNS функция gethostbyaddr запрашивает у сервера имен запись типа PTR в домене in-addr.arpa.

11.5. Функции getservbyname и getservbyport

Службы, как и узлы, также часто идентифицируются по именам. Используя в нашем коде имя службы вместо номера порта, при условии, что имена служб сопоставляются номерам портов в некотором файле (обычно /etc/services), мы получаем следующее преимущество. Если этой службе будет назначен другой номер порта, то нам будет достаточно изменить одну строку в файле /etc/services, вместо того чтобы перекомпилировать все приложения. Следующая функция, getservbyname, ищет службу по ее заданному имени.

ПРИМЕЧАНИЕ

Канонический список номеров портов, назначенных определенным службам, поддерживается IANA и располагается по адресу http://www.iana.org/assignments/port-numbers (см. раздел 2.9). Файл /etc/services чаще всего содержит некоторое подмножество списка IANA.

#include <netdb.h>


struct servent *getservbyname(const char *servname, const char *protoname);

Возвращает: непустой указатель в случае успешного выполнения, NULL в случае ошибки

Функция возвращает указатель на следующую структуру:

struct servent {

 char *s_name;     /* официальное имя службы */

 char **s_aliases; /* список псевдонимов */

 int s_port;       /* номер порта, записанный в сетевом порядке байтов */

 char *s_proto;    /* протокол, который нужно использовать */

};

Имя службы servname должно быть указано обязательно. Если задан и протокол (то есть если protoname — непустой указатель), то в структуре должен быть указан совпадающий протокол. Некоторые службы Интернета позволяют использовать и TCP, и UDP (например, DNS и все службы, представленные в табл. 2.1), в то время как другие поддерживают только один протокол (протоколу FTP требуется TCP). Если аргумент protoname не задан и служба поддерживает несколько протоколов, то возвращаемый номер порта зависит от реализации. Обычно это не имеет значения, поскольку службы, поддерживающие множество протоколов, как правило, используют один и тот же номер порта для протоколов TCP и UDP, но вообще говоря это не гарантируется.

Более всего в структуре servent нас интересует поле номера порта. Поскольку номер порта возвращается в сетевом порядке байтов, мы не должны вызывать функцию htons при записи его в структуру адреса сокета.

Типичные вызовы этой функции могут быть такими:

struct servent *sptr;


sptr = getservbyname("domain", "udp"); /* DNS с использованием UDP */

sptr = getservbyname("ftp", "tcp");    /* FTP с использованием TCP */

sptr = getservbyname("ftp", NULL);     /* FTP с использованием TCP */

sptr = getservbyname("ftp", "udp");    /* этот вызов приведет к ошибке */

Поскольку протоколом FTP поддерживается только TCP, второй и третий вызовы эквивалентны, а четвертый вызов приводит к ошибке. Вот соответствующие строки из файла /etc/services:

freebsd % grep -e ^ftp -e ^domain /etc/services

ftp-data  20/tcp  #File Transfer [Default Data]

ftp       21/tcp  #File Transfer [Control]

domain    53/tcp  #Domain Name Server

domain    53/udp  #Domain Name Server

ftp-agent 574/tcp #FTP Software Agent System

ftp-agent 574/udp #FTP Software Agent System

ftps-data 989/tcp # ftp protocol, data, over TLS/SSL

ftps      990/tcp # ftp protocol, control, over TLS/SSL

Следующая функция, getservbyport, ищет службу по заданному номеру порта и (не обязательно) протоколу.

#include <netdb.h>


struct servent *getservbyport(int port, const char *protname);

Возвращает: непустой указатель в случае успешного выполнения, NULL в случае ошибки

Значение аргумента port должно быть записано в сетевом порядке байтов. Типичные примеры вызова этой функции приведены ниже: