Ru-Board.club
← Вернуться в раздел «Прикладное программирование»

» Реализация SSH протокола из под Delphi

Автор: Delphi6
Дата сообщения: 24.02.2007 18:37
Доброе время суток,

Я пользуюсь Delphi 6 версией (не спрашивайте почему не перехожу на новую версия). Сейчас есть необходимость подконектится к серверу по протоколу SSH2 и запустить пару команд. Имя сервера, пользователя и пароль соответственно известен (что бы не подумали что надо брут форсер писать ).

Так вот в 6 версии компонентов для работы с этим протоколов нету (возможно я не вижу? ), может кто посоветует сторонний? Желательно что бы можно было достать на халяву (ну или в обменщике http://forum.ru-board.com/topic.cgi?forum=35&topic=30992&start=1020)

Заранее спасибо
Автор: KADABRA
Дата сообщения: 25.02.2007 00:37
Delphi6
SecureBlackbox
Shareware.
В обменнике ссыки проскакивали.
Автор: Delphi6
Дата сообщения: 25.02.2007 08:58
KADABRA
Спасибо за пост и совет. Я его тоже вчера нашел, но не смог 4.4 заинсталить, там патч а на оф. сайте только 5.*. Качал 35 метров Сейчас попробую скачать 3.4, она уже с исходниками, но проблема в том что я видел в топике посты где люди жаловались именно на SSH компонент, который отказывался работать, и просили пересмотреть исходники.

А так я еще вот что нарыл, но исходников нету
1) TElSSHLocalPortForwarding
2) wodssh
3) StreamSec SSH tools 3.0.1.256
Автор: Delphi6
Дата сообщения: 25.02.2007 14:58
Даже 3.4 не установился после использования патча, возможно это из за того что у меня был уставнолена 5.0 версия.

Но больше всего меня удивляет вот этот факт, я не буду говорить что SSH реализован по дефолту например в PHP, но тот факт что есть куча исходников и главное на халяву наводит на мысль что Борланд окончательно обнаглел. Я лично смотрел реализацию SSH1 на РНР в исходнике, и ничего сложного, даже думал самому написать SSH2.

Надеюсь мне не придеться изобретать велосипед заново (если да то я сто пудово выложу где-то исходник, что бы эти буржуйские гады знали как просить 1000 долларов за один простенький компонент!!!)

Если кто может посоветовать с чего начать или где что посмотреть, я буду благодарен
Автор: bbEye
Дата сообщения: 25.02.2007 17:50
Delphi6
Есть такая опенсурсовая либа - cryptlib
http://www.cs.auckland.ac.nz/~pgut001/cryptlib/

Умеет много всего, в т.ч. и SSH. Написана на С, собрана в DLL, имеет API для разных языков. Сам не пробовал, но собираюсь попытаться заюзать оттуда SSL/TLS.

И, что немаловажно, продолжает развиваться...
Автор: Delphi6
Дата сообщения: 25.02.2007 20:15
bbEye
ОГРОМНОЕ спасибо, с меня пиво и копия рабочего компонента. Компонент будет уже завтра, а вот пиво надо забирать на месте Надо компонент в массы пустить, что бы этим буржуям BlackBox-у отплатить за те мучения которые они создают программистам на Delphi
Автор: Delphi6
Дата сообщения: 27.02.2007 11:18
Простенький компонент SSHClient уже написал, для основы хватит любому, но возникли вот такие вопросы.

1) Стоит ли сделать компонент на базе TThread что бы не подвисала программа на критических точках (коннекта, или ожидании ответа)?

2) Еще меня вот что интересует, как протоколы реализуют получение ответа от сервера?

Я пока придумал так, типа я законектился, потом скинул команду. Жду определенное время (таймаут) когда мне придет ответ, если за это время не пришел ответ то останавливаю процесс считывания (здесь наверно надо генерить ошибку?).

Но что делать если я получил не весь пакет а его часть?

