Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Программирование в сетях Windows

.pdf
Скачиваний:
538
Добавлен:
11.03.2015
Размер:
3.02 Mб
Скачать

298

ЧАСТЬ II Интерфейс прикладного программирования Winsock

 

Листинг 10-1. {продолжение)

,,v,

II

 

«"»

II Задаем IPX-адрес нашей службы

 

//

 

 

memset(sa_ipx.sa_netnum, 0, sizeof(sa_ipx.sa_netnum)); memset(sa_ipx.sa_nodenum, 0, sizeof(sa_ipx.sa_nodenum)); sa_ipx.sa_family = AF_IPX;

sa_ipx.sa_socket = 0;

socks[1] = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);

ret = bind(socks[1], (SOCKADDR *)&sa_ipx, sizeof(sa_ipx));

 

cb = sizeof(IPX_ADDRESS_DATA);

 

memset (&ipx_data, 0, cb);

 

ipx_data.adapternum = 0;

 

ret = getsockopt(socks[1], NSPROTO_IPX, IPX_ADDRESS,

 

(char *)&ipx_data, &cb);

 

cb = sizeof(SOCKADDR_IPX);

 

getsockname(socks[1], (SOCKADDR *)sa_ipx, &cb);

 

memcpy(sa_ipx.sa_netnum, ipx_data.netnum, sizeof(sa_ipx.sa_netnum));

er

memcpy(sa_ipx.sa_nodenum, ipx_data.nodenum, sizeof(sa_ipx.sa_nodenum));

*?

lpCSAddr[1].iSocketType = SOCK_DGRAM;

 

lpCSAddr[1].iProtocol = NSPROTO_IPX;

 

lpCSAddr[1].LocalAddr.lpSockaddr = (struct sockaddr *)&sa_ipx;

Ff

lpCSAddr[1].LocalAddr.iSockaddrLength = sizeof(sa_ipx);

eP

lpCSAddr[1].RemoteAddr.lpSockaddr = (struct sockaddr •)&sa_ipx;

*

lpCSAddr[1].RemoteAddr.iSockaddrLength = sizeof(sa_ipx);

ч

qs.dwNumberOfCsAddrs = 2 ;

 

ret = WSASetService(&qs, RNRSERVICE.REGISTER, OL);

' n> *"'

В листинге 10-1 показано, как настроить экземпляр службы, чтобы ее клиент мог найти адреса, необходимые для взаимодействия с ней. Прежде всего, следует инициализировать структуру WSAQUERYSET. Кроме того, необходимо задать имя экземпляра службы, назовем его Widget Server. Другой важный шаг — использовать тот же GUID, который применялся для регистрации нашего класса службы. Здесь мы работаем с классом Widget Service Class (определение дано в предыдущем разделе), GUID которого — SVCID_NETWARE(200). Следующий этап — задать интересующие нас пространства имен. Наша служба выполняется по протоколам IPX и UDP, и поэтому мы указываем NS_ALL. Поскольку мы задаем уже существующее пространство имен, присвоим параметру ipNSProviderld значение NULL.

Затем следует настроить структуры SOCKADDR в массиве CSADDRJNFO, которые функция WSASetService передает в качестве поля ipcsaBuffer структу-

Г Л А ВА 10 Регистрация и разрешение имен

299

ры WSAQUERYSET. Как видите, перед настройкой структуры SOCKADDR мы действительно создаем сокеты и связываем их с локальным адресом. Это обусловлено тем, что нам необходимо узнать точный локальный адрес, к которому будут подключаться клиенты. Например, мы связываем создаваемый для сервера UDP-сокет с INADDR^ANY, таким образом, получить реальный IP-адрес без вызова функции getsockname невозможно. На основе полученной от функции getsockname информации можно создать структуру SOCKADDRIN. В структуре CSADDR_INFO задаем тип и протокол сокета. Два других поля содержат локальный (с которым должен быть связан сервер) и удаленный адрес (который клиент будет использовать для подключения к службе).

