БАЗА ДАННЫХ ОБЪЕКТОВ
6.1.
6.2.
Файл-сервер 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 |
int GetBinderyAccessLevel(BYTE *SecurityAccessLevel, long *ObjectID);
Первый параметр этой функции указывает на слово, в которое будет записан уровень доступа, второй - на двойное слово, в которое будет записан идентификатор пользователя.
Вместо функции GetBinderyAccessLevel() можно использовать функцию E3h прерывания INT21h:
На входе: | 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 |
Буфер запроса:
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 |
Буфер запроса:
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
Приведем текст программы 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; } }
Программа BSCAN
Приведем исходный текст программы 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); } }
Просмотр базы объектов
В этом разделе мы рассмотрим задачу сканирования базы данных 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 прерывания INT21h:
На входе: | 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 |
Приведем некоторые имена полей, определенных в 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 |