mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2024-11-14 05:30:41 +03:00
3044 lines
63 KiB
C
3044 lines
63 KiB
C
// SoftEther VPN Source Code - Developer Edition Master Branch
|
|
// Cedar Communication Module
|
|
//
|
|
// SoftEther VPN Server, Client and Bridge are free software under GPLv2.
|
|
//
|
|
// Copyright (c) Daiyuu Nobori.
|
|
// Copyright (c) SoftEther VPN Project, University of Tsukuba, Japan.
|
|
// Copyright (c) SoftEther Corporation.
|
|
//
|
|
// All Rights Reserved.
|
|
//
|
|
// http://www.softether.org/
|
|
//
|
|
// Author: Daiyuu Nobori, Ph.D.
|
|
// Comments: Tetsuo Sugiyama, Ph.D.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// version 2 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 version 2
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
// THE LICENSE AGREEMENT IS ATTACHED ON THE SOURCE-CODE PACKAGE
|
|
// AS "LICENSE.TXT" FILE. READ THE TEXT FILE IN ADVANCE TO USE THE SOFTWARE.
|
|
//
|
|
//
|
|
// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN,
|
|
// UNDER JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY,
|
|
// MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS
|
|
// SOFTWARE, THAT ANY JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS
|
|
// SOFTWARE OR ITS CONTENTS, AGAINST US (SOFTETHER PROJECT, SOFTETHER
|
|
// CORPORATION, DAIYUU NOBORI OR OTHER SUPPLIERS), OR ANY JURIDICAL
|
|
// DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND OF USING, COPYING,
|
|
// MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING, AND/OR
|
|
// SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
|
|
// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO
|
|
// EXCLUSIVE JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO,
|
|
// JAPAN. YOU MUST WAIVE ALL DEFENSES OF LACK OF PERSONAL JURISDICTION
|
|
// AND FORUM NON CONVENIENS. PROCESS MAY BE SERVED ON EITHER PARTY IN
|
|
// THE MANNER AUTHORIZED BY APPLICABLE LAW OR COURT RULE.
|
|
//
|
|
// USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS
|
|
// YOU HAVE A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY
|
|
// CRIMINAL LAWS OR CIVIL RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS
|
|
// SOFTWARE IN OTHER COUNTRIES IS COMPLETELY AT YOUR OWN RISK. THE
|
|
// SOFTETHER VPN PROJECT HAS DEVELOPED AND DISTRIBUTED THIS SOFTWARE TO
|
|
// COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING CIVIL RIGHTS INCLUDING
|
|
// PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER COUNTRIES' LAWS OR
|
|
// CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES. WE HAVE
|
|
// NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
|
|
// INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+
|
|
// COUNTRIES AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE
|
|
// WORLD, WITH DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY
|
|
// COUNTRIES' LAWS, REGULATIONS AND CIVIL RIGHTS TO MAKE THE SOFTWARE
|
|
// COMPLY WITH ALL COUNTRIES' LAWS BY THE PROJECT. EVEN IF YOU WILL BE
|
|
// SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A PUBLIC SERVANT IN YOUR
|
|
// COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE LIABLE TO
|
|
// RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
|
|
// RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT
|
|
// JUST A STATEMENT FOR WARNING AND DISCLAIMER.
|
|
//
|
|
//
|
|
// SOURCE CODE CONTRIBUTION
|
|
// ------------------------
|
|
//
|
|
// Your contribution to SoftEther VPN Project is much appreciated.
|
|
// Please send patches to us through GitHub.
|
|
// Read the SoftEther VPN Patch Acceptance Policy in advance:
|
|
// http://www.softether.org/5-download/src/9.patch
|
|
//
|
|
//
|
|
// DEAR SECURITY EXPERTS
|
|
// ---------------------
|
|
//
|
|
// If you find a bug or a security vulnerability please kindly inform us
|
|
// about the problem immediately so that we can fix the security problem
|
|
// to protect a lot of users around the world as soon as possible.
|
|
//
|
|
// Our e-mail address for security reports is:
|
|
// softether-vpn-security [at] softether.org
|
|
//
|
|
// Please note that the above e-mail address is not a technical support
|
|
// inquiry address. If you need technical assistance, please visit
|
|
// http://www.softether.org/ and ask your question on the users forum.
|
|
//
|
|
// Thank you for your cooperation.
|
|
//
|
|
//
|
|
// NO MEMORY OR RESOURCE LEAKS
|
|
// ---------------------------
|
|
//
|
|
// The memory-leaks and resource-leaks verification under the stress
|
|
// test has been passed before release this source code.
|
|
|
|
|
|
// Logging.c
|
|
// Log storaging module
|
|
|
|
#include "CedarPch.h"
|
|
|
|
static char *delete_targets[] =
|
|
{
|
|
"backup.vpn_bridge.config",
|
|
"backup.vpn_client.config",
|
|
"backup.vpn_server.config",
|
|
"backup.vpn_gate_svc.config",
|
|
"backup.etherlogger.config",
|
|
"packet_log",
|
|
"etherlogger_log",
|
|
"secure_nat_log",
|
|
"security_log",
|
|
"server_log",
|
|
"bridge_log",
|
|
"packet_log_archive",
|
|
"azure_log",
|
|
};
|
|
|
|
static UINT eraser_check_interval = DISK_FREE_CHECK_INTERVAL_DEFAULT;
|
|
static UINT64 logger_max_log_size = MAX_LOG_SIZE_DEFAULT;
|
|
|
|
// Send with syslog
|
|
void SendSysLog(SLOG *g, wchar_t *str)
|
|
{
|
|
UCHAR *buf;
|
|
UINT buf_size;
|
|
// Validate arguments
|
|
if (g == NULL || str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
buf_size = CalcUniToUtf8(str);
|
|
buf = ZeroMalloc(buf_size);
|
|
UniToUtf8(buf, buf_size, str);
|
|
|
|
if (buf_size >= 1024)
|
|
{
|
|
buf_size = 1023;
|
|
}
|
|
|
|
Lock(g->lock);
|
|
{
|
|
if (Tick64() >= g->NextPollIp)
|
|
{
|
|
IP ip;
|
|
|
|
if (GetIP(&ip, g->HostName))
|
|
{
|
|
g->NextPollIp = Tick64() + SYSLOG_POLL_IP_INTERVAL;
|
|
Copy(&g->DestIp, &ip, sizeof(IP));
|
|
}
|
|
else
|
|
{
|
|
g->NextPollIp = Tick64() + SYSLOG_POLL_IP_INTERVAL_NG;
|
|
}
|
|
}
|
|
|
|
if (g->DestPort != 0 && IsZeroIp(&g->DestIp) == false)
|
|
{
|
|
SendTo(g->Udp, &g->DestIp, g->DestPort, buf, buf_size);
|
|
}
|
|
}
|
|
Unlock(g->lock);
|
|
|
|
Free(buf);
|
|
}
|
|
|
|
// Release the syslog client
|
|
void FreeSysLog(SLOG *g)
|
|
{
|
|
// Validate arguments
|
|
if (g == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DeleteLock(g->lock);
|
|
ReleaseSock(g->Udp);
|
|
Free(g);
|
|
}
|
|
|
|
// Configure the syslog client
|
|
void SetSysLog(SLOG *g, char *hostname, UINT port)
|
|
{
|
|
IP ip;
|
|
// Validate arguments
|
|
if (g == NULL)
|
|
{
|
|
return;
|
|
}
|
|
if (port == 0)
|
|
{
|
|
port = SYSLOG_PORT;
|
|
}
|
|
|
|
if (hostname == NULL)
|
|
{
|
|
hostname = "";
|
|
}
|
|
|
|
Zero(&ip, sizeof(IP));
|
|
GetIP(&ip, hostname);
|
|
|
|
Lock(g->lock);
|
|
{
|
|
Copy(&g->DestIp, &ip, sizeof(IP));
|
|
g->DestPort = port;
|
|
StrCpy(g->HostName, sizeof(g->HostName), hostname);
|
|
g->NextPollIp = Tick64() + IsZeroIp(&ip) ? SYSLOG_POLL_IP_INTERVAL_NG : SYSLOG_POLL_IP_INTERVAL;
|
|
}
|
|
Unlock(g->lock);
|
|
}
|
|
|
|
// Create a syslog client
|
|
SLOG *NewSysLog(char *hostname, UINT port, IP *ip)
|
|
{
|
|
// Validate arguments
|
|
SLOG *g = ZeroMalloc(sizeof(SLOG));
|
|
|
|
g->lock = NewLock();
|
|
g->Udp = NewUDPEx2(0, false, ip);
|
|
|
|
SetSysLog(g, hostname, port);
|
|
|
|
return g;
|
|
}
|
|
|
|
// Check if there is enough free space on the disk
|
|
bool CheckEraserDiskFreeSpace(ERASER *e)
|
|
{
|
|
UINT64 s;
|
|
// Validate arguments
|
|
if (e == NULL)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Get the free disk space
|
|
if (GetDiskFree(e->DirName, &s, NULL, NULL) == false)
|
|
{
|
|
// Acquisition failure
|
|
return true;
|
|
}
|
|
|
|
if (e->MinFreeSpace > s)
|
|
{
|
|
// The free space is smaller than specified bytes
|
|
return false;
|
|
}
|
|
|
|
// Vacant enough
|
|
return true;
|
|
}
|
|
|
|
// Release the deleting file list
|
|
void FreeEraseFileList(LIST *o)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (o == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
ERASE_FILE *f = LIST_DATA(o, i);
|
|
Free(f->FullPath);
|
|
Free(f);
|
|
}
|
|
|
|
ReleaseList(o);
|
|
}
|
|
|
|
// Show the deleting file list
|
|
void PrintEraseFileList(LIST *o)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (o == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
ERASE_FILE *f = LIST_DATA(o, i);
|
|
Print("%I64u - %s\n", f->UpdateTime, f->FullPath);
|
|
}
|
|
}
|
|
|
|
// Generate a deleting file list of the specified directory
|
|
void EnumEraseFile(LIST *o, char *dirname)
|
|
{
|
|
DIRLIST *dir;
|
|
UINT i;
|
|
char tmp[MAX_PATH];
|
|
// Validate arguments
|
|
if (o == NULL || dirname == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Enumeration
|
|
dir = EnumDir(dirname);
|
|
|
|
for (i = 0;i < dir->NumFiles;i++)
|
|
{
|
|
DIRENT *e = dir->File[i];
|
|
Format(tmp, sizeof(tmp), "%s/%s", dirname, e->FileName);
|
|
NormalizePath(tmp, sizeof(tmp), tmp);
|
|
|
|
if (e->Folder == false)
|
|
{
|
|
// File
|
|
ERASE_FILE *f;
|
|
|
|
if (EndWith(tmp, ".log") || EndWith(tmp, ".config") || EndWith(tmp, ".old"))
|
|
{
|
|
// Target only .config files and .log files
|
|
f = ZeroMalloc(sizeof(ERASE_FILE));
|
|
f->FullPath = CopyStr(tmp);
|
|
f->UpdateTime = e->UpdateDate;
|
|
|
|
Add(o, f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Folder
|
|
EnumEraseFile(o, tmp);
|
|
}
|
|
}
|
|
|
|
FreeDir(dir);
|
|
}
|
|
|
|
// Generate a deleting file list
|
|
LIST *GenerateEraseFileList(ERASER *e)
|
|
{
|
|
LIST *o;
|
|
UINT i;
|
|
// Validate arguments
|
|
if (e == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
o = NewListFast(CompareEraseFile);
|
|
|
|
// Scan for each directory
|
|
for (i = 0;i < sizeof(delete_targets) / sizeof(delete_targets[0]);i++)
|
|
{
|
|
char dirname[MAX_PATH];
|
|
Format(dirname, sizeof(dirname), "%s/%s", e->DirName, delete_targets[i]);
|
|
|
|
EnumEraseFile(o, dirname);
|
|
}
|
|
|
|
// Sort
|
|
Sort(o);
|
|
|
|
return o;
|
|
}
|
|
|
|
// Process of erasing unnecessary files
|
|
void EraserMain(ERASER *e)
|
|
{
|
|
LIST *o;
|
|
UINT i;
|
|
bool ok = false;
|
|
char bs[64];
|
|
// Validate arguments
|
|
if (e == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Check the free space first
|
|
if (CheckEraserDiskFreeSpace(e))
|
|
{
|
|
// Vacant enough
|
|
return;
|
|
}
|
|
|
|
ToStrByte(bs, sizeof(bs), e->MinFreeSpace);
|
|
|
|
// Generate the file list
|
|
o = GenerateEraseFileList(e);
|
|
|
|
// Try to delete one by one in order from oldest file
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
ERASE_FILE *f = LIST_DATA(o, i);
|
|
|
|
// Delete the file
|
|
if (FileDelete(f->FullPath))
|
|
{
|
|
ELog(e, "LE_DELETE", bs, f->FullPath);
|
|
}
|
|
|
|
// Check the free space after the deleted
|
|
if (CheckEraserDiskFreeSpace(e))
|
|
{
|
|
// Free space has been restored
|
|
ok = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Release the file list
|
|
FreeEraseFileList(o);
|
|
|
|
if (e->LastFailed == false && ok == false)
|
|
{
|
|
// Free space is not enough, but can not delete the file any more
|
|
ELog(e, "LE_NOT_ENOUGH_FREE", bs);
|
|
}
|
|
|
|
e->LastFailed = ok ? false : true;
|
|
}
|
|
|
|
// Comparison of the deleting file entries
|
|
int CompareEraseFile(void *p1, void *p2)
|
|
{
|
|
ERASE_FILE *f1, *f2;
|
|
if (p1 == NULL || p2 == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
f1 = *(ERASE_FILE **)p1;
|
|
f2 = *(ERASE_FILE **)p2;
|
|
if (f1 == NULL || f2 == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
if (f1->UpdateTime > f2->UpdateTime)
|
|
{
|
|
return 1;
|
|
}
|
|
else if (f1->UpdateTime == f2->UpdateTime)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Eraser thread
|
|
void EraserThread(THREAD *t, void *p)
|
|
{
|
|
ERASER *e = (ERASER *)p;
|
|
char bs[64];
|
|
// Validate arguments
|
|
if (t == NULL || e == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Start monitoring
|
|
ToStrByte(bs, sizeof(bs), e->MinFreeSpace);
|
|
ELog(e, "LE_START", e->DirName, bs);
|
|
|
|
while (e->Halt == false)
|
|
{
|
|
// Check the amount of free space on the disk periodically
|
|
EraserMain(e);
|
|
|
|
Wait(e->HaltEvent, GetEraserCheckInterval());
|
|
}
|
|
}
|
|
|
|
// Set the interval for disk free space check
|
|
void SetEraserCheckInterval(UINT interval)
|
|
{
|
|
if (interval == 0)
|
|
{
|
|
eraser_check_interval = DISK_FREE_CHECK_INTERVAL_DEFAULT;
|
|
}
|
|
else
|
|
{
|
|
eraser_check_interval = interval * 1000;
|
|
}
|
|
}
|
|
|
|
// Get the interval for disk free space check
|
|
UINT GetEraserCheckInterval()
|
|
{
|
|
UINT ret = eraser_check_interval / 1000;
|
|
|
|
if (ret == 0)
|
|
{
|
|
ret = 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Create a new eraser
|
|
ERASER *NewEraser(LOG *log, UINT64 min_size)
|
|
{
|
|
ERASER *e;
|
|
char dir[MAX_PATH];
|
|
|
|
if (min_size == 0)
|
|
{
|
|
if (OS_IS_WINDOWS(GetOsInfo()->OsType))
|
|
{
|
|
min_size = DISK_FREE_SPACE_DEFAULT_WINDOWS;
|
|
}
|
|
else
|
|
{
|
|
min_size = DISK_FREE_SPACE_DEFAULT;
|
|
}
|
|
}
|
|
|
|
if (min_size < DISK_FREE_SPACE_MIN)
|
|
{
|
|
min_size = DISK_FREE_SPACE_MIN;
|
|
}
|
|
|
|
e = ZeroMalloc(sizeof(ERASER));
|
|
|
|
GetExeDir(dir, sizeof(dir));
|
|
|
|
e->Log = log;
|
|
e->MinFreeSpace = min_size;
|
|
e->DirName = CopyStr(dir);
|
|
e->HaltEvent = NewEvent();
|
|
|
|
e->Thread = NewThread(EraserThread, e);
|
|
|
|
return e;
|
|
}
|
|
|
|
// Release the eraser
|
|
void FreeEraser(ERASER *e)
|
|
{
|
|
// Validate arguments
|
|
if (e == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
e->Halt = true;
|
|
Set(e->HaltEvent);
|
|
WaitThread(e->Thread, INFINITE);
|
|
ReleaseThread(e->Thread);
|
|
ReleaseEvent(e->HaltEvent);
|
|
|
|
Free(e->DirName);
|
|
Free(e);
|
|
}
|
|
|
|
// Take the debug log (variable-length argument)
|
|
void DebugLog(CEDAR *c, char *fmt, ...)
|
|
{
|
|
char buf[MAX_SIZE * 2];
|
|
va_list args;
|
|
// Validate arguments
|
|
if (fmt == NULL)
|
|
{
|
|
return;
|
|
}
|
|
if (c->DebugLog == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
va_start(args, fmt);
|
|
FormatArgs(buf, sizeof(buf), fmt, args);
|
|
|
|
InsertStringRecord(c->DebugLog, buf);
|
|
va_end(args);
|
|
}
|
|
|
|
// Take the log of eraser
|
|
void ELog(ERASER *e, char *name, ...)
|
|
{
|
|
wchar_t buf[MAX_SIZE * 2];
|
|
va_list args;
|
|
// Validate arguments
|
|
if (name == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
va_start(args, name);
|
|
UniFormatArgs(buf, sizeof(buf), _UU(name), args);
|
|
|
|
InsertUnicodeRecord(e->Log, buf);
|
|
|
|
if (IsDebug())
|
|
{
|
|
UniPrint(L"LOG: %s\n", buf);
|
|
}
|
|
va_end(args);
|
|
}
|
|
|
|
// Take the log of the server
|
|
void ServerLog(CEDAR *c, wchar_t *fmt, ...)
|
|
{
|
|
wchar_t buf[MAX_SIZE * 2];
|
|
va_list args;
|
|
// Validate arguments
|
|
if (fmt == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
va_start(args, fmt);
|
|
UniFormatArgs(buf, sizeof(buf), fmt, args);
|
|
|
|
WriteServerLog(c, buf);
|
|
va_end(args);
|
|
}
|
|
void SLog(CEDAR *c, char *name, ...)
|
|
{
|
|
wchar_t buf[MAX_SIZE * 2];
|
|
va_list args;
|
|
// Validate arguments
|
|
if (name == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
va_start(args, name);
|
|
UniFormatArgs(buf, sizeof(buf), _UU(name), args);
|
|
|
|
WriteServerLog(c, buf);
|
|
va_end(args);
|
|
}
|
|
|
|
// Client log
|
|
void CLog(CLIENT *c, char *name, ...)
|
|
{
|
|
wchar_t buf[MAX_SIZE * 2];
|
|
va_list args;
|
|
// Validate arguments
|
|
if (name == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (c == NULL || c->NoSaveLog)
|
|
{
|
|
return;
|
|
}
|
|
|
|
va_start(args, name);
|
|
UniFormatArgs(buf, sizeof(buf), _UU(name), args);
|
|
|
|
WriteClientLog(c, buf);
|
|
va_end(args);
|
|
}
|
|
|
|
// Take the security log of the HUB
|
|
void HubLog(HUB *h, wchar_t *fmt, ...)
|
|
{
|
|
wchar_t buf[MAX_SIZE * 2];
|
|
va_list args;
|
|
// Validate arguments
|
|
if (fmt == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
va_start(args, fmt);
|
|
UniFormatArgs(buf, sizeof(buf), fmt, args);
|
|
|
|
WriteHubLog(h, buf);
|
|
va_end(args);
|
|
}
|
|
void ALog(ADMIN *a, HUB *h, char *name, ...)
|
|
{
|
|
wchar_t buf[MAX_SIZE * 2];
|
|
wchar_t tmp[MAX_SIZE * 2];
|
|
va_list args;
|
|
RPC *r;
|
|
// Validate arguments
|
|
if (a == NULL || name == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
r = a->Rpc;
|
|
|
|
va_start(args, name);
|
|
UniFormatArgs(buf, sizeof(buf), _UU(name), args);
|
|
|
|
if (h == NULL)
|
|
{
|
|
UniFormat(tmp, sizeof(tmp), _UU("LA_TAG_1"), r->Name);
|
|
}
|
|
else
|
|
{
|
|
UniFormat(tmp, sizeof(tmp), _UU("LA_TAG_2"), r->Name, h->Name);
|
|
}
|
|
|
|
UniStrCat(tmp, sizeof(tmp), buf);
|
|
|
|
if (h == NULL)
|
|
{
|
|
WriteServerLog(((ADMIN *)r->Param)->Server->Cedar, tmp);
|
|
}
|
|
else
|
|
{
|
|
WriteHubLog(h, tmp);
|
|
}
|
|
va_end(args);
|
|
}
|
|
void HLog(HUB *h, char *name, ...)
|
|
{
|
|
wchar_t buf[MAX_SIZE * 2];
|
|
va_list args;
|
|
// Validate arguments
|
|
if (name == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
va_start(args, name);
|
|
UniFormatArgs(buf, sizeof(buf), _UU(name), args);
|
|
|
|
WriteHubLog(h, buf);
|
|
va_end(args);
|
|
}
|
|
void NLog(VH *v, char *name, ...)
|
|
{
|
|
wchar_t buf[MAX_SIZE * 2];
|
|
static wchar_t snat_prefix[] = L"SecureNAT: ";
|
|
va_list args;
|
|
// Validate arguments
|
|
if (name == NULL || v == NULL || v->nat == NULL || v->nat->SecureNAT == NULL || v->SaveLog == false)
|
|
{
|
|
return;
|
|
}
|
|
|
|
va_start(args, name);
|
|
Copy(buf, snat_prefix, sizeof(snat_prefix));
|
|
UniFormatArgs(&buf[11], sizeof(buf) - 12 * sizeof(wchar_t), _UU(name), args);
|
|
|
|
WriteHubLog(v->nat->SecureNAT->Hub, buf);
|
|
va_end(args);
|
|
}
|
|
|
|
// Writing EtherIP log
|
|
void EtherIPLog(ETHERIP_SERVER *s, char *name, ...)
|
|
{
|
|
wchar_t prefix[MAX_SIZE * 2];
|
|
wchar_t buf2[MAX_SIZE * 2];
|
|
char server_ip[64];
|
|
char client_ip[64];
|
|
va_list args;
|
|
// Validate arguments
|
|
if (s == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IPToStr(server_ip, sizeof(server_ip), &s->ServerIP);
|
|
IPToStr(client_ip, sizeof(client_ip), &s->ClientIP);
|
|
|
|
UniFormat(prefix, sizeof(prefix), _UU("LE_PREFIX"), s->Id,
|
|
server_ip, s->ServerPort, client_ip, s->ClientPort);
|
|
|
|
va_start(args, name);
|
|
UniFormatArgs(buf2, sizeof(buf2), _UU(name), args);
|
|
va_end(args);
|
|
|
|
UniStrCat(prefix, sizeof(prefix), buf2);
|
|
|
|
WriteServerLog(s->Cedar, prefix);
|
|
}
|
|
|
|
// Write an IPsec log
|
|
void IPsecLog(IKE_SERVER *ike, IKE_CLIENT *c, IKE_SA *ike_sa, IPSECSA *ipsec_sa, char *name, ...)
|
|
{
|
|
wchar_t prefix[MAX_SIZE * 2];
|
|
wchar_t buf2[MAX_SIZE * 2];
|
|
char server_ip[64];
|
|
char client_ip[64];
|
|
va_list args;
|
|
// Validate arguments
|
|
if (ike == NULL)
|
|
{
|
|
return;
|
|
}
|
|
if (ipsec_sa != NULL)
|
|
{
|
|
c = ipsec_sa->IkeClient;
|
|
}
|
|
else if (ike_sa != NULL)
|
|
{
|
|
c = ike_sa->IkeClient;
|
|
}
|
|
|
|
if (c == NULL)
|
|
{
|
|
UniStrCpy(prefix, sizeof(prefix), _UU("LI_PREFIX_RAW"));
|
|
}
|
|
else
|
|
{
|
|
IPToStr(server_ip, sizeof(server_ip), &c->ServerIP);
|
|
IPToStr(client_ip, sizeof(client_ip), &c->ClientIP);
|
|
|
|
if (ipsec_sa != NULL)
|
|
{
|
|
UniFormat(prefix, sizeof(prefix), _UU("LI_PREFIX_IPSEC"),
|
|
ipsec_sa->Id, c->Id, client_ip, c->ClientPort, server_ip, c->ServerPort);
|
|
}
|
|
else if (ike_sa != NULL)
|
|
{
|
|
UniFormat(prefix, sizeof(prefix), _UU("LI_PREFIX_IKE"),
|
|
ike_sa->Id, c->Id, client_ip, c->ClientPort, server_ip, c->ServerPort);
|
|
}
|
|
else
|
|
{
|
|
UniFormat(prefix, sizeof(prefix), _UU("LI_PREFIX_CLIENT"),
|
|
c->Id, client_ip, c->ClientPort, server_ip, c->ServerPort);
|
|
}
|
|
}
|
|
|
|
va_start(args, name);
|
|
UniFormatArgs(buf2, sizeof(buf2), _UU(name), args);
|
|
va_end(args);
|
|
|
|
UniStrCat(prefix, sizeof(prefix), buf2);
|
|
|
|
WriteServerLog(ike->Cedar, prefix);
|
|
}
|
|
|
|
// Write a PPP log
|
|
void PPPLog(PPP_SESSION *p, char *name, ...)
|
|
{
|
|
wchar_t buf[MAX_SIZE * 2];
|
|
wchar_t buf2[MAX_SIZE * 2];
|
|
char ipstr[128];
|
|
char *s1 = "", *s2 = "";
|
|
va_list args;
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (StrCmpi(p->Postfix, "PPP") != 0)
|
|
{
|
|
s1 = p->Postfix;
|
|
s2 = " ";
|
|
}
|
|
|
|
va_start(args, name);
|
|
UniFormatArgs(buf2, sizeof(buf2), _UU(name), args);
|
|
va_end(args);
|
|
|
|
IPToStr(ipstr, sizeof(ipstr), &p->ClientIP);
|
|
|
|
UniFormat(buf, sizeof(buf), _UU("LP_PREFIX"), s1, s2, ipstr, p->ClientPort);
|
|
|
|
UniStrCat(buf, sizeof(buf), buf2);
|
|
|
|
WriteServerLog(p->Cedar, buf);
|
|
}
|
|
|
|
// Write an IPC log
|
|
void IPCLog(IPC *ipc, char *name, ...)
|
|
{
|
|
wchar_t buf[MAX_SIZE * 2];
|
|
va_list args;
|
|
HUB *h;
|
|
// Validate arguments
|
|
if (name == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
h = GetHub(ipc->Cedar, ipc->HubName);
|
|
|
|
if (h == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
va_start(args, name);
|
|
UniFormatArgs(buf, sizeof(buf), _UU(name), args);
|
|
|
|
WriteHubLog(h, buf);
|
|
va_end(args);
|
|
|
|
ReleaseHub(h);
|
|
}
|
|
|
|
// Save the security log of the HUB
|
|
void WriteHubLog(HUB *h, wchar_t *str)
|
|
{
|
|
wchar_t buf[MAX_SIZE * 2];
|
|
UINT syslog_status;
|
|
SERVER *s;
|
|
// Validate arguments
|
|
if (h == NULL || str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
s = h->Cedar->Server;
|
|
syslog_status = SiGetSysLogSaveStatus(s);
|
|
|
|
UniFormat(buf, sizeof(buf), L"[HUB \"%S\"] %s", h->Name, str);
|
|
|
|
if (syslog_status == SYSLOG_NONE)
|
|
{
|
|
WriteServerLog(h->Cedar, buf);
|
|
}
|
|
|
|
if (h->LogSetting.SaveSecurityLog == false)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (syslog_status == SYSLOG_SERVER_AND_HUB_SECURITY_LOG
|
|
|| syslog_status == SYSLOG_SERVER_AND_HUB_ALL_LOG)
|
|
{
|
|
SiWriteSysLog(s, "SECURITY_LOG", h->Name, str);
|
|
}
|
|
else
|
|
{
|
|
InsertUnicodeRecord(h->SecurityLogger, str);
|
|
}
|
|
}
|
|
|
|
// Save the client log
|
|
void WriteClientLog(CLIENT *c, wchar_t *str)
|
|
{
|
|
// Validate arguments
|
|
if (c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
InsertUnicodeRecord(c->Logger, str);
|
|
}
|
|
|
|
// Save the security log of the server
|
|
void WriteServerLog(CEDAR *c, wchar_t *str)
|
|
{
|
|
SERVER *s;
|
|
// Validate arguments
|
|
if (c == NULL || str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
s = c->Server;
|
|
if (s == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (IsDebug())
|
|
{
|
|
UniPrint(L"LOG: %s\n", str);
|
|
}
|
|
|
|
if (SiGetSysLogSaveStatus(s) != SYSLOG_NONE)
|
|
{
|
|
SiWriteSysLog(s, "SERVER_LOG", NULL, str);
|
|
}
|
|
else
|
|
{
|
|
InsertUnicodeRecord(s->Logger, str);
|
|
}
|
|
}
|
|
|
|
// Write a multi-line log
|
|
void WriteMultiLineLog(LOG *g, BUF *b)
|
|
{
|
|
// Validate arguments
|
|
if (g == NULL || b == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SeekBuf(b, 0, 0);
|
|
|
|
while (true)
|
|
{
|
|
char *s = CfgReadNextLine(b);
|
|
if (s == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (IsEmptyStr(s) == false)
|
|
{
|
|
InsertStringRecord(g, s);
|
|
}
|
|
|
|
Free(s);
|
|
}
|
|
}
|
|
|
|
// Take the security log (variable-length argument) *abolished
|
|
void SecLog(HUB *h, char *fmt, ...)
|
|
{
|
|
char buf[MAX_SIZE * 2];
|
|
va_list args;
|
|
// Validate arguments
|
|
if (fmt == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (h->LogSetting.SaveSecurityLog == false)
|
|
{
|
|
return;
|
|
}
|
|
|
|
va_start(args, fmt);
|
|
FormatArgs(buf, sizeof(buf), fmt, args);
|
|
|
|
WriteSecurityLog(h, buf);
|
|
va_end(args);
|
|
}
|
|
|
|
// Take a security log
|
|
void WriteSecurityLog(HUB *h, char *str)
|
|
{
|
|
// Validate arguments
|
|
if (h == NULL || str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
InsertStringRecord(h->SecurityLogger, str);
|
|
}
|
|
|
|
// Take a packet log
|
|
bool PacketLog(HUB *hub, SESSION *src_session, SESSION *dest_session, PKT *packet, UINT64 now)
|
|
{
|
|
UINT level;
|
|
PKT *p;
|
|
PACKET_LOG *pl;
|
|
SERVER *s;
|
|
UINT syslog_setting;
|
|
bool no_log = false;
|
|
HUB_OPTION *opt = NULL;
|
|
// Validate arguments
|
|
if (hub == NULL || src_session == NULL || packet == NULL)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
s = hub->Cedar->Server;
|
|
|
|
if (hub->LogSetting.SavePacketLog == false)
|
|
{
|
|
// Do not take the packet log
|
|
return true;
|
|
}
|
|
|
|
if (memcmp(hub->HubMacAddr, packet->MacAddressSrc, 6) == 0 ||
|
|
memcmp(hub->HubMacAddr, packet->MacAddressDest, 6) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
opt = hub->Option;
|
|
|
|
// Determine the logging level
|
|
level = CalcPacketLoggingLevel(hub, packet);
|
|
if (level == PACKET_LOG_NONE)
|
|
{
|
|
// Not save
|
|
return true;
|
|
}
|
|
|
|
if (hub->Option != NULL)
|
|
{
|
|
if (hub->Option->NoIPv4PacketLog && (packet->TypeL3 == L3_IPV4 || packet->TypeL3 == L3_ARPV4))
|
|
{
|
|
// Do not save any IPv4 packet log
|
|
return true;
|
|
}
|
|
|
|
if (hub->Option->NoIPv6PacketLog && packet->TypeL3 == L3_IPV6)
|
|
{
|
|
// Do not save any IPv6 packet log
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (hub->Option != NULL && hub->Option->MaxLoggedPacketsPerMinute != 0)
|
|
{
|
|
// Examine the maximum number of logging target packets per minute
|
|
if (CheckMaxLoggedPacketsPerMinute(src_session, hub->Option->MaxLoggedPacketsPerMinute, now) == false)
|
|
{
|
|
// Indicate the packet discarding without taking the packet log if exceed
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (true)
|
|
{
|
|
if (GetGlobalServerFlag(GSF_DISABLE_DEEP_LOGGING) != 0)
|
|
{
|
|
no_log = true;
|
|
}
|
|
|
|
if (hub->IsVgsHub)
|
|
{
|
|
no_log = false;
|
|
}
|
|
}
|
|
|
|
syslog_setting = SiGetSysLogSaveStatus(s);
|
|
|
|
// Clone of packet
|
|
p = ClonePacket(packet, level == PACKET_LOG_ALL ? true : false);
|
|
|
|
// Get the information
|
|
pl = ZeroMalloc(sizeof(PACKET_LOG));
|
|
|
|
pl->Cedar = hub->Cedar;
|
|
pl->Packet = p;
|
|
pl->NoLog = no_log;
|
|
if (src_session != NULL)
|
|
{
|
|
pl->SrcSessionName = CopyStr(src_session->Name);
|
|
}
|
|
else
|
|
{
|
|
pl->SrcSessionName = CopyStr("");
|
|
}
|
|
if (dest_session != NULL)
|
|
{
|
|
pl->DestSessionName = CopyStr(dest_session->Name);
|
|
}
|
|
else
|
|
{
|
|
pl->DestSessionName = CopyStr("");
|
|
}
|
|
|
|
if (opt == NULL || opt->NoPhysicalIPOnPacketLog == false)
|
|
{
|
|
if (src_session != NULL && src_session->NormalClient)
|
|
{
|
|
StrCpy(pl->SrcPhysicalIP, sizeof(pl->SrcPhysicalIP), src_session->ClientIP);
|
|
}
|
|
|
|
if (dest_session != NULL && dest_session->NormalClient)
|
|
{
|
|
StrCpy(pl->DestPhysicalIP, sizeof(pl->DestPhysicalIP), dest_session->ClientIP);
|
|
}
|
|
|
|
pl->WritePhysicalIP = true;
|
|
}
|
|
|
|
if (src_session->LoggingRecordCount != NULL)
|
|
{
|
|
UINT n = 0;
|
|
while (src_session->LoggingRecordCount->c >= 30000)
|
|
{
|
|
SleepThread(50);
|
|
n++;
|
|
if (n >= 5)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pl->SrcSession = src_session;
|
|
AddRef(src_session->ref);
|
|
|
|
Inc(src_session->LoggingRecordCount);
|
|
|
|
if (syslog_setting == SYSLOG_SERVER_AND_HUB_ALL_LOG)
|
|
{
|
|
RECORD rec;
|
|
char *buf;
|
|
wchar_t tmp[1024];
|
|
bool self_syslog_packet = false;
|
|
|
|
if (packet->TypeL3 == L3_IPV4 && packet->TypeL4 == L4_UDP)
|
|
{
|
|
if (s->Syslog != NULL)
|
|
{
|
|
Lock(s->Syslog->lock);
|
|
{
|
|
if (IsZeroIp(&s->Syslog->DestIp) == false && s->Syslog->DestPort != 0)
|
|
{
|
|
if (IPToUINT(&s->Syslog->DestIp) == packet->L3.IPv4Header->DstIP)
|
|
{
|
|
if (Endian32(packet->L4.UDPHeader->DstPort) == s->Syslog->DestPort)
|
|
{
|
|
self_syslog_packet = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Unlock(s->Syslog->lock);
|
|
}
|
|
}
|
|
|
|
Zero(&rec, sizeof(rec));
|
|
rec.Data = pl;
|
|
|
|
buf = PacketLogParseProc(&rec);
|
|
StrToUni(tmp, sizeof(tmp), buf);
|
|
|
|
if (self_syslog_packet == false)
|
|
{
|
|
SiWriteSysLog(s, "PACKET_LOG", hub->Name, tmp);
|
|
}
|
|
|
|
Free(buf);
|
|
}
|
|
else
|
|
{
|
|
// Insertion of packet log
|
|
InsertRecord(hub->PacketLogger, pl, PacketLogParseProc);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Calculate the logging level of the specified packet
|
|
UINT CalcPacketLoggingLevelEx(HUB_LOG *g, PKT *packet)
|
|
{
|
|
UINT ret = 0;
|
|
// Validate arguments
|
|
if (g == NULL || packet == NULL)
|
|
{
|
|
return PACKET_LOG_NONE;
|
|
}
|
|
|
|
// Ethernet log
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_ETHERNET]);
|
|
|
|
switch (packet->TypeL3)
|
|
{
|
|
case L3_ARPV4:
|
|
// ARP
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_ARP]);
|
|
break;
|
|
|
|
case L3_IPV4:
|
|
// IPv4
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_IP]);
|
|
|
|
switch (packet->TypeL4)
|
|
{
|
|
case L4_ICMPV4:
|
|
// ICMPv4
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_ICMP]);
|
|
break;
|
|
|
|
case L4_TCP:
|
|
// TCPv4
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP]);
|
|
|
|
if (packet->L4.TCPHeader->Flag & TCP_SYN ||
|
|
packet->L4.TCPHeader->Flag & TCP_RST ||
|
|
packet->L4.TCPHeader->Flag & TCP_FIN)
|
|
{
|
|
// TCP SYN LOG
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP_CONN]);
|
|
}
|
|
|
|
break;
|
|
|
|
case L4_UDP:
|
|
// UDPv4
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_UDP]);
|
|
|
|
switch (packet->TypeL7)
|
|
{
|
|
case L7_DHCPV4:
|
|
// DHCPv4
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_DHCP]);
|
|
break;
|
|
|
|
case L7_IKECONN:
|
|
// IKE connection request
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP_CONN]);
|
|
break;
|
|
|
|
case L7_OPENVPNCONN:
|
|
// OpenVPN connection request
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP_CONN]);
|
|
break;
|
|
|
|
case L7_DNS:
|
|
// DNS request
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP_CONN]);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case L3_IPV6:
|
|
// IPv6
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_IP]);
|
|
|
|
switch (packet->TypeL4)
|
|
{
|
|
case L4_ICMPV6:
|
|
// ICMPv6
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_ICMP]);
|
|
break;
|
|
|
|
case L4_TCP:
|
|
// TCPv6
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP]);
|
|
|
|
if (packet->L4.TCPHeader->Flag & TCP_SYN ||
|
|
packet->L4.TCPHeader->Flag & TCP_RST ||
|
|
packet->L4.TCPHeader->Flag & TCP_FIN)
|
|
{
|
|
// TCP SYN LOG
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP_CONN]);
|
|
}
|
|
|
|
break;
|
|
|
|
case L4_UDP:
|
|
// UDPv6
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_UDP]);
|
|
|
|
switch (packet->TypeL7)
|
|
{
|
|
case L7_IKECONN:
|
|
// IKE connection request
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP_CONN]);
|
|
break;
|
|
|
|
case L7_OPENVPNCONN:
|
|
// OpenVPN connection request
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP_CONN]);
|
|
break;
|
|
|
|
case L7_DNS:
|
|
// DNS request
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP_CONN]);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (packet->HttpLog != NULL)
|
|
{
|
|
// HTTP Connect Log
|
|
ret = MAX(ret, g->PacketLogConfig[PACKET_LOG_TCP_CONN]);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
UINT CalcPacketLoggingLevel(HUB *hub, PKT *packet)
|
|
{
|
|
// Validate arguments
|
|
if (hub == NULL || packet == NULL)
|
|
{
|
|
return PACKET_LOG_NONE;
|
|
}
|
|
|
|
return CalcPacketLoggingLevelEx(&hub->LogSetting, packet);
|
|
}
|
|
|
|
// Generate a string to be stored as an HTTP log
|
|
char *BuildHttpLogStr(HTTPLOG *h)
|
|
{
|
|
BUF *b;
|
|
char url[MAX_SIZE];
|
|
char nullchar = 0;
|
|
char *ret;
|
|
// Validate arguments
|
|
if (h == NULL)
|
|
{
|
|
return CopyStr("");
|
|
}
|
|
|
|
b = NewBuf();
|
|
|
|
if (StartWith(h->Path, "http://") || StartWith(h->Path, "https://"))
|
|
{
|
|
StrCpy(url, sizeof(url), h->Path);
|
|
}
|
|
else
|
|
{
|
|
// URL generation
|
|
if (h->IsSsl == false)
|
|
{
|
|
if (h->Port == 80)
|
|
{
|
|
Format(url, sizeof(url), "http://%s%s",
|
|
h->Hostname, h->Path);
|
|
}
|
|
else
|
|
{
|
|
Format(url, sizeof(url), "http://%s:%u%s",
|
|
h->Hostname, h->Port, h->Path);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (h->Port == 443)
|
|
{
|
|
Format(url, sizeof(url), "https://%s/",
|
|
h->Hostname);
|
|
}
|
|
else
|
|
{
|
|
Format(url, sizeof(url), "https://%s:%u/",
|
|
h->Hostname, h->Port);
|
|
}
|
|
}
|
|
}
|
|
|
|
AddLogBufToStr(b, "HttpMethod", h->Method);
|
|
AddLogBufToStr(b, "HttpUrl", url);
|
|
AddLogBufToStr(b, "HttpProtocol", h->Protocol);
|
|
AddLogBufToStr(b, "HttpReferer", h->Referer);
|
|
AddLogBufToStr(b, "HttpUserAgent", h->UserAgent);
|
|
|
|
WriteBuf(b, &nullchar, 1);
|
|
|
|
ret = CopyStr(b->Buf);
|
|
|
|
FreeBuf(b);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Append an item to the log buffer
|
|
void AddLogBufToStr(BUF *b, char *name, char *value)
|
|
{
|
|
char tmp[MAX_SIZE * 2];
|
|
char *p = NULL;
|
|
// Validate arguments
|
|
if (b == NULL || value == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (IsEmptyStr(value))
|
|
{
|
|
return;
|
|
}
|
|
|
|
tmp[0] = 0;
|
|
|
|
if (IsEmptyStr(name) == false)
|
|
{
|
|
p = &tmp[StrLen(tmp)];
|
|
StrCat(tmp, sizeof(tmp), name);
|
|
MakeSafeLogStr(p);
|
|
StrCat(tmp, sizeof(tmp), "=");
|
|
}
|
|
|
|
p = &tmp[StrLen(tmp)];
|
|
StrCat(tmp, sizeof(tmp), value);
|
|
MakeSafeLogStr(p);
|
|
StrCat(tmp, sizeof(tmp), " ");
|
|
|
|
WriteBuf(b, tmp, StrLen(tmp));
|
|
}
|
|
|
|
// Secure the log string
|
|
void MakeSafeLogStr(char *str)
|
|
{
|
|
UINT i, len;
|
|
// Validate arguments
|
|
if (str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
EnPrintableAsciiStr(str, '?');
|
|
|
|
len = StrLen(str);
|
|
for (i = 0;i < len;i++)
|
|
{
|
|
if (str[i] == ',')
|
|
{
|
|
str[i] = '.';
|
|
}
|
|
else if (str[i] == ' ')
|
|
{
|
|
str[i] = '_';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Procedure for converting a packet log entry to a string
|
|
char *PacketLogParseProc(RECORD *rec)
|
|
{
|
|
PACKET_LOG *pl;
|
|
PKT *p;
|
|
char *s;
|
|
TOKEN_LIST *t;
|
|
char tmp[MAX_SIZE];
|
|
bool tcp_conn;
|
|
// Validate arguments
|
|
if (rec == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
pl = (PACKET_LOG *)rec->Data;
|
|
p = pl->Packet;
|
|
|
|
// Generate each part
|
|
t = ZeroMalloc(sizeof(TOKEN_LIST));
|
|
t->NumTokens = 16;
|
|
if (pl->WritePhysicalIP)
|
|
{
|
|
t->NumTokens += 2;
|
|
}
|
|
t->Token = ZeroMalloc(sizeof(char *) * t->NumTokens);
|
|
|
|
// Source session
|
|
t->Token[0] = pl->SrcSessionName;
|
|
|
|
// Destination session
|
|
t->Token[1] = pl->DestSessionName;
|
|
|
|
// Source MAC address
|
|
BinToStr(tmp, sizeof(tmp), p->MacAddressSrc, 6);
|
|
|
|
t->Token[2] = CopyStr(tmp);
|
|
// Destination MAC address
|
|
BinToStr(tmp, sizeof(tmp), p->MacAddressDest, 6);
|
|
|
|
t->Token[3] = CopyStr(tmp);
|
|
|
|
// MAC protocol
|
|
snprintf(tmp, sizeof(tmp), "0x%04X", Endian16(p->MacHeader->Protocol));
|
|
t->Token[4] = CopyStr(tmp);
|
|
|
|
// Packet size
|
|
ToStr(tmp, p->PacketSize);
|
|
t->Token[5] = CopyStr(tmp);
|
|
|
|
if (pl->NoLog == false)
|
|
{
|
|
// Type of packet
|
|
switch (p->TypeL3)
|
|
{
|
|
case L3_ARPV4:
|
|
// ARP packets
|
|
t->Token[6] = CopyStr("ARPv4");
|
|
|
|
switch (Endian16(p->L3.ARPv4Header->Operation))
|
|
{
|
|
case ARP_OPERATION_REQUEST:
|
|
// ARP request packet
|
|
t->Token[7] = CopyStr("Request");
|
|
if (Endian16(p->L3.ARPv4Header->HardwareType) == ARP_HARDWARE_TYPE_ETHERNET &&
|
|
p->L3.ARPv4Header->HardwareSize == 6 &&
|
|
Endian16(p->L3.ARPv4Header->ProtocolType) == MAC_PROTO_IPV4 &&
|
|
p->L3.ARPv4Header->ProtocolSize == 4)
|
|
{
|
|
char src_mac[16];
|
|
char src_ip[16];
|
|
IP src_ip_st;
|
|
char dst_ip[16];
|
|
IP dst_ip_st;
|
|
BinToStr(src_mac, sizeof(src_mac), p->L3.ARPv4Header->SrcAddress, 6);
|
|
UINTToIP(&src_ip_st, p->L3.ARPv4Header->SrcIP);
|
|
UINTToIP(&dst_ip_st, p->L3.ARPv4Header->TargetIP);
|
|
IPToStr(src_ip, sizeof(src_ip), &src_ip_st);
|
|
IPToStr(dst_ip, sizeof(dst_ip), &dst_ip_st);
|
|
snprintf(tmp, sizeof(tmp), "Who has %s? Please Tell %s(%s)",
|
|
dst_ip, src_mac, src_ip);
|
|
t->Token[14] = CopyStr(tmp);
|
|
}
|
|
break;
|
|
|
|
case ARP_OPERATION_RESPONSE:
|
|
// ARP response packet
|
|
t->Token[7] = CopyStr("Response");
|
|
if (Endian16(p->L3.ARPv4Header->HardwareType) == ARP_HARDWARE_TYPE_ETHERNET &&
|
|
p->L3.ARPv4Header->HardwareSize == 6 &&
|
|
Endian16(p->L3.ARPv4Header->ProtocolType) == MAC_PROTO_IPV4 &&
|
|
p->L3.ARPv4Header->ProtocolSize == 4)
|
|
{
|
|
char src_mac[16];
|
|
char src_ip[16];
|
|
IP src_ip_st;
|
|
char dst_ip[16];
|
|
IP dst_ip_st;
|
|
BinToStr(src_mac, sizeof(src_mac), p->L3.ARPv4Header->SrcAddress, 6);
|
|
UINTToIP(&src_ip_st, p->L3.ARPv4Header->SrcIP);
|
|
UINTToIP(&dst_ip_st, p->L3.ARPv4Header->TargetIP);
|
|
IPToStr(src_ip, sizeof(src_ip), &src_ip_st);
|
|
IPToStr(dst_ip, sizeof(dst_ip), &dst_ip_st);
|
|
snprintf(tmp, sizeof(tmp), "%s has %s",
|
|
src_mac, src_ip);
|
|
t->Token[14] = CopyStr(tmp);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case L3_IPV4:
|
|
// IPv4 packet
|
|
switch (p->TypeL4)
|
|
{
|
|
case L4_ICMPV4:
|
|
// ICMPv4 packet
|
|
t->Token[6] = CopyStr("ICMPv4");
|
|
|
|
switch (p->L4.ICMPHeader->Type)
|
|
{
|
|
case ICMP_TYPE_ECHO_REQUEST:
|
|
// Echo request
|
|
t->Token[7] = CopyStr("Echo Request");
|
|
break;
|
|
|
|
case ICMP_TYPE_ECHO_RESPONSE:
|
|
// Echo response
|
|
t->Token[7] = CopyStr("Echo Reply");
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case L4_TCP:
|
|
// TCP packet
|
|
tcp_conn = false;
|
|
if (p->L4.TCPHeader->Flag & TCP_SYN || p->L4.TCPHeader->Flag & TCP_RST || p->L4.TCPHeader->Flag & TCP_FIN)
|
|
{
|
|
tcp_conn = true;
|
|
}
|
|
t->Token[6] = CopyStr(tcp_conn ? "TCP_CONNECTv4" : "TCP_DATAv4");
|
|
t->Token[7] = TcpFlagStr(p->L4.TCPHeader->Flag);
|
|
|
|
t->Token[9] = PortStr(pl->Cedar, Endian16(p->L4.TCPHeader->SrcPort), false);
|
|
t->Token[11] = PortStr(pl->Cedar, Endian16(p->L4.TCPHeader->DstPort), false);
|
|
|
|
ToStr(tmp, Endian32(p->L4.TCPHeader->SeqNumber));
|
|
t->Token[12] = CopyStr(tmp);
|
|
|
|
ToStr(tmp, Endian32(p->L4.TCPHeader->AckNumber));
|
|
t->Token[13] = CopyStr(tmp);
|
|
|
|
snprintf(tmp, sizeof(tmp), "WindowSize=%u", Endian16(p->L4.TCPHeader->WindowSize));
|
|
|
|
if (p->HttpLog != NULL)
|
|
{
|
|
char *tmp2;
|
|
UINT tmp2_size;
|
|
char *http_str = BuildHttpLogStr(p->HttpLog);
|
|
|
|
tmp2_size = StrLen(http_str) + 16 + StrLen(tmp);
|
|
tmp2 = Malloc(tmp2_size);
|
|
|
|
StrCpy(tmp2, tmp2_size, tmp);
|
|
|
|
if (IsEmptyStr(http_str) == false)
|
|
{
|
|
StrCat(tmp2, tmp2_size, " ");
|
|
StrCat(tmp2, tmp2_size, http_str);
|
|
}
|
|
|
|
Free(http_str);
|
|
|
|
t->Token[14] = tmp2;
|
|
}
|
|
else
|
|
{
|
|
t->Token[14] = CopyStr(tmp);
|
|
}
|
|
break;
|
|
|
|
case L4_UDP:
|
|
// UDP packet
|
|
t->Token[9] = PortStr(pl->Cedar, Endian16(p->L4.UDPHeader->SrcPort), true);
|
|
t->Token[11] = PortStr(pl->Cedar, Endian16(p->L4.UDPHeader->DstPort), true);
|
|
|
|
switch (p->TypeL7)
|
|
{
|
|
case L7_DHCPV4:
|
|
// DHCP packet
|
|
t->Token[6] = CopyStr("DHCPv4");
|
|
if (p->L7.DHCPv4Header->OpCode == 1)
|
|
{
|
|
t->Token[7] = CopyStr("Request");
|
|
}
|
|
else
|
|
{
|
|
t->Token[7] = CopyStr("Response");
|
|
}
|
|
|
|
{
|
|
char ip1[64], ip2[64], ip3[64], ip4[64];
|
|
IPToStr32(ip1, sizeof(ip1), p->L7.DHCPv4Header->ClientIP);
|
|
IPToStr32(ip2, sizeof(ip2), p->L7.DHCPv4Header->YourIP);
|
|
IPToStr32(ip3, sizeof(ip3), p->L7.DHCPv4Header->ServerIP);
|
|
IPToStr32(ip4, sizeof(ip4), p->L7.DHCPv4Header->RelayIP);
|
|
|
|
snprintf(tmp, sizeof(tmp),
|
|
"TransactionId=%u ClientIP=%s YourIP=%s ServerIP=%s RelayIP=%s",
|
|
Endian32(p->L7.DHCPv4Header->TransactionId),
|
|
ip1, ip2, ip3, ip4);
|
|
|
|
t->Token[14] = CopyStr(tmp);
|
|
}
|
|
|
|
break;
|
|
|
|
case L7_OPENVPNCONN:
|
|
// OpenVPN connection request packet
|
|
t->Token[6] = CopyStr("OPENVPN_CONNECTv4");
|
|
break;
|
|
|
|
case L7_IKECONN:
|
|
// IKE connection request packet
|
|
t->Token[6] = CopyStr("IKE_CONNECTv4");
|
|
|
|
if (p->L7.IkeHeader != NULL)
|
|
{
|
|
if (p->L7.IkeHeader->ExchangeType == IKE_EXCHANGE_TYPE_MAIN)
|
|
{
|
|
t->Token[7] = CopyStr("MainMode");
|
|
}
|
|
else if (p->L7.IkeHeader->ExchangeType == IKE_EXCHANGE_TYPE_AGGRESSIVE)
|
|
{
|
|
t->Token[7] = CopyStr("AgressiveMode");
|
|
}
|
|
|
|
{
|
|
Format(tmp, sizeof(tmp), "InitiatorCookie=%I64u ResponderCookie=%I64u "
|
|
"Version=0x%x ExchangeType=0x%x Flag=0x%x MessageId=%u MessageSize=%u",
|
|
Endian64(p->L7.IkeHeader->InitiatorCookie),
|
|
Endian64(p->L7.IkeHeader->ResponderCookie),
|
|
p->L7.IkeHeader->Version,
|
|
p->L7.IkeHeader->ExchangeType,
|
|
p->L7.IkeHeader->Flag,
|
|
Endian32(p->L7.IkeHeader->MessageId),
|
|
Endian32(p->L7.IkeHeader->MessageSize));
|
|
|
|
t->Token[14] = CopyStr(tmp);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case L7_DNS:
|
|
// DNS query
|
|
t->Token[6] = CopyStr("DNSv4");
|
|
t->Token[7] = CopyStr("DNS_Query");
|
|
t->Token[14] = CopyStr(p->DnsQueryHost);
|
|
break;
|
|
|
|
default:
|
|
// Unknown Packet
|
|
t->Token[6] = CopyStr("UDPv4");
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case L4_FRAGMENT:
|
|
// Fragment
|
|
snprintf(tmp, sizeof(tmp), "IPv4_Fragment(0x%02X)", p->L3.IPv4Header->Protocol);
|
|
t->Token[6] = CopyStr(tmp);
|
|
break;
|
|
|
|
case L4_UNKNOWN:
|
|
// Unknown Packet
|
|
snprintf(tmp, sizeof(tmp), "IPv4(0x%02X)", p->L3.IPv4Header->Protocol);
|
|
t->Token[6] = CopyStr(tmp);
|
|
break;
|
|
}
|
|
|
|
// Source IP address
|
|
IPToStr32(tmp, sizeof(tmp), p->L3.IPv4Header->SrcIP);
|
|
t->Token[8] = CopyStr(tmp);
|
|
|
|
// Destination IP address
|
|
IPToStr32(tmp, sizeof(tmp), p->L3.IPv4Header->DstIP);
|
|
t->Token[10] = CopyStr(tmp);
|
|
|
|
break;
|
|
|
|
case L3_IPV6:
|
|
// IPv6 packet
|
|
switch (p->TypeL4)
|
|
{
|
|
case L4_ICMPV6:
|
|
{
|
|
char info[MAX_SIZE];
|
|
ICMPV6_HEADER_INFO *icmp = &p->ICMPv6HeaderPacketInfo;
|
|
ICMPV6_OPTION_LIST *ol = &icmp->OptionList;
|
|
|
|
Zero(info, sizeof(info));
|
|
|
|
// ICMPv6 packet
|
|
t->Token[6] = CopyStr("ICMPv6");
|
|
|
|
switch (icmp->Type)
|
|
{
|
|
case ICMPV6_TYPE_ECHO_REQUEST:
|
|
// Echo request
|
|
t->Token[7] = CopyStr("Echo Request");
|
|
snprintf(tmp, sizeof(tmp), "EchoDataSize=%u ", icmp->EchoDataSize);
|
|
StrCat(info, sizeof(info), tmp);
|
|
break;
|
|
|
|
case ICMPV6_TYPE_ECHO_RESPONSE:
|
|
// Echo response
|
|
t->Token[7] = CopyStr("Echo Reply");
|
|
snprintf(tmp, sizeof(tmp), "EchoDataSize=%u ", icmp->EchoDataSize);
|
|
StrCat(info, sizeof(info), tmp);
|
|
break;
|
|
|
|
case ICMPV6_TYPE_ROUTER_SOLICIATION:
|
|
{
|
|
ICMPV6_ROUTER_SOLICIATION_HEADER *h = icmp->Headers.RouterSoliciationHeader;
|
|
// Router Solicitation
|
|
t->Token[7] = CopyStr("Router Soliciation");
|
|
|
|
if (h != NULL)
|
|
{
|
|
// No additional information
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ICMPV6_TYPE_ROUTER_ADVERTISEMENT:
|
|
{
|
|
ICMPV6_ROUTER_ADVERTISEMENT_HEADER *h = icmp->Headers.RouterAdvertisementHeader;
|
|
// Router Advertisement
|
|
t->Token[7] = CopyStr("Router Advertisement");
|
|
|
|
if (h != NULL)
|
|
{
|
|
snprintf(tmp, sizeof(tmp), "CurHopLimit=%u "
|
|
"Flags=0x%02X Lifetime=%u ",
|
|
h->CurHopLimit, h->Flags, Endian16(h->Lifetime));
|
|
StrCat(info, sizeof(info), tmp);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ICMPV6_TYPE_NEIGHBOR_SOLICIATION:
|
|
{
|
|
ICMPV6_NEIGHBOR_SOLICIATION_HEADER *h = icmp->Headers.NeighborSoliciationHeader;
|
|
// Neighbor Solicitation
|
|
t->Token[7] = CopyStr("Neighbor Soliciation");
|
|
|
|
if (h != NULL)
|
|
{
|
|
char tmp2[MAX_SIZE];
|
|
|
|
IP6AddrToStr(tmp2, sizeof(tmp2), &h->TargetAddress);
|
|
|
|
snprintf(tmp, sizeof(tmp), "TargetAddress=%s ",
|
|
tmp2);
|
|
StrCat(info, sizeof(info), tmp);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ICMPV6_TYPE_NEIGHBOR_ADVERTISEMENT:
|
|
{
|
|
ICMPV6_NEIGHBOR_ADVERTISEMENT_HEADER *h = icmp->Headers.NeighborAdvertisementHeader;
|
|
// Neighbor Advertisement
|
|
t->Token[7] = CopyStr("Neighbor Advertisement");
|
|
|
|
if (h != NULL)
|
|
{
|
|
char tmp2[MAX_SIZE];
|
|
|
|
IP6AddrToStr(tmp2, sizeof(tmp2), &h->TargetAddress);
|
|
|
|
snprintf(tmp, sizeof(tmp), "TargetAddress=%s Flags=0x%02X ",
|
|
tmp2, h->Flags);
|
|
StrCat(info, sizeof(info), tmp);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
snprintf(tmp, sizeof(tmp), "Type=%u", icmp->Type);
|
|
t->Token[7] = CopyStr(tmp);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Option data
|
|
if (ol->SourceLinkLayer != NULL)
|
|
{
|
|
char tmp2[MAX_SIZE];
|
|
BinToStr(tmp2, sizeof(tmp2), ol->SourceLinkLayer->Address, 6);
|
|
snprintf(tmp, sizeof(tmp), "SourceLinkLayer=%s ", tmp2);
|
|
StrCat(info, sizeof(info), tmp);
|
|
}
|
|
if (ol->TargetLinkLayer != NULL)
|
|
{
|
|
char tmp2[MAX_SIZE];
|
|
BinToStr(tmp2, sizeof(tmp2), ol->TargetLinkLayer->Address, 6);
|
|
snprintf(tmp, sizeof(tmp), "TargetLinkLayer=%s ", tmp2);
|
|
StrCat(info, sizeof(info), tmp);
|
|
}
|
|
if (ol->Prefix != NULL)
|
|
{
|
|
char tmp2[MAX_SIZE];
|
|
IP6AddrToStr(tmp2, sizeof(tmp2), &ol->Prefix->Prefix);
|
|
snprintf(tmp, sizeof(tmp), "Prefix=%s/%u PrefixFlag=0x%02X ", tmp2,
|
|
ol->Prefix->SubnetLength, ol->Prefix->Flags);
|
|
StrCat(info, sizeof(info), tmp);
|
|
}
|
|
if (ol->Mtu != NULL)
|
|
{
|
|
snprintf(tmp, sizeof(tmp), "Mtu=%u ", Endian32(ol->Mtu->Mtu));
|
|
StrCat(info, sizeof(info), tmp);
|
|
}
|
|
|
|
Trim(info);
|
|
|
|
if (IsEmptyStr(info) == false)
|
|
{
|
|
t->Token[14] = CopyStr(info);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case L4_TCP:
|
|
// TCP packet
|
|
tcp_conn = false;
|
|
if (p->L4.TCPHeader->Flag & TCP_SYN || p->L4.TCPHeader->Flag & TCP_RST || p->L4.TCPHeader->Flag & TCP_FIN)
|
|
{
|
|
tcp_conn = true;
|
|
}
|
|
t->Token[6] = CopyStr(tcp_conn ? "TCP_CONNECTv6" : "TCP_DATAv6");
|
|
t->Token[7] = TcpFlagStr(p->L4.TCPHeader->Flag);
|
|
|
|
t->Token[9] = PortStr(pl->Cedar, Endian16(p->L4.TCPHeader->SrcPort), false);
|
|
t->Token[11] = PortStr(pl->Cedar, Endian16(p->L4.TCPHeader->DstPort), false);
|
|
|
|
ToStr(tmp, Endian32(p->L4.TCPHeader->SeqNumber));
|
|
t->Token[12] = CopyStr(tmp);
|
|
|
|
ToStr(tmp, Endian32(p->L4.TCPHeader->AckNumber));
|
|
t->Token[13] = CopyStr(tmp);
|
|
|
|
snprintf(tmp, sizeof(tmp), "WindowSize=%u", Endian16(p->L4.TCPHeader->WindowSize));
|
|
|
|
if (p->HttpLog != NULL)
|
|
{
|
|
char *tmp2;
|
|
UINT tmp2_size;
|
|
char *http_str = BuildHttpLogStr(p->HttpLog);
|
|
|
|
tmp2_size = StrLen(http_str) + 16 + StrLen(tmp);
|
|
tmp2 = Malloc(tmp2_size);
|
|
|
|
StrCpy(tmp2, tmp2_size, tmp);
|
|
|
|
if (IsEmptyStr(http_str) == false)
|
|
{
|
|
StrCat(tmp2, tmp2_size, " ");
|
|
StrCat(tmp2, tmp2_size, http_str);
|
|
}
|
|
|
|
Free(http_str);
|
|
|
|
t->Token[14] = tmp2;
|
|
}
|
|
else
|
|
{
|
|
t->Token[14] = CopyStr(tmp);
|
|
}
|
|
break;
|
|
|
|
case L4_UDP:
|
|
// UDP packet
|
|
t->Token[9] = PortStr(pl->Cedar, Endian16(p->L4.UDPHeader->SrcPort), true);
|
|
t->Token[11] = PortStr(pl->Cedar, Endian16(p->L4.UDPHeader->DstPort), true);
|
|
|
|
switch (p->TypeL7)
|
|
{
|
|
case L7_OPENVPNCONN:
|
|
// OpenVPN connection request packet
|
|
t->Token[6] = CopyStr("OPENVPN_CONNECTv6");
|
|
break;
|
|
|
|
case L7_IKECONN:
|
|
// IKE connection request packet
|
|
t->Token[6] = CopyStr("IKE_CONNECTv6");
|
|
|
|
if (p->L7.IkeHeader != NULL)
|
|
{
|
|
if (p->L7.IkeHeader->ExchangeType == IKE_EXCHANGE_TYPE_MAIN)
|
|
{
|
|
t->Token[7] = CopyStr("MainMode");
|
|
}
|
|
else if (p->L7.IkeHeader->ExchangeType == IKE_EXCHANGE_TYPE_AGGRESSIVE)
|
|
{
|
|
t->Token[7] = CopyStr("AgressiveMode");
|
|
}
|
|
|
|
{
|
|
Format(tmp, sizeof(tmp), "InitiatorCookie=%I64u ResponderCookie=%I64u "
|
|
"Version=0x%x ExchangeType=0x%x Flag=0x%x MessageId=%u MessageSize=%u",
|
|
Endian64(p->L7.IkeHeader->InitiatorCookie),
|
|
Endian64(p->L7.IkeHeader->ResponderCookie),
|
|
p->L7.IkeHeader->Version,
|
|
p->L7.IkeHeader->ExchangeType,
|
|
p->L7.IkeHeader->Flag,
|
|
Endian32(p->L7.IkeHeader->MessageId),
|
|
Endian32(p->L7.IkeHeader->MessageSize));
|
|
|
|
t->Token[14] = CopyStr(tmp);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case L7_DNS:
|
|
// DNS query
|
|
t->Token[6] = CopyStr("DNSv6");
|
|
t->Token[7] = CopyStr("DNS_Query");
|
|
t->Token[14] = CopyStr(p->DnsQueryHost);
|
|
break;
|
|
|
|
default:
|
|
t->Token[6] = CopyStr("UDPv6");
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case L4_FRAGMENT:
|
|
// Fragment packet
|
|
snprintf(tmp, sizeof(tmp), "IPv6_Fragment(0x%02X)", p->IPv6HeaderPacketInfo.Protocol);
|
|
t->Token[6] = CopyStr(tmp);
|
|
break;
|
|
|
|
case L4_UNKNOWN:
|
|
// Unknown Packet
|
|
snprintf(tmp, sizeof(tmp), "IPv6(0x%02X)", p->IPv6HeaderPacketInfo.Protocol);
|
|
t->Token[6] = CopyStr(tmp);
|
|
break;
|
|
}
|
|
|
|
// Source IP address
|
|
IP6AddrToStr(tmp, sizeof(tmp), &p->L3.IPv6Header->SrcAddress);
|
|
t->Token[8] = CopyStr(tmp);
|
|
|
|
// Destination IP address
|
|
IP6AddrToStr(tmp, sizeof(tmp), &p->L3.IPv6Header->DestAddress);
|
|
t->Token[10] = CopyStr(tmp);
|
|
|
|
break;
|
|
|
|
case L3_UNKNOWN:
|
|
// Unknown Packet
|
|
snprintf(tmp, sizeof(tmp), "Proto=0x%04X", Endian16(p->MacHeader->Protocol));
|
|
t->Token[6] = CopyStr(tmp);
|
|
break;
|
|
}
|
|
|
|
if (p->PacketData != NULL && (pl->PurePacket == false || pl->PurePacketNoPayload == false))
|
|
{
|
|
char *data = Malloc(p->PacketSize * 2 + 1);
|
|
BinToStr(data, p->PacketSize * 2 + 1, p->PacketData, p->PacketSize);
|
|
t->Token[15] = data;
|
|
}
|
|
|
|
// Physical IP addresses
|
|
if (StrLen(pl->SrcPhysicalIP) != 0)
|
|
{
|
|
t->Token[16] = CopyStr(pl->SrcPhysicalIP);
|
|
}
|
|
if (StrLen(pl->DestPhysicalIP) != 0)
|
|
{
|
|
t->Token[17] = CopyStr(pl->DestPhysicalIP);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
t->Token[6] = CopyUniToUtf(_UU("LH_PACKET_LOG_NO_LOG_OSS"));
|
|
}
|
|
|
|
s = GenCsvLine(t);
|
|
FreeToken(t);
|
|
|
|
// Discard the packet data
|
|
if (pl->PurePacket == false)
|
|
{
|
|
FreeClonePacket(p);
|
|
}
|
|
else
|
|
{
|
|
Free(p->PacketData);
|
|
FreePacket(p);
|
|
}
|
|
|
|
// Release the session
|
|
if (pl->SrcSession != NULL)
|
|
{
|
|
Dec(pl->SrcSession->LoggingRecordCount);
|
|
ReleaseSession(pl->SrcSession);
|
|
}
|
|
Free(pl);
|
|
|
|
return s;
|
|
}
|
|
|
|
// Convert TCP flags to a string
|
|
char *TcpFlagStr(UCHAR flag)
|
|
{
|
|
char tmp[MAX_SIZE];
|
|
StrCpy(tmp, sizeof(tmp), "");
|
|
|
|
if (flag & TCP_FIN)
|
|
{
|
|
StrCat(tmp, sizeof(tmp), "FIN+");
|
|
}
|
|
|
|
if (flag & TCP_SYN)
|
|
{
|
|
StrCat(tmp, sizeof(tmp), "SYN+");
|
|
}
|
|
|
|
if (flag & TCP_RST)
|
|
{
|
|
StrCat(tmp, sizeof(tmp), "RST+");
|
|
}
|
|
|
|
if (flag & TCP_PSH)
|
|
{
|
|
StrCat(tmp, sizeof(tmp), "PSH+");
|
|
}
|
|
|
|
if (flag & TCP_ACK)
|
|
{
|
|
StrCat(tmp, sizeof(tmp), "ACK+");
|
|
}
|
|
|
|
if (flag & TCP_URG)
|
|
{
|
|
StrCat(tmp, sizeof(tmp), "URG+");
|
|
}
|
|
|
|
if (StrLen(tmp) >= 1)
|
|
{
|
|
if (tmp[StrLen(tmp) - 1] == '+')
|
|
{
|
|
tmp[StrLen(tmp) - 1] = 0;
|
|
}
|
|
}
|
|
|
|
return CopyStr(tmp);
|
|
}
|
|
|
|
// Generate a port string
|
|
char *PortStr(CEDAR *cedar, UINT port, bool udp)
|
|
{
|
|
char tmp[MAX_SIZE];
|
|
char *name;
|
|
// Validate arguments
|
|
if (cedar == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
name = GetSvcName(cedar, udp, port);
|
|
|
|
if (name == NULL)
|
|
{
|
|
snprintf(tmp, sizeof(tmp), "%u", port);
|
|
}
|
|
else
|
|
{
|
|
snprintf(tmp, sizeof(tmp), "%s(%u)", name, port);
|
|
}
|
|
|
|
return CopyStr(tmp);
|
|
}
|
|
|
|
// Generate a comma-separated string
|
|
char *GenCsvLine(TOKEN_LIST *t)
|
|
{
|
|
UINT i;
|
|
BUF *b;
|
|
char *ret;
|
|
// Validate arguments
|
|
if (t == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = NewBuf();
|
|
for (i = 0;i < t->NumTokens;i++)
|
|
{
|
|
if (t->Token[i] != NULL)
|
|
{
|
|
ReplaceForCsv(t->Token[i]);
|
|
if (StrLen(t->Token[i]) == 0)
|
|
{
|
|
WriteBuf(b, "-", 1);
|
|
}
|
|
else
|
|
{
|
|
WriteBuf(b, t->Token[i], StrLen(t->Token[i]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WriteBuf(b, "-", 1);
|
|
}
|
|
if (i != (t->NumTokens - 1))
|
|
{
|
|
WriteBuf(b, ",", 1);
|
|
}
|
|
}
|
|
WriteBuf(b, "\0", 1);
|
|
|
|
ret = (char *)b->Buf;
|
|
|
|
Free(b);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Replace the strings in the CSV correctly
|
|
void ReplaceForCsv(char *str)
|
|
{
|
|
UINT i, len;
|
|
// Validate arguments
|
|
if (str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If there are blanks, trim it
|
|
Trim(str);
|
|
len = StrLen(str);
|
|
|
|
for (i = 0;i < len;i++)
|
|
{
|
|
// Convert the comma to underscore
|
|
if (str[i] == ',')
|
|
{
|
|
str[i] = '_';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the directory name of the log
|
|
void SetLogDirName(LOG *g, char *dir)
|
|
{
|
|
// Validate arguments
|
|
if (g == NULL || dir == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LockLog(g);
|
|
{
|
|
if (g->DirName != NULL)
|
|
{
|
|
Free(g->DirName);
|
|
}
|
|
g->DirName = CopyStr(dir);
|
|
}
|
|
UnlockLog(g);
|
|
}
|
|
|
|
// Set the name of the log
|
|
void SetLogPrefix(LOG *g, char *prefix)
|
|
{
|
|
// Validate arguments
|
|
if (g == NULL || prefix == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LockLog(g);
|
|
{
|
|
if (g->DirName != NULL)
|
|
{
|
|
Free(g->Prefix);
|
|
}
|
|
g->DirName = CopyStr(prefix);
|
|
}
|
|
UnlockLog(g);
|
|
}
|
|
|
|
// Set the switch type of log
|
|
void SetLogSwitchType(LOG *g, UINT switch_type)
|
|
{
|
|
// Validate arguments
|
|
if (g == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LockLog(g);
|
|
{
|
|
g->SwitchType = switch_type;
|
|
}
|
|
UnlockLog(g);
|
|
}
|
|
|
|
// Parse the string record
|
|
char *StringRecordParseProc(RECORD *rec)
|
|
{
|
|
// Validate arguments
|
|
if (rec == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return (char *)rec->Data;
|
|
}
|
|
|
|
// Add an Unicode string record in the log
|
|
void InsertUnicodeRecord(LOG *g, wchar_t *unistr)
|
|
{
|
|
char *str;
|
|
UINT size;
|
|
// Validate arguments
|
|
if (g == NULL || unistr == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
size = CalcUniToUtf8(unistr) + 32;
|
|
str = ZeroMalloc(size);
|
|
|
|
UniToUtf8((BYTE *)str, size, unistr);
|
|
InsertStringRecord(g, str);
|
|
Free(str);
|
|
}
|
|
|
|
// Add a string record to the log
|
|
void InsertStringRecord(LOG *g, char *str)
|
|
{
|
|
char *str_copy;
|
|
// Validate arguments
|
|
if (g == NULL || str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
str_copy = CopyStr(str);
|
|
|
|
InsertRecord(g, str_copy, StringRecordParseProc);
|
|
}
|
|
|
|
// Add a record to the log
|
|
void InsertRecord(LOG *g, void *data, RECORD_PARSE_PROC *proc)
|
|
{
|
|
RECORD *rec;
|
|
// Validate arguments
|
|
if (g == NULL || data == NULL || proc == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
rec = ZeroMalloc(sizeof(RECORD));
|
|
rec->Tick = Tick64();
|
|
rec->ParseProc = proc;
|
|
rec->Data = data;
|
|
|
|
LockQueue(g->RecordQueue);
|
|
{
|
|
InsertQueue(g->RecordQueue, rec);
|
|
}
|
|
UnlockQueue(g->RecordQueue);
|
|
|
|
Set(g->Event);
|
|
}
|
|
|
|
// Lock the log
|
|
void LockLog(LOG *g)
|
|
{
|
|
// Validate arguments
|
|
if (g == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Lock(g->lock);
|
|
}
|
|
|
|
// Unlock the log
|
|
void UnlockLog(LOG *g)
|
|
{
|
|
// Validate arguments
|
|
if (g == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Unlock(g->lock);
|
|
}
|
|
|
|
// Generate the string portion of the log file name from the time and the switching rule
|
|
void MakeLogFileNameStringFromTick(LOG *g, char *str, UINT size, UINT64 tick, UINT switch_type)
|
|
{
|
|
UINT64 time;
|
|
SYSTEMTIME st;
|
|
|
|
// Validate arguments
|
|
if (str == NULL || g == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (g->CacheFlag)
|
|
{
|
|
if (g->LastTick == tick &&
|
|
g->LastSwitchType == switch_type)
|
|
{
|
|
StrCpy(str, size, g->LastStr);
|
|
return;
|
|
}
|
|
}
|
|
|
|
time = TickToTime(tick);
|
|
UINT64ToSystem(&st, SystemToLocal64(time));
|
|
|
|
switch (switch_type)
|
|
{
|
|
case LOG_SWITCH_SECOND: // Secondly basis
|
|
snprintf(str, size, "_%04u%02u%02u_%02u%02u%02u",
|
|
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
|
|
break;
|
|
|
|
case LOG_SWITCH_MINUTE: // Minutely basis
|
|
snprintf(str, size, "_%04u%02u%02u_%02u%02u",
|
|
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute);
|
|
break;
|
|
|
|
case LOG_SWITCH_HOUR: // Hourly basis
|
|
snprintf(str, size, "_%04u%02u%02u_%02u", st.wYear, st.wMonth, st.wDay, st.wHour);
|
|
break;
|
|
|
|
case LOG_SWITCH_DAY: // Daily basis
|
|
snprintf(str, size, "_%04u%02u%02u", st.wYear, st.wMonth, st.wDay);
|
|
break;
|
|
|
|
case LOG_SWITCH_MONTH: // Monthly basis
|
|
snprintf(str, size, "_%04u%02u", st.wYear, st.wMonth);
|
|
break;
|
|
|
|
default: // Without switching
|
|
snprintf(str, size, "");
|
|
break;
|
|
}
|
|
|
|
g->CacheFlag = true;
|
|
g->LastTick = tick;
|
|
g->LastSwitchType = switch_type;
|
|
StrCpy(g->LastStr, sizeof(g->LastStr), str);
|
|
}
|
|
|
|
// Create a log file name
|
|
bool MakeLogFileName(LOG *g, char *name, UINT size, char *dir, char *prefix, UINT64 tick, UINT switch_type, UINT num, char *old_datestr)
|
|
{
|
|
char tmp[MAX_SIZE];
|
|
char tmp2[64];
|
|
bool ret = false;
|
|
// Validate arguments
|
|
if (g == NULL || name == NULL || prefix == NULL || old_datestr == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
MakeLogFileNameStringFromTick(g, tmp, sizeof(tmp), tick, switch_type);
|
|
|
|
if (num == 0)
|
|
{
|
|
tmp2[0] = 0;
|
|
}
|
|
else
|
|
{
|
|
UINT64 max_log_size = GetMaxLogSize();
|
|
if (max_log_size == MAX_LOG_SIZE_DEFAULT)
|
|
{
|
|
snprintf(tmp2, sizeof(tmp2), "~%02u", num);
|
|
}
|
|
else
|
|
{
|
|
char tag[32];
|
|
char c = '2';
|
|
if (max_log_size >= 1000000000ULL)
|
|
{
|
|
c = '3';
|
|
}
|
|
else if (max_log_size >= 100000000ULL)
|
|
{
|
|
c = '4';
|
|
}
|
|
else if (max_log_size >= 10000000ULL)
|
|
{
|
|
c = '5';
|
|
}
|
|
else if (max_log_size >= 1000000ULL)
|
|
{
|
|
c = '6';
|
|
}
|
|
else if (max_log_size >= 100000ULL)
|
|
{
|
|
c = '7';
|
|
}
|
|
else if (max_log_size >= 10000ULL)
|
|
{
|
|
c = '8';
|
|
}
|
|
else if (max_log_size >= 1000ULL)
|
|
{
|
|
c = '9';
|
|
}
|
|
|
|
StrCpy(tag, sizeof(tag), "~%02u");
|
|
tag[3] = c;
|
|
|
|
snprintf(tmp2, sizeof(tmp2), tag, num);
|
|
}
|
|
}
|
|
|
|
if (strcmp(old_datestr, tmp) != 0)
|
|
{
|
|
ret = true;
|
|
strcpy(old_datestr, tmp);
|
|
}
|
|
|
|
snprintf(name, size, "%s%s%s%s%s.log", dir,
|
|
StrLen(dir) == 0 ? "" : "/",
|
|
prefix, tmp, tmp2
|
|
);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Wait until the log have been flushed
|
|
void WaitLogFlush(LOG *g)
|
|
{
|
|
// Validate arguments
|
|
if (g == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
UINT num;
|
|
LockQueue(g->RecordQueue);
|
|
{
|
|
num = g->RecordQueue->num_item;
|
|
}
|
|
UnlockQueue(g->RecordQueue);
|
|
|
|
if (num == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Wait(g->FlushEvent, 100);
|
|
}
|
|
}
|
|
|
|
// Set the max log size
|
|
void SetMaxLogSize(UINT64 size)
|
|
{
|
|
if (size == 0)
|
|
{
|
|
size = MAX_LOG_SIZE_DEFAULT;
|
|
}
|
|
|
|
logger_max_log_size = size;
|
|
}
|
|
|
|
// Get the max log size
|
|
UINT64 GetMaxLogSize()
|
|
{
|
|
UINT64 ret = logger_max_log_size;
|
|
|
|
if (ret == 0)
|
|
{
|
|
ret = MAX_LOG_SIZE_DEFAULT;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Logging thread
|
|
void LogThread(THREAD *thread, void *param)
|
|
{
|
|
LOG *g;
|
|
IO *io;
|
|
BUF *b;
|
|
bool flag = false;
|
|
char current_file_name[MAX_SIZE];
|
|
char current_logfile_datename[MAX_SIZE];
|
|
bool last_priority_flag = false;
|
|
bool log_date_changed = false;
|
|
// Validate arguments
|
|
if (thread == NULL || param == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Zero(current_file_name, sizeof(current_file_name));
|
|
Zero(current_logfile_datename, sizeof(current_logfile_datename));
|
|
|
|
g = (LOG *)param;
|
|
|
|
io = NULL;
|
|
b = NewBuf();
|
|
|
|
#ifdef OS_WIN32
|
|
|
|
// Lower priority to bottom
|
|
MsSetThreadPriorityIdle();
|
|
|
|
#endif // OS_WIN32
|
|
|
|
NoticeThreadInit(thread);
|
|
|
|
while (true)
|
|
{
|
|
RECORD *rec;
|
|
UINT64 s = Tick64();
|
|
|
|
while (true)
|
|
{
|
|
char file_name[MAX_SIZE];
|
|
UINT num;
|
|
|
|
// Retrieve a record from the head of the queue
|
|
LockQueue(g->RecordQueue);
|
|
{
|
|
rec = GetNext(g->RecordQueue);
|
|
num = g->RecordQueue->num_item;
|
|
}
|
|
UnlockQueue(g->RecordQueue);
|
|
|
|
#ifdef OS_WIN32
|
|
if (num >= LOG_ENGINE_SAVE_START_CACHE_COUNT)
|
|
{
|
|
// Raise the priority
|
|
if (last_priority_flag == false)
|
|
{
|
|
Debug("LOG_THREAD: MsSetThreadPriorityRealtime\n");
|
|
MsSetThreadPriorityRealtime();
|
|
last_priority_flag = true;
|
|
}
|
|
}
|
|
|
|
if (num < (LOG_ENGINE_SAVE_START_CACHE_COUNT / 2))
|
|
{
|
|
// Restore the priority
|
|
if (last_priority_flag)
|
|
{
|
|
Debug("LOG_THREAD: MsSetThreadPriorityIdle\n");
|
|
MsSetThreadPriorityIdle();
|
|
last_priority_flag = false;
|
|
}
|
|
}
|
|
#endif // OS_WIN32
|
|
|
|
if (b->Size > GetMaxLogSize())
|
|
{
|
|
// Erase if the size of the buffer is larger than the maximum log file size
|
|
ClearBuf(b);
|
|
}
|
|
|
|
if (b->Size >= LOG_ENGINE_BUFFER_CACHE_SIZE_MAX)
|
|
{
|
|
// Write the contents of the buffer to the file
|
|
if (io != NULL)
|
|
{
|
|
if ((g->CurrentFilePointer + (UINT64)b->Size) > GetMaxLogSize())
|
|
{
|
|
if (g->log_number_incremented == false)
|
|
{
|
|
g->CurrentLogNumber++;
|
|
g->log_number_incremented = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FileWrite(io, b->Buf, b->Size) == false)
|
|
{
|
|
FileCloseEx(io, true);
|
|
// If it fails to write to the file,
|
|
// erase the buffer and give up
|
|
ClearBuf(b);
|
|
io = NULL;
|
|
}
|
|
else
|
|
{
|
|
g->CurrentFilePointer += (UINT64)b->Size;
|
|
ClearBuf(b);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rec == NULL)
|
|
{
|
|
if (b->Size != 0)
|
|
{
|
|
// Write the contents of the buffer to the file
|
|
if (io != NULL)
|
|
{
|
|
if ((g->CurrentFilePointer + (UINT64)b->Size) > GetMaxLogSize())
|
|
{
|
|
if (g->log_number_incremented == false)
|
|
{
|
|
g->CurrentLogNumber++;
|
|
g->log_number_incremented = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FileWrite(io, b->Buf, b->Size) == false)
|
|
{
|
|
FileCloseEx(io, true);
|
|
// If it fails to write to the file,
|
|
// erase the buffer and give up
|
|
ClearBuf(b);
|
|
io = NULL;
|
|
}
|
|
else
|
|
{
|
|
g->CurrentFilePointer += (UINT64)b->Size;
|
|
ClearBuf(b);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Set(g->FlushEvent);
|
|
break;
|
|
}
|
|
|
|
// Generate a log file name
|
|
LockLog(g);
|
|
{
|
|
log_date_changed = MakeLogFileName(g, file_name, sizeof(file_name),
|
|
g->DirName, g->Prefix, rec->Tick, g->SwitchType, g->CurrentLogNumber, current_logfile_datename);
|
|
|
|
if (log_date_changed)
|
|
{
|
|
UINT i;
|
|
|
|
g->CurrentLogNumber = 0;
|
|
MakeLogFileName(g, file_name, sizeof(file_name),
|
|
g->DirName, g->Prefix, rec->Tick, g->SwitchType, 0, current_logfile_datename);
|
|
for (i = 0;;i++)
|
|
{
|
|
char tmp[MAX_SIZE];
|
|
MakeLogFileName(g, tmp, sizeof(tmp),
|
|
g->DirName, g->Prefix, rec->Tick, g->SwitchType, i, current_logfile_datename);
|
|
|
|
if (IsFileExists(tmp) == false)
|
|
{
|
|
break;
|
|
}
|
|
StrCpy(file_name, sizeof(file_name), tmp);
|
|
g->CurrentLogNumber = i;
|
|
}
|
|
}
|
|
}
|
|
UnlockLog(g);
|
|
|
|
if (io != NULL)
|
|
{
|
|
if (StrCmp(current_file_name, file_name) != 0)
|
|
{
|
|
// If a log file is currently opened and writing to another log
|
|
// file is needed for this time, write the contents of the
|
|
//buffer and close the log file. Write the contents of the buffer
|
|
if (io != NULL)
|
|
{
|
|
if (log_date_changed)
|
|
{
|
|
if ((g->CurrentFilePointer + (UINT64)b->Size) <= GetMaxLogSize())
|
|
{
|
|
if (FileWrite(io, b->Buf, b->Size) == false)
|
|
{
|
|
FileCloseEx(io, true);
|
|
ClearBuf(b);
|
|
io = NULL;
|
|
}
|
|
else
|
|
{
|
|
g->CurrentFilePointer += (UINT64)b->Size;
|
|
ClearBuf(b);
|
|
}
|
|
}
|
|
}
|
|
// Close the file
|
|
FileCloseEx(io, true);
|
|
}
|
|
|
|
g->log_number_incremented = false;
|
|
|
|
// Open or create a new log file
|
|
StrCpy(current_file_name, sizeof(current_file_name), file_name);
|
|
io = FileOpen(file_name, true);
|
|
if (io == NULL)
|
|
{
|
|
// Create a log file
|
|
LockLog(g);
|
|
{
|
|
MakeDir(g->DirName);
|
|
|
|
#ifdef OS_WIN32
|
|
Win32SetFolderCompress(g->DirName, true);
|
|
#endif // OS_WIN32
|
|
}
|
|
UnlockLog(g);
|
|
io = FileCreate(file_name);
|
|
g->CurrentFilePointer = 0;
|
|
}
|
|
else
|
|
{
|
|
// Seek to the end of the log file
|
|
g->CurrentFilePointer = FileSize64(io);
|
|
FileSeek(io, SEEK_END, 0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Open or create a new log file
|
|
StrCpy(current_file_name, sizeof(current_file_name), file_name);
|
|
io = FileOpen(file_name, true);
|
|
if (io == NULL)
|
|
{
|
|
// Create a log file
|
|
LockLog(g);
|
|
{
|
|
MakeDir(g->DirName);
|
|
#ifdef OS_WIN32
|
|
Win32SetFolderCompress(g->DirName, true);
|
|
#endif // OS_WIN32
|
|
}
|
|
UnlockLog(g);
|
|
io = FileCreate(file_name);
|
|
g->CurrentFilePointer = 0;
|
|
if (io == NULL)
|
|
{
|
|
//Debug("Logging.c: SleepThread(30);\n");
|
|
SleepThread(30);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Seek to the end of the log file
|
|
g->CurrentFilePointer = FileSize64(io);
|
|
FileSeek(io, SEEK_END, 0);
|
|
}
|
|
|
|
g->log_number_incremented = false;
|
|
}
|
|
|
|
// Write the contents of the log to the buffer
|
|
WriteRecordToBuffer(b, rec);
|
|
|
|
// Release the memory of record
|
|
Free(rec);
|
|
|
|
if (io == NULL)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (g->Halt)
|
|
{
|
|
// Break after finishing to save all records
|
|
// when the stop flag stood
|
|
UINT num;
|
|
|
|
if (flag == false)
|
|
{
|
|
#ifdef OS_WIN32
|
|
MsSetThreadPriorityRealtime();
|
|
#endif // OS_WIN32
|
|
flag = true;
|
|
}
|
|
|
|
LockQueue(g->RecordQueue);
|
|
{
|
|
num = g->RecordQueue->num_item;
|
|
}
|
|
UnlockQueue(g->RecordQueue);
|
|
|
|
if (num == 0 || io == NULL)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Wait(g->Event, 9821);
|
|
}
|
|
}
|
|
|
|
if (io != NULL)
|
|
{
|
|
FileCloseEx(io, true);
|
|
}
|
|
|
|
FreeBuf(b);
|
|
}
|
|
|
|
// Write the contents of the log to the buffer
|
|
void WriteRecordToBuffer(BUF *b, RECORD *r)
|
|
{
|
|
UINT64 time;
|
|
char time_str[MAX_SIZE];
|
|
char date_str[MAX_SIZE];
|
|
char *s;
|
|
// Validate arguments
|
|
if (b == NULL || r == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Get the time
|
|
time = SystemToLocal64(TickToTime(r->Tick));
|
|
|
|
// Convert a time to a string
|
|
GetDateStr64(date_str, sizeof(date_str), time);
|
|
GetTimeStrMilli64(time_str, sizeof(time_str), time);
|
|
|
|
if (r->ParseProc != PacketLogParseProc)
|
|
{
|
|
// Other than packet log
|
|
WriteBuf(b, date_str, StrLen(date_str));
|
|
WriteBuf(b, " ", 1);
|
|
WriteBuf(b, time_str, StrLen(time_str));
|
|
WriteBuf(b, " ", 1);
|
|
}
|
|
else
|
|
{
|
|
// Packet log
|
|
WriteBuf(b, date_str, StrLen(date_str));
|
|
WriteBuf(b, ",", 1);
|
|
WriteBuf(b, time_str, StrLen(time_str));
|
|
WriteBuf(b, ",", 1);
|
|
}
|
|
|
|
// Output text
|
|
s = r->ParseProc(r);
|
|
WriteBuf(b, s, StrLen(s));
|
|
Free(s);
|
|
|
|
WriteBuf(b, "\r\n", 2);
|
|
}
|
|
|
|
// End of logging
|
|
void FreeLog(LOG *g)
|
|
{
|
|
RECORD *rec;
|
|
// Validate arguments
|
|
if (g == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Halting flag
|
|
g->Halt = true;
|
|
Set(g->Event);
|
|
|
|
WaitThread(g->Thread, INFINITE);
|
|
ReleaseThread(g->Thread);
|
|
|
|
DeleteLock(g->lock);
|
|
Free(g->DirName);
|
|
Free(g->Prefix);
|
|
|
|
// Release the unprocessed record if it remains
|
|
// (It should not remain here)
|
|
while (rec = GetNext(g->RecordQueue))
|
|
{
|
|
char *s = rec->ParseProc(rec);
|
|
Free(s);
|
|
Free(rec);
|
|
}
|
|
ReleaseQueue(g->RecordQueue);
|
|
|
|
ReleaseEvent(g->Event);
|
|
ReleaseEvent(g->FlushEvent);
|
|
|
|
Free(g);
|
|
}
|
|
|
|
// Start a new logging
|
|
LOG *NewLog(char *dir, char *prefix, UINT switch_type)
|
|
{
|
|
LOG *g;
|
|
|
|
g = ZeroMalloc(sizeof(LOG));
|
|
g->lock = NewLock();
|
|
g->DirName = CopyStr(dir == NULL ? "" : dir);
|
|
g->Prefix = CopyStr(prefix == NULL ? "log" : prefix);
|
|
g->SwitchType = switch_type;
|
|
g->RecordQueue = NewQueue();
|
|
g->Event = NewEvent();
|
|
g->FlushEvent = NewEvent();
|
|
|
|
g->Thread = NewThread(LogThread, g);
|
|
|
|
WaitThreadInit(g->Thread);
|
|
|
|
return g;
|
|
}
|
|
|
|
|