Мне приходят в ответ в виде пакета (envelop), возможно он не может быть частично полученным, к этому выводу я пришел логически, если ответ зашифрованный, то значит его смогут расшифровать (функция в dll) когда она считает весь ответ. Как вам мое рассуждение?
Автор: OdesitVadim
Дата сообщения: 27.02.2007 16:49
Delphi6
Я думаю стоит посмотреть, как реализовано IdTelnet с Indy компонентов.
Цитата:
Стоит ли сделать компонент на базе TThread что бы не подвисала программа на критических точках

Любой нормальный программер этот код запхает в поток. Может лучше просто класс? Поклонники TurboDelphi будут только за. (там так просто нельзя компоненты чужие ставить)
По поводу ошибок. Генерите исключительную ситуацию и всё. Кому надо - обработает.
Автор: Delphi6
Дата сообщения: 27.02.2007 17:53
OdesitVadim
Вы правы, буду делать все на уровне класса А там пусть кто хочет очень легко доделает. Вот сейчас ни как не получается в поток засунуть только чтение, запись и коннект

Так еще может вы подскажете что есть аналог application.processmesage. Ну что бы пока я жду завершения потока (таймаут время) не останавливать главный поток? Или лучше все это бросить и реализовать уже в программе где я его буду использовать? (просто хотелось что бы в главное программе все было просто, команду отослал, считал ответ, и без потоков)
Автор: OdesitVadim
Дата сообщения: 27.02.2007 17:58
А вот с длл лучше разобраться. Портировать в делфи,к примеру. Чтобы не надо было тянуть за собой
Может я портирую длл в юнит делфовский и слинкуем докучи? Хотя кода там немеряно. Даже не знаю Надо дома покрутить. Может это просто страшно выгдядит
Автор: bbEye
Дата сообщения: 27.02.2007 17:58
Delphi6
Да, лучше просто класс, чем компонент (имхо).


Цитата:
Но что делать если я получил не весь пакет а его часть?

При работе с сокетами это нормально, когда приезжает не весь пакет. Нужно ждать следующей порции, и так до тех пор, пока не придет полностью.

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

OdesitVadim

Цитата:
Любой нормальный программер этот код запхает в поток

Есть еще нормальные программеры, которые будут юзать асинхронные сокеты или I/O Completion Ports

Но, я согласен с тем, что поток пусть организует тот, кто будет использовать этот класс. Саму реализацию класса хотелось бы видеть синхронной.
Автор: OdesitVadim
Дата сообщения: 27.02.2007 18:08
Delphi6

Цитата:
Ну что бы пока я жду завершения потока (таймаут время) не останавливать главный поток?

Пишите прямо. А вот кто будет вызывать ваш код, должен будет завернуть в поток. Не безпокойтесь об этом. При правильной реализации всё должно выглядеть как открытие/чтение/запись файла
bbEye

Цитата:
Есть еще нормальные программеры, которые будут юзать асинхронные сокеты или I/O Completion Ports

Тонкий намёк на Сишников старой закалки? Правда потом разбираться во всех хитросплетениях вызовов - ужос.
Автор: bbEye
Дата сообщения: 27.02.2007 18:40
OdesitVadim

Цитата:
Тонкий намёк на Сишников старой закалки? Правда потом разбираться во всех хитросплетениях вызовов - ужос.

Самое интересное, что - нет.
Сервер, который для обработки пары сотен входящих соединений держит столько же потоков - самый настоящий ужас
А клиентское соединение на обычных блокирующих сокетах - вполне нормальное решение (хотя бы потому, что асинхронный connect() появился только в Windows XP).

Если бы я делал реализацию клиентского [SSH-]соединения для своей программы (не класс для публичного пользования, а конечное решение), то наследовался бы от TThread, выставив наружу метод Send(pbData: pointer; cbData: cardinal), который бы работал асинхронно (послал и забыл), событие OnReceive(pbData: pointer; cbData: cardinal). Выход из потока будет означать закрытие соединения. Коннект можно делать сразу, в начале метода Execute.
Автор: Delphi6
Дата сообщения: 27.02.2007 19:42
bbEye

