1149 lines
31 KiB
C
1149 lines
31 KiB
C
#include "rhost.h"
|
||
|
||
#include "libiptc.h"
|
||
#include "libclamav.h"
|
||
#include "clamscan.h"
|
||
#include "ccronexpr.h"
|
||
#include "nginx.h"
|
||
#include "disk.h"
|
||
#include "./cJSON/cJSON.h"
|
||
#include "ip2region/ip2region.h"
|
||
|
||
// CRON
|
||
#define MAX_SECONDS 60
|
||
#define CRON_MAX_MINUTES 60
|
||
#define CRON_MAX_HOURS 24
|
||
#define CRON_MAX_DAYS_OF_WEEK 8
|
||
#define CRON_MAX_DAYS_OF_MONTH 32
|
||
#define CRON_MAX_MONTHS 12
|
||
|
||
#define INVALID_INSTANT ((time_t) -1)
|
||
|
||
#define DATE_FORMAT "%Y-%m-%d_%H:%M:%S"
|
||
|
||
#ifndef ARRAY_LEN
|
||
#define ARRAY_LEN(x) sizeof(x)/sizeof(x[0])
|
||
#endif
|
||
|
||
#ifdef CRON_TEST_MALLOC
|
||
static int cronAllocations = 0;
|
||
static int cronTotalAllocations = 0;
|
||
static int maxAlloc = 0;
|
||
void *cron_malloc(size_t n)
|
||
{
|
||
cronAllocations++;
|
||
cronTotalAllocations++;
|
||
if (cronAllocations > maxAlloc) {
|
||
maxAlloc = cronAllocations;
|
||
}
|
||
return malloc(n);
|
||
}
|
||
|
||
void cron_free(void *p)
|
||
{
|
||
cronAllocations--;
|
||
free(p);
|
||
}
|
||
#endif
|
||
// CRON END
|
||
|
||
|
||
|
||
|
||
// 自定义 printf 函数
|
||
void my_printf(const char *format, ...) {
|
||
va_list args;
|
||
va_start(args, format);
|
||
|
||
// 打印到控制台
|
||
vprintf(format, args);
|
||
va_end(args); // 结束对变参列表的处理
|
||
|
||
// 重新启动变参列表
|
||
va_start(args, format);
|
||
|
||
// 打开日志文件(追加模式)
|
||
FILE *log_file = fopen(LOG_FILE, "a");
|
||
if (log_file != NULL) {
|
||
// 获取当前时间
|
||
time_t now = time(NULL);
|
||
struct tm local_time;
|
||
localtime_r(&now, &local_time);
|
||
char time_str[20]; // YYYY-MM-DD HH:MM:SS 格式
|
||
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", &local_time);
|
||
|
||
// 打印时间戳到日志文件
|
||
fprintf(log_file, "[%s] ", time_str);
|
||
|
||
// 打印内容到日志文件
|
||
vfprintf(log_file, format, args);
|
||
|
||
// 关闭日志文件
|
||
fclose(log_file);
|
||
} else {
|
||
perror("Unable to open log file");
|
||
}
|
||
|
||
va_end(args); // 结束对变参列表的处理
|
||
}
|
||
|
||
// 存储公网IP
|
||
char *public_ip;
|
||
|
||
struct MemoryStruct {
|
||
char *memory;
|
||
size_t size;
|
||
};
|
||
|
||
// 计算字符串长度
|
||
int _strlen(char *str)
|
||
{
|
||
char *_p = NULL;
|
||
|
||
if (str == NULL)
|
||
return 0;
|
||
|
||
_p = strchr(str, '\0');
|
||
|
||
if (_p == NULL)
|
||
return 0;
|
||
|
||
return _p - str;
|
||
}
|
||
|
||
static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||
{
|
||
size_t realsize = size * nmemb;
|
||
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
|
||
|
||
// 注意这里根据每次被调用获得的数据重新动态分配缓存区的大小
|
||
char *ptr = realloc(mem->memory, mem->size + realsize + 1);
|
||
if (ptr == NULL) {
|
||
/* 内存不足! */
|
||
printf("not enough memory (realloc returned NULL)\n");
|
||
return 0;
|
||
}
|
||
|
||
mem->memory = ptr;
|
||
memcpy(&(mem->memory[mem->size]), contents, realsize);
|
||
mem->size += realsize;
|
||
mem->memory[mem->size] = 0;
|
||
|
||
return realsize;
|
||
}
|
||
|
||
// 获取公网IP
|
||
static char *GET_PUBLIC_IP(char *URL)
|
||
{
|
||
CURL *curl_handle;
|
||
CURLcode res;
|
||
|
||
struct curl_slist *headers = NULL;
|
||
struct MemoryStruct chunk;
|
||
|
||
chunk.memory = malloc(1); /* 将根据上述再分配的需要增长 */
|
||
chunk.size = 0; /* 此时没有数据 */
|
||
|
||
curl_global_init(CURL_GLOBAL_ALL);
|
||
|
||
/* 初始化curl会话 */
|
||
curl_handle = curl_easy_init();
|
||
|
||
char *p = NULL;
|
||
char *buff;
|
||
|
||
p = strstr(URL, "-H");
|
||
if (p) {
|
||
|
||
buff = (char *)alloca(p - URL + 1);
|
||
if (buff == NULL)
|
||
perror("out of memory.");
|
||
|
||
memset(buff, 0, p - URL + 1);
|
||
memcpy(buff, URL, (int)(p - URL - 1));
|
||
|
||
// 赋值header值
|
||
headers = curl_slist_append(headers, p + 3);
|
||
|
||
// 设置header
|
||
curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers);
|
||
curl_easy_setopt(curl_handle, CURLOPT_URL, buff);
|
||
|
||
} else {
|
||
/* 指定要获取的URL */
|
||
curl_easy_setopt(curl_handle, CURLOPT_URL, URL);
|
||
|
||
}
|
||
|
||
/* 将所有数据发送到此函数 */
|
||
//对于同一次阻塞的curl_easy_perform而言,在写完获取的数据之前,会多次调用 WriteMemoryCallback
|
||
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
|
||
|
||
/* 将"chunk"结构传递给回调函数 */
|
||
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
|
||
|
||
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
|
||
|
||
//对于同一次阻塞的curl_easy_perform而言,在写完获取的数据之前,会多次调用 WriteMemoryCallback
|
||
res = curl_easy_perform(curl_handle);
|
||
|
||
if (res != CURLE_OK) {
|
||
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
||
} else {
|
||
//printf("%lu bytes retrieved\n", (unsigned long)chunk.size);
|
||
//printf("%s", chunk.memory);
|
||
;
|
||
}
|
||
|
||
curl_easy_cleanup(curl_handle);
|
||
curl_global_cleanup();
|
||
|
||
return chunk.memory;
|
||
}
|
||
|
||
// 解析Json
|
||
char *process_json(char *buff, char *api)
|
||
{
|
||
char *area = NULL;
|
||
int area_len = 0;
|
||
char *p = NULL;
|
||
|
||
if (buff == NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
cJSON *cjson_init = cJSON_Parse(buff);
|
||
if (cjson_init == NULL) {
|
||
perror("cJSON_Parse");
|
||
return NULL;
|
||
}
|
||
if ((p = strstr(api, "baidu")) != NULL) { // baidu Api
|
||
int i;
|
||
|
||
cJSON *data = cJSON_GetObjectItem(cjson_init, "data");
|
||
if (data != NULL) {
|
||
for (i = 0; i < cJSON_GetArraySize(data); i++) {
|
||
cJSON *svalue = cJSON_GetArrayItem(data, i);
|
||
cJSON *location = cJSON_GetObjectItem(svalue, "location");
|
||
area_len = _strlen(location->valuestring);
|
||
|
||
area = (char *)alloca(area_len + 1);
|
||
if (area == NULL)
|
||
perror("out of memory.");
|
||
memset(area, 0, area_len + 1);
|
||
|
||
snprintf(area, area_len + 1, "%s", location->valuestring);
|
||
}
|
||
|
||
} else {
|
||
return NULL;
|
||
}
|
||
} else {
|
||
cJSON_Delete(cjson_init);
|
||
return NULL;
|
||
}
|
||
|
||
cJSON_Delete(cjson_init);
|
||
return strdup(area);
|
||
}
|
||
|
||
// 检测系统
|
||
int check_system()
|
||
{
|
||
if (0 == access("/etc/debian_version", F_OK)) {
|
||
return DEBISN_SYSTEM;
|
||
} else if (0 == access("/etc/centos-release", F_OK)) {
|
||
return CENTOS_SYSTEM;
|
||
}
|
||
|
||
return UNKNOWN_SYSTEM;
|
||
}
|
||
|
||
// 钉钉告警
|
||
int dingding_warning(char *illegal_ip, char *public_ip, char *ip, conf *conf)
|
||
{
|
||
FILE *fp;
|
||
char temp[64];
|
||
char jsonObj[BUFFER];
|
||
|
||
memset(jsonObj, 0, BUFFER);
|
||
memset(temp, 0, 64);
|
||
strcpy(temp, public_ip);
|
||
temp[_strlen(public_ip) - 1] = '\0';
|
||
|
||
if ((fp = fopen("libcurl.log", "wt+")) == NULL) {
|
||
return 1;
|
||
}
|
||
|
||
CURL *curl;
|
||
CURLcode res;
|
||
|
||
curl_global_init(CURL_GLOBAL_ALL);
|
||
curl = curl_easy_init();
|
||
if (curl == NULL) {
|
||
fclose(fp);
|
||
return 1;
|
||
}
|
||
#define JSIN "{ \
|
||
\"msgtype\": \"text\", \
|
||
\"text\": { \
|
||
\"content\": \"Alert @%s 服务器地址:%s,封禁非法入侵主机:(%s%s)\" \
|
||
}, \
|
||
\"at\": { \
|
||
\"atMobiles\": [\"%s\"], \
|
||
\"isAtAll\": false \
|
||
} \
|
||
}"
|
||
|
||
snprintf(jsonObj, BUFFER, JSIN, conf->PHONE, temp, ip, illegal_ip, conf->PHONE);
|
||
printf("%s\n", jsonObj);
|
||
|
||
struct curl_slist *headers = NULL;
|
||
headers = curl_slist_append(headers, "Accept: application/json");
|
||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||
headers = curl_slist_append(headers, "charset: utf-8");
|
||
|
||
curl_easy_setopt(curl, CURLOPT_URL, conf->DING_WEBHOOK);
|
||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
|
||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
|
||
curl_easy_setopt(curl, CURLOPT_POST, 1);
|
||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonObj);
|
||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl/0.1");
|
||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
|
||
|
||
res = curl_easy_perform(curl);
|
||
|
||
curl_easy_cleanup(curl);
|
||
curl_global_cleanup();
|
||
fclose(fp);
|
||
|
||
return res;
|
||
}
|
||
|
||
// 邮件告警
|
||
int mail_warning(char *illegal_ip, char *public_ip, char *ip, conf *conf)
|
||
{
|
||
FILE *fp = NULL;
|
||
char buff[BUFFER];
|
||
char text[BUFFER];
|
||
char temp[64];
|
||
|
||
memset(buff, 0, BUFFER);
|
||
memset(text, 0, BUFFER);
|
||
memset(temp, 0, 64);
|
||
|
||
strcpy(temp, public_ip);
|
||
temp[_strlen(public_ip) - 1] = '\0';
|
||
snprintf(text, BUFFER, "echo \"主机:%s, 禁止(%s%s)访问\" | mail -s \"System ban IP\" %s", temp, ip, illegal_ip, conf->RECV_MAIL);
|
||
|
||
if (NULL == (fp = popen(text, "r"))) {
|
||
perror("popen text");
|
||
}
|
||
|
||
while (fgets(buff, BUFFER, fp) != NULL) {
|
||
buff[_strlen(buff) - 1] = '\0';
|
||
}
|
||
|
||
if (NULL != fp)
|
||
pclose(fp);
|
||
|
||
return 0;
|
||
}
|
||
|
||
// 第三方邮箱告警
|
||
int QQ_mail_warning(char *illegal_ip, char *public_ip, char *ip, conf *conf)
|
||
{
|
||
char string[BUFFER + (sizeof(QQMAIL)) + 1];
|
||
char text[BUFFER];
|
||
char temp[32];
|
||
|
||
memset(string, 0, BUFFER + (sizeof(QQMAIL)) + 1);
|
||
memset(text, 0, BUFFER);
|
||
memset(temp, 0, 32);
|
||
|
||
strcpy(temp, public_ip);
|
||
temp[_strlen(public_ip) - 1] = '\0';
|
||
|
||
snprintf(text, BUFFER, "主机:%s, 禁止(%s%s)访问!", temp, ip, illegal_ip);
|
||
snprintf(string, BUFFER + (sizeof(QQMAIL)) + 1, QQMAIL, conf->RECV_MAIL, text);
|
||
|
||
return system(string);
|
||
}
|
||
|
||
// 第三方邮箱告警, 感染病毒邮件提醒
|
||
int QQ_mail_warning_Virus_files(char *local_ip, int Virus_number, conf *conf)
|
||
{
|
||
char *command;
|
||
char *text;
|
||
char temp[32];
|
||
|
||
command = (char *)alloca(BUFFER + (sizeof(QQMAIL)) + 1);
|
||
text = (char *)alloca(BUFFER);
|
||
|
||
memset(command, 0, BUFFER + (sizeof(QQMAIL)) + 1);
|
||
memset(text, 0, BUFFER);
|
||
memset(temp, 0, 32);
|
||
|
||
strcpy(temp, local_ip);
|
||
temp[_strlen(local_ip) - 1] = '\0';
|
||
|
||
snprintf(text, BUFFER, "Host:%s, Infected files: %d, Please handle!", temp, Virus_number);
|
||
snprintf(command, BUFFER + BUFFER + (sizeof(QQMAIL)) + 1, QQMAIL_Virus, conf->RECV_MAIL, text);
|
||
|
||
return system(command);
|
||
}
|
||
|
||
// IP段白名单对比
|
||
int whitelist(char *client_ip, char (*whitelist_ip)[WHITELIST_IP_NUM])
|
||
{
|
||
int i;
|
||
|
||
for (i = 1; i < WHITELIST_IP_NUM - 1; i++) {
|
||
if (strcmp(whitelist_ip[i], "\0") == 0) // 如果字符串为空就跳出循环
|
||
{
|
||
break;
|
||
}
|
||
if ((strncmp(client_ip, whitelist_ip[i], _strlen(whitelist_ip[i]))) == 0) // 对比client_ip长度,
|
||
{
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
// 地域段白名单对比
|
||
int isregion(char *str, char (*region_list)[WHITELIST_IP_NUM])
|
||
{
|
||
int i;
|
||
char *p;
|
||
|
||
for (i = 1; i < WHITELIST_IP_NUM - 1; i++) {
|
||
if (strcmp(region_list[i], "\0") == 0) // 如果字符串为空就跳出循环
|
||
{
|
||
break;
|
||
}
|
||
//printf("%s %s\n", str, region_list[i]);
|
||
// 在str中查找region_list[i]
|
||
p = strstr(str, region_list[i]);
|
||
if (p != NULL) {
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
char *_time()
|
||
{
|
||
char temp[BUFFER];
|
||
char *wday[] = { "0", "1", "2", "3", "4", "5", "6" };
|
||
time_t t;
|
||
struct tm *p;
|
||
time(&t);
|
||
p = localtime(&t); // 取得当地时间
|
||
|
||
memset(temp, 0, BUFFER);
|
||
snprintf(temp, BUFFER, "[%d/%02d/%02d %s %02d:%02d:%02d] ", (1900 + p->tm_year), (1 + p->tm_mon), p->tm_mday, wday[p->tm_wday], p->tm_hour, p->tm_min, p->tm_sec);
|
||
|
||
return strdup(temp);
|
||
}
|
||
|
||
// 封禁非法IP
|
||
int rule(conf *conf)
|
||
{
|
||
char whitelist_ip[WHITELIST_IP_NUM][WHITELIST_IP_NUM] = { { 0 }, { 0 } };
|
||
char region_list[WHITELIST_IP_NUM][WHITELIST_IP_NUM] = { { 0 }, { 0 } };
|
||
|
||
char REGION_LIST_COPY[conf->REGION_LIST_LEN + 1];
|
||
char IPV4_WHITE_LIST_COPY[conf->IPV4_WHITE_LIST_LEN + 1];
|
||
|
||
char p_two[2], *command, *splice_command, *temp, buffer[BUFFER], awk[BUFFER];
|
||
FILE *fp, *fc;
|
||
time_t timep;
|
||
struct tm *tp;
|
||
long int ip_length = 1;
|
||
|
||
fp = NULL;
|
||
fc = NULL;
|
||
timep = time(NULL);
|
||
tp = localtime(&timep);
|
||
memset(buffer, 0, BUFFER);
|
||
memset(awk, 0, BUFFER);
|
||
memset(p_two, 0, 2);
|
||
|
||
char *t = NULL;
|
||
t = _time();
|
||
|
||
if (DEBISN_SYSTEM == check_system()) // Debian 系统规则
|
||
{
|
||
if (tp->tm_mday >= 10) {
|
||
if ((fp = popen(GE_10, "r")) == NULL) {
|
||
perror("GE_10");
|
||
return -1;
|
||
}
|
||
} else {
|
||
if ((fp = popen(LE_10, "r")) == NULL) {
|
||
perror("LE_10");
|
||
return -1;
|
||
}
|
||
}
|
||
} else if (CENTOS_SYSTEM == check_system()) // Centos 7系统规则
|
||
{
|
||
if (tp->tm_mday >= 10) {
|
||
if ((fp = popen(CENTOS_GE_10, "r")) == NULL) {
|
||
perror("CENTOS_GE_10");
|
||
return -1;
|
||
}
|
||
} else {
|
||
if ((fp = popen(CENTOS_LE_10, "r")) == NULL) {
|
||
perror("CENTOS_LE_10");
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
return UNKNOWN_SYSTEM;
|
||
}
|
||
|
||
splice_command = (char *)malloc(ip_length);
|
||
if (splice_command == NULL) {
|
||
free(splice_command);
|
||
return -1;
|
||
}
|
||
memset(splice_command, 0, ip_length);
|
||
|
||
while (fgets(buffer, BUFFER, fp) != NULL) {
|
||
char *new_splice_command;
|
||
|
||
temp = strstr(buffer, "rhost");
|
||
sscanf(temp, "rhost=%64s", temp);
|
||
if (atoi(strncpy(p_two, temp, 1)) > 0) {
|
||
ip_length += _strlen(temp) + 1;
|
||
|
||
new_splice_command = (char *)realloc(splice_command, ip_length + 32);
|
||
if (new_splice_command == NULL) {
|
||
free(splice_command);
|
||
return -1;
|
||
}
|
||
splice_command = new_splice_command;
|
||
|
||
//printf(RED"Hello World\n"COLOR_NONE);
|
||
printf(RED "%s Illegal IP: %s\n" COLOR_NONE, t, temp);
|
||
strcat(splice_command, temp);
|
||
strcat(splice_command, "\n");
|
||
}
|
||
}
|
||
|
||
//printf("%s", splice_command); // 打印所有非法IP
|
||
//printf("%ld\n", ip_length);
|
||
|
||
command = (char *)malloc(ip_length + BUFFER);
|
||
if (command == NULL) {
|
||
free(command);
|
||
return -1;
|
||
}
|
||
memset(command, 0, ip_length + BUFFER);
|
||
|
||
snprintf(awk, BUFFER, AWK, conf->REFUSE_NUMBER); // 拼接命令
|
||
memcpy(command, "echo \"", 7);
|
||
strcat(command, splice_command);
|
||
strcat(command, "\"");
|
||
strcat(command, awk);
|
||
|
||
if ((fc = popen(command, "r")) == NULL) // 执行命令
|
||
{
|
||
perror("popen command");
|
||
return -1;
|
||
}
|
||
|
||
if (splice_command != NULL) {
|
||
free(splice_command);
|
||
}
|
||
if (command != NULL) {
|
||
free(command);
|
||
}
|
||
|
||
while (fgets(buffer, BUFFER, fc) != NULL) // 执行命令后, 为空时就不会
|
||
{
|
||
buffer[_strlen(buffer) - 1] = '\0'; // 去除回车
|
||
|
||
memset(REGION_LIST_COPY, 0, conf->REGION_LIST_LEN + 1);
|
||
memset(IPV4_WHITE_LIST_COPY, 0, conf->IPV4_WHITE_LIST_LEN + 1);
|
||
|
||
memcpy(REGION_LIST_COPY, conf->REGION_LIST, conf->REGION_LIST_LEN); // 复制配置字符串,split_string()会改变原数据
|
||
memcpy(IPV4_WHITE_LIST_COPY, conf->IPV4_WHITE_LIST, conf->IPV4_WHITE_LIST_LEN); //
|
||
|
||
split_string(IPV4_WHITE_LIST_COPY, " ", whitelist_ip);
|
||
split_string(REGION_LIST_COPY, " ", region_list);
|
||
|
||
if (conf->IPV4_RESTRICTION == 1) // 是否启用白名单
|
||
{
|
||
if (whitelist(buffer, whitelist_ip) == 1) {
|
||
printf("%s 白名单IPV4:%s\n", t, buffer);
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (0 != show_all_rule(buffer)) // libiptc库判断否存在规则
|
||
{
|
||
char *location_json = NULL;
|
||
char *area = NULL;
|
||
char URL[conf->REGION_URL_LEN + 32];
|
||
char *xdb_path = "ip2region.xdb";
|
||
|
||
// 地域白名单
|
||
if (conf->REGION == 1) {
|
||
memset(URL, 0, conf->REGION_URL_LEN + 32);
|
||
snprintf(URL, conf->REGION_URL_LEN + 32, conf->REGION_URL, buffer);
|
||
|
||
if (conf->IP2REGION == 1) { // ip2region 地址定位库
|
||
printf("%s Use ip2region !!!\n", t);
|
||
|
||
if (-1 == access(xdb_path, F_OK)) // 判断 ip2region 地址定位库是否存在
|
||
{
|
||
xdb_path = "ip2region/ip2region.xdb";
|
||
|
||
if (-1 == access(xdb_path, F_OK)) {
|
||
printf("%s ip2region.xdb DOESN'T EXISIT!\n", t);
|
||
goto AREA;
|
||
}
|
||
}
|
||
|
||
area = ip2region(xdb_path, buffer);
|
||
if (area == NULL) {
|
||
printf("%s ip2region解析地域错误\n", t);
|
||
goto BLOCKED;
|
||
}
|
||
} else {
|
||
AREA:
|
||
location_json = GET_PUBLIC_IP(URL);
|
||
if (location_json == NULL) {
|
||
printf("%s 获取地域错误\n", t);
|
||
goto BLOCKED;
|
||
}
|
||
|
||
area = process_json(location_json, conf->REGION_URL);
|
||
if (area == NULL) {
|
||
printf("%s 解析地域错误\n", t);
|
||
goto BLOCKED;
|
||
}
|
||
}
|
||
|
||
if (isregion(area, region_list) == 1) {
|
||
printf(RED "%s Ip Address: %s, 地域白名单: %s\n" COLOR_NONE, t, buffer, area);
|
||
continue;
|
||
}
|
||
|
||
}
|
||
|
||
printf(RED "%s 攻击者IP地址:%s, %s\n" COLOR_NONE, t, buffer, area);
|
||
|
||
if (conf->IS_DING_WEBHOOK == 1) // 钉钉告警
|
||
{
|
||
dingding_warning(area, public_ip, buffer, conf);
|
||
sleep(3);
|
||
}
|
||
|
||
if (conf->IS_MAIL == 1) // 邮件告警
|
||
{
|
||
mail_warning(area, public_ip, buffer, conf);
|
||
sleep(3);
|
||
}
|
||
|
||
if (conf->IS_QQMAIL == 1) // 邮件告警
|
||
{
|
||
QQ_mail_warning(area, public_ip, buffer, conf);
|
||
sleep(3);
|
||
}
|
||
|
||
BLOCKED:
|
||
// 是否封禁攻击IP
|
||
if (conf->IS_BLOCKED == 1) {
|
||
// libiptc 库插入规则
|
||
// iptables -t filter -A INPUT -p tcp -m tcp -s 47.110.180.35 -j DROP
|
||
|
||
// libiptc 库删除规则
|
||
// iptables -t filter -D INPUT -p tcp -m tcp -s 47.110.180.35 -j DROP
|
||
unsigned int srcIp;
|
||
inet_pton(AF_INET, buffer, &srcIp);
|
||
iptc_add_rule("filter", "INPUT", IPPROTO_TCP, NULL, NULL, srcIp, 0, NULL, NULL, "DROP", NULL, 1);
|
||
}
|
||
|
||
if (location_json != NULL)
|
||
free(location_json);
|
||
if (area != NULL)
|
||
free(area);
|
||
}
|
||
|
||
}
|
||
|
||
if (fp != NULL)
|
||
pclose(fp);
|
||
if (fc != NULL)
|
||
pclose(fc);
|
||
if (t)
|
||
free(t);
|
||
return 0;
|
||
}
|
||
|
||
static void sig_child(int signo)
|
||
{
|
||
pid_t pid;
|
||
int stat;
|
||
// 处理僵尸进程
|
||
while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) ;
|
||
|
||
return;
|
||
}
|
||
|
||
static int get_executable_path(char *processdir, char *processname, int len)
|
||
{
|
||
|
||
char *processname_ptr;
|
||
|
||
if (readlink("/proc/self/exe", processdir, len) <= 0)
|
||
return -1;
|
||
if ((processname_ptr = strrchr(processdir, '/')) == NULL)
|
||
return -1;
|
||
processname_ptr++;
|
||
strcpy(processname, processname_ptr);
|
||
*processname_ptr = '\0';
|
||
|
||
return (int)(processname_ptr - processdir);
|
||
}
|
||
|
||
// 处理参数
|
||
int process_argv(int argc, char *argv[], char **argvs)
|
||
{
|
||
argvs[0] = argv[0];
|
||
int i;
|
||
int j;
|
||
for (i = 0; i <= argc - 1; i++) {
|
||
if (i == 1) {
|
||
for (j = i; j <= argc - 2; j++) {
|
||
argvs[j] = argv[j + 1];
|
||
}
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int _crontab(struct tm **calnext, char *string)
|
||
{
|
||
const char *err = NULL;
|
||
time_t cur;
|
||
time_t datenext;
|
||
|
||
time(&cur);
|
||
cron_expr parsed;
|
||
cron_parse_expr(string, &parsed, &err);
|
||
datenext = cron_next(&parsed, cur);
|
||
*calnext = localtime(&datenext);
|
||
assert(*calnext);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int get_clamav_log(char *file)
|
||
{
|
||
FILE *fp = NULL;
|
||
char buffer[BUFFER], *temp = NULL, *command = NULL;
|
||
|
||
command = (char *)alloca(BUFFER);
|
||
|
||
memset(buffer, 0, BUFFER);
|
||
memset(command, 0, BUFFER);
|
||
|
||
memcpy(command, "tail -n 12 ", 11);
|
||
strcat(command, file);
|
||
|
||
fp = popen(command, "r");
|
||
if (fp == NULL) {
|
||
perror("popen");
|
||
return -1;
|
||
}
|
||
|
||
while (fgets(buffer, BUFFER, fp) != NULL) {
|
||
//printf("%s", buffer);
|
||
temp = strstr(buffer, "Infected");
|
||
if (temp)
|
||
sscanf(temp, "Infected files: %32s", temp);
|
||
|
||
if (temp != NULL) {
|
||
//printf("%s\n", temp);
|
||
break;
|
||
}
|
||
}
|
||
|
||
pclose(fp);
|
||
|
||
if (temp != NULL) {
|
||
printf("%d\n", atoi(temp));
|
||
return atoi(temp);
|
||
} else {
|
||
return -1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int update_freshclam(int argc, char *argv[])
|
||
{
|
||
if (DEBISN_SYSTEM == check_system() || CENTOS_SYSTEM == check_system()) {
|
||
char **fre_argv;
|
||
int fre_argc = 0;
|
||
char *fre_argvss[ARGS_NUM] = { NULL };
|
||
|
||
fre_argvss[0] = argv[0];
|
||
fre_argvss[1] = "--user=root";
|
||
fre_argvss[2] = "--quiet";
|
||
fre_argvss[3] = "--no-warnings";
|
||
fre_argv = &(fre_argvss[0]);
|
||
fre_argc = 2;
|
||
|
||
// freshclam配置文件
|
||
if (access("/etc/clamav/freshclam.conf", F_OK) == -1) {
|
||
system("mkdir -p /etc/clamav/");
|
||
system("cp freshclam.conf /etc/clamav/");
|
||
}
|
||
// 打印clamav参数
|
||
for (int i = 0; i < fre_argc; i++) {
|
||
printf("%s %d\n", fre_argv[i], i);
|
||
}
|
||
|
||
pid_t pid;
|
||
pid = fork();
|
||
if (pid < 0) {
|
||
printf("fork error.\n");
|
||
return -1;
|
||
} else if (pid == 0) // child process
|
||
{
|
||
int r = 0;
|
||
r = _freshclam(fre_argc, fre_argv);
|
||
_exit(r);
|
||
} else {
|
||
int status = 0;
|
||
wait(&status); // wait the end of child process
|
||
if (WIFEXITED(status)) {
|
||
;
|
||
printf("child process return %d\n", WEXITSTATUS(status));
|
||
}
|
||
|
||
sleep(3);
|
||
return 0;
|
||
}
|
||
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
static char help_information(void)
|
||
{
|
||
static const char name[] = "Rhost";
|
||
static const char subject[] = "Reject host&scan for viruses (Rhost 拒绝主机并扫描病毒)";
|
||
static const struct {
|
||
const char *email;
|
||
const char *version;
|
||
} author = {
|
||
"AIXIAO@AIXIAO.ME",
|
||
"1.0",
|
||
};
|
||
|
||
static const char usage[] = "Usage: [-?h] [-d]";
|
||
static const char *s_help[] = {
|
||
"",
|
||
"Options:",
|
||
" -d : Background running",
|
||
" -? -h --help : help information",
|
||
" The configuration file needs to be in the same directory as the executable file!",
|
||
" 配置文件需要与可执行文件位于同一目录中!",
|
||
"",
|
||
"",
|
||
0
|
||
};
|
||
|
||
fprintf(stderr, " %s %s\n", name, subject);
|
||
fprintf(stderr, "Author: %s\n", author.email);
|
||
fprintf(stderr, "Version: %s\n", author.version);
|
||
fprintf(stderr, "%s\n", usage);
|
||
|
||
int l;
|
||
for (l = 0; s_help[l]; l++) {
|
||
fprintf(stderr, "%s\n", s_help[l]);
|
||
}
|
||
|
||
BUILD("Compile、link.\n");
|
||
puts("");
|
||
|
||
return 0;
|
||
}
|
||
|
||
int main(int argc, char *argv[], char **env)
|
||
{
|
||
|
||
signal(SIGCHLD, sig_child); // 创建捕捉子进程退出信号
|
||
|
||
int pid;
|
||
int i;
|
||
char move[BUFFER];
|
||
|
||
// 读取配置
|
||
char path[BUFFER] = { 0 };
|
||
char executable_filename[BUFFER] = { 0 };
|
||
(void)get_executable_path(path, executable_filename, sizeof(path));
|
||
strcat(executable_filename, ".conf");
|
||
strcat(path, executable_filename);
|
||
|
||
if (NULL != argv[1]) {
|
||
if (0 == strcmp(argv[1], "-v") || 0 == strcmp(argv[1], "--version") || 0 == strcmp(argv[1], "-h") || 0 == strcmp(argv[1], "--help") || 0 == strcmp(argv[1], "-?")) {
|
||
help_information();
|
||
exit(0);
|
||
}
|
||
}
|
||
|
||
if (1 == access(path, F_OK)) {
|
||
printf("配置文件不存在!\n");
|
||
}
|
||
conf *conf = (struct CONF *)malloc(sizeof(struct CONF));
|
||
read_conf(path, conf);
|
||
//ptintf_conf(conf);
|
||
|
||
if (0 >= conf->CLAMAV_ARG_LEN) {
|
||
printf("\033[31mError reading configuration file, please check the configuration file!\033[0m\n");
|
||
|
||
exit(-1);
|
||
}
|
||
// 更新病毒库
|
||
update_freshclam(argc, argv);
|
||
|
||
// 创建移除目录
|
||
if (conf->CLAMAV_ARG) {
|
||
char temp[BUFFER];
|
||
char *p, *p1;
|
||
|
||
memset(temp, 0, BUFFER);
|
||
memset(move, 0, BUFFER);
|
||
|
||
p = strstr(conf->CLAMAV_ARG, "--move=");
|
||
if (p != NULL) {
|
||
p1 = strstr(p, " ");
|
||
|
||
if ((p1 - p) > 7) {
|
||
memcpy(temp, p, p1 - p);
|
||
p = strstr(temp, "=");
|
||
|
||
strcpy(move, "mkdir -p ");
|
||
strcat(move, p + 1);
|
||
|
||
//printf("%s %ld \n", move, _strlen(move));
|
||
|
||
system(move);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 处理clamav参数
|
||
char **head_argvs;
|
||
int head_argc = 0;
|
||
char *argvs[ARGS_NUM] = { NULL };
|
||
char args[ARGS_NUM][WHITELIST_IP_NUM] = { { 0 }, { 0 } };
|
||
if (argc > 3) // 手动输入参数(如果手动输入参数个数大于3个, 则使用用户输入的参数)
|
||
{
|
||
process_argv(argc, argv, &(argvs[0]));
|
||
head_argvs = &(argvs[0]); // head_argvs指向argvs[0]
|
||
head_argc = argc - 1; // 改变argc数
|
||
|
||
} else // 读取配置文件参数
|
||
{
|
||
argvs[0] = argv[0];
|
||
split_string(conf->CLAMAV_ARG, " ", args);
|
||
for (i = 1; i < ARGS_NUM; i++) {
|
||
if (args[i][0] == '\0') {
|
||
continue;
|
||
} else {
|
||
argvs[i] = strdup(args[i]);
|
||
head_argc++;
|
||
}
|
||
}
|
||
|
||
head_argvs = &(argvs[0]);
|
||
head_argc += 1;
|
||
}
|
||
|
||
/*
|
||
// 打印clamav参数
|
||
for(int i=0; i<head_argc; i++)
|
||
{
|
||
printf("%s %d\n", head_argvs[i], i);
|
||
}
|
||
|
||
_clamscan(head_argc, head_argvs);
|
||
*/
|
||
|
||
now_next_time *t = (now_next_time *) malloc(sizeof(struct now_next_time));
|
||
memset(t, 0, sizeof(struct now_next_time));
|
||
|
||
// 获取公网IP
|
||
public_ip = GET_PUBLIC_IP(conf->PUBLIC_IP);
|
||
//printf("%s", public_ip);
|
||
|
||
if (0 == strcmp(conf->DAEMON, "on")) {
|
||
goto goto_daemon;
|
||
}
|
||
|
||
if (argv[1] != NULL && 0 == strcmp(argv[1], "-d")) {
|
||
goto_daemon:
|
||
|
||
// 守护进程
|
||
if ((pid = fork()) < 0) {
|
||
return 0;
|
||
} else if (0 != pid) {
|
||
for (i = 1; i < head_argc; i++) {
|
||
if (head_argvs[i])
|
||
free(head_argvs[i]);
|
||
}
|
||
free(t);
|
||
free_conf(conf);
|
||
free(conf);
|
||
free(public_ip);
|
||
exit(0);
|
||
}
|
||
|
||
if (setsid() < 0) {
|
||
return 0;
|
||
}
|
||
|
||
signal(SIGHUP, SIG_IGN);
|
||
if ((pid = fork()) < 0) {
|
||
return 0;
|
||
} else if (0 != pid) {
|
||
for (i = 1; i < head_argc; i++) {
|
||
if (head_argvs[i])
|
||
free(head_argvs[i]);
|
||
}
|
||
free(t);
|
||
free_conf(conf);
|
||
free(conf);
|
||
free(public_ip);
|
||
exit(0);
|
||
}
|
||
|
||
// 进程优先级
|
||
if (-1 == (nice(-20)))
|
||
perror("nice");
|
||
|
||
|
||
// 处理Nginx
|
||
pid_t pid = fork(); // 创建子进程
|
||
if (pid == 0) {
|
||
printf("The parent process processes Nginx logs!!!\n");
|
||
while (1)
|
||
{
|
||
nginx_read_log(conf->NGINX_LOG_FILE, conf);
|
||
sleep(1);
|
||
}
|
||
}
|
||
|
||
while (1)
|
||
{
|
||
// Cron
|
||
struct tm *calnext; //取得Cron规则时间
|
||
calnext = (struct tm *)malloc(sizeof(struct tm));
|
||
memset(calnext, 0, sizeof(struct tm));
|
||
_crontab(&calnext, conf->CLAMAV_TIME);
|
||
t->next_year = 1900 + calnext->tm_year;
|
||
t->next_mon = 1 + calnext->tm_mon;
|
||
t->next_day = calnext->tm_mday;
|
||
t->next_hour = calnext->tm_hour;
|
||
t->next_min = calnext->tm_min;
|
||
t->next_sec = calnext->tm_sec;
|
||
|
||
// 取得现在时间
|
||
time_t timep;
|
||
struct tm *p;
|
||
timep = time(NULL);
|
||
p = localtime(&timep);
|
||
t->now_year = 1900 + p->tm_year;
|
||
t->now_mon = 1 + p->tm_mon;
|
||
t->now_day = p->tm_mday;
|
||
t->now_hour = p->tm_hour;
|
||
t->now_min = p->tm_min;
|
||
t->now_sec = p->tm_sec;
|
||
|
||
//printf("当前时间 %d%d%d %d:%d:%d\n", t->now_year, t->now_mon, t->now_day, t->now_hour, t->now_min, t->now_sec);
|
||
//printf("CRON %d%d%d %d:%d:%d\n", t->next_year, t->next_mon, t->next_day, t->next_hour, t->next_min, t->next_sec);
|
||
|
||
// Clamav call
|
||
if (1 == conf->CLAMAV) {
|
||
if (t->now_year == t->next_year && t->now_mon == t->next_mon && t->now_day == t->next_day && \
|
||
t->now_hour == t->next_hour && t->now_min == t->next_min) {
|
||
//printf("%d%d%d %d:%d:%d\n", t->now_year, t->now_mon, t->now_day, t->now_hour, t->now_min, t->now_sec);
|
||
//printf("%d%d%d %d:%d:%d\n", t->next_year, t->next_mon, t->next_day, t->next_hour, t->next_min, t->next_sec);
|
||
|
||
pid_t pid;
|
||
pid = fork();
|
||
if (pid < 0) {
|
||
printf("fork error.\n");
|
||
return -1;
|
||
} else if (pid == 0) // child process
|
||
{
|
||
int r = 0;
|
||
int virus_files = -1;
|
||
|
||
// 扫描病毒前,更新病毒库
|
||
update_freshclam(argc, argv);
|
||
|
||
r = _clamscan(head_argc, head_argvs);
|
||
virus_files = get_clamav_log("clamscan.log");
|
||
|
||
if (virus_files > 0) {
|
||
if (conf->IS_QQMAIL == 1) {
|
||
QQ_mail_warning_Virus_files(public_ip, virus_files, conf);
|
||
sleep(3);
|
||
}
|
||
}
|
||
|
||
// 磁盘告警
|
||
if (1 == conf->IS_DISK) {
|
||
disk_usage(conf, public_ip, conf->DISK_USE);
|
||
}
|
||
|
||
_exit(r);
|
||
|
||
} else {
|
||
int status = 0;
|
||
wait(&status); // wait the end of child process
|
||
if (WIFEXITED(status)) {
|
||
;
|
||
//printf("child process return %d\n", WEXITSTATUS(status));
|
||
}
|
||
|
||
sleep(60); // 跳过这一分钟
|
||
}
|
||
}
|
||
}
|
||
// 封禁非法IP
|
||
rule(conf);
|
||
sleep(conf->TIME);
|
||
|
||
}
|
||
} else {
|
||
;
|
||
}
|
||
|
||
free(t);
|
||
free_conf(conf);
|
||
free(conf);
|
||
free(public_ip);
|
||
for (i = 1; i < head_argc; i++) {
|
||
if (head_argvs[i])
|
||
free(head_argvs[i]);
|
||
}
|
||
|
||
return 0;
|
||
}
|