Следующий шаг — настроить службу, выполняющуюся по протоколу IPX. Из материалов главы б вы знаете, что серверы должны быть связаны с номером внутренней сети, для этого номер сети и узла должны быть нулевыми. Опять же, таким образом вы не сможете получить необходимый клиентам адрес. Чтобы получить его, вызовите параметр сокета IPX_ADDRESS. Заполняя структуру CSADDRJNFO для протокола IPX, укажите SOCKJDGRAM и NSPROTOJPX в качестве типа сокета и протокола соответственно.

Завершающий этап — присвоить полю dwNumberOfCsAddrs структуры WSAQUERYSET значение 2, поскольку для установления соединения клиенты могут использовать два адреса — UDP и IPX. Затем вызовите функцию WSASetService, передав ей структуру WSAQUERYSET, флаг RNRSERVICE_REGISTER и

не передавая управляющих флагов. Управляющий флаг SERVICE_MULTIPLE не указывается, чтобы при удалении сведений о службе удалялась информация обо всех ее экземплярах (IPX- и UDP-адреса).

В приведенном примере не учитывается один случай: компьютеры с несколькими сетевыми адаптерами. Если вы создадите сервер на основе протокола UDP, привязывающийся KINADDRANYHZL компьютерах с несколькими сетевыми адаптерами, клиент сможет подключаться к этому серверу, используя любой из доступных интерфейсов. В протоколе IP функции getsockname недостаточно: вам потребуется получить все локальные IP-адреса. Это можно осуществить несколькими способами, в зависимости от платформы, на которой вы работаете. Один из распространенных методов, применимый на всех платформах — вызвать функцию gethostbyname, которая вернет список IP-адресов для имени. В Winsock можно также вызвать ioctl-команду SIOJGETJNTERFACEJJST. Для Windows 2000 доступна ioctl-команда SIO_ADDRESSJJST_QUERY.

Кроме того, можно воспользоваться функциями IP helper (приложение В). Простое разрешение имен TCP/IP и функция обсуждаются в главе 6, а команды ioctl — в главе 9- На прилагаемом компакт-диске содержится пример (файл Rnrcs.c), в котором реализована работа с компьютерами с несколькими сетевыми адаптерами.

Запрос к службе

Теперь рассмотрим, как клиент может запросить пространство имен для данной службы и получить информацию, необходимую для установления связи. Разрешение имен несколько проще, чем регистрация служб. Для выпол-

300

ЧАСТЬ II Интерфейс прикладного программирования Winsock

нения запросов используются три функции: WSALookupServiceBegin, WSALookupServiceNext и WSALookupServiceEnd.

Первый этап — вызвать функцию WSALookupServiceBegin, которая инициирует запрос, задавая ограничения для его выполнения:

INT WSALookupServiceBegin ( LPWSAQUERYSET lpqsRestrictions, DWORDdwControlFlags,

LPHANDLE lphLookup

);

Первый параметр — структура WSAQUERYSET, ограничивающая запрос, например в части количества опрашиваемых пространств имен. Второй параметр — dwControlFlags, определяет глубину поиска. Модель поведения запроса и то, какие данные он вернет, определяют следующие флаги.

II LUPDEEP — в иерархичных пространствах имен задает глубину запроса по отношению к первому уровню.

ШLUPCONTAINERS вернуть только объекты-контейнеры. Этот флаг действителен лишь в иерархичных пространствах имен.

ШLUPNOCONTAINERS не возвращать какие-либо контейнеры. Флаг также действителен лишь в иерархичных пространствах имен.

LUPFLUSHCACHE игнорировать кэш и опрашивать непосредственно пространство имен. Заметьте: не все поставщики пространств имен кэшируют запросы.

ШLUPFLUSHPREVIOUS — указать поставщику пространства имен отбросить ранее возвращенный набор сведений. Обычно используется после того, как WSALookupServiceNext вернет WSA_NOT_ENOUGH_MEMORY. Набор, не помещающийся в предоставленный буфер, отбрасывается, после чего извлекается следующий.