Цитата:
Если бы я делал реализацию клиентского [SSH-]соединения для своей программы (не класс для публичного пользования, а конечное решение), то наследовался бы от TThread, выставив наружу метод Send(pbData: pointer; cbData: cardinal), который бы работал асинхронно (послал и забыл), событие OnReceive(pbData: pointer; cbData: cardinal). Выход из потока будет означать закрытие соединения. Коннект можно делать сразу, в начале метода Execute.

А что идея Сейчас попробую так сделать.

Ну если рассуждать про класс для массы, то там все очень просто. CryptSession уже автоматом делает большинство работы заменяя. Единственный недостаток, что момент получения ответа не видно. А именно я пакую данные, потом посылаю (все в try except что бы отловить ошибку отсылки) и все на этом конец. Потом уже другими функциями можно проверить есть ли ответ. Если он есть то считать его, после многочисленных тестов я пришел к выводу что если там что-то приходи то только полностью, возможно TCryptSession так реализован, в доке ничего не сказано, и если честно очень скудно написано, хотя дареному коню в зубы не смотрят Там и так все что можно уже сделано, куда еще просить доку по подробней


Цитата:
А вот с длл лучше разобраться. Портировать в делфи,к примеру. Чтобы не надо было тянуть за собой

Если вы о реализации функций DLL в Дельфи то она уже есть


Цитата:
При работе с сокетами это нормально, когда приезжает не весь пакет. Нужно ждать следующей порции, и так до тех пор, пока не придет полностью.

Ну а если не весь, то как мне об этом узнать?
Автор: bbEye
Дата сообщения: 27.02.2007 21:15
Delphi6
Есть такая либа для работы с сокетами, тоже опенсурсовая - Synapse.
В ней реализованы различные протоколы, в т.ч. SSL и SSH (только вместе с cryptlib). Она может использовать в качестве шифрователя одну из трех сторонних либ - cryptlib, openssl, StrSecII и SecureBlackBox. Можно в ней подсмотреть, как использовать cryptlib.


Цитата:
Ну а если не весь, то как мне об этом узнать?

Подозреваю, что в этом случае на выходе cryptPopData() будет код ошибки, а bytesReceived вернет зеро.
Автор: Delphi6
Дата сообщения: 27.02.2007 21:34
bbEye
А вы уверены что там точно есть SSH, что SSL есть я точно знаю а вот SSH там не реализован, я с самого начала на нее попал но там не было SSH Как модуль называется?
Автор: bbEye
Дата сообщения: 27.02.2007 21:39
Delphi6
Модуль называется ssl_cryptlib.pas (релиз 37). Я тоже думал, что там только SSL, пока не заглянул внутрь ...

Цитата:
Content: SSL/SSH support by Peter Gutmann's CryptLib
Автор: Delphi6
Дата сообщения: 27.02.2007 21:47
bbEye
Спасибо, сейчас гляну.

p.s. Странно они как-то назвали этот файл
Автор: Delphi6
Дата сообщения: 28.02.2007 10:53
bbEye
Ну наверно начну с того что в SSL_Cryptlib много чего не подсмотришь Там все реализовано почти так как у меня, за исключением одной веши которая мне понравилась, а именно, вместо того что бы считывать по 64 байта, они считывают макс. 32*** байта и потом копируют именно столько байтов сколько было считано. Просто а я как-то и не сообразил

Теперь что касается реализации считывания через поток, я все таки придерживаюсь мысли что лучше все команды TSSHClient-а делать через поток. Тем более что сегодня вот решил посмотреть TIdTelnet и вот что я нашел:


Цитата:
TelnetThread: TIdTelnetReadThread;
Specifies the thread used to read data and commands for the TELNET client.


