Таблица 11.5. Четыре типа данных, относящихся к сетям
Тип данных | Файл | Структура | Функции поиска по ключу |
Узлы | /etc/hosts | Hostent | gethostbyaddr, gethostbyname |
Сети | /etc/networks | Netent | getnetbyaddr, getnetbyname |
Протоколы | /etc/protocols | Protoent | getprotobyname, getprotobynumber |
Службы | /etc/services | Servent | getservbyname, getservbyport |
Как это применяется, если используется DNS? Прежде всего, с помощью DNS возможен доступ только к информации об узле и о сети. Информация о протоколе и службах всегда считывается из соответствующего файла. Ранее в этой главе мы отмечали (см. подраздел «Альтернативы DNS»), что в разных реализациях отличаются способы, с помощью которых администратор определяет, что именно использовать для получения информации об узле и сети — DNS или файл.
Далее, если DNS используется для получения информации об узле и о сети, имеют смысл только функции поиска по ключу. Используя, например, функцию
gethostent
, не стоит надеяться, что она выполнит последовательный перебор всех записей DNS! Если вызывается функция
gethostent
, она считывает только информацию об узлах и не использует DNS.
ПРИМЕЧАНИЕ
Хотя информацию о сети можно сделать доступной с помощью DNS, очень немногие пользуются этим. На с. 347-348 [1] рассказывается об этой возможности. Однако обычно администраторы создают и обслуживают файл /etc/networks, используемый вместо DNS. Программа netstat с параметром -i использует этот файл, если он есть, и выводит имя каждой сети. Однако бесклассовая адресация (см. раздел А.4) делает эти функции бесполезными, а поскольку они не поддерживают IPv6, новые приложения не должны использовать их.
11.22. Резюме
Набор функций, вызываемых приложением для преобразования имени узла в IP- адрес и обратно, называется распознавателем. Две функции,
gethostbyname
и
gethostbyaddr
, являются типичными точками входа. С переходом на IPv6 и многопоточное программирование полезными становятся
getaddrinfo
и
getnameinfo
, способные работать с адресами IPv6 и безопасные в многопоточной среде.
Для работы с именами служб и номерами портов широко используется функция
getservbyname
, принимающая имя службы и возвращающая структуру, содержащую номер порта. Преобразование чаще всего осуществляется на основании данных, содержащихся в некотором текстовом файле. Существует возможность сопоставления имен и номеров протоколов, а также имен и номеров сетей, но используется она реже.
Альтернативой DNS, которую мы не упомянули, является непосредственный вызов функций распознавателя вместо использования функций
gethostbyname
и
gethostbyaddr
. Таким способом пользуется, например, программа
sendmail
, предназначенная для поиска записи типа MX, чего не может сделать функция
gethostby<i>XXX</i>
. У функций распознавателя имена начинаются с
res_
. Примером такой функции является функция
res_init
, которую мы описали в разделе 11.4. Описание этих функций и пример вызывающей их программы находятся в главе 15 книги [1]. При вводе в командной строке man
resolver
должны отобразиться страницы руководства для этих функций.
Упражнения
1. Измените программу, представленную в листинге 11.1, так, чтобы для каждого возвращаемого адреса вызывалась функция
gethostbyaddr
, а затем выведите возвращаемое имя
h_name
. Сначала запустите программу, задав имя узла только с одним IP-адресом, а затем — с несколькими IP-адресами. Что происходит?
2. Устраните проблему, показанную в предыдущем упражнении.
3. Запустите программу, показанную в листинге 11.4, задав имя службы
chargen
.
4. Запустите программу, показанную в листинге 11.4, задав IP-адрес в точечно- десятичной записи в качестве имени узла. Допускает ли это ваш распознаватель? Измените листинг 11.4, чтобы разрешить IP-адрес в виде строки десятичных чисел с точками в качестве имени узла и строку с десятичным номером порта в качестве имени службы. В каком порядке должно выполняться тестирование IP-адреса для строки в точечно-десятичной записи и для имени?
5. Измените программу в листинге 11.4 так, чтобы можно было работать либо с IPv4, либо с IPv6.
6. Измените программу в листинге 8.5 так, чтобы сделать запрос DNS, и сравните возвращаемый IP-адрес со всеми IP-адресами узла получателя, то есть вызовите функцию
gethostbyaddr
, используя IP-адрес, возвращаемый функцией
recvfrom
, а затем вызовите
gethostbyname
для поиска всех IP-адресов для узла.
7. Измените листинг 11.6, чтобы вызвать функцию
getnameinfo
вместо функции
sock_ntop
. Какие флаги вы должны передать функции
getnameinfo
?
8. В разделе 7.5 мы обсуждали завладение портом с помощью параметра сокета
SO_REUSEADDR
. Чтобы увидеть, как это происходит, создайте не зависящий от протокола сервер времени и даты UDP, показанный в листинге 11.13. Запустите один экземпляр сервера в одном окне, свяжите его с универсальным адресом и некоторым портом, который вы выберете. Запустите в другом окне клиент и убедитесь, что этот сервер выполняет обработку клиента (отметьте вызов функции
printf
на узле сервера). Затем запустите другой экземпляр сервера в другом окне, и на этот раз свяжите его с одним из адресов направленной передачи узла и тем же портом, что и первый сервер. С какой проблемой вы сразу же столкнетесь? Устраните эту проблему и перезапустите второй сервер. Запустите клиент, отправьте дейтаграмму и проверьте, что второй сервер захватил порт первого сервера. Если возможно, запустите второй сервер снова с учетной записью, отличной от учетной записи первого сервера, чтобы проверить, происходит ли по-прежнему захват порта, поскольку некоторые производители не допускают второго связывания, если идентификатор пользователя отличен от идентификатора процесса, уже связанного с портом.
9. В конце раздела 2.12 мы показали два примера Telnet: сервер времени и даты и эхо-сервер. Зная, что клиент проходит через два этапа — функцию
gethostbyname
и функцию connect, определите, к каким этапам относятся строки вывода клиента.