/* * Paglo Crawler * Copyright (C) 2006-2008 Paglo Labs Inc. All rights reserved. * www.paglo.com * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ //--------------------------------------------------------------------------- #include "ScanAgentUtils.h" #include "Debug.h" #include "AgentConfig.h" #include #include #include #ifdef WIN32 #include #endif #include "ServerCmd.h" #include "CommunicationThread.h" #include #include #include #include //--------------------------------------------------------------------------- void CloseSocket(int Sock) { bool Error; #ifdef WIN32 Error = (closesocket(Sock) == SOCKET_ERROR); #else Error = (close(Sock) < 0); #endif if (Error) { LogError("Error closing socket."); } } //--------------------------------------------------------------------------- string Int2String(int Value) { char Buffer[20]; sprintf(Buffer, "%d", Value); return string(Buffer); } //--------------------------------------------------------------------------- int WDay(std::string Day) { if (Day == "SUN" || Day == "Sun" || Day == "sun") { return 0; } else if (Day == "MON" || Day == "Mon" || Day == "mon") { return 1; } else if (Day == "TUE" || Day == "Tue" || Day == "tue") { return 2; } else if (Day == "WED" || Day == "Wed" || Day == "wed") { return 3; } else if (Day == "THU" || Day == "Thu" || Day == "thu") { return 4; } else if (Day == "FRI" || Day == "Fri" || Day == "fri") { return 5; } else if (Day == "SAT" || Day == "Sat" || Day == "sat") { return 6; } else { string Msg = Day; Msg += " is not a valid day abbreviation."; throw runtime_error(Msg); } } //--------------------------------------------------------------------------- void MillisecondSleep(unsigned int Period) { #ifdef WIN32 Sleep(Period); #else usleep(Period * 1000); #endif } //--------------------------------------------------------------------------- char *EscapeString(const unsigned char *Str, int OldLen, bool EscapeQuotes) { int NewLen; const unsigned char *OldChar = Str; char *NewStr, *NewChar; NewLen = OldLen; /* * Allocate for the worst case scenario -- every char will need to be * escaped as a hex value. */ NewStr = (char *)calloc(OldLen * 4 + 1, sizeof(char)); NewChar = NewStr; /* * Look for characters to escape. */ for (int i = 0; i < OldLen; i++, OldChar++) { if (isprint(*OldChar)) { if (EscapeQuotes && *OldChar == '"') { NewLen++; *NewChar++ = '\\'; *NewChar = '"'; } else { *NewChar = *OldChar; } } else { NewChar[0] = '\\'; switch (*OldChar) { case '\a': NewLen++; NewChar[1] = 'a'; break; case '\b': NewLen++; NewChar[1] = 'b'; break; case '\f': NewLen++; NewChar[1] = 'f'; break; case '\n': NewLen++; NewChar[1] = 'n'; break; case '\r': NewLen++; NewChar[1] = 'r'; break; case '\t': NewLen++; NewChar[1] = 't'; break; case '\v': NewLen++; NewChar[1] = 'v'; break; default: NewLen += 3; NewChar[1] = 'x'; NewChar += 2; snprintf(NewChar, 3, "%.2x", *OldChar); NewChar += 2; continue; } NewChar++; } NewChar++; } NewStr[NewLen] = '\0'; return NewStr; } //--------------------------------------------------------------------------- char *XMLEscapeString(const unsigned char *Str, int OldLen) { int NewLen; const unsigned char *OldChar = Str; char *NewStr, *NewChar; NewLen = OldLen; /* * Allocate for the worst case scenario -- every char will need to be * escaped as a hex value. */ NewStr = (char *)calloc(OldLen * 4 + 1, sizeof(char)); NewChar = NewStr; /* * Look for characters to escape. */ for (int i = 0; i < OldLen; i++, OldChar++) { if (isprint(*OldChar)) { *NewChar = *OldChar; } else { switch (*OldChar) { case '\r': case '\n': case '\t': *NewChar = *OldChar; break; default: NewChar[0] = '\\'; NewLen += 3; NewChar[1] = 'x'; NewChar += 2; snprintf(NewChar, 3, "%.2x", *OldChar); NewChar += 2; continue; } } NewChar++; } NewStr[NewLen] = '\0'; return NewStr; } //--------------------------------------------------------------------------- std::string EscapeString(std::string Str, bool EscapeQuotes) { std::string Output; char *Raw = EscapeString((const unsigned char*)Str.c_str(), Str.length(), EscapeQuotes); Output = Raw; free(Raw); return Output; } //--------------------------------------------------------------------------- std::string XMLEscapeString(std::string Str) { std::string Output; char *Raw = XMLEscapeString((const unsigned char*)Str.c_str(), Str.length()); Output = Raw; free(Raw); return Output; } //--------------------------------------------------------------------------- unsigned char *UnEscapeString(const char *Str, int *NewLen) { int CharVal; const char *OldChar = Str; unsigned char *NewStr, *NewChar; NewStr = (unsigned char *)calloc(strlen(Str) + 1, sizeof(char)); NewChar = NewStr; *NewLen = 0; /* * Look for escaped characters. */ while (*OldChar) { /* * Replace the escape sequence with the appropriate * character. */ if (*OldChar == '\\') { OldChar++; switch (*OldChar) { case '"': *NewChar = '"'; OldChar++; break; case 'a': *NewChar = '\a'; OldChar++; break; case 'b': *NewChar = '\b'; OldChar++; break; case 'f': *NewChar = '\f'; OldChar++; break; case 'n': *NewChar = '\n'; OldChar++; break; case 'r': *NewChar = '\r'; OldChar++; break; case 't': *NewChar = '\t'; OldChar++; break; case 'v': *NewChar = '\v'; OldChar++; break; case 'x': if (strlen(++OldChar) >= 2) { CharVal = strtoul(OldChar, NULL, 16); if (CharVal <= 255 && CharVal >= 0) { *NewChar = CharVal; OldChar += 2; } } break; case '\0': DEBUG_MESSAGE(DEBUG_PARSER, ("Unescaped '\\'. Reached end of string: \"%s\"", Str)); break; default: DEBUG_MESSAGE(DEBUG_PARSER, ("Invalid escape sequence: \\%c.", *OldChar)); *NewChar = '\\'; } } else { /* * Just copy the character to the new string * if it's not an escape sequence. */ *NewChar = *OldChar; OldChar++; } (*NewLen)++; NewChar++; } //DEBUG_MESSAGE(DEBUG_PARSER, ("Str (%d): %s", strlen(Str), Str)); //DEBUG_MESSAGE(DEBUG_PARSER, ("NewStr (%d):%s", strlen(NewStr), NewStr)); NewStr = (unsigned char*)realloc(NewStr, *NewLen * sizeof(char)); return NewStr; } //--------------------------------------------------------------------------- FILE *_LogHandle = NULL; void LogInit(void) { LogClose(); _LogHandle = fopen(_GlobalConfig.GetLogFilename().c_str(), "a"); #ifndef HAVE_GUI if (_LogHandle == NULL) { fprintf(stderr, "Unable to open log file '%s': %s\n", _GlobalConfig.GetLogFilename().c_str(), strerror(errno)); } #endif } //--------------------------------------------------------------------------- void LogClose(void) { if (_LogHandle && _LogHandle != stdout && _LogHandle != stderr) { fclose(_LogHandle); _LogHandle = NULL; } } //--------------------------------------------------------------------------- static void Log(char *Prefix, char *Format, va_list Args) { char TimeStamp[20]; time_t TimeCurr; struct tm TmTimeCurr; pthread_mutex_lock(&_DebugMutex); if (!_LogHandle) { LogInit(); } TimeCurr = time(NULL); //localtime_r(&TimeCurr, &TmTimeCurr); gmtime_r(&TimeCurr, &TmTimeCurr); strftime(TimeStamp, 20, "%b %d %T GMT", &TmTimeCurr); fprintf(_LogHandle, "%s -- ", TimeStamp); vfprintf(_LogHandle, Format, Args); fprintf(_LogHandle, "\n"); fflush(_LogHandle); pthread_mutex_unlock(&_DebugMutex); } //--------------------------------------------------------------------------- void LogMesg(char *Format, ...) { va_list Args; va_start(Args, Format); Log("", Format, Args); va_end(Args); } //--------------------------------------------------------------------------- void LogError(char *Format, ...) { va_list Args; va_start(Args, Format); Log("ERROR: ", Format, Args); va_end(Args); } //--------------------------------------------------------------------------- /* * List of issues. */ std::vector _IssuesList; TLock _IssuesListLock; void LogIssue(TIssueType IssueType, TIssueSeverity IssueSeverity, char *Format, ...) { va_list Args; /* * Generate the message. */ char Buf[1024]; va_start(Args, Format); vsnprintf(Buf, 1024, Format, Args); Buf[1024-1] = '\0'; va_end(Args); /* * Put the issue in the log. */ va_start(Args, Format); Log("ISSUE: ", Format, Args); va_end(Args); /* * See if we already have this issue. */ TIssueInfo *Info = NULL; TLocker Locker(_IssuesListLock); for (unsigned int i = 0; i < _IssuesList.size(); i++) { TIssueInfo *Issue = _IssuesList[i]; if (Issue->IssueType == IssueType && Issue->IssueSeverity == IssueSeverity && Issue->Message == Buf) { Info = Issue; } } if (!Info) { Info = new TIssueInfo(); Info->IssueType = IssueType; Info->IssueSeverity = IssueSeverity; Info->Message = Buf; Info->Occurances = 0; Info->FirstOccurance = time(NULL); _IssuesList.push_back(Info); } Info->LastOccurance = time(NULL); Info->Occurances++; /* * Tell the server about our issue. */ if (_CommThread) { TTreeBuilder IssueNode; IssueNode.StartTree("apps"); IssueNode.StartTree("com"); IssueNode.StartTree("paglo"); IssueNode.StartTree("crawlers"); IssueNode.StartTree("crawler", "guid = '" + _GlobalConfig.GetGUID() + "'"); IssueNode.AddValue("guid", _GlobalConfig.GetGUID()); IssueNode.StartTree("support_messages"); IssueNode.StartTree("message", string("contents = '") + TTreeBuilder::EscapeValue(Buf) + string("'")); IssueNode.AddValue("contents", Buf); time_t TimeCurr; struct tm TmTimeCurr; char TimeStamp[30]; TimeCurr = time(NULL); gmtime_r(&TimeCurr, &TmTimeCurr); strftime(TimeStamp, 29, "%Y-%m-%dT%H:%M:%S.0000000Z", &TmTimeCurr); TimeStamp[28] = '\0'; IssueNode.AddValue("timestamp", TimeStamp); IssueNode.EndTree(); IssueNode.EndTree(); IssueNode.EndTree(); IssueNode.EndTree(); IssueNode.EndTree(); IssueNode.EndTree(); IssueNode.EndTree(); TSubmitTreeCmd *Cmd = new TSubmitTreeCmd(IssueNode); _CommThread->EnqueueCmd(Cmd); } } //--------------------------------------------------------------------------- string HexDump(const unsigned char *Data, int Length) { string HexStr; char Buf[3], LineBuf[5]; for (int i = 0; i < Length; i++) { snprintf(Buf, 3, "%02X", Data[i]); if (i % 16 == 0) { if (i > 0) { HexStr += "\n"; } snprintf(LineBuf, 5, "%04X", i); HexStr += LineBuf; HexStr += ": "; } HexStr += Buf; HexStr += (i % 8) ? " " : " "; } return HexStr; } //--------------------------------------------------------------------------- string WcsToStr(wchar_t *WcsPtr) { int ConvertedSize = 0; char *CStrPtr = NULL; string ConvertedString; if (WcsPtr) { ConvertedSize = wcstombs(NULL, WcsPtr, 0) + 1; CStrPtr = (char *)malloc(ConvertedSize * sizeof(char)); wcstombs(CStrPtr, WcsPtr, ConvertedSize); ConvertedString = CStrPtr; free(CStrPtr); } return ConvertedString; } //--------------------------------------------------------------------------- string JoinStrings(string Delim, list &Strings) { string JoinedStrings; list::iterator CurrString, LastString; for (CurrString = Strings.begin(); CurrString != Strings.end(); CurrString++) { if (!CurrString->empty()) { JoinedStrings += *CurrString + Delim; } } if (!JoinedStrings.empty()) { string S = JoinedStrings.substr(JoinedStrings.length() - Delim.size()); if (S == Delim) { JoinedStrings.resize(JoinedStrings.length() - Delim.size()); } } return JoinedStrings; } //--------------------------------------------------------------------------- string ReplaceString(string S, string SubS, string Replacement) { string NewVal = S; unsigned int i = 0; i = NewVal.find(SubS, 0); while (i != string::npos) { NewVal.replace(i, SubS.size(), Replacement, 0, Replacement.size()); i += Replacement.size(); i = NewVal.find(SubS, i); } return NewVal; } //--------------------------------------------------------------------------- list SplitString(string &S, string &Delim) { list Strings; unsigned int i = 0, i2 = 0; while (true) { i2 = S.find(Delim, i); if (i2 == string::npos) { string SubS = S.substr(i); Strings.push_back(SubS); break; } else { string SubS = S.substr(i, i2 - i); Strings.push_back(SubS); i = i2 + 1; } } return Strings; } //--------------------------------------------------------------------------- string Base64Encode(const string &Input) { return Base64Encode((unsigned char *)Input.c_str(), Input.length()); } //--------------------------------------------------------------------------- string Base64Encode(const unsigned char *Input, int Len) { string Output; BIO *bmem, *b64; BUF_MEM *bptr; if (Input) { b64 = BIO_new(BIO_f_base64()); bmem = BIO_new(BIO_s_mem()); b64 = BIO_push(b64, bmem); BIO_write(b64, Input, Len); BIO_flush(b64); BIO_get_mem_ptr(b64, &bptr); Output = string((char *)bptr->data, bptr->length - 1); BIO_free_all(b64); } return Output; } //--------------------------------------------------------------------------- string Hex2String(const unsigned char *Hex, int Len) { string Output; char Buf[3]; for (int i = 0; i < Len; i++) { snprintf(Buf, 3, "%02X", Hex[i]); Buf[2] = '\0'; Output += Buf; } return Output; } //--------------------------------------------------------------------------- string GenerateSHA1(string &Data) { unsigned char SHA1Bytes[EVP_MAX_MD_SIZE]; // Array the digest will be written to unsigned int DigestSize; // Number of bytes written to SHA1Bytes /* * Setup the message digest context and tell it we want to use SHA1. */ EVP_MD_CTX *SHA1Context = EVP_MD_CTX_create(); EVP_DigestInit_ex(SHA1Context, EVP_sha1(), NULL); /* * Hash Data. */ EVP_DigestUpdate(SHA1Context, Data.c_str(), Data.size()); /* * Write the hash to SHA1Bytes and destroy our message digest context. */ EVP_DigestFinal(SHA1Context, SHA1Bytes, &DigestSize); EVP_MD_CTX_destroy(SHA1Context); /* * Return a string representation of SHA1Bytes. */ return Hex2String(SHA1Bytes, DigestSize); } //--------------------------------------------------------------------------- int GetWeekHour(void) { time_t Now = time(NULL); struct tm TmNow; memset(&TmNow, 0x00, sizeof(struct tm)); localtime_r(&Now, &TmNow); /* * Rebase the weekday returned so that Monday is 0 and Sunday is 6. */ int WeekDay = (TmNow.tm_wday > 0) ? TmNow.tm_wday - 1 : 6; return (WeekDay * 24) + TmNow.tm_hour; } //--------------------------------------------------------------------------- string GenerateAESKey(void) { unsigned char *Key; EVP_CIPHER_CTX Ctx; EVP_EncryptInit(&Ctx, EVP_aes_256_cbc(), NULL, NULL); int KeyLen = EVP_CIPHER_CTX_key_length(&Ctx); EVP_CIPHER_CTX_cleanup(&Ctx); Key = (unsigned char *)malloc(KeyLen * sizeof(unsigned char)); RAND_bytes(Key, KeyLen); return Hex2String(Key, KeyLen); } //--------------------------------------------------------------------------- string EncryptAESwithSalt(string &Key, string &Data) { string S; if (!Key.empty()) { int KeyLength = Key.length() / 2 + Key.length() % 2; unsigned char *KeyBytes = (unsigned char *)malloc(sizeof(unsigned char) * KeyLength); /* * Convert the key to bytes. */ // int N = Key.length(); for (unsigned int i = 0, j = 0; i < Key.length(); i += 2) { string S = Key.substr(i, 2); char *EndPtr; unsigned char Byte = strtoul(S.c_str(), &EndPtr, 16); KeyBytes[j++] = Byte; } EVP_CIPHER_CTX ctx; EVP_EncryptInit(&ctx, EVP_aes_256_cbc(), KeyBytes, NULL); unsigned char *out = (unsigned char *)malloc((Data.length() + 4 + EVP_CIPHER_CTX_block_size(&ctx)) * sizeof(unsigned char)); int n, out_len; /* * Prepare a buffer w/4 bytes of salt and then the data we want to encrypt. */ unsigned char *buf = (unsigned char *)malloc(Data.length() + 4); RAND_bytes(buf, 4); memcpy(buf + 4, Data.c_str(), Data.length()); EVP_EncryptUpdate(&ctx, out, &n, (unsigned char *)buf, Data.length() + 4); out_len = n; EVP_EncryptFinal(&ctx, (out) + n, &n); out_len += n; EVP_CIPHER_CTX_cleanup(&ctx); S = Hex2String(out, out_len); free(KeyBytes); free(buf); free(out); } return S; } //--------------------------------------------------------------------------- string DecryptAESwithSalt(string &Key, string &Data) { string S; if (!Key.empty() && !Data.empty()) { /* * Convert the key to bytes. */ int KeyLength = Key.length() / 2 + Key.length() % 2; unsigned char *KeyBytes = (unsigned char *)malloc(sizeof(unsigned char) * KeyLength); for (int i = 0, j = 0; i < (int)Key.length() && j < KeyLength; i += 2) { string S = Key.substr(i, 2); char *EndPtr; unsigned char Byte = strtoul(S.c_str(), &EndPtr, 16); KeyBytes[j++] = Byte; } /* * Convert the data to bytes. */ int DataLength = Data.length() / 2 + Data.length() % 2; unsigned char *DataBytes = (unsigned char *)malloc(sizeof(unsigned char) * DataLength); for (int i = 0, j = 0; i < (int)Data.length() && j < DataLength; i += 2) { string S = Data.substr(i, 2); char *EndPtr; unsigned char Byte = strtoul(S.c_str(), &EndPtr, 16); DataBytes[j++] = Byte; } EVP_CIPHER_CTX ctx; EVP_DecryptInit(&ctx, EVP_aes_256_cbc(), KeyBytes, NULL); char *out = (char *)malloc(DataLength + EVP_CIPHER_CTX_block_size(&ctx) + 1); int out_len, n; EVP_DecryptUpdate(&ctx, (unsigned char *)out, &n, (unsigned char *)DataBytes, DataLength); out_len = n; EVP_DecryptFinal(&ctx, ((unsigned char *)out) + n, &n); out_len += n; out[out_len] = '\0'; EVP_CIPHER_CTX_cleanup(&ctx); strcpy(out, out + 4); S = out; free(KeyBytes); free(DataBytes); free(out); } return S; } //--------------------------------------------------------------------------- string TIssueInfo::FullMessage() { if (Occurances > 1) { return Message + " " + Int2String(Occurances) + " times."; } else { return Message; } } //---------------------------------------------------------------------------