Вот только не совсем понятно как можно реализовать event OnDataAvailable? Признаюсь я ивенты только перекрывал, а сам никогда не создавал (за исключением случаев когда это были системные и я просто приписывал что за данное сообщение отвечает вот эта функция). Может просветите? Единственное что мне приходит в голову это доп. поток которые работе с паузами в 100мс. и проверяет fCryptSession.PopData на наличие данных. И при их появлении генерирует ивент которые в последствии будет отлавливать OnDataAvailable.

Спасибо за просветление

п.с. Мне еще не совсем понятно как можно реализовать ожидание завершения потока? Может TThread.WaitFor? А уже в потоке потом следить за временем?
Автор: OdesitVadim
Дата сообщения: 28.02.2007 19:03
Делать event'ы придельно просто.
вначале обявлеш новый тип, который соответствует С# "делегаты", а в паскале был очень давно

Код:
TMyEvent = procedure(Sender: TObject;foo:integer) of object;//параметры в скобках твои
Автор: Delphi6
Дата сообщения: 28.02.2007 20:06
OdesitVadim
Сейчас попробую сделать, могу порадовать что кажется что то получается, все сделал через поток (Connect, Diconect, PushData, PopData) и главное при тестовых подключениях, отдаче и ожидании данных программа не подвисает Пока что все считывания вызываю автоматом после Connect (для получения приветствия) и после Execute (результат команды). Вот плько есть одна трабла, если сработал таймаут на чтение приветсивя то после запуска Execute результат не верен, ибо считываться опоздавшее приветствие Сейчас работаю над решением этой проблемы.

Теперь немного о ивентах. Если я все правильно понял, то во всем коде после объявления OnError например при возникновении ошибки вызываю процедуру обработки OnError с описанием ошибки. Поправьте если что.

А как мне следить за получением данных? Создавать поток с периодом опроса в 100мсек? И при поступлении считывать все и генерить ивент OnDataReceive?

p.s. Выше указанные ивенты будут добавлены, в них действительно есть потребность.
Автор: bbEye
Дата сообщения: 28.02.2007 20:40
Вызовы event'ов надо обязательно синхронизировать, иначе обработчики будут выполняться в контексте потока соединения, а не главного (всплывет всякая бяка).
Н-р:

Код:
TReceiveEvent = procedure(pbData: pointer; cbData: cardinal) of object;

TMyThread = class(TThread)
private
fMyEvent: TReceiveEvent;
procedure FireMyEvent;
...
protected
procedure Execute; override;
public
property MyEvent: TReceiveEvent read fMyEvent write fMyEvent;
end;

procedure TMyThread.FireMyEvent;
begin
if Assigned(fMyEvent) then fMyEvent(...);
end;

procedure TMyThread.Execute;
begin
...
Synchronize(FireMyEvent);
...
end;
Автор: Delphi6
Дата сообщения: 01.03.2007 12:31
Окончательно запутался Оказалось реально что ответ может приходит порциями Тогда не совсем понятно как определить что ответ на мою отправленную команду уже закончился? На одно сервере при тестировании заметил что после отсылки команды, мне приходит два пакета, первый который содержит саму команду и потом второй который содержит уже данные. Значит сделал так, после отсылки команды ls считывал дважды (каждый раз ожидал 5сек). Но потом решил протестить на другом сервере а там уже пакеты вообще порезанные начали приходить. Вот лог (сделал что бы дебагить компонент) действий POP значит считывание с сокета PUSH запись в сокет и отсылка.

Думал даже что ожидать пока в пакете не будет снова символа приветствия шела # либо $ но они же могут просто присутствовать в пакете . Помогите плиз, неужели разработчики cryptlib не учли такую важную возможность как контролировать размер пакета? (не того который сейчас пришел, а общий размер который должен быть получен на в ответ)