LUPNEAREST вернуть результаты, упорядочив их по расстоянию. Мера расстояния определяется поставщиком имен, поскольку при регистрации службы соответствующие сведения не указываются. Поставщикам имен не требуется поддерживать данную концепцию.

LUPRESSERVICE указывает, что локальные адреса должны быть возвращены в структуре CSADDRJNFO.

U LUP_RETURN_ADDR вернуть адреса как ipcsaBuffer.

LUP_RETURN_ALIASES — получить только сведения о псевдонимах. Каждый псевдоним будет возвращаться при успешных вызовах функции WSALookupServiceNext и для него будет задан флаг RESULT JS_AUAS.

ШLUP_RETURN_ALL — вернуть все доступные сведения.

ШLUPRETURNBLOB вернуть все частные данные как ipBlob.

ШLUPRETURNCOMMENT вернуть комментарий как ipszComment.

ЖLUPRETURNNAME — вернуть имя как ipszServicelnstanceName.

ШLUP_RETURN_TYPE вернуть тип как ipServiceClassId.

ЖLUP_RETURN_VERSION вернуть версию как ipVersion,

Г Л А ВА 10 Регистрация и разрешение имен

301

Последний параметр имеет тип HANDLE и инициализируется при возвращении функции. В случае успеха возвращенное значение равно 0, иначе — SOCKETJERROR. Если один или несколько параметров не действительны, функция WSAGetLastError возвращает WSAEINVAL. Если имя найдено в пространстве имен, но заданным ограничениям не соответствуют какие-либо данные, будет возвращен код ошибки WSANOJDATA. Если службы не существует,функцияWSAGetLastErrorвозвращаетWSAEINVAL.

После вызова функция возвращает дескриптор WSALookupServiceBegin, которыйпередается функции WSALookupServiceNext, возвращающей информацию:

INT WSALookupServiceNext (

HANDLE hLookup,

DWORD dwControlFlags,

LPDWORD lpdwBufferLength,

LPWSAQUERYSET lpqsResults

ФункцияWSALookupServiceBeginвозвращаетдескрипторhLookup.Параметр dwControlFlags имеет в функции WSALookupServiceBegin то же самое значение, ноподдерживаетсялишьLUP_FLUSHPREVIOUS.ПараметрlpdwBufferLength

длина буфера, переданного в качестве lpqsResults. Поскольку структура WSAQUERYSET может содержать данные больших двоичных объектов, часто требуется передать буфер, объем которого превосходит объем структуры. Если размер буфера не достаточен для возвращенных данных, произойдет сбой и функциявернетWSA_NOT_ENOUGH_MEMORY.

ИнициировавзапросспомощьюфункцииWSALookupServiceBegin,вызывайте функцию WSALookupServiceNext до тех пор, пока система не выдаст сообщениеобошибкеWSA_E_NO_MORE(10110).Помните,чтовпредыдущихверсиях Winsock при отсутствии данных возвращалась ошибка WSAENOMORE (10102), поэтому надежное приложение должно проверять оба кода. Получив вседанныеилизавершивопрос,вызовитефункциюWSALookupServiceEnd, передав ей использовавшуюся в запросах переменнуюHANDLE:

INT WSALookupServiceEnd ( HANDLE hLookup );

Создание запроса

Рассмотрим, какопроситьзарегистрированнуювпредыдущемразделеслуж* бу.Преждевсего,необходимонастроитьструктуруWSAQUERYSET,опреде» ляющую запрос:

WSAQUERYSET qs;

 

GUID

fluid

= SVCID_NETWARE(200);

AFPROTOCOLS

afp[2] * {{AF_IPX, NSPROTO.IPX}, {AF.INET, IPPROTOJJDP}};

HANDLE

 

hLookup;

int

ret;

 

memset(&qs, 0, sizeof(qs)); qs.dwSlze = sizeof (WSAQUERYSET);

302

ЧАСТЬ II

Интерфейс прикладного программирования Winsock

 

qs.lpszServicelnstanceName = "Widget Server";

 

qs.lpServiceClassId

= &guid;

%

qs.dwNameSpace = NS_ALL;

/

qs.dwNumberOfProtocols =2;

Л

qs.lpafpProtocols = afp;

 

ret = WSALookupServiceBegin(&qs, LUP_RETURN_ADDR | LUP_RETURN_NAME, &hLookup);

if (ret == SOCKET_ERROR) // Ошибка

Помните, что все операции поиска служб основаны на GUID класса службы, на котором построена искомая служба. Переменной guid присваивается идентификатор класса службы сервера. Сначала вы инициализируете переменную qs со значением 0 и сохраняете в поле dwSize размер структуры. Следующий шаг — указать имя искомой службы. Это может быть как точное имя, так и звездочка (*); в последнем случае функция вернет все службы с данным GUID класса службы. Далее с помощью константы NS_ALL указано, что поиск должен вестись во всех пространствах имен. Последний этап — настройка протоколов, по которым может соединяться клиент, в нашем случае это IPX и UDP/IP. Для этого используется массив из двух структур AFPROTOCOLS.

Теперь можно начать опрос: вызовите функцию WSALookupServiceBegin. Первый параметр — структура WSAQUERYSET, последующие — флаги, определяющие, какие данные будут возвращены при обнаружении искомой службы. Здесь вы указываете, что требуются сведения об адресах и имя службы; для этого создается логическое условие «ИЛИ» из флагов LUP_RETURN_ADDR

и LUP_RETURN_NAME. Флаг LUPJRETURN_NAME необходим, только если в качестве имени службы вы указали звездочку (*), в противном случае имя службы уже известно. Последний параметр — переменная HANDLE, идентифицирующая данный конкретный запрос. Она инициализируется при успешном возвращении данных.

После успешного открытия запроса вызывайте функцию WSALookupServiceNext, пока не будет возвращен код WSA_E_NO_MORE. При каждом успешном вызове функции возвращаются сведения о службе, соответствующие заданным критериям:

char

buff[sizeof(WSAQUERYSET) + 2000];

DWORD

dwLength, dwErr;

WSAQUERYSET

*pqs = NULL;

SOCKADDR

*addr;

int

I;

pqs = (WSAQUERYSET *)buff;

dwLength = sizeof(WSAQUERYSET) + 2000; while (1)

{

ret = WSALookupServiceNext(hLookup, 0, idwLength, pqs); if (ret == SOCKET_ERR0R)

J

Г Л А ВА 10

Регистрация и разрешение имен

303

if ((dwErr = WSAGetLastErrorO) == WSAEFAULT)

,чЭ

,

printf("Buffer too small; required size is:

Xd\n", dwLength);jf1|

break;

 

 

 

}

 

 

 

else if ((dwErr == WSA_E_NO_MORE) || (dwErr = WSAENOMORE))

'*

break;

 

 

"

else

 

 

 

{

 

 

«a

printf("Failed with error: Xd\n", dwErr);

 

 

break;

 

 

 

}

 

 

.[1

}

 

 

 

for (i = 0; i < pqs->dwNumberOfCsAddrs;

i++)

 

 

{

 

 

 

addr = (SOCKADDR *)pqs->lpcsaBuffer[i].RemoteAddr.lpSockaddr;

 

if (addr->sa_family == AF_INET)

 

 

 

{

 

 

"

SOCKADDR_IN *ipaddr = (SOCKADDR_IN *)addr;

 

 

printf("IP address:port = Xs:Xd\n", inet_ntoa(addr->sin_addr),

f

addr->sin_port);

 

 

 

}

 

 

,A

else if (addr->sa_family == AF_IPX)

{

SOCKAODR.IPX «ipxaddr = (SOCKADDR.IPX «)addr; printf("X02XX02XX02XX02X.X02XX02XX02XX02XX02XX02X:X04X", v f

(unsigned char)ipxaddr->sa_netnum[O],

J

i;t')OM

,Г^Ш£

(unsigned char)ipxaddr->sa_netnum[1],

j й ЭООПбв

(unsigned char)ipxaddr->sa_netnum[2],

 

(unsigned char)ipxaddr->sa_netnum[3],

 

(unsigned char)ipxaddr->sa_nodenum[0],

 

(unsigned char)ipxaddr->sa_nodenum[1],

 

(unsigned char)ipxaddr->sa_nodenum[2],

 

(unsigned char)ipxaddr->sa_nodenum[3],

 

(unsigned char)ipxaddr->sa_nodenum[4],

 

(unsigned char)ipxaddr->sa_nodenum[5],

 

ntohs(ipxaddr->sa_socket));

 

 

M

WSALookupServiceEnd(hLookup);

Этот код вполне понятен, хотя и упрощен. При вызове функции WSALookupServiceNext требуются только действительный дескриптор запроса, непосредственно возвращаемый буфер и его длина. Указывать какие-либо управляющие флаги не нужно, поскольку единственный допустимый флаг Данной функции — LUPJFLUSHPREVIOUS. Если размер переданного буфера не достаточен, и данный флаг задан, результаты вызова функции отбрасываются. Тем не менее, в примере флаг LUP_FLUSHPREVIOUS не используется, и поэтому при недостаточном размере буфера генерируется ошибка WSAEFAULT.

I

304

ЧАСТЬ II Интерфейс прикладного программирования Winsock

В этом случае параметру ipdwBufferLength присваивается значение, соответствующее требуемому размеру буфера В примере используется буфер с фиксированным размером, равным размеру структуры WSAQUERYSETплюс 2000 байт Поскольку вам необходимы только имена и адреса службы, такого размера должно хватить Конечно, серьезные приложения должны уметь обрабатыватьошибкуWSAEFAULT

После успешного вызова функции WSALookupServiceNext в буфер WSAQUERYSET помещается структура, содержащая результаты В запросе требовалось получить имена и адреса, и поэтому наиболее интересные для нас поля структуры WSAQUERYSET — это ipszServicelnstanceName и ipcsaBuffer Поле IpszServicelnstanceName содержит имя службы, а поле IpcsaBuffer —

представляет массив структур CSADDRJNFO с ее адресами Параметр dwNumberOfCsAddrs указывает, сколько адресов возвращено В коде примера мы просто выводим все адреса Убедитесь, что будут выводиться только IP- и IPXадреса, поскольку это единственные семейства адресов, указанные при открытии запроса

Если в запросе в качестве имени службы указана звездочка (*), при каждом вызове функции будет возвращен конкретный экземпляр этой службы, выполняющийся на одном из компьютеров сети (конечно, при условии, что несколько экземпляров действительно зарегистрированы и выполняются) После того, как функция WSA_E_NO_MORE вернет все экземпляры службы, будет сгенерирована ошибка и цикл прекратится Последнее, что необходимо сделать — вызвать функцию WSALookupServiceEnd, передав ей дескриптор запроса При этом будут освобождены все выделенные запросу ресурсы.

Запрос к DNS

Как уже упоминалось, пространство имен DNS статично, то есть не позволяет динамически зарегистрировать собственную службу Тем не менее, для запросов к DNS можно воспользоваться функциями разрешения имен Winsock Выполнить DNS-запрос сложнее, чем обычный запрос о наличии зарегистрированной службы, поскольку поставщик пространства имен DNS возвращает информацию в форме BLOB Почему' В главе 6, где обсуждалась функция gethostbyname, мы говорили, что при поиске по имени возвращается структура HOSTENT, содержащая не только IP-адреса, но и их псевдонимы Зачастую эта информация не умещается в поля структуры WSAQUERYSET. Формат BLOB-данных плохо документирован, поэтому для прямых запросов к DNS приходится изобретать обходные пути Прежде всего рассмотрим, как открыть запрос В файле Dnsqueryc на прилагаемом компакт-дис- ке содержится полный код для прямого запроса к DNS, а здесь мы рассмот-

рим его поэтапно Следующий код инициализирует DNS-запрос

WSAQUERYSET

qs;

AFPROTOCOLS

afp [2] = {{AF_INET, IPPROTOJJDP},{AF_INET, IPPROTO_TCP}}\

GUID

hostnameguid = SVCID_INET_HOSTADDRBYNAME,

DWORD

dwLength = sizeof(WSAQUERYSET) + sizeof(HOSTENT) + 2048;

HANDLE

hQuery;

ГЛАВА 10 Регистрация и разрешение имен

305

qs = (WSAQUERYSET *)buff; memset(&qs, 0, sizeof(qs));

qs dwSize = sizeof(WSAQUERYSET); qs.lpszServicelnstanceName = argv[1]; qs.lpServiceClassId = ihostnameguid; qs.dwNameSpace = NS_DNS; qs.dwNumberOfProtocols = 2; qs.lpafProtocols = afp;

ret = WSALookupServiceBegin(&qs, LUP_RETURN_NAME | LUP_RETURN_BLOB, ihQuery);

if (ret == SOCKET.ERROR) II Ошибка

Настройка запроса осуществляется почти так же, как и в предыдущем примере Наиболее значительное отличие — используется предопределенный GUID SVCIDJNETJiOSTADDRBYNAME, он идентифицирует запросы имен компьютеров Параметр ipszServicelnstanceName — имя компьютера, которое требуется разрешить Поскольку имена разрешаются с использованием DNS,

вкачестве параметра dwNameSpace следует передать лишь NSJDNS Наконец,

впараметре ipaJProtocols передается массив из двух структур AFPROTOCOLS, которые определяют используемые запросом протоколы TCP/IP и UDP/IP.

После создания запроса вызовите функцию WSALookupServiceNext, чтобы

вернуть данные

-j

char

buff[sizeof(WSAQUERYSET) + sizeof(HOSTENT) + 2048)];

 

DWORD

dwLength = sizeof(WSAQUERYSET) + sizeof(HOSTENT) + 2048;

 

WSAQUERYSET *pqs;

 

HOSTENT

*hostent;

 

pqs = (WSAQUERYSET »)buff; pqs->dwSize = sizeof(WSAQUERYSET);

ret = WSALookupServiceNext(hQuery, 0, idwLength, pqs); if (ret == SOCKET_ERROR)

// Ошибка WSALookupServiceEnd(hQuery);

hostent = pqs->lpBlob->pBlobData;

Поскольку поставщик пространства имен DNS возвращает сведения о компьютере в форме BLOB, необходимо выделить буфер достаточного размера Именно поэтому используется буфер, равный по объему сумме «структура WSAQUERYSET + структура HOSTENT + дополнительные 2048 байт» Если и такого размера окажется не достаточно, при вызове функции произойдет сбой и будет возвращено значение WSAEFAULT В DNS-запросе все сведения о компьютере возвращаются в структуре HOSTENT, даже если имя компьютера связано с несколькими IP-адресами Таким образом, не требуется многократно вызывать функцию WSALookupServiceNext

306 ЧАСТЬ II Интерфейс прикладного программирования Winsock

Теперь наступает самый сложный этап — расшифровка BLOB, возвращенного запросом Из главы 6 вы знаете, что структура HOSTENT определена так

typedef struct hostent

{

char FAR • h_name,

'

char FAR * FAR * h_aliases,

short

h_addrtype,

l

short

h_length,

 

char FAR * FAR * h_addr_list, } HOSTENT,

Когда структура HOSTENT возвращается в виде BLOB-данных, указатели внутри нее представляют собой смещения по адресам памяти, где хранятся данные Смещение отсчитывается от начала BLOB-данных В связи с этим для доступа к данным требуется зафиксировать указатели и сделать так, чтобы они ссылались на абсолютные адреса памяти На рис 10-1 изображена структура HOSTENT и карта возвращенной памяти

Массивспискаадресов Массив списка псевдонимов

«Ой8

HOSTENT

Рис. 10-1. BLOB-формат структуры HOSTENT

DNS-запрос выполняется по имени компьютера Riven, связанного с одним IP-адресом и не имеющего псевдонимов У каждого поля структуры имеется значение смещения Чтобы поля ссылались на действительное расположение данных, необходимо прибавить значение смещения к адресу в заголовке структуры HOSTENT Подобную операцию следует выполнить для полей hjname, h_ahases и h_addrjist Кроме того, поля h_ahases и h_addrjist

представляют собой массивы указателей Итак, получен верный указатель на массив указателей каждое 32-битное

поле в этом массиве ссылается на область памяти, содержащую указатели В поле h_addr_hst (рис 10-1) начальное смещение составляет 16 байт — это ссылка на байт, следующий за структурой HOSTENT, массив указателей на четырехбайтный IP-адрес Тем не менее, смещение первого указателя в массиве составляет 28 байт Для ссылки на действительное расположение данных прибавьте к адресу структуры HOSTENT 28 байт и вы прлучите ссылку на четырехбайтную область с данными 0x9D36B9BA, представляющими собой IP-адрес 157 54 185 186 Затем можно взять 4 байта после записи со смещением 28 байт, получив в результате 0

Если бы с этим именем компьютера было связано несколько IP адресов, присутствовало бы и другое смещение, и потребовалось бы по аналогии с первым случаем изменить указатель Точно такая же операция позволяет

ГЛАВА 10 Регистрация и разрешение имен

307

исправить указатель и массив указателей, на который он ссылается В данном примере у компьютера нет псевдонимов Первая запись массива 0, и это означает, что в отношении данного поля какие-либо дополнительные действия не нужны Последнее поле — hjiame, исправить которое достаточно просто Следует лишь добавить смещение к адресу структуры HOSTENT, и поле будет указывать на начало оканчивающейся нулем строки

Код, превращающий смещения в реальные адреса, прост, хотя и включает некоторые арифметические действия с указателями Для корректировки поля Ьпате подойдет следующая процедура настройки смещения

hostent->h_name = (PCHAR)((DWORD_PTR)hostent->h_name) + (PCHAR)hostent,

Чтобы изменить массив указателей (например, поля h_ahases и h_addrjtst), необходим более сложный код, который будет просматривать массив и изменять ссылки, пока не достигнет нулевой записи

PCHAR «addr,

!

if (hostent->h_aliases)

;

<

I

addr=hostent->h_aliases=(PCHAR)((DWORD_PTR)hostent»h_aliases+

,

(PCHAR)hostent),

 

while (addr)

 

{

Л

addr = (PCHAR)((DWORD_PTR)addr + (PCHAR Ohostent),

!

addr++,

;

Этот код переходит от одной записи массива к другой и добавляет начальный адрес структуры HOSTENT к заданному смещению, в результате получается новое значение текущей записи Конечно, по достижении нулевой записи просмотр массива прекращается Данную операцию следует выполнить и для поля b_addr_hst После того как смещения будут изменены, со структурой HOSTENT можно работать в обычном порядке

Резюме

Функции регистрации и разрешения имен могут показаться сложными, однако они обеспечивают значительную гибкость при разработке клиент-сер- верных приложений Реальные ограничения на регистрацию имен связаны непосредственно с пространством имен Удивительно, что при всей популярности пакета протоколов TCP/IP доступен единственный метод разрешения имен — DNS, который не обеспечивает требуемой гибкости В доменных пространствах Windows 2000 и Windows NT доступен постоянный, не зависящий от протокола метод разрешения имен, обеспечивающий необходимую гибкость для разработки устойчивых приложений Кроме того, приложениям на основе протокола IPX/SPX доступны другие пространства имен (например, SAP), предоставляющие большинство из возможностей NTDS (за исключением независимости от протокола)