Разрешение доменных имён (Windows)

Предыдущая тема Следующая тема Перейти вниз

Разрешение доменных имён (Windows)

Сообщение  Замабувараев в Вс Июн 21, 2015 10:08 am

Разрешение доменного имени — это получение его IP‐адреса. Раньше для DNS‐запросов необходимо было использовать функции inet_addr и gethostbyname. Затем информацию нужно было вручную заносить в структуру sockaddr_in и только затем использовать её.

К счастью, это больше не нужно и даже нежелательно, если нужно использовать не только IPv4, но и IPv6. Корпорация Micro$oft объявила данный метод устаревшим. В современном мире есть функция GetAddrInfoW, которая выполняет всю работу DNS‐запросов, и заполняет структуры необходимыми данными. И эта функция работает с юникодом.

Вот она (функция уже объявлена в заголовочных файлах, дополнительно объявлять её не нужно):
Код:


Declare Function GetAddrInfoW( _
      ByRef pNodeName As WString, _ /' например "www.example.com" или IP '/
      ByRef pServiceName As WString, _ /' например "http" или номер порта '/
      ByVal pHints As AddrInfoW Ptr, _ /' параметры запроса '/
      ByVal ppResult As AddrInfoW Ptr Ptr _ /' результат '/
)


В функцию нужно передать три входных параметра, а она заполняет память по указателю связанным списком результатов.

Первый параметр — имя хоста (доменное имя) или IP‐адрес для соединения.

Следующий параметр — сервис, который может быть номером порта ("80") или названием сервиса ("http"). Если указать сервис, то должен находиться в списке портов IANA или в файле %WINDIR%\system32\drivers\etc\services.

Наконец, третий параметр — указатель на структуру AddrInfoW, которую нужно заполнить минимальной информацией.

Вот пример вызова функции в случае, если вы — сервер и хотите слушать сеть на порту 80 или клиент и хотите соединиться с сервером через 80‐ый порт. Этот код ничего не делает, кроме создания структур, котрые будут использоваться в дальнейшем:

Код:


' Заголовочники
#ifndef unicode
   #define unicode
#endif
#include "windows.bi"
#include once "win\winsock2.bi"
#include once "win\ws2tcpip.bi"

' Инициализация сокетов
Dim objWsaData As WSAData = Any
' Нужна версия 2.2
If WSAStartup(MAKEWORD(2, 2), @objWsaData) = NO_ERROR Then
   ' Параметр функции
   Dim hints As AddrInfoW
   With hints
      ' Версия протокола IP не важна
      .ai_family = AF_UNSPEC  ' Можно поставить AF_INET6, чтобы получить только IPv6‐адреса, или AF_INET , чтобы получить IPv4
      .ai_socktype = SOCK_STREAM ' Использование протокола TCP
      .ai_protocol = IPPROTO_TCP
   End With
   ' Связанный список результата
   Dim pResult As AddrInfoW Ptr
   
   ' Имя сервера в юникоде, порт, параметр функции, результат
   Dim intResult As Integer = GetAddrInfoW("example.org", "80", @hints, @pResult)
   If intResult = 0 Then
      Print "Всё хорошо"
   Else
      Print "Ошибка", *gai_strerror(intResult)
   End If
End If

Функция GetAddrInfoW возвращает 0 в случае успеха и не ноль при ошибке. Подробное описание ошибок можно найти на сайте MSDN https://msdn.microsoft.com/en-us/library/windows/desktop/ms738519%28v=vs.85%29.aspx

В ai_family назначена константа AF_UNSPEC, тем самым указывая, что всё равно, использовать ли IPv4 или IPv6. Можно устанавливать этот флаг в AF_INET или AF_INET6, если нужно указать конкретный протокол.

Так как теперь в pResult содержится связанный список всего ответа на DNS‐запрос, то нужно немедленно пройтись по нему и распечатать информацию. Этот код необходим только для вывода информации об адресе доменного имени на экран.

Код:


' Узел для обхода списка
Dim pPtr As AddrInfoW Ptr = pResult
' Счётчик записей
Dim i As Integer
Dim sockaddr_ip As LPSOCKADDR = Any ' Это не нужно обнулять
Dim ipBuffer As WString*256 = Any ' Это не нужно обнулять
Dim ipBufferLength As Integer = 100