[more=WORK LOG]CONNECT
9656345: POP:
9656355: POP:
9656355: POP:
9656365: POP:
9656385: POP:
9656385: POP:
9656395: POP:
9656405: POP:
9656425: POP:
9656425: POP:
9656435: POP:
9656445: POP:
9656465: POP:
9656465: POP:
9656475: POP:
9656475: POP:
9656485: POP:
9656505: POP:
9656505: POP:
9656515: POP:
9656515: POP:
9656535: POP:
9656555: POP:
9656565: POP:
9656575: POP:
9656585: POP:
9656595: POP:
9656595: POP:
9656615: POP:
9656635: POP:
9656635: POP:
9656645: POP:
9656655: POP:
9656665: POP:
9656675: POP:
9656675: POP:
9656695: POP:
9656705: POP: Last login: Wed Feb 28 02:15:14 2007 from *****
9656715: PUSH: ls
9656715: POP:
9656745: POP:
9656755: POP:
9656765: POP:
9656785: POP:
9656785: POP:
9656795: POP:
9656805: POP:
9656815: POP:
9656825: POP:
9656835: POP:
9656845: POP:
9656855: POP:
9656865: POP:
9656875: POP:
9656875: POP:
9656895: POP:
9656905: POP:
9656915: POP:
9656915: POP:
9656945: POP:
9656955: POP:
9656955: POP:
9656965: POP: ls //первое удачное считывание
9656986: POP:
9656996: POP:
9656996: POP:
9657016: POP:
9657026: POP:
9657036: POP:
9657046: POP:
9657066: POP:
9657066: POP:
9657076: POP:
9657086: POP:
9657096: POP:
9657106: POP:
9657116: POP:
9657116: POP:
9657126: POP:
9657156: POP:
9657156: POP:
9657166: POP:
9657186: POP:
9657196: POP:
9657196: POP:
9657216: POP:
9657226: POP:
9657236: POP:
9657246: POP:
9657256: POP:
9657266: POP:
9657276: POP:
9657276: POP:
9657296: POP:
9657306: POP: gesoft@gesoft.org [~]# ls //второе удачное считывание, здесь уже должен быть результат команды
9657316: PUSH: uptime
9657316: POP:
9657346: POP:
9657356: POP:
9657366: POP:
9657376: POP:
9657386: POP:
9657396: POP:
9657396: POP:
9657426: POP:
9657426: POP: ./ .canna etc/ public_ftp/
../ .contactemail .filter public_html/
21762 cookie .ftpquota .spamkey
access_log .cpaddons/ .gnupg/ tmp/
 .cpanel-datastore/ .htpasswd .tmp
//первое удачное считывание, часть пакета на ответ команды ls, а должно было возвратить просто uptime

9657436: POP:
9657456: POP:
9657466: POP:
9657476: POP:
9657476: POP:
9657486: POP:
9657506: POP:
9657516: POP:
9657526: POP:
9657546: POP:
9657556: POP:
9657566: POP:
9657576: POP: .bash_history .cpanel-ducache .htpasswds/ .trash/
.bash_logout .dns job.txt wget-log
.bash_profile .emacs .lastlogin www@
.bashrc error_log mail/ .zshrc
gesoft@gesoft.org [~]#
//второе удачное считывание, конец команды ls а должно было быть результат команды uptime
[/more]

