mirror of
https://github.com/Zxilly/UA2F.git
synced 2026-01-10 12:18:19 +00:00
470 lines
13 KiB
C
470 lines
13 KiB
C
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
|
|
#ifndef __USE_GNU
|
|
#define __USE_GNU
|
|
#endif
|
|
|
|
#include "ipset_hook.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <wait.h>
|
|
#include <syslog.h>
|
|
#include <signal.h>
|
|
#include <arpa/inet.h>
|
|
|
|
|
|
#include <libmnl/libmnl.h>
|
|
|
|
#include <linux/netfilter/nfnetlink_queue.h>
|
|
#include <libnetfilter_queue/libnetfilter_queue.h>
|
|
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
|
|
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
|
|
#include <libnetfilter_queue/pktbuff.h>
|
|
#include <linux/netfilter/nfnetlink_conntrack.h>
|
|
|
|
#define NF_ACCEPT 1
|
|
|
|
|
|
static struct mnl_socket *nl;
|
|
static const int queue_number = 10010;
|
|
|
|
static long long UAcount = 0;
|
|
//static long long httpnouacount = 0;
|
|
static long long tcpcount = 0;
|
|
static long long UAmark = 0;
|
|
static long long noUAmark = 0;
|
|
static long long oldhttpcount = 4;
|
|
//static long long http1_0count = 0;
|
|
|
|
static time_t start_t, current_t;
|
|
|
|
static int debugflag = 0;
|
|
static int debugflag2 = 0;
|
|
static char timestr[60];
|
|
|
|
static struct ipset *Pipset;
|
|
|
|
static char *time2str(int sec) {
|
|
memset(timestr, 0, sizeof(timestr));
|
|
if (sec <= 60) {
|
|
sprintf(timestr, "%d seconds", sec);
|
|
} else if (sec <= 3600) {
|
|
sprintf(timestr, "%d minutes and %d seconds", sec / 60, sec % 60);
|
|
} else if (sec <= 86400) {
|
|
sprintf(timestr, "%d hours, %d minutes and %d seconds", sec / 3600, sec % 3600 / 60, sec % 60);
|
|
} else {
|
|
sprintf(timestr, "%d days, %d hours, %d minutes and %d seconds", sec / 86400, sec % 86400 / 3600,
|
|
sec % 3600 / 60,
|
|
sec % 60);
|
|
}
|
|
return timestr;
|
|
}
|
|
|
|
static int parse_attrs(const struct nlattr *attr, void *data) {
|
|
const struct nlattr **tb = data;
|
|
int type = mnl_attr_get_type(attr);
|
|
|
|
tb[type] = attr;
|
|
|
|
return MNL_CB_OK;
|
|
}
|
|
|
|
static void
|
|
nfq_send_verdict(int queue_num, uint32_t id, struct pkt_buff *pktb, uint32_t mark, bool noUA,
|
|
char addcmd[50]) { // http mark = 24, ukn mark = 16-20, no http mark = 23
|
|
char buf[0xffff + (MNL_SOCKET_BUFFER_SIZE / 2)];
|
|
struct nlmsghdr *nlh;
|
|
struct nlattr *nest;
|
|
uint32_t setmark;
|
|
|
|
debugflag2 = 0;
|
|
debugflag2++;//flag1
|
|
|
|
nlh = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, queue_num);
|
|
nfq_nlmsg_verdict_put(nlh, id, NF_ACCEPT);
|
|
|
|
debugflag2++;//flag2
|
|
|
|
if (pktb_mangled(pktb)) {
|
|
nfq_nlmsg_verdict_put_pkt(nlh, pktb_data(pktb), pktb_len(pktb));
|
|
}
|
|
|
|
debugflag2++;//flag3
|
|
|
|
|
|
if (noUA) {
|
|
if (mark == 1) {
|
|
nest = mnl_attr_nest_start(nlh, NFQA_CT);
|
|
mnl_attr_put_u32(nlh, CTA_MARK, htonl(16));
|
|
mnl_attr_nest_end(nlh, nest);
|
|
}
|
|
|
|
if (mark >= 16 && mark <= 40) {
|
|
setmark = mark + 1;
|
|
nest = mnl_attr_nest_start(nlh, NFQA_CT);
|
|
mnl_attr_put_u32(nlh, CTA_MARK, htonl(setmark));
|
|
mnl_attr_nest_end(nlh, nest);
|
|
}
|
|
|
|
if (mark == 41) { // 21 统计确定此连接为不含UA连接
|
|
|
|
nest = mnl_attr_nest_start(nlh, NFQA_CT);
|
|
mnl_attr_put_u32(nlh, CTA_MARK, htonl(43));
|
|
mnl_attr_nest_end(nlh, nest); // 加 CONNMARK
|
|
|
|
ipset_parse_line(Pipset, addcmd); //加 ipset 标记
|
|
|
|
noUAmark++;
|
|
}
|
|
} else {
|
|
if (mark != 44) {
|
|
nest = mnl_attr_nest_start(nlh, NFQA_CT);
|
|
mnl_attr_put_u32(nlh, CTA_MARK, htonl(44));
|
|
mnl_attr_nest_end(nlh, nest);
|
|
UAmark++;
|
|
}
|
|
}
|
|
|
|
|
|
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
|
|
perror("mnl_socket_send");
|
|
syslog(LOG_ERR, "Exit at breakpoint 1.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
debugflag2++;//flag4
|
|
|
|
tcpcount++;
|
|
pktb_free(pktb);
|
|
debugflag2++;//flag5
|
|
}
|
|
|
|
static int queue_cb(const struct nlmsghdr *nlh, void *data) {
|
|
struct nfqnl_msg_packet_hdr *ph = NULL;
|
|
struct nlattr *attr[NFQA_MAX + 1] = {};
|
|
struct nlattr *ctattr[CTA_MAX + 1] = {};
|
|
struct nlattr *originattr[CTA_TUPLE_MAX + 1] = {};
|
|
struct nlattr *ipattr[CTA_IP_MAX + 1] = {};
|
|
struct nlattr *portattr[CTA_PROTO_MAX + 1] = {};
|
|
uint16_t plen;
|
|
struct pkt_buff *pktb;
|
|
struct iphdr *ippkhdl;
|
|
struct tcphdr *tcppkhdl;
|
|
struct nfgenmsg *nfg;
|
|
char *tcppkpayload;
|
|
unsigned int tcppklen;
|
|
unsigned int uaoffset = 0;
|
|
unsigned int ualength = 0;
|
|
char *str = NULL;
|
|
void *payload;
|
|
uint32_t mark = 0;
|
|
bool noUA = false;
|
|
char *ip;
|
|
uint16_t port = 0;
|
|
|
|
char addcmd[50];
|
|
|
|
|
|
debugflag = 0;
|
|
|
|
|
|
if (nfq_nlmsg_parse(nlh, attr) < 0) {
|
|
perror("problems parsing");
|
|
return MNL_CB_ERROR;
|
|
}
|
|
|
|
nfg = mnl_nlmsg_get_payload(nlh);
|
|
|
|
if (attr[NFQA_PACKET_HDR] == NULL) {
|
|
// fputs("metaheader not set\n", stderr);
|
|
syslog(LOG_ERR, "metaheader not set");
|
|
return MNL_CB_ERROR;
|
|
}
|
|
|
|
if (attr[NFQA_CT]) {
|
|
mnl_attr_parse_nested(attr[NFQA_CT], parse_attrs, ctattr);
|
|
|
|
if (ctattr[CTA_MARK]) {
|
|
mark = ntohl(mnl_attr_get_u32(ctattr[CTA_MARK]));
|
|
} else {
|
|
mark = 1; // no mark 1
|
|
} // NFQA_CT 一定存在,不存在说明有其他问题
|
|
|
|
if (ctattr[CTA_TUPLE_ORIG]) {
|
|
mnl_attr_parse_nested(ctattr[CTA_TUPLE_ORIG], parse_attrs, originattr);
|
|
if (originattr[CTA_TUPLE_IP]) {
|
|
mnl_attr_parse_nested(originattr[CTA_TUPLE_IP], parse_attrs, ipattr);
|
|
if (ipattr[CTA_IP_V4_DST]) {
|
|
uint32_t tmp = mnl_attr_get_u32(ipattr[CTA_IP_V4_DST]);
|
|
struct in_addr tmp2;
|
|
tmp2.s_addr = tmp;
|
|
ip = inet_ntoa(tmp2);
|
|
} else {
|
|
ip = "0.0.0.0";
|
|
}
|
|
} else {
|
|
ip = "0.0.0.0";
|
|
}
|
|
if (originattr[CTA_TUPLE_PROTO]) {
|
|
mnl_attr_parse_nested(originattr[CTA_TUPLE_PROTO], parse_attrs, portattr);
|
|
if (portattr[CTA_PROTO_DST_PORT]) {
|
|
port = ntohs(mnl_attr_get_u16(portattr[CTA_PROTO_DST_PORT]));
|
|
}
|
|
}
|
|
if (ip && port != 0) {
|
|
sprintf(addcmd, "add nohttp %s,%d", ip, port);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
debugflag++; //1
|
|
|
|
ph = mnl_attr_get_payload(attr[NFQA_PACKET_HDR]);
|
|
|
|
debugflag++; //2
|
|
|
|
plen = mnl_attr_get_payload_len(attr[NFQA_PAYLOAD]);
|
|
payload = mnl_attr_get_payload(attr[NFQA_PAYLOAD]);
|
|
|
|
|
|
debugflag++; //3
|
|
|
|
pktb = pktb_alloc(AF_INET, payload, plen, 0); //IP包
|
|
|
|
if (!pktb) {
|
|
syslog(LOG_ERR, "pktb malloc failed");
|
|
return MNL_CB_ERROR;
|
|
}
|
|
|
|
debugflag++; //4
|
|
|
|
ippkhdl = nfq_ip_get_hdr(pktb); //获取ip header
|
|
|
|
if (nfq_ip_set_transport_header(pktb, ippkhdl) < 0) {
|
|
syslog(LOG_ERR, "set transport header failed");
|
|
pktb_free(pktb);
|
|
return MNL_CB_ERROR;
|
|
}
|
|
|
|
|
|
tcppkhdl = nfq_tcp_get_hdr(pktb); //获取 tcp header
|
|
tcppkpayload = nfq_tcp_get_payload(tcppkhdl, pktb); //获取 tcp载荷
|
|
tcppklen = nfq_tcp_get_payload_len(tcppkhdl, pktb); //获取 tcp长度
|
|
|
|
if (tcppkpayload) {
|
|
char *uapointer = memmem(tcppkpayload, tcppklen, "\r\nUser-Agent: ", 14);
|
|
|
|
if (uapointer) {
|
|
debugflag++; //flag5
|
|
|
|
|
|
|
|
debugflag++; //flag6
|
|
|
|
uaoffset = uapointer - tcppkpayload + 14;
|
|
for (int i = 0; i < tcppklen - uaoffset - 4; ++i) {
|
|
if (*(uapointer + 14 + i) == '\r') {
|
|
ualength = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
debugflag++; //flag7
|
|
|
|
if (uaoffset && ualength) {
|
|
str = malloc(ualength);
|
|
if (!str) {
|
|
pktb_free(pktb);
|
|
return MNL_CB_ERROR;
|
|
}
|
|
memset(str, 'F', ualength);
|
|
|
|
if (uaoffset >= tcppklen) {
|
|
syslog(LOG_ERR, "Offset overflow");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (ualength >= tcppklen) {
|
|
syslog(LOG_ERR, "UA overflow");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (nfq_tcp_mangle_ipv4(pktb, uaoffset, ualength, str, ualength) == 1) {
|
|
UAcount++; //记录修改包的数量
|
|
free(str);//用完就丢
|
|
noUA = false;
|
|
} else {
|
|
free(str);
|
|
pktb_free(pktb);
|
|
return MNL_CB_ERROR;
|
|
}
|
|
}
|
|
|
|
debugflag++; //flag8
|
|
} else {
|
|
noUA = true;
|
|
}
|
|
}
|
|
|
|
debugflag++; //flag5 / 9
|
|
|
|
nfq_send_verdict(ntohs(nfg->res_id), ntohl((uint32_t) ph->packet_id), pktb, mark, noUA, addcmd);
|
|
|
|
debugflag++; //flag6 / 10
|
|
|
|
if (UAcount / oldhttpcount == 2 || UAcount - oldhttpcount >= 8192) {
|
|
oldhttpcount = UAcount;
|
|
current_t = time(NULL);
|
|
syslog(LOG_INFO,
|
|
"UA2F has handled %lld ua http, %lld tcp. Set %lld mark and %lld noUA mark in %s",
|
|
UAcount, tcpcount, UAmark, noUAmark,
|
|
time2str((int) difftime(current_t, start_t)));
|
|
}
|
|
|
|
debugflag++;//flag7 / 11
|
|
|
|
return MNL_CB_OK;
|
|
}
|
|
|
|
static void debugfunc() {
|
|
syslog(LOG_ERR, "Catch SIGSEGV at breakpoint %d and breakpoint2 %d", debugflag, debugflag2);
|
|
mnl_socket_close(nl);
|
|
syslog(LOG_ALERT, "Meet fatal error, try to restart.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
char *buf;
|
|
size_t sizeof_buf = 0xffff + (MNL_SOCKET_BUFFER_SIZE / 2);
|
|
struct nlmsghdr *nlh;
|
|
ssize_t ret;
|
|
unsigned int portid;
|
|
int child_status;
|
|
|
|
int errcount = 0;
|
|
|
|
signal(SIGSEGV, debugfunc);
|
|
|
|
signal(SIGCHLD, SIG_IGN);
|
|
signal(SIGHUP, SIG_IGN);
|
|
|
|
while (true) {
|
|
child_status = fork();
|
|
if (child_status < 0) {
|
|
syslog(LOG_ERR, "Failed to give birth.");
|
|
syslog(LOG_ERR, "Exit at breakpoint 2.");
|
|
exit(EXIT_FAILURE);
|
|
} else if (child_status == 0) {
|
|
syslog(LOG_NOTICE, "UA2F processor start at [%d].", getpid());
|
|
break;
|
|
} else {
|
|
syslog(LOG_NOTICE, "Try to start UA2F processor at [%d].", child_status);
|
|
int deadstat;
|
|
int deadpid;
|
|
deadpid = wait(&deadstat);
|
|
if (deadpid == -1) {
|
|
syslog(LOG_ERR, "Child sucide.");
|
|
} else {
|
|
syslog(LOG_ERR, "Meet fatal error.[%d] dies by %d", deadpid, deadstat);
|
|
}
|
|
}
|
|
errcount++;
|
|
if (errcount > 50) {
|
|
syslog(LOG_ERR, "Meet too many fatal error, no longer try to recover.");
|
|
syslog(LOG_ERR, "Exit at breakpoint 3.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
|
|
openlog("UA2F", LOG_PID, LOG_SYSLOG);
|
|
|
|
start_t = time(NULL);
|
|
|
|
ipset_load_types();
|
|
Pipset = ipset_init();
|
|
|
|
if (!Pipset) {
|
|
syslog(LOG_ERR, "Pipset not inited.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
ipset_custom_printf(Pipset, func, func2, func3, NULL); // hook 掉退出的输出函数
|
|
|
|
syslog(LOG_NOTICE, "Pipset inited.");
|
|
|
|
nl = mnl_socket_open(NETLINK_NETFILTER);
|
|
|
|
if (nl == NULL) {
|
|
perror("mnl_socket_open");
|
|
syslog(LOG_ERR, "Exit at breakpoint 4.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
|
|
perror("mnl_socket_bind");
|
|
syslog(LOG_ERR, "Exit at breakpoint 5.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
portid = mnl_socket_get_portid(nl);
|
|
|
|
buf = malloc(sizeof_buf);
|
|
if (!buf) {
|
|
perror("allocate receive buffer");
|
|
syslog(LOG_ERR, "Exit at breakpoint 6.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_number);
|
|
nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_BIND);
|
|
|
|
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
|
|
perror("mnl_socket_send");
|
|
syslog(LOG_ERR, "Exit at breakpoint 7.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_number);
|
|
nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff);
|
|
|
|
mnl_attr_put_u32_check(nlh, MNL_SOCKET_BUFFER_SIZE, NFQA_CFG_FLAGS,
|
|
htonl(NFQA_CFG_F_GSO | NFQA_CFG_F_FAIL_OPEN | NFQA_CFG_F_CONNTRACK));
|
|
mnl_attr_put_u32_check(nlh, MNL_SOCKET_BUFFER_SIZE, NFQA_CFG_MASK,
|
|
htonl(NFQA_CFG_F_GSO | NFQA_CFG_F_FAIL_OPEN | NFQA_CFG_F_CONNTRACK));
|
|
|
|
|
|
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
|
|
perror("mnl_socket_send");
|
|
syslog(LOG_ERR, "Exit at breakpoint 8.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
ret = 1;
|
|
mnl_socket_setsockopt(nl, NETLINK_NO_ENOBUFS, &ret, sizeof(int));
|
|
|
|
syslog(LOG_NOTICE, "UA2F has inited successful.");
|
|
|
|
while (1) {
|
|
ret = mnl_socket_recvfrom(nl, buf, sizeof_buf);
|
|
if (ret == -1) { //stop at failure
|
|
perror("mnl_socket_recvfrom");
|
|
syslog(LOG_ERR, "Exit at breakpoint 9.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
debugflag++; //1 或 16
|
|
ret = mnl_cb_run(buf, ret, 0, portid, (mnl_cb_t) queue_cb, NULL);
|
|
debugflag++; //15
|
|
if (ret < 0) { //stop at failure
|
|
// printf("errno=%d\n", errno);
|
|
perror("mnl_cb_run");
|
|
syslog(LOG_ERR, "Exit at breakpoint 10.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
}
|