Do Until pPtr = 0
   i += 1
   Print "Номер записи", i
   Print "Флаги", pPtr->ai_flags
   Print "Семейство протоколов";
   Select Case pPtr->ai_family
      Case AF_UNSPEC
         Print " неопределено"
      Case AF_INET
         Print " AF_INET (IPv4)"
         ' Адрес
         sockaddr_ip = Cast(LPSOCKADDR, pPtr->ai_addr)
         ' Печатаем адрес
         If WSAAddressToString(sockaddr_ip, Cast(DWORD, pPtr->ai_addrlen), 0, ipBuffer, @ipBufferLength) = 0 Then
            Print "Человеческий адрес", ipBuffer
         Else
            Print "Ошибка", WSAGetLastError()
         End If
      Case AF_INET6
         Print " AF_INET6 (IPv6)"
         ' Адрес
         sockaddr_ip = Cast(LPSOCKADDR, pPtr->ai_addr)
         ' Печатаем адрес
         If WSAAddressToString(sockaddr_ip, Cast(DWORD, pPtr->ai_addrlen), 0, ipBuffer, @ipBufferLength) = 0 Then
            Print "Человеческий адрес", ipBuffer
         Else
            Print "Ошибка", WSAGetLastError()
         End If
      Case Else
         Print " другое", pPtr->ai_family
   End Select
   
   Print "Тип сокета";
   Select Case pPtr->ai_socktype
      Case 0
         Print " неопределено"
      Case SOCK_STREAM
         Print " SOCK_STREAM (stream)"
      Case SOCK_DGRAM
         Print " SOCK_DGRAM (datagram)"
      Case SOCK_RAW
         Print " SOCK_RAW (raw)"
      Case SOCK_RDM
         Print " SOCK_RDM (reliable message datagram)"
      Case SOCK_SEQPACKET
         Print " SOCK_SEQPACKET (pseudo-stream packet)"
      Case Else
         Print " другой", pPtr->ai_socktype
   End Select
   
   Print "Протокол";
   Select Case pPtr->ai_protocol
      Case 0
         Print " не определён"
      Case IPPROTO_TCP
         Print " IPPROTO_TCP"
      Case IPPROTO_UDP
         Print " IPPROTO_UDP"
      Case Else
         Print " другой", pPtr->ai_protocol
   End Select
   Print "Длина адреса:", pPtr->ai_addrlen
   Print "Каноническое имя:", pPtr->ai_canonname
   
   Print ""
Loop

Теперь, когда есть вся информация, можно использовать полученные от GetAddrInfoW() результаты в других функциях, чтобы установить сетевое соединение.

Код:


' Открыть сокет
' Так как указана константа AF_INET, то будем соединяться только с IPv4 адресами
' но можно указать AF_INET6, если нужны IPv6
Dim iSocket As SOCKET = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED)
If iSocket <> INVALID_SOCKET Then
   ' Обойти список адресов и соединиться с сервером
   Dim pPtr As AddrInfoW Ptr = pResult
   Do Until pPtr = 0
      If pPtr->ai_family = AF_INET Then
         If WSAConnect(iSocket, Cast(LPSOCKADDR, pPtr->ai_addr), pPtr->ai_addrlen, 0, 0, 0, 0) <> SOCKET_ERROR Then
            ' Соединение было успешным
            Exit Do
         End If
      End If
      pPtr = pPtr->ai_next
   Loop
End If

После всех манипуляций необходима очистка.

Код:


' Очистка памяти от связанного списка
FreeAddrInfoW(pResult)
' Завершение работы с сокетами
WSACleanup()


Последний раз редактировалось: Замабувараев (Вс Июн 21, 2015 2:19 pm), всего редактировалось 1 раз(а) (Обоснование : Опечатки)
avatar
Замабувараев

Сообщения : 99
Дата регистрации : 2008-08-20
Возраст : 33
Откуда : Красноярск

Посмотреть профиль http://www.freebasic.su

Вернуться к началу Перейти вниз

Re: Разрешение доменных имён (Windows)

Сообщение  trew в Вс Июн 21, 2015 4:04 pm

Здорово, теперь есть уже несколько статей про сеть. Спасибо!

trew

Сообщения : 331
Дата регистрации : 2010-10-14

Посмотреть профиль

Вернуться к началу Перейти вниз

Предыдущая тема Следующая тема Вернуться к началу


 
Права доступа к этому форуму:
Вы не можете отвечать на сообщения