| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782 |
- //---------------------------------------------------------------------------
- #include <vcl.h>
- #pragma hdrstop
- #include <stdio.h>
- #include "PuttyIntf.h"
- #include "Net.h"
- #include "Interface.h"
- #include "SecureShell.h"
- #include "TextsCore.h"
- #include "HelpCore.h"
- #include "Common.h"
- #include "ScpMain.h"
- #include "Security.h"
- #ifndef AUTO_WINSOCK
- #include <winsock2.h>
- #endif
- //---------------------------------------------------------------------------
- #pragma package(smart_init)
- //---------------------------------------------------------------------------
- #define MAX_BUFSIZE 32768
- const TColor LogLineColors[] =
- {clGreen, clRed, clMaroon, clBlue, clGray};
- //---------------------------------------------------------------------------
- __fastcall TSecureShell::TSecureShell()
- {
- FSessionData = new TSessionData("");
- FActive = False;
- ResetConnection();
- FLog = new TSessionLog(this);
- FOnQueryUser = NULL;
- FOnPromptUser = NULL;
- FOnDisplayBanner = NULL;
- FOnShowExtendedException = NULL;
- FOnUpdateStatus = NULL;
- FOnStdError = NULL;
- FOnCaptureOutput = NULL;
- FOnClose = NULL;
- FConfig = new Config();
- FSocket = new SOCKET;
- }
- //---------------------------------------------------------------------------
- __fastcall TSecureShell::~TSecureShell()
- {
- ClearStdError();
- Active = false;
- SAFE_DESTROY(FSessionData);
- SAFE_DESTROY(FLog);
- delete FConfig;
- FConfig = NULL;
- UserObject = NULL;
- delete FSocket;
- FSocket = NULL;
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::ResetConnection()
- {
- PendLen = 0;
- PendSize = 0;
- Pending = NULL;
- FStdErrorTemp = "";
- FBytesSent = 0;
- FBytesReceived = 0;
- FCSCipher = cipWarn; // = not detected yet
- FSCCipher = cipWarn;
- FReachedStatus = 0;
- UpdateStatus(sshClosed);
- FMaxPacketSize = NULL;
- FBufSize = 0;
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::Open()
- {
- try
- {
- DoOpen();
- }
- __finally
- {
- UpdateStatus(-1, false);
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::DoOpen()
- {
- const char * InitError;
- char * RealHost;
- FStoredPasswordTried = false;
- FStoredPasswordTriedForKI = false;
- FReachedStatus = 0;
- {
- TCoreGuard Guard;
- SessionsCount++;
- if (SessionsCount == 1)
- {
- UpdateStatus(sshInitWinSock);
- NetInitialize();
- }
- else
- {
- Log->Id = reinterpret_cast<unsigned int>(this);
- }
- }
- Log->AddStartupInfo();
- Active = false;
- FBackend = &ssh_backend;
- FAuthenticationLog = "";
- UpdateStatus(sshLookupHost);
- SessionData->StoreToConfig(FConfig);
- InitError = FBackend->init(this, &FBackendHandle, FConfig,
- SessionData->HostName.c_str(), SessionData->PortNumber, &RealHost, 0,
- FConfig->tcp_keepalives);
- if (InitError)
- {
- FatalError(InitError);
- }
- FRealHost = RealHost;
- UpdateStatus(sshConnect);
- /*FLoggingContext = log_init(this, (void *)FConfig);
- FBackend->provide_logctx(FBackendHandle, FLoggingContext);*/
- Init();
- CheckConnection(CONNECTION_FAILED);
- FLastDataSent = Now();
- FLoginTime = Now();
- Log->AddSeparator();
- UpdateStatus(sshAuthenticated);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::Reopen(int /*Params*/)
- {
- if (Active)
- {
- Close();
- }
- ResetConnection();
- Open();
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::Init()
- {
- try
- {
- try
- {
- while (!FBackend->sendok(FBackendHandle))
- {
- if (Configuration->LogProtocol >= 1)
- {
- LogEvent("Waiting for the server to continue with the initialisation");
- }
- WaitForData(true);
- }
- }
- catch(Exception & E)
- {
- if ((FReachedStatus == sshAuthenticate) && !FAuthenticationLog.IsEmpty())
- {
- FatalError(&E, FMTLOAD(AUTHENTICATION_LOG, (FAuthenticationLog)));
- }
- else
- {
- throw;
- }
- }
- }
- catch(Exception & E)
- {
- if (FReachedStatus == sshAuthenticate)
- {
- FatalError(&E, LoadStr(AUTHENTICATION_FAILED));
- }
- else
- {
- throw;
- }
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::PuttyLogEvent(const AnsiString & Str)
- {
- #define SERVER_VERSION_MSG "Server version: "
- // Gross hack
- if (Str.Pos(SERVER_VERSION_MSG) == 1)
- {
- FSshVersionString = Str.SubString(strlen(SERVER_VERSION_MSG) + 1,
- Str.Length() - strlen(SERVER_VERSION_MSG));
- }
- LogEvent(Str);
- }
- //---------------------------------------------------------------------------
- AnsiString __fastcall TSecureShell::GetSshImplementation()
- {
- const char * Ptr = strchr(FSshVersionString.c_str(), '-');
- if (Ptr != NULL)
- {
- Ptr = strchr(Ptr + 1, '-');
- }
- return (Ptr != NULL) ? AnsiString(Ptr + 1) : AnsiString();
- }
- //---------------------------------------------------------------------
- AnsiString __fastcall TSecureShell::GetPassword()
- {
- return (FPassword.IsEmpty() ? AnsiString() :
- DecryptPassword(FPassword, SessionData->SessionName));
- }
- //---------------------------------------------------------------------
- bool __fastcall TSecureShell::GetStoredPasswordTried()
- {
- return FStoredPasswordTried || FStoredPasswordTriedForKI;
- }
- //---------------------------------------------------------------------------
- TDateTime __fastcall TSecureShell::GetIdleInterval()
- {
- return (FSessionData->PingType != ptOff) ? FSessionData->PingIntervalDT :
- TDateTime(0);
- }
- //---------------------------------------------------------------------------
- bool __fastcall TSecureShell::PromptUser(const AnsiString Prompt,
- AnsiString & Response, bool IsPassword)
- {
- USEDPARAM(IsPassword);
- assert(IsPassword); // false only for username prompts
- bool Result;
- if (Prompt.Pos("Passphrase for key ") == 1)
- {
- AnsiString Key(Prompt);
- int P = Prompt.Pos("\"");
- if (P > 0)
- {
- Key.Delete(1, P);
- P = Key.LastDelimiter("\"");
- if (P > 0)
- {
- Key.SetLength(P - 1);
- }
- }
- LogEvent(FORMAT("Passphrase prompt (%s)", (Prompt)));
- Result = DoPromptUser(FMTLOAD(PROMPT_KEY_PASSPHRASE, (Key)),
- pkPassphrase, Response);
- }
- else if (Prompt.Pos("'s password: "))
- {
- LogEvent(FORMAT("Session password prompt (%s)", (Prompt)));
- if (!SessionData->Password.IsEmpty() && !FStoredPasswordTried)
- {
- LogEvent("Using stored password.");
- Result = true;
- Response = SessionData->Password;
- FStoredPasswordTried = true;
- }
- else
- {
- LogEvent("Asking user for password.");
- Result = DoPromptUser(
- FMTLOAD(PROMPT_SESSION_PASSWORD, (SessionData->SessionName)),
- pkPassword, Response);
- }
- }
- else
- {
- // in other cases we assume TIS/Cryptocard/keyboard-interactive authentification prompt
- LogEvent(FORMAT("%s prompt from server", (Prompt)));
- if (!SessionData->Password.IsEmpty() &&
- SessionData->AuthKIPassword && !FStoredPasswordTriedForKI)
- {
- LogEvent("Responding with stored password.");
- Result = true;
- Response = SessionData->Password;
- FStoredPasswordTriedForKI = true;
- }
- else
- {
- LogEvent("Asking user for response.");
- static const AnsiString ResponseSuffix("\r\nResponse: ");
- // Strip Cryptocard/TIS "Response" suffix
- AnsiString UserPrompt = Prompt;
- if (UserPrompt.SubString(UserPrompt.Length() - ResponseSuffix.Length() + 1,
- ResponseSuffix.Length()) == ResponseSuffix)
- {
- UserPrompt.SetLength(UserPrompt.Length() - ResponseSuffix.Length());
- }
- Result = DoPromptUser(UserPrompt, pkServerPrompt, Response);
- }
- };
- if (Configuration->RememberPassword)
- {
- FPassword = EncryptPassword(Response, SessionData->SessionName);
- }
- return Result;
- }
- //---------------------------------------------------------------------------
- bool __fastcall TSecureShell::DoPromptUser(AnsiString Prompt, TPromptKind Kind,
- AnsiString & Response)
- {
- bool Result = false;
- if (OnPromptUser != NULL)
- {
- OnPromptUser(this, Prompt, Kind, Response, Result, NULL);
- }
- return Result;
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::GotHostKey()
- {
- // due to re-key GotHostKey() may be called again later during session
- if (FReachedStatus < sshAuthenticate)
- {
- UpdateStatus(sshAuthenticate);
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::FromBackend(Boolean IsStdErr, char * Data, Integer Length)
- {
- CheckConnection();
- // Following is taken from scp.c from_backend() and modified
- if (IsStdErr)
- {
- AddStdError(AnsiString(Data, Length), false);
- }
- else
- {
- unsigned char *p = (unsigned char *)Data;
- unsigned Len = (unsigned)Length;
- // If this is before the real session begins, raise exception.
- if (!OutPtr)
- {
- FatalError("Internal error: Session not yet begun.");
- }
- if ((OutLen > 0) && (Len > 0))
- {
- unsigned Used = OutLen;
- if (Used > Len) Used = Len;
- memcpy(OutPtr, p, Used);
- OutPtr += Used; OutLen -= Used;
- p += Used; Len -= Used;
- }
- if (Len > 0)
- {
- if (PendSize < PendLen + Len)
- {
- PendSize = PendLen + Len + 4096;
- Pending = (char *)
- (Pending ? srealloc(Pending, PendSize) : smalloc(PendSize));
- if (!Pending) FatalError("Out of memory");
- }
- memcpy(Pending + PendLen, p, Len);
- PendLen += Len;
- }
- }
- }
- //---------------------------------------------------------------------------
- Integer __fastcall TSecureShell::Receive(char * Buf, Integer Len)
- {
- CheckConnection();
- if (Len > 0)
- {
- // Following is taken from scp.c ssh_scp_recv() and modified
- OutPtr = Buf;
- OutLen = Len;
- /*
- * See if the pending-input block contains some of what we
- * need.
- */
- if (PendLen > 0)
- {
- unsigned PendUsed = PendLen;
- if (PendUsed > OutLen)
- {
- PendUsed = OutLen;
- }
- memcpy(OutPtr, Pending, PendUsed);
- memmove(Pending, Pending + PendUsed, PendLen - PendUsed);
- OutPtr += PendUsed;
- OutLen -= PendUsed;
- PendLen -= PendUsed;
- if (PendLen == 0)
- {
- PendSize = 0;
- sfree(Pending);
- Pending = NULL;
- }
- }
- // I don't undestand this yet, but it works :-)
- while (OutLen > 0)
- {
- if (Configuration->LogProtocol >= 1)
- {
- LogEvent(FORMAT("Waiting for another %u bytes", (static_cast<int>(OutLen))));
- }
- WaitForData(false);
- }
- // This seems ambiguous
- if (Len <= 0) FatalError(LoadStr(LOST_CONNECTION));
- };
- FBytesReceived += Len;
- if (Configuration->LogProtocol >= 1)
- {
- LogEvent(FORMAT("Received %u bytes", (static_cast<int>(Len))));
- }
- return Len;
- }
- //---------------------------------------------------------------------------
- AnsiString __fastcall TSecureShell::ReceiveLine()
- {
- unsigned Index;
- Char Ch;
- AnsiString Line;
- Boolean EOL = False;
- do
- {
- // If there is any buffer of received chars
- if (PendLen > 0)
- {
- Index = 0;
- // Repeat until we walk thru whole buffer or reach end-of-line
- while ((Index < PendLen) && (!Index || (Pending[Index-1] != '\n')))
- {
- Index++;
- }
- EOL = (Boolean)(Index && (Pending[Index-1] == '\n'));
- Integer PrevLen = Line.Length();
- Line.SetLength(PrevLen + Index);
- Receive(Line.c_str() + PrevLen, Index);
- }
- // If buffer don't contain end-of-line character
- // we read one more which causes receiving new buffer of chars
- if (!EOL)
- {
- Receive(&Ch, 1);
- Line += Ch;
- EOL = (Ch == '\n');
- }
- }
- while (!EOL);
- // We don't want end-of-line character
- Line.SetLength(Line.Length()-1);
- CaptureOutput(llOutput, Line, false);
- return Line;
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::SendSpecial(int Code)
- {
- LogEvent(FORMAT("Sending special code: %d", (Code)));
- CheckConnection();
- FBackend->special(FBackendHandle, (Telnet_Special)Code);
- CheckConnection();
- FLastDataSent = Now();
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::SendEOF()
- {
- SendSpecial(TS_EOF);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::Send(const char * Buf, Integer Len)
- {
- CheckConnection();
- FBufSize = FBackend->send(FBackendHandle, (char *)Buf, Len);
- if (Configuration->LogProtocol >= 1)
- {
- LogEvent(FORMAT("Sent %u bytes", (static_cast<int>(Len))));
- LogEvent(FORMAT("There are %u bytes remaining in the send buffer", (FBufSize)));
- }
- FLastDataSent = Now();
- FBytesSent += Len;
- while (FBufSize > MAX_BUFSIZE)
- {
- if (Configuration->LogProtocol >= 1)
- {
- LogEvent(FORMAT("There are %u bytes remaining in the send buffer, "
- "need to send at least another %u bytes",
- (FBufSize, FBufSize - MAX_BUFSIZE)));
- }
- // it seems that this does not work anyway
- // (i.e. once the send buffer fills we hang here)
- WaitForData(true);
- FBufSize = FBackend->sendbuffer(FBackendHandle);
- if (Configuration->LogProtocol >= 1)
- {
- LogEvent(FORMAT("There are %u bytes remaining in the send buffer", (FBufSize)));
- }
- }
- CheckConnection();
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::SendNull()
- {
- LogEvent("Sending NULL.");
- Send("", 1);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::SendStr(AnsiString Str)
- {
- CheckConnection();
- Send(Str.c_str(), Str.Length());
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::SendLine(AnsiString Line)
- {
- SendStr(Line);
- Send("\n", 1);
- Log->Add(llInput, Line);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::TranslateAuthenticationMessage(AnsiString & Message)
- {
- struct TMapping
- {
- const char * Original;
- const char * Additional;
- int Translation;
- };
- TMapping Mapping[] = {
- { "Using username \"", NULL, AUTH_TRANSL_USERNAME },
- { "Using keyboard-interactive authentication.", NULL, AUTH_TRANSL_KEYB_INTER },
- { "Authenticating with public key \"", "from agent", AUTH_TRANSL_PUBLIC_KEY_AGENT },
- { "Authenticating with public key \"", NULL, AUTH_TRANSL_PUBLIC_KEY },
- { "Authenticated using RSA key \"", NULL, AUTH_TRANSL_PUBLIC_KEY_AGENT },
- { "Wrong passphrase", NULL, AUTH_TRANSL_WRONG_PASSPHRASE },
- { "Access denied", NULL, AUTH_TRANSL_ACCESS_DENIED },
- { "Trying public key authentication", NULL, AUTH_TRANSL_TRY_PUBLIC_KEY }
- };
- for (int Index = 0; Index < LENOF(Mapping); Index++)
- {
- const char * Original = Mapping[Index].Original;
- size_t OriginalLen = strlen(Original);
- if (strncmp(Message.c_str(), Original, OriginalLen) == 0)
- {
- if (Original[OriginalLen - 1] == '"')
- {
- AnsiString MessageRest =
- Message.SubString(OriginalLen + 1, Message.Length() - OriginalLen);
- int P = MessageRest.Pos("\"");
- if (P > 0)
- {
- const char * Additional = Mapping[Index].Additional;
- if ((Additional == NULL) || (MessageRest.Pos(Additional) > 0))
- {
- MessageRest.SetLength(P - 1);
- Message = FMTLOAD(Mapping[Index].Translation, (MessageRest));
- break;
- }
- }
- }
- else
- {
- Message = LoadStr(Mapping[Index].Translation);
- break;
- }
- }
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::AddStdError(AnsiString Str, bool LogOnly)
- {
- StdError += Str;
- Integer P;
- Str = DeleteChar(Str, '\r');
- // We send only whole line at once to log, so we have to cache
- // incoming std error data
- FStdErrorTemp += Str;
- AnsiString Line;
- // Do we have at least one complete line in std error cache?
- while ((P = FStdErrorTemp.Pos("\n")) > 0)
- {
- Line = FStdErrorTemp.SubString(1, P-1);
- FStdErrorTemp.Delete(1, P);
- AddStdErrorLine(Line, LogOnly);
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::AddStdErrorLine(AnsiString Str, bool LogOnly)
- {
- if (Status == sshAuthenticate)
- {
- TranslateAuthenticationMessage(Str);
- FAuthenticationLog += (FAuthenticationLog.IsEmpty() ? "" : "\n") + Str;
- }
- if (!LogOnly && (OnStdError != NULL))
- {
- OnStdError(this, llStdError, Str);
- }
- CaptureOutput(llStdError, Str, LogOnly);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::ClearStdError()
- {
- // Flush std error cache
- if (!FStdErrorTemp.IsEmpty())
- {
- if (Status == sshAuthenticate)
- {
- FAuthenticationLog +=
- (FAuthenticationLog.IsEmpty() ? "" : "\n") + FStdErrorTemp;
- }
- CaptureOutput(llStdError, FStdErrorTemp, false);
- FStdErrorTemp = "";
- }
- StdError = "";
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::CaptureOutput(TLogLineType Type,
- const AnsiString & Line, bool LogOnly)
- {
- if (!LogOnly && (FOnCaptureOutput != NULL))
- {
- FOnCaptureOutput(this, Type, Line);
- }
- Log->Add(Type, Line);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::FatalError(Exception * E, AnsiString Msg)
- {
- if (FActive)
- {
- // We log this instead of exception handler, because Close() would
- // probably cause exception handler to loose pointer to TShellLog()
- LogEvent("Attempt to close connection due to fatal exception:");
- Log->Add(llException, Msg);
- Log->AddException(E);
- Close();
- }
- SSH_FATAL_ERROR_EXT(E, Msg);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::FatalError(AnsiString Error)
- {
- FatalError(NULL, Error);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::SetSocket(void * value)
- {
- assert(value);
- // now this can be called repeatedly, so allow being called again with
- // the same socket
- if (FActive && (*static_cast<SOCKET*>(value) != INVALID_SOCKET) &&
- (*static_cast<SOCKET*>(FSocket) != *static_cast<SOCKET*>(value)))
- FatalError("Cannot set socket during connection");
- assert(FSocket);
- *static_cast<SOCKET*>(FSocket) = *static_cast<SOCKET*>(value);
- if (*static_cast<SOCKET*>(FSocket) != INVALID_SOCKET)
- {
- FActive = true;
- }
- else
- {
- Discard();
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::SetSessionData(TSessionData * value)
- {
- assert(!FActive);
- FSessionData->Assign(value);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::SetActive(bool value)
- {
- if (FActive != value)
- {
- if (value)
- {
- Open();
- }
- else
- {
- Close();
- }
- }
- }
- //---------------------------------------------------------------------------
- bool __fastcall TSecureShell::GetActive() const
- {
- return FActive;
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::Discard()
- {
- bool WasActive = FActive;
- FActive = false;
- if (WasActive)
- {
- if (FReachedStatus)
- {
- TCoreGuard Guard;
- SessionsCount--;
- if (SessionsCount == 0)
- {
- NetFinalize();
- }
- }
- if (OnClose)
- {
- OnClose(this);
- }
- }
- FStatus = sshClosed;
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::Close()
- {
- LogEvent("Closing connection.");
- CheckConnection();
- FBackend->free(FBackendHandle);
- Discard();
- }
- //---------------------------------------------------------------------------
- void inline __fastcall TSecureShell::CheckConnection(int Message)
- {
- if (!FActive || get_ssh_state_closed(FBackendHandle))
- {
- AnsiString Str = LoadStr(Message >= 0 ? Message : NOT_CONNECTED);
- int ExitCode = get_ssh_exitcode(FBackendHandle);
- if (ExitCode >= 0)
- {
- Str += " " + FMTLOAD(SSH_EXITCODE, (ExitCode));
- }
- FatalError(Str);
- }
- }
- //---------------------------------------------------------------------------
- bool __fastcall TSecureShell::Select(int Sec)
- {
- CheckConnection();
- struct timeval time;
- fd_set readfds;
- FD_ZERO(&readfds);
- FD_SET(*static_cast<SOCKET*>(FSocket), &readfds);
- time.tv_sec = Sec;
- time.tv_usec = 0;
- int R = select(1, &readfds, NULL, NULL, &time);
- if (R < 0)
- {
- SSH_FATAL_ERROR(FMTLOAD(UNKNOWN_SOCKET_STATUS, (R)));
- }
- if (Configuration->LogProtocol >= 2)
- {
- LogEvent(FORMAT("Select result is %d", (R)));
- }
- return (R > 0);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::PoolForData(unsigned int & Result)
- {
- if (Configuration->LogProtocol >= 2)
- {
- LogEvent("Pooling for data in case they finally arrives");
- }
- if (Select(0))
- {
- LogEvent("Data has arrived, closing query to user.");
- Result = qaRetry;
- }
- }
- //---------------------------------------------------------------------------
- extern int select_result(WPARAM, LPARAM);
- void __fastcall TSecureShell::WaitForData(bool Sending)
- {
- bool NeedToWait = true;
- SOCKET & Socket = *static_cast<SOCKET*>(FSocket);
- if (socket_writable(Socket))
- {
- if (Configuration->LogProtocol >= 1)
- {
- LogEvent("Checking low level send buffer");
- }
- select_result((WPARAM)(Socket), (LPARAM)FD_WRITE);
- }
- if (FBufSize > 0)
- {
- if (Configuration->LogProtocol >= 1)
- {
- LogEvent(FORMAT("Trying to dispatch send buffer (%u bytes)", (FBufSize)));
- }
- int NewBufSize = FBackend->send(FBackendHandle, "", 0);
- if ((NewBufSize < FBufSize) && Sending)
- {
- NeedToWait = false;
- }
- FBufSize = NewBufSize;
- if (Configuration->LogProtocol >= 1)
- {
- LogEvent(FORMAT("There are %u bytes remaining in the send buffer", (FBufSize)));
- }
- }
- bool IncomingData;
- if (!NeedToWait)
- {
- if (Configuration->LogProtocol >= 2)
- {
- LogEvent("Looking for incoming data");
- }
- // This is just attempt to make it as close as possible to previous behaviour
- // Maybe it is not necessary at all.
- IncomingData = Select(0);
- }
- else
- {
- do
- {
- if (Configuration->LogProtocol >= 2)
- {
- LogEvent("Looking for incoming data");
- }
- IncomingData = Select(FSessionData->Timeout);
- if (!IncomingData)
- {
- LogEvent("Waiting for data timed out, asking user what to do.");
- TQueryParams Params(qpFatalAbort | qpAllowContinueOnError);
- Params.Timer = 500;
- Params.TimerEvent = PoolForData;
- Params.TimerMessage = FMTLOAD(TIMEOUT_STILL_WAITING, (FSessionData->Timeout));
- Params.TimerAnswers = qaAbort;
- if (DoQueryUser(FMTLOAD(CONFIRM_PROLONG_TIMEOUT, (FSessionData->Timeout)),
- qaRetry | qaAbort, &Params) != qaRetry)
- {
- FatalError(LoadStr(USER_TERMINATED));
- }
- }
- }
- while (!IncomingData);
- }
- if (IncomingData)
- {
- select_result((WPARAM)(Socket), (LPARAM)FD_READ);
- }
- }
- //---------------------------------------------------------------------------
- bool __fastcall TSecureShell::SshFallbackCmd() const
- {
- return ssh_fallback_cmd(FBackendHandle);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::Error(const AnsiString Error) const
- {
- SSH_ERROR(Error);
- }
- //---------------------------------------------------------------------------
- extern int (WINAPI *p_WSAEnumNetworkEvents)
- (SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents);
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::Idle()
- {
- if (Configuration->LogProtocol >= 1)
- {
- LogEvent("Session upkeep");
- }
- noise_regular();
- // Keep session alive
- if ((FSessionData->PingType != ptOff) &&
- (Now() - FLastDataSent > FSessionData->PingIntervalDT))
- {
- KeepAlive();
- // in case keep alive could not be processed, postpone next attempt
- FLastDataSent = Now();
- }
- call_ssh_timer(FBackendHandle);
- // to let detect dropped connection immediatelly, also to let
- // process SSH-level communication with the server (KEX particularly)
- if (Select(0))
- {
- LogEvent("Detected incoming data while idle");
- select_result((WPARAM)(*static_cast<SOCKET*>(FSocket)), (LPARAM)FD_READ);
- CheckConnection();
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::KeepAlive()
- {
- LogEvent("Sending null packet to keep session alive.");
- SendSpecial(TS_PING);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::SetLog(TSessionLog * value)
- {
- FLog->Assign(value);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::SetConfiguration(TConfiguration *value)
- {
- FConfiguration = value;
- Log->Configuration = value;
- }
- //---------------------------------------------------------------------------
- TDateTime __fastcall TSecureShell::GetDuration() const
- {
- return Now() - FLoginTime;
- }
- //---------------------------------------------------------------------------
- int __fastcall TSecureShell::RemainingSendBuffer()
- {
- return MAX_BUFSIZE - FBufSize;
- }
- //---------------------------------------------------------------------------
- unsigned long __fastcall TSecureShell::MaxPacketSize()
- {
- if (SshVersion == 1)
- {
- return 0;
- }
- else
- {
- if (FMaxPacketSize == NULL)
- {
- FMaxPacketSize = ssh2_remmaxpkt(FBackendHandle);
- }
- return *FMaxPacketSize;
- }
- }
- //---------------------------------------------------------------------------
- TCompressionType __fastcall TSecureShell::FuncToCompression(
- const void * Compress) const
- {
- if (SshVersion == 1)
- {
- return get_ssh1_compressing(FBackendHandle) ? ctZLib : ctNone;
- }
- else
- {
- return (ssh_compress *)Compress == &ssh_zlib ? ctZLib : ctNone;
- }
- }
- //---------------------------------------------------------------------------
- TCompressionType __fastcall TSecureShell::GetCSCompression() const
- {
- assert(Active);
- return FuncToCompression(get_cscomp(FBackendHandle));
- }
- //---------------------------------------------------------------------------
- TCompressionType __fastcall TSecureShell::GetSCCompression() const
- {
- assert(Active);
- return FuncToCompression(get_sccomp(FBackendHandle));
- }
- //---------------------------------------------------------------------------
- int __fastcall TSecureShell::GetSshVersion() const
- {
- assert(Active);
- return get_ssh_version(FBackendHandle);
- }
- //---------------------------------------------------------------------------
- TCipher __fastcall TSecureShell::FuncToSsh1Cipher(const void * Cipher) const
- {
- const ssh_cipher *CipherFuncs[] =
- {&ssh_3des, &ssh_des, &ssh_blowfish_ssh1};
- const TCipher TCiphers[] = {cip3DES, cipDES, cipBlowfish};
- assert(LENOF(CipherFuncs) == LENOF(TCiphers));
- TCipher Result = cipWarn;
- for (int Index = 0; Index < LENOF(TCiphers); Index++)
- {
- if ((ssh_cipher *)Cipher == CipherFuncs[Index])
- {
- Result = TCiphers[Index];
- }
- }
- assert(Result != cipWarn);
- return Result;
- }
- //---------------------------------------------------------------------------
- TCipher __fastcall TSecureShell::FuncToSsh2Cipher(const void * Cipher) const
- {
- const ssh2_ciphers *CipherFuncs[] =
- {&ssh2_3des, &ssh2_des, &ssh2_aes, &ssh2_blowfish};
- const TCipher TCiphers[] = {cip3DES, cipDES, cipAES, cipBlowfish};
- assert(LENOF(CipherFuncs) == LENOF(TCiphers));
- TCipher Result = cipWarn;
- for (int C = 0; C < LENOF(TCiphers); C++)
- {
- for (int F = 0; F < CipherFuncs[C]->nciphers; F++)
- {
- if ((ssh2_cipher *)Cipher == CipherFuncs[C]->list[F])
- {
- Result = TCiphers[C];
- }
- }
- }
- assert(Result != cipWarn);
- return Result;
- }
- //---------------------------------------------------------------------------
- TCipher __fastcall TSecureShell::GetCSCipher()
- {
- assert(Active);
- if (FCSCipher == cipWarn)
- {
- if (SshVersion == 1)
- {
- FCSCipher = FuncToSsh1Cipher(get_cipher(FBackendHandle));
- }
- else
- {
- FCSCipher = FuncToSsh2Cipher(get_cscipher(FBackendHandle));
- }
- }
- return FCSCipher;
- }
- //---------------------------------------------------------------------------
- TCipher __fastcall TSecureShell::GetSCCipher()
- {
- CheckConnection();
- if (FSCCipher == cipWarn)
- {
- if (SshVersion == 1)
- {
- FSCCipher = FuncToSsh1Cipher(get_cipher(FBackendHandle));
- }
- else
- {
- FSCCipher = FuncToSsh2Cipher(get_sccipher(FBackendHandle));
- }
- }
- return FSCCipher;
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::DoShowExtendedException(Exception * E)
- {
- DoHandleExtendedException(E);
- if (OnShowExtendedException != NULL)
- {
- OnShowExtendedException(this, E, NULL);
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::DoHandleExtendedException(Exception * E)
- {
- Log->AddException(E);
- }
- //---------------------------------------------------------------------------
- int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
- TStrings * MoreMessages, int Answers, const TQueryParams * Params, TQueryType Type)
- {
- LogEvent(FORMAT("Asking user:\n%s (%s)", (Query, (MoreMessages ? MoreMessages->CommaText : AnsiString() ))));
- int Answer = AbortAnswer(Answers);
- if (FOnQueryUser)
- {
- FOnQueryUser(this, Query, MoreMessages, Answers, Params, Answer, Type, NULL);
- }
- return Answer;
- }
- //---------------------------------------------------------------------------
- int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
- const AnsiString OtherMessage, int Answers, const TQueryParams * Params,
- TQueryType Type)
- {
- TStrings * MoreMessages = new TStringList();
- Integer Result;
- try {
- if (!OtherMessage.IsEmpty()) MoreMessages->Add(OtherMessage);
- Result = DoQueryUser(Query, MoreMessages, Answers, Params, Type);
- } __finally {
- delete MoreMessages;
- }
- return Result;
- }
- //---------------------------------------------------------------------------
- int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
- int Answers, const TQueryParams * Params, TQueryType Type)
- {
- return DoQueryUser(Query, "", Answers, Params, Type);
- }
- //---------------------------------------------------------------------------
- int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
- Exception * E, int Answers, const TQueryParams * Params, TQueryType Type)
- {
- int Result;
- TStrings * MoreMessages = new TStringList();
- try
- {
- if (E->InheritsFrom(__classid(ExtException)) &&
- ((ExtException*)E)->MoreMessages)
- {
- MoreMessages->Assign(((ExtException*)E)->MoreMessages);
- }
- if (!E->Message.IsEmpty() && !Query.IsEmpty()) MoreMessages->Insert(0, E->Message);
- Result = DoQueryUser(!Query.IsEmpty() ? Query : E->Message,
- MoreMessages->Count ? MoreMessages : NULL,
- Answers, Params, Type);
- }
- __finally
- {
- delete MoreMessages;
- }
- return Result;
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::VerifyHostKey(const AnsiString Host, int Port,
- const AnsiString KeyType, const AnsiString KeyStr, const AnsiString Fingerprint)
- {
- GotHostKey();
- int Result;
- FHostKeyFingerprint = Fingerprint;
- // Verify the key against the registry.
- Result = verify_host_key(Host.c_str(), Port, KeyType.c_str(), KeyStr.c_str());
- if (Result != 0)
- {
- if (Configuration->DisableAcceptingHostKeys)
- {
- FatalError(LoadStr(KEY_NOT_VERIFIED));
- }
- else
- {
- TQueryParams Params;
- Params.HelpKeyword = (Result == 1 ? HELP_UNKNOWN_KEY : HELP_DIFFERENT_KEY);
- int R = DoQueryUser(
- FMTLOAD((Result == 1 ? UNKNOWN_KEY2 : DIFFERENT_KEY2), (KeyType, Fingerprint)),
- qaYes | qaNo | qaCancel, &Params, qtWarning);
- switch (R) {
- case qaYes:
- store_host_key(Host.c_str(), Port, KeyType.c_str(), KeyStr.c_str());
- break;
- case qaCancel:
- FatalError(LoadStr(KEY_NOT_VERIFIED));
- }
- }
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::AskAlg(const AnsiString AlgType,
- const AnsiString AlgName)
- {
- AnsiString Msg;
- if (AlgType == "key-exchange algorithm")
- {
- Msg = FMTLOAD(KEX_BELOW_TRESHOLD, (AlgName));
- }
- else
- {
- int CipherType;
- if (AlgType == "cipher")
- {
- CipherType = CIPHER_TYPE_BOTH;
- }
- else if (AlgType == "client-to-server cipher")
- {
- CipherType = CIPHER_TYPE_CS;
- }
- else if (AlgType == "server-to-client cipher")
- {
- CipherType = CIPHER_TYPE_SC;
- }
- else
- {
- assert(false);
- }
- Msg = FMTLOAD(CIPHER_BELOW_TRESHOLD, (LoadStr(CipherType), AlgName));
- }
- if (DoQueryUser(Msg, qaYes | qaNo, NULL, qtWarning) == qaNo)
- {
- Abort();
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::DoDisplayBanner(const AnsiString & Banner,
- bool & Log)
- {
- if (OnDisplayBanner != NULL)
- {
- Log = false;
- AddStdError(Banner, true);
- if (Configuration->ForceBanners ||
- Configuration->ShowBanner(SessionData->SessionKey, Banner))
- {
- bool NeverShowAgain = false;
- int Options =
- FLAGMASK(Configuration->ForceBanners, boDisableNeverShowAgain);
- OnDisplayBanner(this, SessionData->SessionName, Banner,
- NeverShowAgain, Options);
- if (!Configuration->ForceBanners && NeverShowAgain)
- {
- Configuration->NeverShowBanner(SessionData->SessionKey, Banner);
- }
- }
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::DisplayBanner(const AnsiString & Banner,
- bool & Log)
- {
- DoDisplayBanner(Banner, Log);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::OldKeyfileWarning()
- {
- DoQueryUser(LoadStr(OLD_KEY), qaOK, NULL, qtWarning);
- }
- //---------------------------------------------------------------------------
- bool __fastcall TSecureShell::DoQueryReopen(Exception * E, int Params)
- {
- bool Result;
- if (FLAGSET(Params, ropNoConfirmation))
- {
- Result = true;
- }
- else
- {
- LogEvent("Connection was lost, asking what to do.");
- TQueryParams Params(qpAllowContinueOnError);
- Params.Timeout = Configuration->SessionReopenAuto;
- Params.TimeoutAnswer = qaRetry;
- TQueryButtonAlias Aliases[1];
- Aliases[0].Button = qaRetry;
- Aliases[0].Alias = LoadStr(RECONNECT_BUTTON);
- Params.Aliases = Aliases;
- Params.AliasesCount = LENOF(Aliases);
- Result = (DoQueryUser("", E, qaRetry | qaAbort, &Params, qtError) == qaRetry);
- }
- return Result;
- }
- //---------------------------------------------------------------------------
- bool __fastcall TSecureShell::QueryReopen(Exception * E, int Params)
- {
- bool Result = DoQueryReopen(E, Params);
- if (Result)
- {
- do
- {
- try
- {
- if (FLAGSET(Params, ropNoConfirmation))
- {
- Sleep(Configuration->SessionReopenNoConfirmation);
- }
- Reopen(Params);
- }
- catch(Exception & E)
- {
- if (!Active)
- {
- Result = DoQueryReopen(&E, Params);
- }
- else
- {
- throw;
- }
- }
- }
- while (!Active && Result);
- }
- return Result;
- }
- //---------------------------------------------------------------------------
- int __fastcall TSecureShell::GetStatus() const
- {
- return FStatus;
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::UpdateStatus(int Value, bool Active)
- {
- bool Update = (FStatus != Value) && Active;
- if (Update)
- {
- FStatus = Value;
- if (FStatus > FReachedStatus) FReachedStatus = FStatus;
- }
- if ((Update || !Active) && (FOnUpdateStatus != NULL))
- {
- FOnUpdateStatus(this, Active);
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSecureShell::SetUserObject(TObject * value)
- {
- if (UserObject != value)
- {
- if (UserObject)
- {
- delete UserObject;
- }
- FUserObject = value;
- }
- }
- //=== TSessionLog -----------------------------------------------------------
- const char *LogLineMarks = "<>!.*";
- __fastcall TSessionLog::TSessionLog(TSecureShell * AOwner): TStringList()
- {
- FEnabled = true;
- FOwner = AOwner;
- FFile = NULL;
- FLoggedLines = 0;
- FTopIndex = -1;
- FFileName = "";
- FId = 0;
- }
- //---------------------------------------------------------------------------
- __fastcall TSessionLog::~TSessionLog()
- {
- CloseLogFile();
- }
- //---------------------------------------------------------------------------
- AnsiString __fastcall TSessionLog::GetSessionName()
- {
- assert(FOwner != NULL);
- assert(FOwner->SessionData != NULL);
- return FOwner->SessionData->SessionName;
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::SetLine(Integer Index, AnsiString value)
- {
- Strings[Index] = AnsiString(Strings[Index - FTopIndex][1]) + value;
- }
- //---------------------------------------------------------------------------
- AnsiString __fastcall TSessionLog::GetLine(Integer Index)
- {
- return Strings[Index - FTopIndex].SubString(2, Strings[Index - FTopIndex].Length() - 1);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::SetType(Integer Index, TLogLineType value)
- {
- Strings[Index - FTopIndex][1] = LogLineMarks[value];
- }
- //---------------------------------------------------------------------------
- TLogLineType __fastcall TSessionLog::GetType(Integer Index)
- {
- int P = AnsiString(LogLineMarks).Pos(Strings[Index - FTopIndex][1]);
- if (P)
- {
- return TLogLineType(P - 1);
- }
- else
- {
- assert(false);
- return (TLogLineType)0;
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::DoAdd(TLogLineType aType, AnsiString aLine)
- {
- assert(Configuration);
- if (IsLogging())
- {
- try
- {
- if (Configuration->Logging)
- {
- BeginUpdate();
- }
- try
- {
- while (!aLine.IsEmpty())
- {
- AnsiString MarkStr = LogLineMarks[aType];
- AnsiString NewStr = CutToChar(aLine, '\n', False);
- if (Configuration->Logging)
- {
- TStringList::Add(MarkStr + NewStr);
- FLoggedLines++;
- }
- DoAddLine(aType, NewStr);
- if (Configuration->Logging && Configuration->LogToFile)
- {
- if (!FFile) OpenLogFile();
- if (FFile)
- {
- AnsiString Timestamp = FormatDateTime(" yyyy-mm-dd hh:nn:ss.zzz ", Now());
- AnsiString Buf = MarkStr + Timestamp + NewStr;
- // use fwrite instead of fprintf to make sure that even
- // non-ascii data (unicode) gets in.
- fwrite(Buf.c_str(), Buf.Length(), 1, (FILE *)FFile);
- fputc('\n', (FILE *)FFile);
- }
- }
- }
- if (Configuration->Logging)
- {
- if (FTopIndex < 0) FTopIndex = 0;
- DeleteUnnecessary();
- }
- }
- __finally
- {
- if (Configuration->Logging)
- {
- EndUpdate();
- }
- }
- }
- catch (Exception &E)
- {
- // We failed logging, turn it of and notify user.
- Configuration->Logging = False;
- try
- {
- throw ExtException(&E, LOG_ERROR);
- }
- catch (Exception &E)
- {
- FOwner->DoShowExtendedException(&E);
- }
- }
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::Add(TLogLineType aType, AnsiString aLine)
- {
- DoAdd(aType, aLine);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::AddFromOtherLog(TObject * /*Sender*/,
- TLogLineType aType, const AnsiString AddedLine)
- {
- DoAdd(aType, AddedLine);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::AddException(Exception * E)
- {
- if (E)
- {
- Add(llException, ExceptionLogString(E));
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::SetConfiguration(TConfiguration * value)
- {
- if (FConfiguration != value)
- {
- FConfiguration = value;
- ReflectSettings();
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::ReflectSettings()
- {
- // if logging to file was turned off or log file was change -> close current log file
- if (FFile && (!LogToFile || (FFileName != LogFileName))) CloseLogFile();
- // we used to open log file here, now it is posponed until first call to DoAdd()
- // this allows TSecureShell to change the Id sooner.
- DeleteUnnecessary();
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::SetEnabled(bool value)
- {
- if (FEnabled != value)
- {
- FEnabled = value;
- ReflectSettings();
- }
- }
- //---------------------------------------------------------------------------
- Boolean __fastcall TSessionLog::GetLogToFile()
- {
- return Enabled && (Configuration != NULL) && Configuration->Logging &&
- Configuration->LogToFile;
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::CloseLogFile()
- {
- if (FFile)
- {
- fclose((FILE *)FFile);
- FFile = NULL;
- }
- FFileName = "";
- }
- //---------------------------------------------------------------------------
- void TSessionLog::OpenLogFile()
- {
- if (LogToFile)
- {
- try
- {
- assert(!FFile);
- assert(Configuration);
- AnsiString NewFileName = LogFileName;
- TDateTime N = Now();
- for (int Index = 1; Index < NewFileName.Length(); Index++)
- {
- if (NewFileName[Index] == '&')
- {
- AnsiString Replacement;
- switch (tolower(NewFileName[Index + 1]))
- {
- case 'y':
- Replacement = FormatDateTime("yyyy", N);
- break;
- case 'm':
- Replacement = FormatDateTime("mm", N);
- break;
- case 'd':
- Replacement = FormatDateTime("dd", N);
- break;
- case 't':
- Replacement = FormatDateTime("hhnnss", N);
- break;
- case 'h':
- Replacement = MakeValidFileName(FOwner->SessionData->HostName);
- break;
- case 's':
- Replacement = MakeValidFileName(FOwner->SessionData->SessionName);
- break;
- case '&':
- Replacement = "&";
- break;
- default:
- Replacement = AnsiString("&") + NewFileName[Index + 1];
- break;
- }
- NewFileName.Delete(Index, 2);
- NewFileName.Insert(Replacement, Index);
- Index += Replacement.Length() - 1;
- }
- }
- if (Id != 0)
- {
- NewFileName = FORMAT("%s%s.%.8x%s", (ExtractFilePath(NewFileName),
- ExtractFileName(NewFileName), static_cast<int>(FId), ExtractFileExt(NewFileName)));
- }
- FFile = fopen(NewFileName.c_str(), (Configuration->LogFileAppend ? "a" : "w"));
- if (FFile)
- {
- setvbuf((FILE *)FFile, NULL, _IONBF, BUFSIZ);
- FFileName = NewFileName;
- }
- else
- {
- throw Exception(FMTLOAD(LOG_OPENERROR, (NewFileName)));
- }
- }
- catch (Exception &E)
- {
- // We failed logging to file, turn it of and notify user.
- FFileName = "";
- Configuration->LogToFile = false;
- try
- {
- throw ExtException(&E, LOG_ERROR);
- }
- catch (Exception &E)
- {
- FOwner->DoShowExtendedException(&E);
- }
- }
- }
- }
- //---------------------------------------------------------------------------
- void TSessionLog::DeleteUnnecessary()
- {
- BeginUpdate();
- try
- {
- if (!Configuration || !Configuration->Logging)
- {
- Clear();
- }
- else
- {
- while (!Configuration->LogWindowComplete && (Count > Configuration->LogWindowLines))
- {
- Delete(0);
- FTopIndex++;
- }
- }
- }
- __finally
- {
- EndUpdate();
- }
- }
- //---------------------------------------------------------------------------
- TColor __fastcall TSessionLog::GetColor(int Index)
- {
- return LogLineColors[Type[Index]];
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::DoAddLine(TLogLineType Type, const AnsiString AddedLine)
- {
- if (FOnAddLine)
- {
- FOnAddLine(this, Type, AddedLine);
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::AddStartupInfo()
- {
- assert(FOwner != NULL);
- TSessionData * Data = FOwner->SessionData;
- assert(Data != NULL);
- assert(Configuration);
- if (Configuration->Logging || FOnAddLine)
- {
- BeginUpdate();
- try
- {
- #define ADF(S, F) Add(llMessage, FORMAT(S, F));
- AddSeparator();
- ADF("WinSCP %s (OS %s)", (Configuration->VersionStr, Configuration->OSVersionStr));
- ADF("Login time: %s", (FormatDateTime("dddddd tt", Now())));
- AddSeparator();
- ADF("Session name: %s", (Data->SessionName));
- ADF("Host name: %s (Port: %d)", (Data->HostName, Data->PortNumber));
- ADF("User name: %s (Password: %s, Key file: %s)",
- (Data->UserName, BooleanToEngStr(!Data->Password.IsEmpty()),
- BooleanToEngStr(!Data->PublicKeyFile.IsEmpty())))
- ADF("Transfer Protocol: %s", (Data->FSProtocolStr));
- ADF("SSH protocol version: %s; Compression: %s",
- (Data->SshProtStr, BooleanToEngStr(Data->Compression)));
- ADF("Agent forwarding: %s; TIS/CryptoCard: %s; KI: %s; GSSAPI: %s",
- (BooleanToEngStr(Data->AgentFwd), BooleanToEngStr(Data->AuthTIS),
- BooleanToEngStr(Data->AuthKI), BooleanToEngStr(Data->AuthGSSAPI)));
- ADF("Ciphers: %s; Ssh2DES: %s",
- (Data->CipherList, BooleanToEngStr(Data->Ssh2DES)));
- char * PingTypes = "-NC";
- ADF("Ping type: %s, Ping interval: %d sec; Timeout: %d sec",
- (AnsiString(PingTypes[Data->PingType]), Data->PingInterval, Data->Timeout));
- AnsiString Bugs;
- char const * BugFlags = "A+-";
- for (int Index = 0; Index < BUG_COUNT; Index++)
- {
- Bugs += AnsiString(BugFlags[Data->Bug[(TSshBug)Index]])+(Index<BUG_COUNT-1?",":"");
- }
- ADF("SSH Bugs: %s", (Bugs));
- Bugs = "";
- for (int Index = 0; Index < SFTP_BUG_COUNT; Index++)
- {
- Bugs += AnsiString(BugFlags[Data->SFTPBug[(TSftpBug)Index]])+(Index<SFTP_BUG_COUNT-1?",":"");
- }
- ADF("SFTP Bugs: %s", (Bugs));
- ADF("Proxy: %s", (ProxyMethodList[Data->ProxyMethod]));
- if (Data->ProxyMethod != pmNone)
- {
- ADF("HostName: %s (Port: %d); Username: %s; Passwd: %s",
- (Data->ProxyHost, Data->ProxyPort,
- Data->ProxyUsername, BooleanToEngStr(!Data->ProxyPassword.IsEmpty())));
- if (Data->ProxyMethod == pmTelnet)
- {
- ADF("Telnet command: %s", (Data->ProxyTelnetCommand));
- }
- }
- ADF("Return code variable: %s; Lookup user groups: %s",
- ((Data->DetectReturnVar ? AnsiString("Autodetect") : Data->ReturnVar),
- BooleanToEngStr(Data->LookupUserGroups)));
- ADF("Shell: %s, EOL: %d", ((Data->Shell.IsEmpty()? AnsiString("default") : Data->Shell), Data->EOLType));
- ADF("Local directory: %s, Remote directory: %s, Update: %s, Cache: %s",
- ((Data->LocalDirectory.IsEmpty() ? AnsiString("default") : Data->LocalDirectory),
- (Data->RemoteDirectory.IsEmpty() ? AnsiString("home") : Data->RemoteDirectory),
- BooleanToEngStr(Data->UpdateDirectories),
- BooleanToEngStr(Data->CacheDirectories)));
- ADF("Cache directory changes: %s, Permanent: %s",
- (BooleanToEngStr(Data->CacheDirectoryChanges),
- BooleanToEngStr(Data->PreserveDirectoryChanges)));
- ADF("Clear aliases: %s, Unset nat.vars: %s, Resolve symlinks: %s",
- (BooleanToEngStr(Data->ClearAliases), BooleanToEngStr(Data->UnsetNationalVars),
- BooleanToEngStr(Data->ResolveSymlinks)));
- ADF("Alias LS: %s, Ign LS warn: %s, Scp1 Comp: %s",
- (BooleanToEngStr(Data->AliasGroupList),
- BooleanToEngStr(Data->IgnoreLsWarnings),
- BooleanToEngStr(Data->Scp1Compatibility)));
- AddSeparator();
- #undef ADF
- }
- __finally
- {
- EndUpdate();
- }
- }
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::AddSeparator()
- {
- Add(llMessage, "--------------------------------------------------------------------------");
- }
- //---------------------------------------------------------------------------
- int __fastcall TSessionLog::GetIndexes(int Index)
- {
- assert((Index >= 0) && (Index < Count));
- int Result = TopIndex + Index;
- assert((Result >= 0) && (Result < LoggedLines));
- return Result;
- }
- //---------------------------------------------------------------------------
- int __fastcall TSessionLog::GetBottomIndex()
- {
- return (Count ? Indexes[Count-1] : -1);
- }
- //---------------------------------------------------------------------------
- AnsiString __fastcall TSessionLog::GetLogFileName()
- {
- assert(Configuration);
- AnsiString Result = StripPathQuotes(Configuration->LogFileName);
- return Result;
- }
- //---------------------------------------------------------------------------
- Boolean __fastcall TSessionLog::GetLoggingToFile()
- {
- return
- (Configuration && Configuration->Logging && Configuration->LogToFile && FFile);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::Clear()
- {
- FTopIndex += Count;
- TStringList::Clear();
- }
|