Файл-сервер Novell NetWare содержит базу данных объектов, в которой есть сведения о ресурсах, доступных в сети (файл-серверы, серверы печати и т. п.), о пользователях и группах пользователей и т. д. Эта база данных называется Bindery. Физически она находится в двух скрытых файлах с именами net$bind.sys и net$bval.sys, расположенных в каталоге SYS:SYSTEM.
Каждый объект, хранящийся в базе данных, имеет свое имя, уникальный для данного файл-сервера идентификатор, тип, байт доступа. Кроме того, объект может быть статическим или динамическим.
Например, каждый пользователь, добавленный супервизором сети или администратором, становится объектом, имеющим имя и идентификатор. Тип такого объекта соответствует значению 1, и это статический объект. Статический объект хранится в базе до тех пор, пока он не будет удален явным образом.
Если в сети имеются несколько файл-серверов, то для каждого сервера в базе Bindery имеются объекты, соответствующие всем серверам, активным в сети. Это временные объекты, которые удаляются автоматически, когда соответствующий файл-сервер прекращает свою работу. Вы можете воспользоваться этим обстоятельством для получения списка активных серверов, имеющихся в сети.
Программы пользователя могут не только считывать содержимое базы объектов, но и добавлять свои собственные записи, а также редактировать имеющиеся. Для сокращения объема книги мы рассмотрим только способы извлечения информации из базы Bindery. Полностью работа с базой данных объектов описана в документации, поставляющейся вместе с библиотекой Novell NetWare C Interface.
База данных Bindery хранит объекты, которые имеют такие атрибуты, как идентификатор, имя, тип, флаг (статический или динамический объект), байт доступа.
Идентификатор объекта - число размером 4 байта, которое является уникальным для данного файл-сервера. Никакие два объекта, сведения о которых хранятся в одной базе данных на одном сервере, не могут иметь одинаковые идентификаторы.
Имя объекта представляет собой текстовую строку размером не более 47 байт. В операциях поиска объектов в базе данных по имени в поле имени допускается указывать символы "*" и "?", которые интерпретируются обычным способом, как и в MS-DOS.
Библиотека NetWare C Interface имеет функции, позволяющие просматривать список всех объектов базы данных, искать объекты определенного типа или по имени с использованием шаблона и символов "*" и "?". По идентификатору объекта вы можете легко получить его имя и наоборот, по имени можно узнать идентификатор объекта.
Тип объекта определяет сетевой ресурс и может принимать следующие значения:
Значение |
Описание |
0 |
Неклассифицируемый (неизвестный) объект
|
1 |
Пользователь |
2 |
Группа пользователей |
3 |
Очередь печати |
4 |
Файл-сервер |
5 |
Сервер заданий |
6 |
Шлюз |
7 |
Сервер печати |
8 |
Очередь для архивирования |
9 |
Сервер для архивирования |
A |
Очередь заданий |
B |
Администратор |
24 |
Сервер удаленного моста |
Флаг объекта характеризует время жизни объекта. Если этот флаг равен нулю, объект статический и для его уничтожения необходимо вызывать специальную функцию. Если флаг равен единице, то это динамический объект, который удаляется автоматически, как только исчезает соответствующий сетевой ресурс.
Например, если в сети имеется несколько файл-серверов, в базе каждого сервера имеются объекты, описывающие все активные серверы сети. Как только один из серверов прекращает свою работу, из всех баз данных, расположенных на остальных файл-серверах, удаляются объекты, описывающие завершивший работу файл-сервер.
Байт доступа используется для определения прав, необходимых для поиска, чтения, создания, редактирования или удаления объекта. Две тетрады байта отвечают за доступ на чтение и доступ на запись. Младшие четыре бита байта доступа отвечают за чтение, старшие - за запись.
Для тетрад определены значения от 0 до 4 - уровни доступа. Для того чтобы пользователь или другой объект получили доступ, тетрады его собственного байта доступа должны иметь значения, равные или превышающие значения в байте доступа объекта, к которому запрашивается доступ.
Приведем список возможных значений для уровней доступа:
0 |
Anyone |
Объект не подключен к файл-серверу |
1 |
Logged |
Объект подключен к файл-серверу |
2 |
Object |
Объект подключен к файл-серверу с именем
и паролем |
3 |
Supervisor |
Объект имеет права супервизора |
4 |
NetWare |
Объект имеет права операционной системы
Novell NetWare |
Для определения собственного уровня доступа и идентификатора пользователя программа может воспользоваться функцией GetBinderyAccessLevel():
int GetBinderyAccessLevel(BYTE *SecurityAccessLevel, long *ObjectID);
Первый параметр этой функции указывает на слово, в которое будет записан уровень доступа, второй - на двойное слово, в которое будет записан идентификатор пользователя.
Вместо функции GetBinderyAccessLevel() можно использовать функцию E3h прерывания INT 21h:
На входе: |
AH |
= |
E3h; |
DS:SI |
= |
Адрес буфера запроса; |
|
ES:DI |
= |
Адрес буфера ответа. |
|
На выходе: |
AL |
= |
Код ошибки или 0, если операция
завершилась без ошибок. |
Буфер запроса имеет следующий формат:
struct REQUEST { WORD PacketLength; // размер пакета запроса BYTE Function; // должно быть равно 70 };
Буфер ответа имеет следующий формат:
struct REPLAY { WORD PacketLength; // размер пакета BYTE SecurityAccessLevel; // уровень доступа long ObjectID; // идентификатор объекта };
По идентификатору объекта вы можете получить его имя и тип с помощью функции GetBinderyObjectName():
int GetBinderyObjectName(long ObjectID, char *ObjectName, WORD *ObjectType);
Для объекта, идентификатор которого задан первым параметром, функция возвращает имя объекта и его тип, записывая их в области памяти, указанные при помощи второго и третьего параметров.
Функция возвращает 0 при успешном завершении или код ошибки:
Код ошибки |
Значение |
0x96 |
Мало памяти на сервере |
0xFC |
В базе нет объекта с указанным
идентификатором |
0xFE |
База данных Bindery заблокирована |
0xFF |
Сбой базы данных Bindery |
Вместо функции GetBinderyObjectName() вы также можете использовать функцию E3h прерывания INT 21h. При этом необходимо использовать форматы буфера запроса и буфера ответа, приведенные ниже.
Буфер запроса:
struct REQUEST { WORD PacketLength; // размер пакета запроса BYTE Function; // должно быть равно 54 long ObjectID; // идентификатор объекта };
Буфер ответа:
struct REPLAY { WORD PacketLength; // размер пакета long ObjectID; // идентификатор объекта WORD ObjectType; // тип объекта BYTE ObjectName[48]; // имя объекта };
Для получения идентификатора объекта по его имени и типу вы можете воспользоваться функцией GetBinderyObjectID():
int GetBinderyObjectID(char *ObjectName,WORD ObjectType, long *ObjectID);
Имя и тип объекта задаются первым и вторым параметрами, идентификатор записывается по адресу, заданному третьим параметром.
Функция возвращает 0 при успешном завершении или код ошибки:
Код ошибки |
Значение |
0x96 |
Мало памяти на сервере |
0xEF |
Имя объекта указано неправильно |
0xF0 |
Не допускаются символы шаблона "*",
"?" |
0xFC |
В базе нет объекта с указанным
идентификатором |
0xFE |
База данных Bindery заблокирована |
0xFF |
Сбой базы данных Bindery |
Вместо функции GetBinderyObjectID() можно использовать функцию E3h прерывания INT 21h. Приведем форматы буфера запроса и буфера ответа.
Буфер запроса:
struct REQUEST { WORD PacketLength; // размер пакета запроса BYTE Function; // должно быть равно 53 long ObjectType; // тип объекта BYTE ObjectNameLength; // длина имени объекта BYTE ObjectName[ObjectNameLength]; // имя объекта };
Буфер ответа:
struct REPLAY { WORD PacketLength; // размер пакета long ObjectID; // идентификатор объекта WORD ObjectType; // тип объекта BYTE ObjectName[48]; // имя объекта };
Приведем текст программы BACCESS (листинг 26), которая определяет и выводит на экран имя, идентификатор, тип объекта, а также его уровень доступа. В программе используются описанные выше функции.
// =================================================== // Листинг 26. Программа для просмотра уровня // доступа рабочей станции // Файл baccess\baccess.cpp // // (C) A. Frolov, 1993 // =================================================== #include <stdlib.h> #include <stdio.h> #define WORD unsigned int #define BYTE unsigned char extern "C" int GetNetWareShellVersion(char *,char *, char *); extern "C" int GetBinderyAccessLevel(BYTE *, long *); extern "C" int GetBinderyObjectName(long, char*, WORD*); void main(void) { char MajorVersion=0; char MinorVersion=0; char Revision=0; int ccode; BYTE SecurityAccessLevel; long ObjectID; char ObjectName[48]; WORD ObjectType; printf("\n*BACCESS* (C) Frolov A., 1993\n"); // Проверяем присутствие сетевой оболочки asm push si GetNetWareShellVersion(&MajorVersion, &MinorVersion, &Revision); asm pop si if(MajorVersion == 0) { printf("\nОболочка NetWare не загружена\n"); return; } // Получаем свой идентификатор и уровень доступа GetBinderyAccessLevel(&SecurityAccessLevel, &ObjectID); // По идентификатору определяем свое имя ccode = GetBinderyObjectName(ObjectID, ObjectName, &ObjectType); // Если пользователь подключился к файл-серверу, // выводим его имя, идентификатор и тип if(!ccode) { printf("Пользователь %s, ID = %lX, Type = %d\n", ObjectName, ObjectID, ObjectType); } // Выводим права доступа на чтение printf("Права доступа на чтение:\t"); switch(SecurityAccessLevel & 0x0f) { case 0: printf("Anyone\t(не подключен к файл-серверу)\n"); break; case 1: printf("Logged\t(подключен к файл-серверу)\n"); break; case 2: printf("Object\t(подключен к файл-серверу " "с именем и паролем)\n"); break; case 3: printf("Supervisor\t(права супервизора)\n"); break; case 4: printf("NetWare\t(права Novell NetWare)\n"); break; } // Выводим права доступа на запись printf("Права доступа на запись:\t"); switch((SecurityAccessLevel >> 4) & 0x0f) { case 0: printf("Anyone\t(не подключен к файл-серверу)\n"); break; case 1: printf("Logged\t(подключен к файл-серверу)\n"); break; case 2: printf("Object\t(подключен к файл-серверу " "с именем и паролем)\n"); break; case 3: printf("Supervisor\t(права супервизора)\n"); break; case 4: printf("NetWare\t(права Novell NetWare)\n"); break; } }
В этом разделе мы рассмотрим задачу сканирования базы данных Bindery с целью получения списка имеющихся в ней объектов. Например, вам может потребоваться список активных серверов в сети, список пользователей или список рабочих групп.
Для получения списка объектов, сведения о которых хранятся в базе данных Bindery, предназначена функция ScanBinderyObject():
int ScanBinderyObject(char *SearchObjectName, WORD SearchObjectType, long *ObjectID, char *ObjectName, WORD *ObjectType, char *ObjectHasProperties, char *ObjectFlag, char *ObjectSecurity);
Эта функция должна использоваться в цикле. При первом вызове в переменную, на которую указывает параметр ObjectID, необходимо записать значение -1. В дальнейшем в эту переменную будет записываться идентификатор найденного объекта.
Для поиска следует указать имя объекта (параметр SearchObjectName) и тип объекта (параметр SearchObjectType). В качестве имени объекта можно использовать шаблон с символами "*" и "?". Тип объекта может быть задан конкретно, либо можно указать значение -1. В последнем случае функция будет искать объекты всех типов. Для того чтобы найти все объекты всех типов, в качестве имени надо указать строку "*", в качестве типа задать значение -1.
Для найденных объектов в соответствующие переменные, указанные параметрами функции, будут записаны имя объекта (параметр ObjectName), тип объекта (параметр ObjectType), флаг (параметр ObjectFlag), байт доступа (параметр ObjectSecurity). Кроме того, в переменную, на которую указывает параметр ObjectHasProperties, записывается значение 0xFF, если объект имеет дополнительную связанную с ним информацию (Properties), которую можно извлечь специально предназначенными для этого функциями.
Функция возвращает 0 при успешном завершении или код ошибки:
Код ошибки |
Значение |
0x96 |
Мало памяти на сервере |
0xEF |
Имя объекта указано неправильно |
0xFC |
В базе нет объекта с указанным
идентификатором |
0xFE |
База данных Bindery заблокирована |
0xFF |
Сбой базы данных Bindery |
Ваша программа должна вызывать функцию ScanBinderyObject() в цикле до тех пор, пока она не возвратит код ошибки, отличный от нуля.
Вместо функции ScanBinderyObject() можно использовать функцию E3h прерывания INT 21h:
На входе: |
AH |
= |
E3h; |
DS:SI |
= |
Адрес буфера запроса; |
|
ES:DI |
= |
Адрес буфера ответа. |
|
На выходе: |
AL |
= |
Код ошибки или 0, если операция
завершилась без ошибок. |
Буфер запроса имеет следующий формат:
struct REQUEST { WORD PacketLength; // размер пакета запроса BYTE Function; // должно быть равно 55 BYTE ObjectID; // идентификатор объекта WORD SearchObjectType; // тип объекта BYTE NameLength; // длина имени образца для // поиска объекта BYTE SearchObjectName[NameLength]; // имя образца для // поиска объекта };
Буфер ответа имеет следующий формат:
struct REPLAY { WORD PacketLength; // размер пакета long ObjectID; // идентификатор объекта WORD ObjectType; // тип объекта BYTE ObjectName[48]; // имя объекта BYTE ObjectFlag; // флаг объекта BYTE SecurityAccessLevel; // уровень доступа BYTE ObjectHasProperties; // есть записи };
В базе данных объектов Bindery с каждым объектом может быть связано несколько дополнительных записей, содержащих данные (property). Каждая такая запись имеет свое имя, флаг и байт доступа. Если объект имеет записи, вы можете получить список их имен и других атрибутов при помощи функции ScanProperty():
int ScanProperty(char *ObjectName, WORD ObjectType, char *SearchPropertyName, long *SequenceNumber, char *PropertyName, char *PropertyFlag, char char *PropertySecurity, char *PropertyHasValue, char *MoreProperties);
Функция должна вызываться в цикле. При первом вызове переменная, на которую указывает параметр SequenceNumber, должна содержать значение -1. При последующих вызовах содержимое этой переменной будет изменяться автоматически.
Для считывания полей функции необходимо указать имя сканируемого объекта (параметр ObjectName), тип объекта (параметр ObjectType), а также имя записи или шаблон имени записи (параметр SearchPropertyName). В шаблоне можно использовать символы "*" и "?".
Для найденных записей в соответствующие переменные, указанные параметрами функции, будут записаны имя записи (параметр PropertyName), флаг записи (параметр PropertyFlag), байт доступа (параметр PropertySecurity), признак того, что запись имеет значения (параметр PropertyHasValue), признак того, что в объекте есть еще и другие записи (MoreProperties).
Функция возвращает 0 при успешном завершении или код ошибки:
Код ошибки |
Значение |
0x96 |
Мало памяти на сервере |
0xF1 |
Неправильный код доступа |
0xFB |
Указанная запись не найдена |
0xFC |
В базе нет объекта с указанным
идентификатором |
0xFE |
База данных Bindery заблокирована |
0xFF |
Сбой базы данных Bindery |
В Novell NetWare для каждого типа объекта существует определенный набор записей, которые могут быть связаны с этим объектом. Например, с объектом типа 1 (обычный пользователь) связаны такие записи, как PASSWORD (пароль) и SECURITY_EQUALS (эквивалентность прав доступа). Содержимое записей можно считать при помощи функции ReadPropertyValue(), которая описана в документации по библиотеке NetWare C Interface. Для этого пользователь, запустивший программу, должен обладать достаточным уровнем доступа.
Приведем некоторые имена полей, определенных в NetWare:
Имя записи |
Тип объекта |
Доступ, запись/чтение |
BLOCKS_READ |
Файл-сервер |
3/1 |
BLOCKS_WRITTEN |
Файл-сервер |
3/1 |
CONNECT_TIME |
Файл-сервер |
3/1 |
GROUP_MEMBERS |
Группа пользователей |
3/1 |
GROUPS_I'M_IN |
Пользователь |
3/1 |
IDENTIFICATION |
Пользователь |
3/1 |
NET_ADDRESS |
Файл-сервер |
4/0 |
OLD_PASSWORDS |
Пользователь |
3/3 |
OPERATORS |
Файл-сервер |
3/3 |
PASSWORDS |
Пользователь |
4/4 |
SECURITY_EQUALS |
Пользователь |
3/2 |
Полный список полей и подробное их описание вы найдете в документации по библиотеке NetWare C Interface.
Приведем исходный текст программы BSCAN (листинг 27), которая просматривает базу данных объектов. Для каждого найденного объекта программа выводит имя и расшифрованный тип объекта, флаг и уровень доступа. Если объект имеет дополнительные записи (properties), вызывается функция, которая выводит имена найденных записей.
// =================================================== // Листинг 27. Программа для просмотра содержимого // базы данных объектов // Файл bscan\bscan.cpp // // (C) A. Frolov, 1993 // =================================================== #include <stdlib.h> #include <stdio.h> #include <string.h> #define WORD unsigned int #define BYTE unsigned char extern "C" int GetNetWareShellVersion(char *,char *, char *); extern "C" int ScanBinderyObject(char *, WORD, long *, char *, WORD *, char *, char *, char *); extern "C" int ScanProperty(char *, WORD, char *, long *, char *, char *, char *, char *, char *); void Property(char *ObjectName, WORD ObjectType); void main(void) { char MajorVersion=0; char MinorVersion=0; char Revision=0; int ccode; BYTE ObjectSecurity; long ObjectID; char SearchObjectName[48]; char ObjectName[48]; WORD SearchObjectType; WORD ObjectType; char ObjectHasProperties; char ObjectFlag; printf("\n*BSCAN* (C) Frolov A., 1993\n"); // Проверяем присутствие сетевой оболочки asm push si GetNetWareShellVersion(&MajorVersion, &MinorVersion, &Revision); asm pop si if(MajorVersion == 0) { printf("\nОболочка NetWare не загружена\n"); return; } // Просматриваем в цикле содержимое базы объектов, // ищем объекты всех типов SearchObjectType = -1; // Маска для поиска всех объектов strcpy(SearchObjectName, "*"); for(ObjectID = -1;;) { // Получаем очередной объект ccode = ScanBinderyObject(SearchObjectName, SearchObjectType, &ObjectID, ObjectName, &ObjectType, &ObjectHasProperties, &ObjectFlag, &ObjectSecurity); // Если больше нет объектов или произошла ошибка, завершаем цикл if(ccode) break; // Выводим имя и тип объекта printf("\n%-18s\t", ObjectName); switch(ObjectType) { case 0: printf("??? "); break; case 1: printf("Пользователь "); break; case 2: printf("Группа "); break; case 3: printf("Очередь на печать "); break; case 4: printf("Файл-сервер "); break; case 5: printf("Сервер заданий "); break; case 6: printf("Шлюз "); break; case 7: printf("Сервер печати "); break; case 8: printf("Очередь архивирования "); break; case 9: printf("Сервер для архивирования"); break; case 0xA: printf("Очередь заданий "); break; case 0xb: printf("Администратор "); break; case 0x26: printf("Сервер удаленного моста "); break; default: printf("Объект 0x%04.4X ", ObjectType); break; } // Выводим флаг объекта, который может иметь два значения: // 0 для постоянных объектов и 1 для временных if(ObjectFlag) printf("Временный "); else printf("Постоянный"); // Выводим байт прав, необходимых для получения доступа к объекту printf(" Доступ %02.2X", ObjectSecurity); // Если для объекта имеются дополнительные записи, // выводим их названия if(ObjectHasProperties) Property(ObjectName, ObjectType); } } // ================================================================= // Функция Property выводит названия дополнительных записей объектов // ================================================================= void Property(char *ObjectName, WORD ObjectType) { int ccode; BYTE PropertySecurity; long ObjectID; char SearchPropertyName[16]; char PropertyName[16]; WORD SearchObjectType; char PropertyFlag; long SequenceNumber; char PropertyHasValue; char MoreProperties; // Маска для поиска всех записей strcpy(SearchPropertyName, "*"); for(SequenceNumber=-1;;) { // Получаем запись ccode = ScanProperty(ObjectName, ObjectType, SearchPropertyName, &SequenceNumber, PropertyName, &PropertyFlag, &PropertySecurity, &PropertyHasValue, &MoreProperties); // Если записей больше нет, завершаем цикл if(ccode) break; // Выводим название записи printf("\n\tProperty %s", PropertyName); } }