Вот делаю программку, но она мне уже скоро ночами будет снится, поэтому прошу взглянуть свежим взглядом на логику и указать на возможные баги/ошибки.
Система клиент/сервер. Клиенты бывают разных типов, т.е. есть подсистема "Менеджер", "Аудитор" и т.д. Сервер же один. Общение происходит по TCP/IP (компонент, а не API).
Сервер работает с базой данных, в БД есть следующие таблицы (упрощенная схема):
- Код: Выделить всё
Приложения
APPLICATION
-----------
APPL_KEY - Long
CODE - Text
NAME - Text
PASSWORD - Text
- Код: Выделить всё
Пользователи
USERS
-----
USER_KEY - Long
LOGIN - Text
PASSWORD - Text
NAME - Text
На сервере имеется слушающий сокет и массив динамично добавляемых клиентских. Наряду с массивом сокетов имеется массив пользовательского типа, каждый элемент которого сопоставлен с сокетом, в пользовательском типе хранится вспомогательная информация.
- Код: Выделить всё
Private Enum ClientConnectStates
ccsNotConnect = 0
ccsConnecting = 10
ccsConnecting_WaitAppID = 11
ccsConnecting_WrongAppID = 12
ccsConnecting_SendPassword = 13
ccsConnecting_ReceivePassword = 14
ccsConnecting_WrongPassword = 15
ccsConnecting_Complete = 19
ccsAuthorizing = 20
ccsAuthorizing_WaitUser = 21
ccsAuthorizing_WrongUser = 22
ccsAuthorizing_WrongAccess = 23
ccsAuthorizing_Complete = 29
ccsConnect = 30
ccsDisconnecting = 90
End Enum
Private Type WinSockInfo
ConnectState As ClientConnectStates
Client As ClientTypeEnum
User As String
APPL_KEY As Long
USER_KEY As Long
Data As String
End Type
Private ws(1 To MaxConnections) As WinSockInfo
Массив клиентских сокетов начинается с нуля, но нулевой элемент используется для специальных целей (когда лимит подключений исчерпан, сервер подключает клиента на нулевой сокет, отсылает ему сообщение "лимит подключений исчерпан" и отключает).
Когда клиент подключается к серверу, сервер выделяет ему новый сокет и производит подключение. При этом статус соответствующего сокета отмечается, как ccsConnecting_WaitAppID.
Клиент должен прислать на сервер строку, идентифицирующую тип клиента (например "MANAGER").
Сервер ищет эту запись в БД, если найти не удается, то на клиента отсылается сообщение об ошибке ("приложение не зарегистрировано в БД") и сокет отключается.
Если запись найдена, то генерируется строка случайных символов и отсылается на сервер, статус переводится в ccsConnecting_ReceivePassword. Одновременно получанная случайная строка шифруется паролем приложения (который берется с БД), с нее берется хэш и результат запоминается в Data.
На клиенте получанная строка также шифруется паролем (который прошит в клиентской части), с нее берется хэш и отправляется обратно на сервер.
Сервер сравнивает полученную строку с вычисленной заранее, если они совпадают, то приложение авторизуется, статус переводится в ccsAuthorizing_WaitUser, при этом все пакеты данных, которыми обмениваются клиент и сервер, шифруются той самой случайной фразой, которая использовалась для авторизации клиента; шифрование выполняется и на сервере и на клиенте. Кроме того, пакет данных имеет следующий вид <хэш><код>, здесь <код> - зашифрованные данные, а <хэш> - хэш на <код>.
Дальше идет авторизация пользователя. Клиентская подсистема запрашивает у пользователя логин и пароль. Полученные данные упаковываются в строку вида <AUTH: ><логин><@><хэш пароля>.
Сервер ищет пользователя в БД, если находит, то сравниваются хэши паролей, того, что в БД, и того, который присылает клиентская подсистема. Если все верно, то сравнивается уровень доступа пользователя и если все в порядке, то теперь уже авторизуется и пользователь и статус переводится в состояние ccsConnect.
После этого уже система клиент-сервер работает в своем дежурном режиме.
Я вот смотрю и мест, где логика хромает, не вижу, но я уже столько на этот алгоритм смотрю, что не увижу даже явный баг.