diff --git a/UA2F/CMakeLists.txt b/UA2F/CMakeLists.txt index fc76170fa..cbfab3c5d 100644 --- a/UA2F/CMakeLists.txt +++ b/UA2F/CMakeLists.txt @@ -4,6 +4,7 @@ project(UA2F LANGUAGES C CXX) set(CMAKE_C_STANDARD 17) OPTION(UA2F_BUILD_TESTS "Build tests" OFF) +OPTION(UA2F_ENABLE_UCI "Enable UCI support" ON) find_package(Git) if(GIT_FOUND) @@ -81,7 +82,14 @@ add_executable(ua2f src/config.c src/third/nfqueue-mnl.c) -target_link_libraries(ua2f mnl netfilter_queue pthread nfnetlink uci) +target_link_libraries(ua2f mnl netfilter_queue pthread nfnetlink) + +if (UA2F_ENABLE_UCI) + add_compile_definitions(UA2F_ENABLE_UCI=1) + target_link_libraries(ua2f uci) +else () + message(STATUS "UCI support is disabled.") +endif () install(TARGETS ua2f RUNTIME DESTINATION bin) diff --git a/UA2F/README.md b/UA2F/README.md index ab68bbd7a..8a10f6807 100644 --- a/UA2F/README.md +++ b/UA2F/README.md @@ -61,11 +61,15 @@ uci commit ua2f > UA2F 不会修改包的大小,因此即使自定义了 User-Agent, 运行时实际的 User-Agent 会是一个从 custom ua 中截取的长度与原始 User-Agent 相同的子串,长度不足时会在末尾补空格。 +## 在非 OpenWRT 系统上运行 + +自 `v4.5.0 起`,UA2F 支持在非 OpenWRT 系统上运行,但是需要手动配置防火墙规则,将需要处理的流量转发到 `netfilter-queue` 的 10010 队列中。 + +编译时,需要添加 `-DUA2F_ENABLE_UCI=OFF` flag 至 CMake。 + ## TODO - [ ] pthread 支持,由不同线程完成入队出队 -- [ ] 清除 TCP Header 中的 timestamp,有论文认为这可以被用来识别 NAT 后的多设备,劫持 NTP 服务器并不一定有效 - ## License [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FZxilly%2FUA2F.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FZxilly%2FUA2F?ref=badge_large) diff --git a/UA2F/openwrt/Makefile b/UA2F/openwrt/Makefile index e8f977cb8..62500a7e1 100644 --- a/UA2F/openwrt/Makefile +++ b/UA2F/openwrt/Makefile @@ -1,7 +1,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=UA2F -PKG_VERSION:=4.4.1 +PKG_VERSION:=4.5.0 PKG_RELEASE:=1 PKG_LICENSE:=GPL-3.0-only diff --git a/UA2F/src/cli.c b/UA2F/src/cli.c index 30e209173..061d5f388 100644 --- a/UA2F/src/cli.c +++ b/UA2F/src/cli.c @@ -21,11 +21,16 @@ void try_print_info(const int argc, char *argv[]) { #else printf("Embed UA: not set\n"); #endif + +#ifdef UA2F_ENABLE_UCI if (config.use_custom_ua) { printf("Config UA: %s\n", config.custom_ua); } else { printf("Config UA: not set\n"); } +#else + printf("UCI support disabled\n"); +#endif exit(0); } diff --git a/UA2F/src/config.c b/UA2F/src/config.c index 7d186226b..e925bcec9 100644 --- a/UA2F/src/config.c +++ b/UA2F/src/config.c @@ -1,3 +1,4 @@ +#ifdef UA2F_ENABLE_UCI #include #include #include @@ -38,4 +39,5 @@ void load_config() { cleanup: uci_free_context(ctx); -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/UA2F/src/config.h b/UA2F/src/config.h index ac9c2db8f..c5827c888 100644 --- a/UA2F/src/config.h +++ b/UA2F/src/config.h @@ -1,3 +1,4 @@ +#ifdef UA2F_ENABLE_UCI #ifndef UA2F_CONFIG_H #define UA2F_CONFIG_H @@ -13,3 +14,4 @@ void load_config(); extern struct ua2f_config config; #endif //UA2F_CONFIG_H +#endif diff --git a/UA2F/src/handler.c b/UA2F/src/handler.c index 182a464e8..bb9a6f6d4 100644 --- a/UA2F/src/handler.c +++ b/UA2F/src/handler.c @@ -3,16 +3,19 @@ #include "cache.h" #include "util.h" #include "statistics.h" -#include "config.h" #include "custom.h" +#ifdef UA2F_ENABLE_UCI +#include "config.h" +#endif + #include #include #include #include #define MAX_USER_AGENT_LENGTH (0xffff + (MNL_SOCKET_BUFFER_SIZE / 2)) -static char* replacement_user_agent_string = NULL; +static char *replacement_user_agent_string = NULL; #define USER_AGENT_MATCH "\r\nUser-Agent:" #define USER_AGENT_MATCH_LENGTH 13 @@ -27,36 +30,42 @@ static char* replacement_user_agent_string = NULL; void init_handler() { replacement_user_agent_string = malloc(MAX_USER_AGENT_LENGTH); + bool ua_set = false; + +#ifdef UA2F_ENABLE_UCI if (config.use_custom_ua) { memset(replacement_user_agent_string, ' ', MAX_USER_AGENT_LENGTH); strncpy(replacement_user_agent_string, config.custom_ua, strlen(config.custom_ua)); syslog(LOG_INFO, "Using config user agent string: %s", replacement_user_agent_string); + ua_set = true; } - else { +#endif + #ifdef UA2F_CUSTOM_UA - memset(replacement_user_agent_string, ' ', MAX_USER_AGENT_LENGTH); - strncpy(replacement_user_agent_string, UA2F_CUSTOM_UA, strlen(UA2F_CUSTOM_UA)); - syslog(LOG_INFO, "Using embed user agent string: %s", replacement_user_agent_string); -#else + memset(replacement_user_agent_string, ' ', MAX_USER_AGENT_LENGTH); + strncpy(replacement_user_agent_string, UA2F_CUSTOM_UA, strlen(UA2F_CUSTOM_UA)); + syslog(LOG_INFO, "Using embed user agent string: %s", replacement_user_agent_string); + ua_set = true; +#endif + + if (!ua_set) { memset(replacement_user_agent_string, 'F', MAX_USER_AGENT_LENGTH); syslog(LOG_INFO, "Custom user agent string not set, using default F-string."); -#endif } syslog(LOG_INFO, "Handler initialized."); } // should free the ret value -static char* ip_to_str(const ip_address_t* ip, const uint16_t port, const int ip_version) { +static char *ip_to_str(const ip_address_t *ip, const uint16_t port, const int ip_version) { ASSERT(ip_version == IPV4 || ip_version == IPV6); - char* ip_buf = malloc(MAX_ADDR_PORT_LENGTH); + char *ip_buf = malloc(MAX_ADDR_PORT_LENGTH); memset(ip_buf, 0, MAX_ADDR_PORT_LENGTH); - const char* retval = NULL; + const char *retval = NULL; if (ip_version == IPV4) { retval = inet_ntop(AF_INET, &ip->in4, ip_buf, INET_ADDRSTRLEN); - } - else { + } else { retval = inet_ntop(AF_INET6, &ip->in6, ip_buf, INET6_ADDRSTRLEN); } ASSERT(retval != NULL); @@ -74,11 +83,11 @@ struct mark_op { }; static void send_verdict( - const struct nf_queue* queue, - const struct nf_packet* pkt, - const struct mark_op mark, - struct pkt_buff* mangled_pkt_buff) { - struct nlmsghdr* nlh = nfqueue_put_header(pkt->queue_num, NFQNL_MSG_VERDICT); + const struct nf_queue *queue, + const struct nf_packet *pkt, + const struct mark_op mark, + struct pkt_buff *mangled_pkt_buff) { + struct nlmsghdr *nlh = nfqueue_put_header(pkt->queue_num, NFQNL_MSG_VERDICT); if (nlh == NULL) { syslog(LOG_ERR, "failed to put nfqueue header"); goto end; @@ -86,7 +95,7 @@ static void send_verdict( nfq_nlmsg_verdict_put(nlh, pkt->packet_id, NF_ACCEPT); if (mark.should_set) { - struct nlattr* nest = mnl_attr_nest_start_check(nlh, SEND_BUF_LEN, NFQA_CT); + struct nlattr *nest = mnl_attr_nest_start_check(nlh, SEND_BUF_LEN, NFQA_CT); if (nest == NULL) { syslog(LOG_ERR, "failed to put nfqueue attr"); goto end; @@ -107,7 +116,7 @@ static void send_verdict( syslog(LOG_ERR, "failed to send verdict: %s", strerror(errno)); } -end: + end: if (nlh != NULL) { free(nlh); } @@ -116,67 +125,66 @@ end: static bool conntrack_info_available = true; static bool cache_initialized = false; -static void add_to_cache(const struct nf_packet* pkt) { - char* ip_str = ip_to_str(&pkt->orig.dst, pkt->orig.dst_port, pkt->orig.ip_version); +static void add_to_cache(const struct nf_packet *pkt) { + char *ip_str = ip_to_str(&pkt->orig.dst, pkt->orig.dst_port, pkt->orig.ip_version); cache_add(ip_str); free(ip_str); } -static struct mark_op get_next_mark(const struct nf_packet* pkt, const bool has_ua) { +static struct mark_op get_next_mark(const struct nf_packet *pkt, const bool has_ua) { if (!conntrack_info_available) { - return (struct mark_op){false, 0}; + return (struct mark_op) {false, 0}; } // I didn't think this will happen, but just in case // firewall should already have a rule to return all marked with CONNMARK_NOT_HTTP packets if (pkt->conn_mark == CONNMARK_NOT_HTTP) { syslog(LOG_WARNING, "Packet has already been marked as not http. Maybe firewall rules are wrong?"); - return (struct mark_op){false, 0}; + return (struct mark_op) {false, 0}; } if (pkt->conn_mark == CONNMARK_HTTP) { - return (struct mark_op){false, 0}; + return (struct mark_op) {false, 0}; } if (has_ua) { - return (struct mark_op){true, CONNMARK_HTTP}; + return (struct mark_op) {true, CONNMARK_HTTP}; } if (!pkt->has_connmark || pkt->conn_mark == 0) { - return (struct mark_op){true, CONNMARK_ESTIMATE_LOWER}; + return (struct mark_op) {true, CONNMARK_ESTIMATE_LOWER}; } if (pkt->conn_mark == CONNMARK_ESTIMATE_VERDICT) { add_to_cache(pkt); - return (struct mark_op){true, CONNMARK_NOT_HTTP}; + return (struct mark_op) {true, CONNMARK_NOT_HTTP}; } if (pkt->conn_mark >= CONNMARK_ESTIMATE_LOWER && pkt->conn_mark <= CONNMARK_ESTIMATE_UPPER) { - return (struct mark_op){true, pkt->conn_mark + 1}; + return (struct mark_op) {true, pkt->conn_mark + 1}; } syslog(LOG_WARNING, "Unexpected connmark value: %d, Maybe other program has changed connmark?", pkt->conn_mark); - return (struct mark_op){true, pkt->conn_mark + 1}; + return (struct mark_op) {true, pkt->conn_mark + 1}; } -bool should_ignore(const struct nf_packet* pkt) { +bool should_ignore(const struct nf_packet *pkt) { bool retval = false; - char* ip_str = ip_to_str(&pkt->orig.dst, pkt->orig.dst_port, pkt->orig.ip_version); + char *ip_str = ip_to_str(&pkt->orig.dst, pkt->orig.dst_port, pkt->orig.ip_version); retval = cache_contains(ip_str); free(ip_str); return retval; } -void handle_packet(const struct nf_queue* queue,const struct nf_packet* pkt) { +void handle_packet(const struct nf_queue *queue, const struct nf_packet *pkt) { if (conntrack_info_available) { if (!pkt->has_conntrack) { conntrack_info_available = false; syslog(LOG_WARNING, "Packet has no conntrack. Switching to no cache mode."); syslog(LOG_WARNING, "Note that this may lead to performance degradation. Especially on low-end routers."); - } - else { + } else { if (!cache_initialized) { init_not_http_cache(); cache_initialized = true; @@ -184,9 +192,9 @@ void handle_packet(const struct nf_queue* queue,const struct nf_packet* pkt) { } } - struct pkt_buff* pkt_buff = NULL; + struct pkt_buff *pkt_buff = NULL; if (conntrack_info_available && should_ignore(pkt)) { - send_verdict(queue, pkt, (struct mark_op){true, CONNMARK_NOT_HTTP}, NULL); + send_verdict(queue, pkt, (struct mark_op) {true, CONNMARK_NOT_HTTP}, NULL); goto end; } @@ -198,21 +206,18 @@ void handle_packet(const struct nf_queue* queue,const struct nf_packet* pkt) { if (conntrack_info_available) { type = pkt->orig.ip_version; - } - else { + } else { const __auto_type ip_hdr = nfq_ip_get_hdr(pkt_buff); if (ip_hdr == NULL) { type = IPV6; - } - else { + } else { type = IPV4; } } if (type == IPV4) { count_ipv4_packet(); - } - else { + } else { count_ipv6_packet(); } @@ -222,8 +227,7 @@ void handle_packet(const struct nf_queue* queue,const struct nf_packet* pkt) { syslog(LOG_ERR, "Failed to set ipv4 transport header"); goto end; } - } - else { + } else { const __auto_type ip_hdr = nfq_ip6_get_hdr(pkt_buff); if (nfq_ip6_set_transport_header(pkt_buff, ip_hdr, IPPROTO_TCP) < 0) { syslog(LOG_ERR, "Failed to set ipv6 transport header"); @@ -234,7 +238,7 @@ void handle_packet(const struct nf_queue* queue,const struct nf_packet* pkt) { const __auto_type tcp_hdr = nfq_tcp_get_hdr(pkt_buff); if (tcp_hdr == NULL) { // This packet is not tcp, pass it - send_verdict(queue, pkt, (struct mark_op){false, 0}, NULL); + send_verdict(queue, pkt, (struct mark_op) {false, 0}, NULL); syslog(LOG_WARNING, "Received non-tcp packet. You may set wrong firewall rules."); goto end; } @@ -264,7 +268,7 @@ void handle_packet(const struct nf_queue* queue,const struct nf_packet* pkt) { // } count_http_packet(); - const void* search_start = tcp_payload; + const void *search_start = tcp_payload; unsigned int search_length = tcp_payload_len; bool has_ua = false; @@ -274,21 +278,21 @@ void handle_packet(const struct nf_queue* queue,const struct nf_packet* pkt) { break; } - char* ua_pos = memncasemem(search_start, search_length, USER_AGENT_MATCH, USER_AGENT_MATCH_LENGTH); + char *ua_pos = memncasemem(search_start, search_length, USER_AGENT_MATCH, USER_AGENT_MATCH_LENGTH); if (ua_pos == NULL) { break; } has_ua = true; - void* ua_start = ua_pos + USER_AGENT_MATCH_LENGTH; + void *ua_start = ua_pos + USER_AGENT_MATCH_LENGTH; // for non-standard user-agent like User-Agent:XXX with no space after colon - if (*(char *)ua_start == ' ') { + if (*(char *) ua_start == ' ') { ua_start++; } - const void* ua_end = memchr(ua_start, '\r', tcp_payload_len - (ua_start - tcp_payload)); + const void *ua_end = memchr(ua_start, '\r', tcp_payload_len - (ua_start - tcp_payload)); if (ua_end == NULL) { syslog(LOG_INFO, "User-Agent header is not terminated with \\r, not mangled."); send_verdict(queue, pkt, get_next_mark(pkt, true), NULL); @@ -300,8 +304,7 @@ void handle_packet(const struct nf_queue* queue,const struct nf_packet* pkt) { // Looks it's impossible to mangle pocket failed, so we just drop it if (type == IPV4) { nfq_tcp_mangle_ipv4(pkt_buff, ua_offset, ua_len, replacement_user_agent_string, ua_len); - } - else { + } else { nfq_tcp_mangle_ipv6(pkt_buff, ua_offset, ua_len, replacement_user_agent_string, ua_len); } @@ -315,7 +318,7 @@ void handle_packet(const struct nf_queue* queue,const struct nf_packet* pkt) { send_verdict(queue, pkt, get_next_mark(pkt, has_ua), pkt_buff); -end: + end: free(pkt->payload); if (pkt_buff != NULL) { pktb_free(pkt_buff); diff --git a/UA2F/src/ua2f.c b/UA2F/src/ua2f.c index cfc31b47b..f6cb01a5f 100644 --- a/UA2F/src/ua2f.c +++ b/UA2F/src/ua2f.c @@ -2,9 +2,12 @@ #include "handler.h" #include "util.h" #include "cli.h" -#include "config.h" #include "third/nfqueue-mnl.h" +#ifdef UA2F_ENABLE_UCI +#include "config.h" +#endif + #include #include #include @@ -23,7 +26,11 @@ void signal_handler(const int signum) { int main(const int argc, char *argv[]) { openlog("UA2F", LOG_PID, LOG_SYSLOG); +#ifdef UA2F_ENABLE_UCI load_config(); +#else + syslog(LOG_INFO, "uci support is disabled"); +#endif try_print_info(argc, argv);