| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793 |
- //---------------------------------------------------------------------------
- #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.");
- AddStdError(LoadStr(AUTH_PASSWORD) + "\n", false);
- 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.");
- AddStdError(LoadStr(AUTH_PASSWORD) + "\n", false);
- 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;
- FCurrentLogFileName = "";
- FCurrentFileName = "";
- 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 (Logging)
- {
- 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()
- {
- FLogging = (FConfiguration->Logging || (OnAddLine != NULL));
- // if logging to file was turned off or log file was change -> close current log file
- if (FFile && (!LogToFile() || (FCurrentLogFileName != FConfiguration->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();
- }
- }
- //---------------------------------------------------------------------------
- bool __fastcall TSessionLog::LogToFile()
- {
- return (FConfiguration != NULL) && FConfiguration->Logging && FConfiguration->LogToFile;
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::CloseLogFile()
- {
- if (FFile)
- {
- fclose((FILE *)FFile);
- FFile = NULL;
- }
- FCurrentLogFileName = "";
- FCurrentFileName = "";
- }
- //---------------------------------------------------------------------------
- void TSessionLog::OpenLogFile()
- {
- if (LogToFile())
- {
- try
- {
- assert(!FFile);
- assert(Configuration);
- FCurrentLogFileName = FConfiguration->LogFileName;
- AnsiString NewFileName = StripPathQuotes(FCurrentLogFileName);
- 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);
- FCurrentFileName = NewFileName;
- }
- else
- {
- throw Exception(FMTLOAD(LOG_OPENERROR, (NewFileName)));
- }
- }
- catch (Exception &E)
- {
- // We failed logging to file, turn it off and notify user.
- FCurrentLogFileName = "";
- FCurrentFileName = "";
- 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 < FLoggedLines));
- return Result;
- }
- //---------------------------------------------------------------------------
- int __fastcall TSessionLog::GetBottomIndex()
- {
- return (Count ? Indexes[Count-1] : -1);
- }
- //---------------------------------------------------------------------------
- Boolean __fastcall TSessionLog::GetLoggingToFile()
- {
- return
- (Configuration && Configuration->Logging && Configuration->LogToFile && FFile);
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::Clear()
- {
- FTopIndex += Count;
- TStringList::Clear();
- }
- //---------------------------------------------------------------------------
- void __fastcall TSessionLog::SetOnAddLine(TLogAddLineEvent value)
- {
- if (OnAddLine != value)
- {
- FOnAddLine = value;
- ReflectSettings();
- }
- }
|