Програмістські нотатки на полях Indy v

Нотатки програміста для самого себе та інших

Indy v.9, SMTP та SSL

Виробнича необхідність, хто ж, як вона, родима, поставила завдання: треба зробити те щоб з'єднання з SMTP сервером здійснювалося з допомогою захищеного SSL каналу.

Є написаний досить давно поштовий клієнт. Клієнт написано на Delphi 6 з використанням набору компонентів Indy версії 9.0.18. Такими є початкові умови. Необхідно, не переписуючи кардинально програму, розширити її функціонал необхідним чином, тобто, навчити клієнта спілкуватися з SMTP серверами за допомогою захищеного SSL каналу.

Для початку, створюємо в Delphi тестовий проект, не курити ж софтину, що відразу працює багато років. Тим більше, що очікуються певні труднощі з реалізацією, так би мовити, підказує чуття. Накидаємо на форму всілякі контроли, які дозволять вказати нам такі параметри:

Отже, продовжуємо накидати компоненти на форму тестової програми. На цей раз це будуть:

Запускаємо програму, вбиваємо значення для тестування (не забуваємо, що використовуємо SMTP сервер GMail) та дивимося, що вийшло. Нічого доброго.

Як то кажуть, сам дурень. Indy працює з SSL через динамічні бібліотеки OpenSSL, і ці бібліотеки треба Indy надати. Інакше маємо те, що маємо.

Виправляюсь, завантажую останню версію OpenSSL бібліотек та підкладаю libeay32.dll та ssleay32.dll у каталог із програмою. Підсумок - таке саме повідомлення про помилку. Десь глибоко в пам'яті починають спливати невиразні спогади, але рефлекси швидше: налагодження показує, що не всі функції, які намагається отримати з цих бібліотек Indy, є в останніх, щойно завантажених версіях DLL. Ну так звичайно,пам'ять не дарма видавала невиразні образи: старі версії Indy вимагають для своєї роботи спеціально скомпільовані для Indy версії OpenSSL. Добре, що можна завантажити старі версії також. В результаті методу спроб і помилок з'ясовую, що з моєю встановленою версією Indy без додаткових патчів працює OpenSSL версії 0.9.6m.

Добре, поїхали далі. програма запущена, дані введені, маємо:

  • якщо не використовуємо захищене з'єднання, то отримуємо помилку, що у випадку з сервером SMTP очікувано
  • якщо використовуємо версію SSL 2, то отримуємо помилку - звичайно, SMTP сервер GMail вимагає SSL 3
  • якщо використовуємо SSL 3, диво! Працює!
Щось якось все відносно гладко. Ну і добре. Ще один експеримент і баеньки: виставляємо використання TLS 1, міняємо порт, запускаємо. не працює.

А щастя було поряд. Але не біда. Після деякого часу, проведеного за читанням різних джерел, з'ясовується, що Indy 9 не підтримує так званого explicit TLS, тобто очевидно вказати, що хочу використовувати TLS, після чого просто використовувати його, не вийде. Однак джерела стверджують, що працювати з TLS все-таки можна, треба лише вручну використовувати команду STARTTLS. Процес має виглядати так: намагаєшся з'єднатися з сервером, він тобі у відповідь надсилає вказівку, що можеш спробувати використовувати STARTTLS. Після цього посилаєш серверу цю команду і все має налагодитись. Що ж, спробуємо. Трохи змінюємо код:

Запускаємо прогу. Не працює. Дебагуємо та розуміємо, що до аналізу повернення від сервера справа просто не доходить. Після виклику методу Connect об'єктаTidSMTPотримуємо Exception. Куримо документацію та різноманітні форуми в інтернеті. Зрештою, на одному із форумівзнаходжу згадку про те, що після посилки команди STARTTLS властивість PassThrough у об'єктаTIdSSLIOHandlerSocketтреба встановити False. У перекладі з езопової мови це означає, що перед посилкою цієї команди ця властивість має бути встановлена ​​в True. Подальші дослідження прояснили суть якості PassThrough: встановлення цієї властивості дозволяє ігнорувати використання SSL. Тобто, встановивши цю властивість у True, ми намагаємося з'єднатися з сервером по відкритому каналу, і у відповідь на це сервер нам і має повернути пропозицію спробувати використати команду STARTTLS. Виправляємо код.

Запускаємо. Знову помилка, але цього разу проблема з автентифікацією. Виявилося, що після встановлення TLS з'єднання (проходження процедури TLS handshake) компонентTidSMPTповинен дізнатися, що змінилося на стороні сервера, наприклад, у реалізації AUTH. Для цього необхідно використовувати команду EHLO і після її успішного завершення оновити властивість AuthSchemesSupported об'єктаTIdSMTP. Для оновлення цієї властивості уTidSMTPє метод GetAuthTypes, але він, за законом бутерброду, оголошений у секції protected. Тому для доступу до нього доведеться застосувати давно відомий метод доступу до protected елементів класів, а саме, оголосити прямого спадкоємця від класу, що цікавить, і використовувати перетворення типів: перетворитиTidSMTPдо його спадкоємця для доступу до protected методу (відомо, що Delphi дозволяє доступ до protected елементів класу з інших класів, описаних у тому самому вихідному файлі). Після такої зміни коду все працює.

І ще. завантажити вихідні файли тестового проекту можна за наступним посиланням: Вихідні тексти а файл, що виконується разом з бібліотеками OpenSSL за посиланням: Виконані файли та бібліотеки