From b5bd70ec7120e4672b3ab8f094cc7fe507b8dbff Mon Sep 17 00:00:00 2001 From: aixiao Date: Tue, 21 May 2024 09:08:14 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Ng=E9=98=B2=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 2 +- nginx.c | 127 +++ nginx.h | 15 + rhost.c | 2536 ++++++++++++++++++++++++---------------------------- rhost.conf | 9 +- rhost.h | 2 - 6 files changed, 1304 insertions(+), 1387 deletions(-) create mode 100644 nginx.c create mode 100644 nginx.h diff --git a/Makefile b/Makefile index d49ab05..1aaca5f 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ LIBCOMMON_LIB += ./clamav/common/cert_util.c.o ./clamav/common/actions.c.o ./cla all: libclamav_rust libclamav rhost -rhost: conf.o rhost.o libiptc.o ccronexpr.o +rhost: conf.o rhost.o libiptc.o ccronexpr.o nginx.o $(CC) $(ip2region_CFLAGS) ip2region/ip2region.c $(CC) $(ip2region_CFLAGS) ip2region/xdb_searcher.c $(CC) $(cJSON_CFLAGS) cJSON/cJSON.c diff --git a/nginx.c b/nginx.c new file mode 100644 index 0000000..d9db137 --- /dev/null +++ b/nginx.c @@ -0,0 +1,127 @@ +#include "nginx.h" +#include "ip2region/ip2region.h" + +#define EVENT_SIZE (sizeof(struct inotify_event)) +#define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16)) +#define INITIAL_BUFFER_SIZE 8192 + +int IP_location(char *string) { + char *area = NULL; + char *xdb_path = "ip2region.xdb"; + char *p = strchr(string, ' '); + char IP[64]; + memset(IP, 0, 64); + + if ((p - string) > 0) { + memmove(IP, string, p - string); + } else { + printf("Invalid IP string format.\n"); + return -1; + } + + if (access(xdb_path, F_OK) == -1) { // 判断 ip2region 地址定位库是否存在 + xdb_path = "ip2region/ip2region.xdb"; + if (access(xdb_path, F_OK) == -1) { + printf("ip2region.xdb DOESN'T EXIST!\n"); + return -1; + } + } + + area = ip2region(xdb_path, IP); + if (area == NULL) { + printf("ip2region解析地域错误\n"); + return -1; + } + + printf("IP地址:%s, %s\n", IP, area); + + return 0; +} + +void nginx_read_log(const char *filename) { + int fd = open(filename, O_RDONLY); + if (fd == -1) { + perror("open"); + exit(EXIT_FAILURE); + } + + // Move to the end of the file + if (lseek(fd, 0, SEEK_END) == -1) { + perror("lseek"); + close(fd); + exit(EXIT_FAILURE); + } + + int inotify_fd = inotify_init(); + if (inotify_fd < 0) { + perror("inotify_init"); + close(fd); + exit(EXIT_FAILURE); + } + + int wd = inotify_add_watch(inotify_fd, filename, IN_MODIFY); + if (wd == -1) { + perror("inotify_add_watch"); + close(inotify_fd); + close(fd); + exit(EXIT_FAILURE); + } + + char buffer[EVENT_BUF_LEN]; + + // Set the file descriptor to non-blocking mode + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) { + perror("fcntl F_GETFL"); + inotify_rm_watch(inotify_fd, wd); + close(inotify_fd); + close(fd); + exit(EXIT_FAILURE); + } + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { + perror("fcntl F_SETFL"); + inotify_rm_watch(inotify_fd, wd); + close(inotify_fd); + close(fd); + exit(EXIT_FAILURE); + } + + // Initial dynamic buffer allocation + size_t buffer_size = INITIAL_BUFFER_SIZE; + char *read_buf = alloca(buffer_size); + if (!read_buf) { + perror("alloca"); + inotify_rm_watch(inotify_fd, wd); + close(inotify_fd); + close(fd); + exit(EXIT_FAILURE); + } + + while (1) { + int length = read(inotify_fd, buffer, EVENT_BUF_LEN); + if (length < 0) { + perror("read"); + break; + } + + for (int i = 0; i < length;) { + struct inotify_event *event = (struct inotify_event *) &buffer[i]; + if (event->mask & IN_MODIFY) { + int bytes_read; + while ((bytes_read = read(fd, read_buf, buffer_size - 1)) > 0) { + read_buf[bytes_read] = '\0'; + IP_location(read_buf); + } + if (bytes_read == -1 && errno != EAGAIN) { + perror("read"); + break; + } + } + i += EVENT_SIZE + event->len; + } + } + + inotify_rm_watch(inotify_fd, wd); + close(inotify_fd); + close(fd); +} diff --git a/nginx.h b/nginx.h new file mode 100644 index 0000000..c5b0e66 --- /dev/null +++ b/nginx.h @@ -0,0 +1,15 @@ +#ifndef NGINX_H +#define NGINX_H + +#include +#include +#include +#include +#include +#include +#include + + +extern void nginx_read_log(const char *filename); + +#endif diff --git a/rhost.c b/rhost.c index 13b2cc9..47a87c9 100644 --- a/rhost.c +++ b/rhost.c @@ -1,1379 +1,1157 @@ -#include "conf.h" -#include "rhost.h" -#include "libiptc.h" -#include "libclamav.h" -#include "clamscan.h" - -#include "ccronexpr.h" - -// 存储公网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); -} - -// 第三方邮箱告警, 磁盘使用率 -int QQ_mail_warning_Disk_Use(char *local_ip, int disk_use, 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, Disk usage reaches threshold!, Please handle!", temp); - snprintf(command, BUFFER, QQMAIL_DISK_USE, 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 *remove_space(char *str) -{ - unsigned int i = 0, j = 0; - unsigned int uLen = _strlen(str); - char *strRet; - - if (0 == uLen) - { - return '\0'; - } - - strRet = (char *)malloc(uLen + 2); - memset(strRet, 0, uLen + 2); - - for (i = 0; i < uLen; i++) - { - if (str[i] != ' ') - { - strRet[j++] = str[i]; - } - } - strRet[j] = '\0'; - - return strRet; -} - -// 磁盘使用率 -int disk_waring(int threshold) -{ - FILE *fp = NULL; - char buffer[BUFFER]; - char command[BUFFER]; - int is = 0; - - #define DF "for u in `df -mh | grep -E -e \".:.\" -e \"^/dev\" | awk '{print $5}' | sed 's|%%||g'`; do if test \"$u\" -ge %d; then echo \"$u\"; fi done" - - memset(buffer, 0, BUFFER); - memset(command, 0, BUFFER); - - snprintf(command, BUFFER, DF, threshold); - - //printf("%s\n", command); - fp = popen(command, "r"); - - while(fgets(buffer, BUFFER, fp) != NULL) - { - printf("%s", buffer); - is = 1; - break; - } - pclose(fp); - - - return is; -} - -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); -} - -int system_version() { - FILE *fp = fopen("/etc/debian_version", "r"); - - if (fp == NULL) { - perror("Failed to open file"); - return 0; - } - - char buff[256]; - if (fgets(buff, sizeof(buff), fp) == NULL) { - fclose(fp); - perror("Failed to read file"); - return 0; - } - - fclose(fp); - - size_t len = strlen(buff); - if (len <= 0) { - return 0; - } - - // Remove trailing newline character, if present - if (buff[len - 1] == '\n') { - buff[len - 1] = '\0'; - } - - return atoi(buff); -} - -// 封禁非法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 (system_version() >= 12) - { - if ((fp = popen(GE_12, "r")) == NULL) - { - perror("GE_12"); - return -1; - } - } - else - { - 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 xxxx -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= 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 } }; - //printf("%d\n", argc); - 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; iPUBLIC_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 (daemon(1, 1)) // 守护进程 - { - perror("daemon"); - return -1; - } -*/ - - // 守护进程 - if ((pid = fork()) < 0) { - return 0; - } else if (0 != pid) { - for(i=1; iCLAMAV_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) - { - if (disk_waring(conf->DISK_USE) == 1) - { - printf("Disk usage reaches threshold!, Please handle!\n"); - if (conf->IS_QQMAIL == 1) - { - QQ_mail_warning_Disk_Use(public_ip, 0, conf); - sleep(3); - } - } - else - { - printf("Disk usage does not reach threshold!\n"); - } - } - - _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); // 跳过这一分钟 - } - } - } - - - rule(conf); - sleep(conf->TIME); - } - } - else - { - - rule(conf); - } - - free(t); - free_conf(conf); - free(conf); - free(public_ip); - for(i=1; imemory, 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); +} + +// 第三方邮箱告警, 磁盘使用率 +int QQ_mail_warning_Disk_Use(char *local_ip, int disk_use, 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, Disk usage reaches threshold!, Please handle!", temp); + snprintf(command, BUFFER, QQMAIL_DISK_USE, 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 *remove_space(char *str) +{ + unsigned int i = 0, j = 0; + unsigned int uLen = _strlen(str); + char *strRet; + + if (0 == uLen) { + return '\0'; + } + + strRet = (char *)malloc(uLen + 2); + memset(strRet, 0, uLen + 2); + + for (i = 0; i < uLen; i++) { + if (str[i] != ' ') { + strRet[j++] = str[i]; + } + } + strRet[j] = '\0'; + + return strRet; +} + +// 磁盘使用率 +int disk_waring(int threshold) +{ + FILE *fp = NULL; + char buffer[BUFFER]; + char command[BUFFER]; + int is = 0; + +#define DF "for u in `df -mh | grep -E -e \".:.\" -e \"^/dev\" | awk '{print $5}' | sed 's|%%||g'`; do if test \"$u\" -ge %d; then echo \"$u\"; fi done" + + memset(buffer, 0, BUFFER); + memset(command, 0, BUFFER); + + snprintf(command, BUFFER, DF, threshold); + + //printf("%s\n", command); + fp = popen(command, "r"); + + while (fgets(buffer, BUFFER, fp) != NULL) { + printf("%s", buffer); + is = 1; + break; + } + pclose(fp); + + return is; +} + +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 + // 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 } }; + //printf("%d\n", argc); + 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; iPUBLIC_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"); + + pid_t pid; + + // 创建一个新进程 + pid = fork(); + + if (pid < 0) { + // fork 失败 + fprintf(stderr, "Fork 失败\n"); + return 1; + } else if (pid == 0) { + // 子进程 + 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) { + if (disk_waring(conf->DISK_USE) == 1) { + printf("Disk usage reaches threshold!, Please handle!\n"); + if (conf->IS_QQMAIL == 1) { + QQ_mail_warning_Disk_Use(public_ip, 0, conf); + sleep(3); + } + } else { + printf("Disk usage does not reach threshold!\n"); + } + } + + _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); // 跳过这一分钟 + } + } + } + + rule(conf); + sleep(conf->TIME); + } + } else { + // 父进程 + + while(1) + { + nginx_read_log("/usr/local/nginx/logs/access.log"); + + sleep(1); + } + } + } 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; +} diff --git a/rhost.conf b/rhost.conf index 9a8e828..8cc3011 100644 --- a/rhost.conf +++ b/rhost.conf @@ -13,7 +13,7 @@ global { REFUSE_NUMBER = 3; // 拒绝攻击次数 CLAMAV = 1; // clamav 是否扫描病毒(1开启,非1关闭) - CLAMAV_ARG = "-r / --exclude-dir=^/sys|^/dev|^/proc|^/opt/infected --move=/opt/infected --max-filesize 1024M -l clamscan.log"; + CLAMAV_ARG = "-r / --exclude-dir=^/sys|^/dev|^/proc|^/opt/infected|^/root|^/home|^/mnt|^/usr|^/var --move=/opt/infected --max-filesize 1024M -l clamscan.log"; CLAMAV_TIME = "* 7 23 * * *"; // clamav 扫描时间(Cron格式, 秒 分 时 天 月 周) @@ -23,18 +23,17 @@ global { REGION = 1; // 是否启用地域白名单(1开启,非1关闭) IP2REGION = 1; // 是否使用本地 ip2region 地址定位库(1使用,非1不使用) - REGION_URL = "http://opendata.baidu.com/api.php?query=%s&co=&resource_id=6006&oe=utf8"; // 获取IP地域API REGION_LIST = "河南 郑州 上海"; // 地域列表(空格隔开) IS_MAIL = 0; // 开启邮件告警(1开启,非1关闭) - IS_DING_WEBHOOK = 0; // 开启叮叮告警(1开启,非1关闭) + IS_DING_WEBHOOK = 1; // 开启叮叮告警(1开启,非1关闭) PHONE = "15565979082"; // @的人手机号 - DING_WEBHOOK = "https://oapi.dingtalk.com/robot/send?access_token=7f069c672cb878987aa6772cca336740eece4ce36bde12b51b45e9f440e0565a"; // 钉钉WEBHOOK + DING_WEBHOOK = "https://oapi.dingtalk.com/robot/send?access_token=396bce0384cded025087cff3c176ea5e9afb9bd8fcaa46d6fa8c51dd172ba513"; // 钉钉WEBHOOK - IS_QQMAIL = 1; // 开启QQ邮箱告警(默认使用gomail:https://git.aixiao.me/aixiao/gomail.git)(1开启,非1关闭) + IS_QQMAIL = 1; // 开启QQ邮箱告警(默认使用gomail: https://git.aixiao.me/aixiao/gomail.git)(1开启,非1关闭) RECV_MAIL = "1605227279@qq.com"; // 接收者邮箱 } diff --git a/rhost.h b/rhost.h index 393db05..950dee2 100644 --- a/rhost.h +++ b/rhost.h @@ -107,9 +107,7 @@ void cron_free(void* p) #define BUILD(fmt...) do { fprintf(stderr,"%s %s ",__DATE__,__TIME__); fprintf(stderr, ##fmt); } while(0) #define AWK " | awk -v num=%d '{a[$1]+=1;} END {for(i in a){if (a[i] >= num) {print i;}}}' " - #define GE_10 "grep -E \"^$(LC_ALL=\"C\" date \"+%h\").$(LC_ALL=\"C\" date \"+%d\")\" /var/log/auth.log | grep failure | grep rhost" -#define GE_12 "grep -E \"^$(LC_ALL=\"C\" date +\"%Y-%m-%d\")\" /var/log/auth.log | grep failure | grep rhost" #define LE_10 "grep -E \"^$(LC_ALL=\"C\" date \"+%h\")..$(LC_ALL=\"C\" date | awk '{print $3}')\" /var/log/auth.log | grep failure | grep rhost" #define CENTOS_GE_10 "grep -E \"^$(LC_ALL=\"C\" date \"+%h\").$(LC_ALL=\"C\" date \"+%d\")\" /var/log/secure | grep failure | grep rhost"