diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c968ffd --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "files.associations": { + "assert.h": "c", + "ip2region.h": "c", + "conf.h": "c", + "ccronexpr.h": "c", + "clamscan.h": "c", + "libiptc.h": "c", + "stdio.h": "c", + "nginx.h": "c" + } +} \ No newline at end of file diff --git a/Makefile b/Makefile index d49ab05..008976e 100644 --- a/Makefile +++ b/Makefile @@ -36,9 +36,9 @@ LIBCOMMON__CFLAGS += -DHAVE_CONFIG_H -I./clamav/common -I./clamav/libclamav -I./ LIBCOMMON_LIB += ./clamav/common/cert_util.c.o ./clamav/common/actions.c.o ./clamav/common/clamdcom.c.o ./clamav/common/getopt.c.o ./clamav/common/hostid.c.o ./clamav/common/idmef_logging.c.o ./clamav/common/misc.c.o ./clamav/common/optparser.c.o ./clamav/common/output.c.o ./clamav/common/tar.c.o ./clamav/common/linux/cert_util_linux.c.o -all: libclamav_rust libclamav rhost +all: libclamav_rust libclamav rhost nginx.o -rhost: conf.o rhost.o libiptc.o ccronexpr.o +rhost: conf.o rhost.o libiptc.o ccronexpr.o nginx.o disk.o $(CC) $(ip2region_CFLAGS) ip2region/ip2region.c $(CC) $(ip2region_CFLAGS) ip2region/xdb_searcher.c $(CC) $(cJSON_CFLAGS) cJSON/cJSON.c @@ -69,7 +69,7 @@ libclamav: test: - echo $(CMAKE) $(ARCH) + echo $(CMAKE) $(ARCH) $(CFLAGS) static: conf.o rhost.o libiptc.o $(CC) $(IPTC_CFLAGS) -c libiptc/libip4tc.c -o libiptc/libip4tc.o diff --git a/README.md b/README.md index 552ae6f..1b9d2aa 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Debian freshclam # 更新病毒库(必要) - Debian系统使用libiptc库需要nftables切换到iptables + Debian系统使用libiptc库需要nftables切换到iptables (使用了libip4tc-dev库) Switching to the legacy version:(切换到 iptables) update-alternatives --set iptables /usr/sbin/iptables-legacy update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy @@ -65,10 +65,27 @@ make all ./rhost # 只处理非法攻击 -关闭 +关闭: killall rhost +帮助: +./rhost -h + Rhost Reject host&scan for viruses (Rhost 拒绝主机并扫描病毒) +Author: AIXIAO@AIXIAO.ME +Version: 1.0 +Usage: [-?h] [-d] + +Options: + -d : Background running + -? -h --help : help information + The configuration file needs to be in the same directory as the executable file! + 配置文件需要与可执行文件位于同一目录中! + + +May 22 2024 15:05:59 Compile、link. + + ``` ```text @@ -78,8 +95,10 @@ global { DAEMON = "off"; // on开启后台运行,off不开启(弃用) TIME = "10"; // 睡眠时间(大于等于1,单位秒) + PUBLIC_IP = "http://inet-ip.info"; // 获取公网IP + IS_DISK = 1; // 磁盘使用率(1开启,非1关闭) DISK_USE = 95; // 任意某块磁盘使用率告警(大于等于1) @@ -87,32 +106,36 @@ global { IS_BLOCKED = 1; // 是否封禁攻击IP(1开启,非1关闭) REFUSE_NUMBER = 3; // 拒绝攻击次数 + CLAMAV = 1; // clamav 是否扫描病毒(1开启,非1关闭) 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 = "* 1 4 * * *"; // clamav 扫描时间(Cron格式, 秒 分 时 天 月 周) + CLAMAV_TIME = "* 45 11 * * *"; // clamav 扫描时间(Cron格式, 秒 分 时 天 月 周) IPV4_RESTRICTION = 1; // 是否启用IP白名单(1开启,非1关闭) - IPV4_WHITE_LIST = "1.1.1.1 2.2.2.2 "; // IP白名单(空格隔开) + IPV4_WHITE_LIST = "1.1.1.1 2.2.2.2 "; // IP白名单(空格隔开) 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地域(aliyun付费API, 弃用) - REGION_URL = "https://api01.aliyun.venuscn.com/ip?ip=%s -H Authorization:APPCODE a1d842b8afda418c8ea24271a4e16b1f"; - REGION_LIST = "河南 郑州 上海"; // 地域列表(空格隔开) + REGION_LIST = "河南 郑州 上海"; // 地域列表(空格隔开) + + + NGINX = 1; // 是否启用Nginx白名单 + NGINX_LOG_FILE= "/usr/local/nginx/logs/access.log"; // Nginx 日志文件 + NGINX_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关闭) - RECV_MAIL = "1605227279@qq.com"; // 接收者QQ + IS_QQMAIL = 1; // 开启QQ邮箱告警(默认使用gomail: https://git.aixiao.me/aixiao/gomail.git)(1开启,非1关闭) + RECV_MAIL = "1605227279@qq.com"; // 接收者邮箱 } diff --git a/clamav/libclamav_rust/.rustc_info.json b/clamav/libclamav_rust/.rustc_info.json index c486876..cc7f143 100644 --- a/clamav/libclamav_rust/.rustc_info.json +++ b/clamav/libclamav_rust/.rustc_info.json @@ -1 +1 @@ -{"rustc_fingerprint":537842707314038760,"outputs":{"10376369925670944939":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/usr\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.63.0\nbinary: rustc\ncommit-hash: unknown\ncommit-date: unknown\nhost: x86_64-unknown-linux-gnu\nrelease: 1.63.0\nLLVM version: 14.0.6\n","stderr":""},"15493033989842322569":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/usr\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"15697416045686424142":{"success":false,"status":"exit status: 1","code":1,"stdout":"","stderr":"error: `-Csplit-debuginfo` is unstable on this platform\n\n"},"9218888252049904301":{"success":false,"status":"exit status: 1","code":1,"stdout":"","stderr":"error: `-Csplit-debuginfo` is unstable on this platform\n\n"}},"successes":{}} \ No newline at end of file +{"rustc_fingerprint":5376818386984183904,"outputs":{"14371922958718593042":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/usr\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.71.1\nbinary: rustc\ncommit-hash: unknown\ncommit-date: unknown\nhost: x86_64-unknown-linux-gnu\nrelease: 1.71.1\nLLVM version: 16.0.6\n","stderr":""},"15729799797837862367":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/usr\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""}},"successes":{}} \ No newline at end of file diff --git a/conf.c b/conf.c index 7c189bc..03a7178 100644 --- a/conf.c +++ b/conf.c @@ -218,6 +218,24 @@ static void parse_global_module(char *content, conf * conf) val_begin_len = val_end - val_begin; conf->DISK_USE = atoi(val_begin); } + + // NGINX + if (strcasecmp(var, "NGINX") == 0) { + val_begin_len = val_end - val_begin; + conf->NGINX = atoi(val_begin); + } + if (strcasecmp(var, "NGINX_LOG_FILE") == 0) { + val_begin_len = val_end - val_begin; + conf->NGINX_LOG_FILE_LEN = val_begin_len; + if (copy_new_mem(val_begin, val_begin_len, &conf->NGINX_LOG_FILE) != 0) + return; + } + if (strcasecmp(var, "NGINX_REGION_LIST") == 0) { + val_begin_len = val_end - val_begin; + conf->NGINX_REGION_LIST_LEN = val_begin_len; + if (copy_new_mem(val_begin, val_begin_len, &conf->NGINX_REGION_LIST) != 0) + return; + } content = strchr(lineEnd + 1, '\n'); } @@ -313,6 +331,11 @@ void free_conf(conf * conf) if (conf->CLAMAV_ARG) free(conf->CLAMAV_ARG); + // NGINX + if (conf->NGINX_LOG_FILE) + free(conf->NGINX_LOG_FILE); + if (conf->NGINX_REGION_LIST) + free(conf->NGINX_REGION_LIST); return; } @@ -351,6 +374,12 @@ void ptintf_conf(conf * conf) if (conf->CLAMAV_ARG) printf("CLAMAV_ARG %s %d\n", conf->CLAMAV_ARG, conf->CLAMAV_ARG_LEN); + + // Nginx + if (conf->NGINX_LOG_FILE) + printf("CLAMAV_ARG %s %d\n", conf->NGINX_LOG_FILE, conf->NGINX_LOG_FILE_LEN); + if (conf->NGINX_REGION_LIST) + printf("CLAMAV_ARG %s %d\n", conf->NGINX_REGION_LIST, conf->NGINX_REGION_LIST_LEN); } void split_string(char string[], char delims[], char (*whitelist_ip)[WHITELIST_IP_NUM]) diff --git a/conf.h b/conf.h index 62e9309..6a9595b 100644 --- a/conf.h +++ b/conf.h @@ -43,6 +43,13 @@ typedef struct CONF int IPV4_RESTRICTION; char *IPV4_WHITE_LIST; int IPV4_WHITE_LIST_LEN; + + // NGINX + int NGINX; + char *NGINX_LOG_FILE; + int NGINX_LOG_FILE_LEN; + char *NGINX_REGION_LIST; + int NGINX_REGION_LIST_LEN; int IS_MAIL; diff --git a/disk.c b/disk.c new file mode 100644 index 0000000..50da416 --- /dev/null +++ b/disk.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include + +// 获取指定路径的磁盘使用率 +int get_disk_usage(const char *path, double *usage) { + struct statvfs stat; + + if (statvfs(path, &stat) != 0) { + // 处理错误 + perror("statvfs failed"); + return -1; + } + + // 计算磁盘使用率 + unsigned long total_blocks = stat.f_blocks; + unsigned long free_blocks = stat.f_bfree; + unsigned long used_blocks = total_blocks - free_blocks; + + *usage = (double)used_blocks / total_blocks * 100; + return 0; +} + +int disk_usage() { + FILE *mounts; + struct mntent *ent; + + mounts = setmntent("/etc/mtab", "r"); + if (mounts == NULL) { + perror("setmntent failed"); + return 1; + } + + while ((ent = getmntent(mounts)) != NULL) + { + double usage = 0; + if (strstr(ent->mnt_fsname, "/dev/") != NULL) + { + //printf("%s %s %s\n", ent->mnt_fsname, ent->mnt_dir, ent->mnt_type); + + if (get_disk_usage(ent->mnt_dir, &usage) != 0) { + fprintf(stderr, "Failed to get disk usage for %s\n", ent->mnt_dir); + continue; + } + + int threshold = 1; + if (usage > threshold) { + printf("挂载点: %s 使用率: %.2f%% 阀值: %d%%\n", ent->mnt_dir, usage, threshold); + + } + } + + } + + endmntent(mounts); + return 0; +} diff --git a/disk.h b/disk.h new file mode 100644 index 0000000..7593432 --- /dev/null +++ b/disk.h @@ -0,0 +1,12 @@ +#ifndef DISK_H +#define DISK_H + +#include +#include +#include +#include +#include + +extern int disk_usage(); + +#endif diff --git a/libiptc.c b/libiptc.c index 3a98df9..1ebcb95 100644 --- a/libiptc.c +++ b/libiptc.c @@ -105,7 +105,8 @@ struct ipt_entry_match *get_match(const char *sports, const char *dports, unsign return match; } -int iptc_add_rule(const char *table, const char *chain, int protocol, const char *iniface, const char *outiface, unsigned int src, unsigned int dest, const char *srcports, const char *destports, const char *target, const char *dnat_to, const int append) +int iptc_add_rule(const char *table, const char *chain, int protocol, const char *iniface, const char *outiface, \ + unsigned int src, unsigned int dest, const char *srcports, const char *destports, const char *target, const char *dnat_to, const int append) { struct xtc_handle *handle; struct ipt_entry *chain_entry; @@ -116,6 +117,10 @@ int iptc_add_rule(const char *table, const char *chain, int protocol, const char int result = 0; chain_entry = (struct ipt_entry *)calloc(1, sizeof(*chain_entry)); + if (chain_entry == NULL) { + perror("calloc failed"); + return 1; + } if (src != 0) { chain_entry->ip.src.s_addr = src; @@ -137,7 +142,8 @@ int iptc_add_rule(const char *table, const char *chain, int protocol, const char if (IPPROTO_TCP == protocol) entry_match = get_match(srcports, destports, &chain_entry->nfcache, "tcp"); - if (strcmp(target, "") == 0 || strcmp(target, IPTC_LABEL_ACCEPT) == 0 || strcmp(target, IPTC_LABEL_DROP) == 0 || strcmp(target, IPTC_LABEL_QUEUE) == 0 || strcmp(target, IPTC_LABEL_RETURN) == 0) { + if (strcmp(target, "") == 0 || strcmp(target, IPTC_LABEL_ACCEPT) == 0 || strcmp(target, IPTC_LABEL_DROP) == 0 || \ + strcmp(target, IPTC_LABEL_QUEUE) == 0 || strcmp(target, IPTC_LABEL_RETURN) == 0) { size_t size; size = IPT_ALIGN(sizeof(struct ipt_entry_target)) + IPT_ALIGN(sizeof(int)); entry_target = (struct ipt_entry_target *)calloc(1, size); @@ -192,6 +198,7 @@ int iptc_add_rule(const char *table, const char *chain, int protocol, const char { result = iptc_insert_entry(labelit, chain_entry, 0, handle); } + if (!result) { printf("libiptc error: Can't add, %s\n", iptc_strerror(errno)); diff --git a/libiptc.h b/libiptc.h index 749a83f..6fed12d 100644 --- a/libiptc.h +++ b/libiptc.h @@ -30,6 +30,7 @@ #define IPTC_FULL_SIZE IPTC_ENTRY_SIZE + IPTC_MATCH_SIZE + IPTC_TARGET_SIZE int show_all_rule(char *ipv4); -int iptc_add_rule(const char *table, const char *chain, int protocol, const char *iniface, const char *outiface, unsigned int src, unsigned int dest, const char *srcports, const char *destports, const char *target, const char *dnat_to, const int append); +int iptc_add_rule(const char *table, const char *chain, int protocol, const char *iniface, const char *outiface, \ + unsigned int src, unsigned int dest, const char *srcports, const char *destports, const char *target, const char *dnat_to, const int append); #endif diff --git a/libiptc/iptc_delete_rule.c b/libiptc/iptc_delete_rule.c new file mode 100644 index 0000000..183dcea --- /dev/null +++ b/libiptc/iptc_delete_rule.c @@ -0,0 +1,275 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NFC_IP_SRC_PT 0x0200 +#define NFC_IP_DST_PT 0x0400 + +#ifndef IPT_MIN_ALIGN +#define IPT_MIN_ALIGN (__alignof__(struct ipt_entry)) +#endif + +#define IPT_ALIGN(s) (((s) + ((IPT_MIN_ALIGN)-1)) & ~((IPT_MIN_ALIGN)-1)) + +#define IPTC_ENTRY_SIZE XT_ALIGN(sizeof(struct ipt_entry)) +#define IPTC_MATCH_SIZE XT_ALIGN(sizeof(struct ipt_entry_match) + sizeof(struct ipt_udp)) +#define IPTC_TARGET_SIZE XT_ALIGN(sizeof(struct ipt_entry_target)) +#define IPTC_FULL_SIZE IPTC_ENTRY_SIZE + IPTC_MATCH_SIZE + IPTC_TARGET_SIZE + +#define IPTC_LABEL_ACCEPT "ACCEPT" +#define IPTC_LABEL_DROP "DROP" +#define IPTC_LABEL_QUEUE "QUEUE" +#define IPTC_LABEL_RETURN "RETURN" + +static u_int16_t parse_port(const char *port) +{ + return atoi(port); +} + +static void parse_ports(const char *portstring, u_int16_t *ports) +{ + char *buffer; + char *cp; + + buffer = strdup(portstring); + if ((cp = strchr(buffer, ':')) == NULL) + ports[0] = ports[1] = parse_port(buffer); + else { + *cp = '\0'; + cp++; + + ports[0] = buffer[0] ? parse_port(buffer) : 0; + ports[1] = cp[0] ? parse_port(cp) : 0xFFFF; + } + + free(buffer); +} + +struct ipt_entry_match *get_match(const char *sports, const char *dports, unsigned int *nfcache, const char *protocol) +{ + struct ipt_entry_match *match; + struct ipt_udp *udpinfo; + size_t size; + + size = IPT_ALIGN(sizeof(*match)) + IPT_ALIGN(sizeof(*udpinfo)); + match = (struct ipt_entry_match *)calloc(1, size); + if (!match) { + perror("calloc failed for match"); + return NULL; + } + + match->u.match_size = size; + strncpy(match->u.user.name, protocol, IPT_FUNCTION_MAXNAMELEN - 2); + match->u.user.name[IPT_FUNCTION_MAXNAMELEN - 2] = '\0'; + + udpinfo = (struct ipt_udp *)match->data; + udpinfo->spts[1] = udpinfo->dpts[1] = 0xFFFF; + + if (sports) { + *nfcache |= NFC_IP_SRC_PT; + parse_ports(sports, udpinfo->spts); + } + if (dports) { + *nfcache |= NFC_IP_DST_PT; + parse_ports(dports, udpinfo->dpts); + } + + return match; +} + +// 打印 IP 地址 +void print_ip_address(const char *label, struct in_addr addr) { + char ip_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &addr, ip_str, sizeof(ip_str)); + printf("%s: %s\n", label, ip_str); +} + +// 打印规则详细信息 +void print_rule(const char *label, const struct ipt_entry *entry) { + printf("%s:\n", label); + print_ip_address(" src", entry->ip.src); + print_ip_address(" dst", entry->ip.dst); + printf(" proto: %u\n", entry->ip.proto); + printf(" iniface: %s\n", entry->ip.iniface); + printf(" outiface: %s\n", entry->ip.outiface); + printf(" target_offset: %u\n", entry->target_offset); + printf(" next_offset: %u\n", entry->next_offset); + printf(" counters: pkts=%llu, bytes=%llu\n", entry->counters.pcnt, entry->counters.bcnt); +} + +// 打印匹配结构详细信息 +void print_match(const char *label, const struct ipt_entry_match *match) { + printf("%s:\n", label); + printf(" match_size: %u\n", match->u.user.match_size); + printf(" name: %s\n", match->u.user.name); + printf(" revision: %u\n", match->u.user.revision); + // 如果有特定协议的数据,例如 TCP 或 UDP,可以在这里打印具体内容 + // 例如:匹配 TCP 协议时,打印源端口和目标端口 + if (strcmp(match->u.user.name, "tcp") == 0) { + struct ipt_tcp *tcpinfo = (struct ipt_tcp *)match->data; + printf(" TCP source port: %u\n", ntohs(tcpinfo->spts[0])); + printf(" TCP destination port: %u\n", ntohs(tcpinfo->dpts[0])); + } else if (strcmp(match->u.user.name, "udp") == 0) { + struct ipt_udp *udpinfo = (struct ipt_udp *)match->data; + printf(" UDP source port: %u\n", ntohs(udpinfo->spts[0])); + printf(" UDP destination port: %u\n", ntohs(udpinfo->dpts[0])); + } +} + +// 打印目标结构详细信息 +void print_target(const char *label, const struct ipt_entry_target *target) { + printf("%s:\n", label); + printf(" target_size: %u\n", target->u.user.target_size); + printf(" name: %s\n", target->u.user.name); + printf(" revision: %u\n", target->u.user.revision); +} + +int iptc_delete_rule(const char *table, const char *chain, int protocol, const char *iniface, const char *outiface, \ + unsigned int src, unsigned int dest, const char *srcports, const char *destports, const char *target) +{ + struct xtc_handle *handle = NULL; + struct ipt_entry *chain_entry = NULL; + struct ipt_entry_match *entry_match = NULL; + struct ipt_entry_target *entry_target = NULL; + ipt_chainlabel labelit; + long match_size = 0; + int result = 1; // Default to failure + + // Initialize rule entry + chain_entry = (struct ipt_entry *)calloc(1, sizeof(*chain_entry)); + if (!chain_entry) { + perror("calloc failed"); + return 1; + } + + if (src != 0) { + chain_entry->ip.src.s_addr = src; + chain_entry->ip.smsk.s_addr = 0xFFFFFFFF; + } + if (dest != 0) { + chain_entry->ip.dst.s_addr = dest; + chain_entry->ip.dmsk.s_addr = 0xFFFFFFFF; + } + + if (iniface) + strncpy(chain_entry->ip.iniface, iniface, IFNAMSIZ); + if (outiface) + strncpy(chain_entry->ip.outiface, outiface, IFNAMSIZ); + + chain_entry->ip.proto = protocol; + if (IPPROTO_UDP == protocol) + entry_match = get_match(srcports, destports, &chain_entry->nfcache, "udp"); + if (IPPROTO_TCP == protocol) + entry_match = get_match(srcports, destports, &chain_entry->nfcache, "tcp"); + + print_match("entry_match", entry_match); + + if (strcmp(target, "") == 0 || strcmp(target, IPTC_LABEL_ACCEPT) == 0 || strcmp(target, IPTC_LABEL_DROP) == 0 || strcmp(target, IPTC_LABEL_QUEUE) == 0 || strcmp(target, IPTC_LABEL_RETURN) == 0) { + size_t size; + size = IPT_ALIGN(sizeof(struct ipt_entry_target)) + IPT_ALIGN(sizeof(int)); + entry_target = (struct ipt_entry_target *)calloc(1, size); + if (!entry_target) { + perror("calloc failed for target"); + goto cleanup; + } + entry_target->u.user.target_size = size; + strncpy(entry_target->u.user.name, target, XT_EXTENSION_MAXNAMELEN); + entry_target->u.user.name[XT_EXTENSION_MAXNAMELEN - 1] = '\0'; + } + match_size = entry_match ? entry_match->u.match_size : 0; + print_target("entry_target", entry_target); + + + struct ipt_entry *tmp_ipt = chain_entry; + chain_entry = (struct ipt_entry *)realloc(chain_entry, sizeof(*chain_entry) + match_size + entry_target->u.target_size); + if (!chain_entry) { + free(tmp_ipt); + free(entry_target); + if (entry_match) free(entry_match); + perror("realloc failed"); + return 1; + } + + memcpy(chain_entry->elems + match_size, entry_target, entry_target->u.target_size); + chain_entry->target_offset = sizeof(*chain_entry) + match_size; + chain_entry->next_offset = sizeof(*chain_entry) + match_size + entry_target->u.target_size; + if (entry_match) { + memcpy(chain_entry->elems, entry_match, match_size); + } + + handle = iptc_init(table); + if (!handle) { + fprintf(stderr, "libiptc error: Can't initialize table %s, %s\n", table, iptc_strerror(errno)); + goto cleanup; + } + + strncpy(labelit, chain, sizeof(ipt_chainlabel)); + labelit[sizeof(ipt_chainlabel) - 1] = '\0'; + if (0 == iptc_is_chain(labelit, handle)) { + fprintf(stderr, "libiptc error: Chain %s does not exist!\n", labelit); + goto cleanup; + } + + // 打印 chain_entry 详细信息 + print_rule("Chain entry", chain_entry); + + // Find and delete the matching rule + const struct ipt_entry *entry; + unsigned int rule_num = 0; + for (entry = iptc_first_rule(labelit, handle); entry; (entry = iptc_next_rule(entry, handle), ++rule_num)) + { + + if (entry->ip.proto == chain_entry->ip.proto && + entry->ip.src.s_addr == chain_entry->ip.src.s_addr && + entry->ip.dst.s_addr == chain_entry->ip.dst.s_addr && + strncmp(entry->ip.iniface, chain_entry->ip.iniface, IFNAMSIZ) == 0 && + strncmp(entry->ip.outiface, chain_entry->ip.outiface, IFNAMSIZ) == 0) + { + // 打印当前 entry 详细信息 + print_rule("Current entry", entry); + + printf("Matching rule found. Deleting rule number: %u\n", rule_num); + + // 删除规则 + if (!iptc_delete_num_entry(labelit, rule_num, handle)) { + fprintf(stderr, "libiptc error: Can't delete entry, %s\n", iptc_strerror(errno)); + iptc_free(handle); + return result; + } + + // 提交更改 + if (!iptc_commit(handle)) { + fprintf(stderr, "libiptc error: Commit error, %s\n", iptc_strerror(errno)); + } else { + printf("Rule deleted successfully\n"); + result = 0; + break; + } + } + } + +cleanup: + if (entry_match) free(entry_match); + if (entry_target) free(entry_target); + if (chain_entry) free(chain_entry); + if (handle) iptc_free(handle); + + return result; +} + +int main(int argc, char *argv[]) { + // Test deleting rule + unsigned int srcIp; + inet_pton(AF_INET, argv[1], &srcIp); + + int result = iptc_delete_rule("filter", "INPUT", IPPROTO_TCP, NULL, NULL, srcIp, 0, NULL, NULL, IPTC_LABEL_DROP); + + return result; +} diff --git a/nginx.c b/nginx.c new file mode 100644 index 0000000..f8dc501 --- /dev/null +++ b/nginx.c @@ -0,0 +1,155 @@ +#include "nginx.h" + +#define EVENT_SIZE (sizeof(struct inotify_event)) +#define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16)) +#define INITIAL_BUFFER_SIZE 8192 + +void nginx_iptc(char *ip) +{ + unsigned int srcIp; + inet_pton(AF_INET, ip, &srcIp); + iptc_add_rule("filter", "INPUT", IPPROTO_TCP, NULL, NULL, srcIp, 0, NULL, NULL, "DROP", NULL, 1); +} + +int IP_location(char *string, conf *config) +{ + char *area = NULL; + char *xdb_path = "ip2region.xdb"; + char *p = strchr(string, ' '); + char IP[64]; + memset(IP, 0, 64); + + char *t = _time(); + char nginx_region_list[WHITELIST_IP_NUM][WHITELIST_IP_NUM] = { { 0 }, { 0 } }; + char NGINX_REGION_LIST_COPY[config->NGINX_REGION_LIST_LEN + 1]; + + memset(NGINX_REGION_LIST_COPY, 0, config->NGINX_REGION_LIST_LEN + 1); + memcpy(NGINX_REGION_LIST_COPY, config->NGINX_REGION_LIST, config->NGINX_REGION_LIST_LEN); // 复制配置字符串,split_string()会改变原数据 + + split_string(NGINX_REGION_LIST_COPY, " ", nginx_region_list); + + // IP 地理位置获取 + 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; + } + + my_printf("IP地址:%s, %s\n", IP, area); + //printf("%s, %s\n", config->NGINX_LOG_FILE, config->NGINX_REGION_LIST); + + if (config->NGINX == 1) // 开启Nginx防御 + { + if (isregion(area, nginx_region_list) == 1) { // 返回1表示在白名单列表 + printf(RED "%s Nginx Ip Address: %s, 属于地域白名单: %s\n" COLOR_NONE, t, IP, area); + } else { + my_printf(RED "%s Nginx 封禁 Ip Address: %s, 地址: %s!!!\n" COLOR_NONE, t, IP, area); + nginx_iptc(IP); + } + } + + return 0; +} + +int nginx_read_log(const char *filename, conf *p) +{ + int fd = open(p->NGINX_LOG_FILE, O_RDONLY); + if (fd == -1) { + perror("open"); + + return -1; + } + // Move to the end of the file + if (lseek(fd, 0, SEEK_END) == -1) { + perror("lseek"); + close(fd); + return -1; + } + + int inotify_fd = inotify_init(); + if (inotify_fd < 0) { + perror("inotify_init"); + close(fd); + return -1; + } + + int wd = inotify_add_watch(inotify_fd, p->NGINX_LOG_FILE, IN_MODIFY); + if (wd == -1) { + perror("inotify_add_watch"); + close(inotify_fd); + close(fd); + return -1; + } + + 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); + return -1; + } + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { + perror("fcntl F_SETFL"); + inotify_rm_watch(inotify_fd, wd); + close(inotify_fd); + close(fd); + return -1; + } + // 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); + return -1; + } + + 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, p); + } + 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); + + return 0; +} diff --git a/nginx.h b/nginx.h new file mode 100644 index 0000000..e9f9be9 --- /dev/null +++ b/nginx.h @@ -0,0 +1,19 @@ +#ifndef NGINX_H +#define NGINX_H + +#include +#include +#include +#include +#include +#include +#include + +#include "rhost.h" +#include "libiptc.h" +#include "ip2region/ip2region.h" + + +extern int nginx_read_log(const char *filename, conf * conf); + +#endif diff --git a/rhost.c b/rhost.c index ed9b0ee..1cb5cf1 100644 --- a/rhost.c +++ b/rhost.c @@ -1,16 +1,96 @@ -#include "conf.h" #include "rhost.h" + #include "libiptc.h" #include "libclamav.h" #include "clamscan.h" - #include "ccronexpr.h" +#include "nginx.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 -{ +struct MemoryStruct { char *memory; size_t size; }; @@ -19,17 +99,16 @@ struct MemoryStruct int _strlen(char *str) { char *_p = NULL; - - + if (str == NULL) return 0; - + _p = strchr(str, '\0'); - + if (_p == NULL) return 0; - return _p-str; + return _p - str; } static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) @@ -39,8 +118,7 @@ static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, voi // 注意这里根据每次被调用获得的数据重新动态分配缓存区的大小 char *ptr = realloc(mem->memory, mem->size + realsize + 1); - if (ptr == NULL) - { + if (ptr == NULL) { /* 内存不足! */ printf("not enough memory (realloc returned NULL)\n"); return 0; @@ -129,9 +207,8 @@ char *process_json(char *buff, char *api) char *area = NULL; int area_len = 0; char *p = NULL; - - if (buff == NULL) - { + + if (buff == NULL) { return NULL; } @@ -140,7 +217,7 @@ char *process_json(char *buff, char *api) perror("cJSON_Parse"); return NULL; } - if ((p = strstr(api, "baidu")) != NULL) { // baidu Api + if ((p = strstr(api, "baidu")) != NULL) { // baidu Api int i; cJSON *data = cJSON_GetObjectItem(cjson_init, "data"); @@ -149,21 +226,19 @@ char *process_json(char *buff, char *api) 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); + + snprintf(area, area_len + 1, "%s", location->valuestring); } } else { return NULL; } - } - else - { + } else { cJSON_Delete(cjson_init); return NULL; } @@ -175,12 +250,9 @@ char *process_json(char *buff, char *api) // 检测系统 int check_system() { - if (0 == access("/etc/debian_version", F_OK)) - { + if (0 == access("/etc/debian_version", F_OK)) { return DEBISN_SYSTEM; - } - else if (0 == access("/etc/centos-release", F_OK)) - { + } else if (0 == access("/etc/centos-release", F_OK)) { return CENTOS_SYSTEM; } @@ -188,7 +260,7 @@ int check_system() } // 钉钉告警 -int dingding_warning(char *illegal_ip, char *public_ip, char *ip, conf * conf) +int dingding_warning(char *illegal_ip, char *public_ip, char *ip, conf *conf) { FILE *fp; char temp[64]; @@ -199,8 +271,7 @@ int dingding_warning(char *illegal_ip, char *public_ip, char *ip, conf * conf) strcpy(temp, public_ip); temp[_strlen(public_ip) - 1] = '\0'; - if ((fp = fopen("libcurl.log", "wt+")) == NULL) - { + if ((fp = fopen("libcurl.log", "wt+")) == NULL) { return 1; } @@ -209,8 +280,7 @@ int dingding_warning(char *illegal_ip, char *public_ip, char *ip, conf * conf) curl_global_init(CURL_GLOBAL_ALL); curl = curl_easy_init(); - if (curl == NULL) - { + if (curl == NULL) { fclose(fp); return 1; } @@ -252,7 +322,7 @@ int dingding_warning(char *illegal_ip, char *public_ip, char *ip, conf * conf) } // 邮件告警 -int mail_warning(char *illegal_ip, char *public_ip, char *ip, conf * conf) +int mail_warning(char *illegal_ip, char *public_ip, char *ip, conf *conf) { FILE *fp = NULL; char buff[BUFFER]; @@ -267,13 +337,11 @@ int mail_warning(char *illegal_ip, char *public_ip, char *ip, conf * conf) 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"))) - { + if (NULL == (fp = popen(text, "r"))) { perror("popen text"); } - while (fgets(buff, BUFFER, fp) != NULL) - { + while (fgets(buff, BUFFER, fp) != NULL) { buff[_strlen(buff) - 1] = '\0'; } @@ -284,7 +352,7 @@ int mail_warning(char *illegal_ip, char *public_ip, char *ip, conf * conf) } // 第三方邮箱告警 -int QQ_mail_warning(char *illegal_ip, char *public_ip, char *ip, conf * conf) +int QQ_mail_warning(char *illegal_ip, char *public_ip, char *ip, conf *conf) { char string[BUFFER + (sizeof(QQMAIL)) + 1]; char text[BUFFER]; @@ -298,56 +366,54 @@ int QQ_mail_warning(char *illegal_ip, char *public_ip, char *ip, conf * conf) 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); + 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) +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); - + 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) +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); } @@ -356,13 +422,12 @@ 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) // 如果字符串为空就跳出循环 + 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长度, + if ((strncmp(client_ip, whitelist_ip[i], _strlen(whitelist_ip[i]))) == 0) // 对比client_ip长度, { return 1; } @@ -377,18 +442,15 @@ 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) // 如果字符串为空就跳出循环 + 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) - { + if (p != NULL) { return 1; } } @@ -396,33 +458,6 @@ int isregion(char *str, char (*region_list)[WHITELIST_IP_NUM]) 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) { @@ -430,25 +465,23 @@ int disk_waring(int threshold) 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" - + +#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) - { + + while (fgets(buffer, BUFFER, fp) != NULL) { printf("%s", buffer); is = 1; break; } pclose(fp); - return is; } @@ -456,56 +489,26 @@ int disk_waring(int threshold) char *_time() { char temp[BUFFER]; - char *wday[] = {"0", "1", "2", "3", "4", "5", "6"}; + char *wday[] = { "0", "1", "2", "3", "4", "5", "6" }; time_t t; struct tm *p; time(&t); - p = localtime(&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); - + 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) +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 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; @@ -513,8 +516,6 @@ int rule(conf * conf) struct tm *tp; long int ip_length = 1; - - fp = NULL; fc = NULL; timep = time(NULL); @@ -522,13 +523,10 @@ int rule(conf * conf) 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) @@ -548,6 +546,7 @@ int rule(conf * conf) perror("GE_10"); return -1; } + } else { @@ -561,72 +560,53 @@ int rule(conf * conf) } else if (CENTOS_SYSTEM == check_system()) // Centos 7系统规则 { - if (tp->tm_mday >= 10) - { - if ((fp = popen(CENTOS_GE_10, "r")) == NULL) - { + 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) - { + } else { + if ((fp = popen(CENTOS_LE_10, "r")) == NULL) { perror("CENTOS_LE_10"); return -1; } } - } - else - { + } 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) - { + 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; - + 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); + 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) { @@ -634,7 +614,6 @@ int rule(conf * conf) return -1; } memset(command, 0, ip_length + BUFFER); - snprintf(awk, BUFFER, AWK, conf->REFUSE_NUMBER); // 拼接命令 memcpy(command, "echo \"", 7); @@ -642,72 +621,65 @@ int rule(conf * conf) 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); // + + 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 (conf->IPV4_RESTRICTION == 1) // 是否启用白名单 { - if (whitelist(buffer, whitelist_ip) == 1) - { + if (whitelist(buffer, whitelist_ip) == 1) { printf("%s 白名单IPV4:%s\n", t, buffer); continue; } } - if (0 != show_all_rule(buffer)) // libiptc库判断否存在规则 + 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) - { + if (conf->REGION == 1) { memset(URL, 0, conf->REGION_URL_LEN + 32); - snprintf(URL, conf->REGION_URL_LEN+32, conf->REGION_URL, buffer); + snprintf(URL, conf->REGION_URL_LEN + 32, conf->REGION_URL, buffer); - - if (conf->IP2REGION == 1) { // ip2region 地址定位库 + if (conf->IP2REGION == 1) { // ip2region 地址定位库 printf("%s Use ip2region !!!\n", t); - if (-1 == access(xdb_path, F_OK)) // 判断 ip2region 地址定位库是否存在 + 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); @@ -720,25 +692,22 @@ AREA: 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); + 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); - + + printf(RED "%s 攻击者IP地址:%s, %s\n" COLOR_NONE, t, buffer, area); if (conf->IS_DING_WEBHOOK == 1) // 钉钉告警 { @@ -746,29 +715,30 @@ AREA: sleep(3); } - if (conf->IS_MAIL == 1) // 邮件告警 + if (conf->IS_MAIL == 1) // 邮件告警 { mail_warning(area, public_ip, buffer, conf); sleep(3); } - if (conf->IS_QQMAIL == 1) // 邮件告警 + 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 + 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); @@ -780,7 +750,6 @@ BLOCKED: if (fp != NULL) pclose(fp); - if (fc != NULL) pclose(fc); if (t) @@ -811,7 +780,6 @@ static int get_executable_path(char *processdir, char *processname, int len) strcpy(processname, processname_ptr); *processname_ptr = '\0'; - return (int)(processname_ptr - processdir); } @@ -821,12 +789,9 @@ 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++) - { + for (i = 0; i <= argc - 1; i++) { + if (i == 1) { + for (j = i; j <= argc - 2; j++) { argvs[j] = argv[j + 1]; } } @@ -840,62 +805,57 @@ 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; - + 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) - { + while (fgets(buffer, BUFFER, fp) != NULL) { //printf("%s", buffer); temp = strstr(buffer, "Infected"); if (temp) sscanf(temp, "Infected files: %32s", temp); - - if (temp != NULL) - { + + if (temp != NULL) { //printf("%s\n", temp); break; } } - + pclose(fp); - + if (temp != NULL) { printf("%d\n", atoi(temp)); return atoi(temp); - } - else - { + } else { return -1; } - - + return 0; } @@ -905,60 +865,51 @@ int update_freshclam(int argc, char *argv[]) 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) - { + + 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) - { + 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) - { + if (p != NULL) { p1 = strstr(p, " "); - - if ((p1-p) > 7) - { - memcpy(temp, p, p1-p); + + if ((p1 - p) > 7) { + memcpy(temp, p, p1 - p); p = strstr(temp, "="); - + strcpy(move, "mkdir -p "); - strcat(move, p+1); + 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个, 则使用用户输入的参数) + if (argc > 3) // 手动输入参数(如果手动输入参数个数大于3个, 则使用用户输入的参数) { process_argv(argc, argv, &(argvs[0])); - head_argvs = &(argvs[0]); // head_argvs指向argvs[0] - head_argc = argc - 1; // 改变argc数 + head_argvs = &(argvs[0]); // head_argvs指向argvs[0] + head_argc = argc - 1; // 改变argc数 - } - else // 读取配置文件参数 + } 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")) - { + if (0 == strcmp(conf->DAEMON, "on")) { goto goto_daemon; } - if (argv[1] != NULL && 0 == strcmp(argv[1], "-d")) - { + 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; iNGINX_LOG_FILE, conf); + sleep(1); + } + } + while (1) { // Cron @@ -1277,102 +1142,83 @@ goto_daemon: 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 ) - { + 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 - { + } 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) - { + 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) - { + 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) - { + if (conf->IS_QQMAIL == 1) { QQ_mail_warning_Disk_Use(public_ip, 0, conf); sleep(3); } - } - else - { + } else { printf("Disk usage does not reach threshold!\n"); } } - _exit(r); - } - else - { + + } else { int status = 0; - wait(&status); // wait the end of child process - if (WIFEXITED(status)) - { + wait(&status); // wait the end of child process + if (WIFEXITED(status)) { ; //printf("child process return %d\n", WEXITSTATUS(status)); } - - sleep(60); // 跳过这一分钟 + + sleep(60); // 跳过这一分钟 } } } - - + // 封禁非法IP rule(conf); sleep(conf->TIME); + } - } - else - { - - rule(conf); + } else { + ; } free(t); free_conf(conf); free(conf); free(public_ip); - for(i=1; i #include #include -#include -#include -#include #include +#include +#include -#include "./cJSON/cJSON.h" -#include "ip2region/ip2region.h" + +#include "conf.h" typedef struct now_next_time @@ -42,51 +41,6 @@ typedef struct now_next_time -#include -#include -#include -#include -#include - -#include "ccronexpr.h" - -#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 - #define COLOR_NONE "\033[0m" //表示清除前面设置的格式 @@ -107,9 +61,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" @@ -120,10 +72,14 @@ void cron_free(void* p) #define QQMAIL_Virus "gomail -r %s -s \"System Virus Infected\" -t \"%s\"" #define QQMAIL_DISK_USE "gomail -r %s -s \"System Disk Use\" -t \"%s\"" +#define LOG_FILE "nginx.log" extern void read_conf(char *filename, conf * configure); extern void free_conf(conf * conf); extern void ptintf_conf(conf * conf); +extern void my_printf(const char *format, ...); +extern char *_time(); +extern int isregion(char *str, char (*region_list)[WHITELIST_IP_NUM]); #endif