п.с. Уже подумываю над реализацией через РНР, типа подсоединяюсь через HTTPS к скрипту, передаю данные для входа в шел и работаю через него, хотя у меня уже реализовано все в РНР, но практика показала что он работает очень медленно
Автор: Delphi6
Дата сообщения: 01.03.2007 15:59
Я вот что сообразил, а может окончание пакета можно определить символом новой строки (#10) или возвратом каретки (#13)?

Добавлено:
Самое интересное что и в Telnet нет возможности определить что от сервера все пакеты получены Что бы стало ясным что я подразумеваю под словом "все пакеты" то возьмем к примеру TIdHTTP или TIdFTP в которых при начале работы передается размер данных которые должны, либо быть получены либо переданы.
Автор: bbEye
Дата сообщения: 02.03.2007 07:48
Да, видимо, надо самому парсить получаемые данные, чтобы определить конец пакета.
Автор: Delphi6
Дата сообщения: 02.03.2007 14:10
bbEye
А есть хоть какие-то идеи? Я вот думал может после коннекта считывать как выглядит приглашение Shell-а и потом ответ контролировать именно по нему, пока нет пакета который содержит приглашение Shell-а до того ожидать. Хотя есть вероятность что имя файла или еще что будет содержать такую комбинацию (в именах файлах кажется запрещены символы # и $ а где еще может присутствовать к примеру"soft@gesoft.org [~]#")
Автор: idiMAN
Дата сообщения: 02.03.2007 22:32
Delphi6

В компонентах wodSSH эта проблема решается указанием строки приглашения.
Вот кусок из моей программы:

Код:
// Устанавливаем блокирующий режим работы
wodSSH.Blocking := True;
// Открываем SSH-соединение
// (иногда с первого раза не соединяется, поэтому делаем три попытки)
Tries := 0;
repeat
try
Tries := Tries + 1;
wodSSH.Connect;
except
end;
until (wodSSH.State = Connected) or (Tries >= 3);
// Если соединились...
if wodSSH.State = Connected
then begin
// Ждём приглашения
wodSSH.WaitFor('regex:\w+@\w+\s~.+[\$%#>]\s$');
// Устанавливаем вид приглашения для блокирующих команд
wodSSH.Prompt := 'regex:\w+@\w+\s~.+[\$%#>]\s$';
// Посылаем команды
// ... перейти в рабочий каталог
wodSSH.Execute('cd /usr/home/mailadm/email'#13#10);
// ... запустить на скрипт на обновление
wodSSH.Execute('./regen.sh'#13#10);
// ... получить список пользователей
UserList.Text := wodSSH.Execute('cdbdump < /var/db/mail/users.cdb'#13#10);
...
// Закрываем SSH-соединение
wodSSH.Disconnect;
Автор: Delphi6
Дата сообщения: 02.03.2007 23:57
idiMAN
Отлично, значит уже есть на что положится, раз там реализовано именно так то и мы сможем положится на "приветствие", сейчас доработаю и посмотрим что получится. Сейчас у меня вот такая мысль, буду в потоке проверять соке, при получении очередного пакета буду проверять на наличие "приветствие", если его нет то просто прибавляем к буферу и ждем следующей порции, если уже есть то генерируем ивент OnDataAvailable. Что думаете на счет этого?

п.с. Полный исходник с примером как я и обещал будет выложен, даже если не смогу компонент дописать выложу на том этапе на котором я буду остановлен
Автор: idiMAN
Дата сообщения: 03.03.2007 08:07
Delphi6

Цитата:
Сейчас у меня вот такая мысль, буду в потоке проверять соке, при получении очередного пакета буду проверять на наличие "приветствие", если его нет то просто прибавляем к буферу и ждем следующей порции, если уже есть то генерируем ивент OnDataAvailable. Что думаете на счет этого?


Думаю это то, что нужно. Ещё бы предусмотреть таймаут, в течении которого мы готовы ждать ответ от сервера, а то вдруг коннект отвалился или посланная команда подвисла.
Автор: Swappp
Дата сообщения: 03.03.2007 13:54
Delphi6

Цитата:
(в именах файлах кажется запрещены символы # и $ а где еще может присутствовать к примеру"soft@gesoft.org [~]#")

В юниксах, на сколько мне известно, нет запрещенных символов в именах файлов (может только 0x00)... Главное правильно их (символы) заэкранировать, тогда можно даже перенос строки вставить...

Вообще в openssh возможно выполнение одной команды, при этом сама команда указывается в аргументах при вызове ssh, может стоит посмотреть как там реализовано? Выглядит это практически как выполнение локальной программы, чем то похоже на sudo.

Страницы: 12

Предыдущая тема: socket не компилируется :(


Форум Ru-Board.club — поднят 15-09-2016 числа. Цель - сохранить наследие старого Ru-Board, истории становления российского интернета. Сделано для людей.