mirror of
https://github.com/Zxilly/UA2F.git
synced 2026-01-06 10:33:47 +00:00
refactor: remove ipset
This commit is contained in:
parent
f3c90cdd09
commit
84e1ac90f1
@ -8,8 +8,13 @@ include_directories("/usr/local/include")
|
||||
add_compile_options(-fsanitize=address)
|
||||
add_link_options(-fsanitize=address)
|
||||
|
||||
add_executable(ua2f src/ua2f.c src/statistics.c)
|
||||
add_executable(ua2f
|
||||
src/ua2f.c
|
||||
src/statistics.c
|
||||
src/child.c
|
||||
src/util.c
|
||||
src/cache.c)
|
||||
|
||||
target_link_libraries(ua2f mnl netfilter_queue ipset)
|
||||
target_link_libraries(ua2f mnl netfilter_queue pthread)
|
||||
|
||||
install(TARGETS ua2f RUNTIME DESTINATION bin)
|
||||
|
||||
13
Makefile
13
Makefile
@ -15,19 +15,24 @@ define Package/ua2f
|
||||
SUBMENU:=Routing and Redirection
|
||||
TITLE:=Change User-Agent to Fwords on the fly.
|
||||
URL:=https://github.com/Zxilly/UA2F
|
||||
DEPENDS:=+ipset +iptables-mod-conntrack-extra +iptables-mod-nfqueue \
|
||||
+libnetfilter-conntrack +libnetfilter-queue +iptables-mod-filter +iptables-mod-ipopt
|
||||
DEPENDS:=+iptables-mod-conntrack-extra +iptables-mod-nfqueue \
|
||||
+libnetfilter-conntrack +libnetfilter-queue +iptables-mod-filter
|
||||
endef
|
||||
|
||||
define Package/ua2f/description
|
||||
Change User-agent to Fwords to prevent being checked by Dr.Com.
|
||||
endef
|
||||
|
||||
EXTRA_LDFLAGS += -lmnl -lnetfilter_queue -lipset
|
||||
EXTRA_LDFLAGS += -lmnl -lnetfilter_queue -lpthread
|
||||
|
||||
define Build/Compile
|
||||
$(TARGET_CC) $(TARGET_CFLAGS) $(TARGET_LDFLAGS) $(EXTRA_LDFLAGS) \
|
||||
$(PKG_BUILD_DIR)/ua2f.c $(PKG_BUILD_DIR)/statistics.c -o $(PKG_BUILD_DIR)/ua2f
|
||||
$(PKG_BUILD_DIR)/ua2f.c \
|
||||
$(PKG_BUILD_DIR)/statistics.c \
|
||||
$(PKG_BUILD_DIR)/child.c \
|
||||
$(PKG_BUILD_DIR)/util.c \
|
||||
$(PKG_BUILD_DIR)/cache.c \
|
||||
-o $(PKG_BUILD_DIR)/ua2f
|
||||
endef
|
||||
|
||||
define Package/ua2f/install
|
||||
|
||||
@ -111,7 +111,7 @@ Mon Jan 4 13:25:04 2021 syslog.info UA2F[5219]: UA2F has handled 65536 http pac
|
||||
```
|
||||
Sat Mar 13 14:26:48 2021 user.notice : Try to start UA2F processor at [24049].
|
||||
Sat Mar 13 14:26:48 2021 user.notice : UA2F processor start at [24049].
|
||||
Sat Mar 13 14:26:48 2021 syslog.notice UA2F[24049]: Pipset inited.
|
||||
Sat Mar 13 14:26:48 2021 syslog.notice UA2F[24049]: ipset inited.
|
||||
Sat Mar 13 14:26:48 2021 syslog.notice UA2F[24049]: UA2F has inited successful.
|
||||
Sat Mar 13 14:26:51 2021 syslog.info UA2F[24049]: UA2F has handled 8 http, 0 http 1.0, 0 noua http, 58 tcp. Set 0 mark and 0 nohttp mark in 3 seconds
|
||||
Sat Mar 13 14:26:57 2021 syslog.info UA2F[24049]: UA2F has handled 16 http, 0 http 1.0, 1 noua http, 140 tcp. Set 0 mark and 1 nohttp mark in 9 seconds
|
||||
|
||||
@ -31,7 +31,6 @@ setup_firewall() {
|
||||
config_get_bool handle_intranet "firewall" "handle_intranet" "0"
|
||||
config_get_bool handle_mmtls "firewall" "handle_mmtls" "0"
|
||||
|
||||
ipset create nohttp hash:ip,port hashsize 16384 timeout 300
|
||||
$IPT_M -N ua2f
|
||||
$IPT_M -A ua2f -d 10.0.0.0/8 -j RETURN
|
||||
$IPT_M -A ua2f -d 172.16.0.0/12 -j RETURN
|
||||
@ -45,7 +44,6 @@ setup_firewall() {
|
||||
[ "$handle_tls" -eq "1" ] || $IPT_M -A ua2f -p tcp --dport 443 -j RETURN # 不处理 HTTPS
|
||||
$IPT_M -A ua2f -p tcp --dport 80 -j CONNMARK --set-mark 44
|
||||
$IPT_M -A ua2f -m connmark --mark 43 -j RETURN # 不处理标记为非 http 的流 (实验性)
|
||||
$IPT_M -A ua2f -m set --match-set nohttp dst,dst -j RETURN
|
||||
[ "$handle_mmtls" -eq "1" ] || $IPT_M -A ua2f -p tcp --dport 80 -m string --string "/mmtls/" --algo bm -j RETURN # 不处理微信的mmtls
|
||||
$IPT_M -A ua2f -j NFQUEUE --queue-num 10010
|
||||
$IPT_M -A FORWARD -p tcp -m conntrack --ctdir ORIGINAL -j ua2f
|
||||
@ -94,8 +92,7 @@ stop_service() {
|
||||
$IPT_M -D FORWARD -p tcp -m conntrack --ctdir REPLY
|
||||
$IPT_M -F ua2f
|
||||
$IPT_M -X ua2f
|
||||
ipset flush nohttp
|
||||
ipset destroy nohttp
|
||||
|
||||
echo > "$FW_CONF"
|
||||
) 2>"/dev/null"
|
||||
}
|
||||
|
||||
67
src/cache.c
Normal file
67
src/cache.c
Normal file
@ -0,0 +1,67 @@
|
||||
#include "cache.h"
|
||||
#include "hashmap.h"
|
||||
#include "rwmutex.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
RWMutex lock;
|
||||
|
||||
const double CACHE_TIMEOUT = 600;
|
||||
|
||||
struct hashmap_s no_http_dst_cache;
|
||||
|
||||
static int iterate_pairs(void *const context, struct hashmap_element_s *const e) {
|
||||
__auto_type current_time = (time_t) context;
|
||||
|
||||
__auto_type store_time = (time_t) e->data;
|
||||
|
||||
if (difftime(current_time, store_time) > CACHE_TIMEOUT) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_Noreturn static void check_cache() {
|
||||
while (true) {
|
||||
rw_mutex_write_lock(&lock);
|
||||
|
||||
__auto_type current_time = time(NULL);
|
||||
|
||||
hashmap_iterate_pairs(&no_http_dst_cache, iterate_pairs, (void *) current_time);
|
||||
|
||||
rw_mutex_read_unlock(&lock);
|
||||
|
||||
// wait for 1 minute
|
||||
thrd_sleep(&(struct timespec) {60, 0}, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void init_cache() {
|
||||
rw_mutex_init(&lock);
|
||||
hashmap_create(1024, &no_http_dst_cache);
|
||||
|
||||
pthread_t cleanup_thread;
|
||||
__auto_type ret = pthread_create(&cleanup_thread, NULL, (void *(*)(void *)) check_cache, NULL);
|
||||
if (ret) {
|
||||
syslog(LOG_ERR, "Failed to create cleanup thread: %d", ret);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
bool check_addr_port(const char *addr_port, const int len) {
|
||||
rw_mutex_read_lock(&lock);
|
||||
__auto_type ret = hashmap_get(&no_http_dst_cache, addr_port, len) != NULL;
|
||||
rw_mutex_read_unlock(&lock);
|
||||
|
||||
rw_mutex_write_lock(&lock);
|
||||
if (ret) {
|
||||
hashmap_put(&no_http_dst_cache, addr_port, len, (void *) time(NULL));
|
||||
}
|
||||
rw_mutex_write_unlock(&lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
8
src/cache.h
Normal file
8
src/cache.h
Normal file
@ -0,0 +1,8 @@
|
||||
//
|
||||
// Created by zxilly on 2023/4/13.
|
||||
//
|
||||
|
||||
#ifndef UA2F_CACHE_H
|
||||
#define UA2F_CACHE_H
|
||||
|
||||
#endif //UA2F_CACHE_H
|
||||
61
src/child.c
Normal file
61
src/child.c
Normal file
@ -0,0 +1,61 @@
|
||||
#include "child.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static __pid_t child_pid = 0;
|
||||
static u_int8_t failure_count = 0;
|
||||
|
||||
const u_int8_t MAX_RETRY_COUNT = 8;
|
||||
|
||||
static volatile sig_atomic_t graceful_exit_requested = false;
|
||||
|
||||
void parent_sigterm_handler(int signum) {
|
||||
graceful_exit_requested = true;
|
||||
}
|
||||
|
||||
void child_sigterm_handler(int signum) {
|
||||
syslog(LOG_NOTICE, "Received SIGTERM, gracefully exiting.");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
void works_as_child() {
|
||||
while (!graceful_exit_requested) {
|
||||
if (failure_count++ > MAX_RETRY_COUNT) {
|
||||
syslog(LOG_ERR, "UA2F processor failed to start after [%d] times.", MAX_RETRY_COUNT);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
child_pid = fork();
|
||||
if (child_pid < 0) {
|
||||
syslog(LOG_ERR, "Failed to fork child process");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (child_pid == 0) {
|
||||
syslog(LOG_NOTICE, "UA2F processor start at [%d].", getpid());
|
||||
signal(SIGTERM, child_sigterm_handler);
|
||||
return;
|
||||
}
|
||||
|
||||
signal(SIGTERM, parent_sigterm_handler);
|
||||
|
||||
syslog(LOG_NOTICE, "Try to start UA2F processor at [%d].", child_pid);
|
||||
|
||||
int exit_stat;
|
||||
waitpid(child_pid, &exit_stat, 0);
|
||||
|
||||
if (WIFEXITED(exit_stat)) {
|
||||
syslog(LOG_NOTICE, "UA2F processor at [%d] exit with code [%d].", child_pid, WEXITSTATUS(exit_stat));
|
||||
if (WEXITSTATUS(exit_stat) == 0) {
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
syslog(LOG_NOTICE, "Received SIGTERM, gracefully exited.");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
10
src/child.h
Normal file
10
src/child.h
Normal file
@ -0,0 +1,10 @@
|
||||
//
|
||||
// Created by zxilly on 2023/4/13.
|
||||
//
|
||||
|
||||
#ifndef UA2F_CHILD_H
|
||||
#define UA2F_CHILD_H
|
||||
|
||||
void works_as_child();
|
||||
|
||||
#endif //UA2F_CHILD_H
|
||||
718
src/hashmap.h
Normal file
718
src/hashmap.h
Normal file
@ -0,0 +1,718 @@
|
||||
/*
|
||||
The latest version of this library is available on GitHub;
|
||||
https://github.com/sheredom/hashmap.h
|
||||
*/
|
||||
|
||||
/*
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
*/
|
||||
#ifndef SHEREDOM_HASHMAP_H_INCLUDED
|
||||
#define SHEREDOM_HASHMAP_H_INCLUDED
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Workaround a bug in the MSVC runtime where it uses __cplusplus when not
|
||||
// defined.
|
||||
#pragma warning(push, 0)
|
||||
#pragma warning(disable : 4668)
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if (defined(_MSC_VER) && defined(__AVX__)) || \
|
||||
(!defined(_MSC_VER) && defined(__SSE4_2__))
|
||||
#define HASHMAP_X86_SSE42
|
||||
#endif
|
||||
|
||||
#if defined(HASHMAP_X86_SSE42)
|
||||
#include <nmmintrin.h>
|
||||
#endif
|
||||
|
||||
#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32)
|
||||
#define HASHMAP_ARM_CRC32
|
||||
#endif
|
||||
|
||||
#if defined(HASHMAP_ARM_CRC32)
|
||||
#include <arm_acle.h>
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
/* Stop MSVC complaining about unreferenced functions */
|
||||
#pragma warning(disable : 4505)
|
||||
/* Stop MSVC complaining about not inlining functions */
|
||||
#pragma warning(disable : 4710)
|
||||
/* Stop MSVC complaining about inlining functions! */
|
||||
#pragma warning(disable : 4711)
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#pragma clang diagnostic ignored "-Wstatic-in-inline"
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define HASHMAP_WEAK __inline
|
||||
#elif defined(__clang__) || defined(__GNUC__)
|
||||
#define HASHMAP_WEAK __attribute__((weak))
|
||||
#else
|
||||
#error Non clang, non gcc, non MSVC compiler found!
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define HASHMAP_ALWAYS_INLINE __forceinline
|
||||
#elif (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
|
||||
defined(__cplusplus)
|
||||
#define HASHMAP_ALWAYS_INLINE __attribute__((always_inline)) inline
|
||||
#else
|
||||
/* If we cannot use inline, its not safe to use always_inline, so we mark the
|
||||
* function weak. */
|
||||
#define HASHMAP_ALWAYS_INLINE HASHMAP_WEAK
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1920)
|
||||
typedef unsigned __int8 hashmap_uint8_t;
|
||||
typedef unsigned __int32 hashmap_uint32_t;
|
||||
typedef unsigned __int64 hashmap_uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
typedef uint8_t hashmap_uint8_t;
|
||||
typedef uint32_t hashmap_uint32_t;
|
||||
typedef uint64_t hashmap_uint64_t;
|
||||
#endif
|
||||
|
||||
typedef struct hashmap_element_s {
|
||||
const void *key;
|
||||
hashmap_uint32_t key_len;
|
||||
int in_use;
|
||||
void *data;
|
||||
} hashmap_element_t;
|
||||
|
||||
typedef hashmap_uint32_t (*hashmap_hasher_t)(hashmap_uint32_t seed,
|
||||
const void *key,
|
||||
hashmap_uint32_t key_len);
|
||||
typedef int (*hashmap_comparer_t)(const void *a, hashmap_uint32_t a_len,
|
||||
const void *b, hashmap_uint32_t b_len);
|
||||
|
||||
typedef struct hashmap_s {
|
||||
hashmap_uint32_t log2_capacity;
|
||||
hashmap_uint32_t size;
|
||||
hashmap_hasher_t hasher;
|
||||
hashmap_comparer_t comparer;
|
||||
struct hashmap_element_s *data;
|
||||
} hashmap_t;
|
||||
|
||||
#define HASHMAP_LINEAR_PROBE_LENGTH (8)
|
||||
|
||||
typedef struct hashmap_create_options_s {
|
||||
hashmap_hasher_t hasher;
|
||||
hashmap_comparer_t comparer;
|
||||
hashmap_uint32_t initial_capacity;
|
||||
hashmap_uint32_t _;
|
||||
} hashmap_create_options_t;
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief Create a hashmap.
|
||||
/// @param initial_capacity The initial capacity of the hashmap.
|
||||
/// @param out_hashmap The storage for the created hashmap.
|
||||
/// @return On success 0 is returned.
|
||||
HASHMAP_WEAK int hashmap_create(const hashmap_uint32_t initial_capacity,
|
||||
struct hashmap_s *const out_hashmap);
|
||||
|
||||
/// @brief Create a hashmap.
|
||||
/// @param options The options to create the hashmap with.
|
||||
/// @param out_hashmap The storage for the created hashmap.
|
||||
/// @return On success 0 is returned.
|
||||
///
|
||||
/// The options members work as follows:
|
||||
/// - initial_capacity The initial capacity of the hashmap.
|
||||
/// - hasher Which hashing function to use with the hashmap (by default the
|
||||
// crc32 with Robert Jenkins' mix is used).
|
||||
HASHMAP_WEAK int hashmap_create_ex(struct hashmap_create_options_s options,
|
||||
struct hashmap_s *const out_hashmap);
|
||||
|
||||
/// @brief Put an element into the hashmap.
|
||||
/// @param hashmap The hashmap to insert into.
|
||||
/// @param key The string key to use.
|
||||
/// @param len The length of the string key.
|
||||
/// @param value The value to insert.
|
||||
/// @return On success 0 is returned.
|
||||
///
|
||||
/// The key string slice is not copied when creating the hashmap entry, and thus
|
||||
/// must remain a valid pointer until the hashmap entry is removed or the
|
||||
/// hashmap is destroyed.
|
||||
HASHMAP_WEAK int hashmap_put(struct hashmap_s *const hashmap,
|
||||
const void *const key, const hashmap_uint32_t len,
|
||||
void *const value);
|
||||
|
||||
/// @brief Get an element from the hashmap.
|
||||
/// @param hashmap The hashmap to get from.
|
||||
/// @param key The string key to use.
|
||||
/// @param len The length of the string key.
|
||||
/// @return The previously set element, or NULL if none exists.
|
||||
HASHMAP_WEAK void *hashmap_get(const struct hashmap_s *const hashmap,
|
||||
const void *const key,
|
||||
const hashmap_uint32_t len);
|
||||
|
||||
/// @brief Remove an element from the hashmap.
|
||||
/// @param hashmap The hashmap to remove from.
|
||||
/// @param key The string key to use.
|
||||
/// @param len The length of the string key.
|
||||
/// @return On success 0 is returned.
|
||||
HASHMAP_WEAK int hashmap_remove(struct hashmap_s *const hashmap,
|
||||
const void *const key,
|
||||
const hashmap_uint32_t len);
|
||||
|
||||
/// @brief Remove an element from the hashmap.
|
||||
/// @param hashmap The hashmap to remove from.
|
||||
/// @param key The string key to use.
|
||||
/// @param len The length of the string key.
|
||||
/// @return On success the original stored key pointer is returned, on failure
|
||||
/// NULL is returned.
|
||||
HASHMAP_WEAK const void *
|
||||
hashmap_remove_and_return_key(struct hashmap_s *const hashmap,
|
||||
const void *const key,
|
||||
const hashmap_uint32_t len);
|
||||
|
||||
/// @brief Iterate over all the elements in a hashmap.
|
||||
/// @param hashmap The hashmap to iterate over.
|
||||
/// @param iterator The function pointer to call on each element.
|
||||
/// @param context The context to pass as the first argument to f.
|
||||
/// @return If the entire hashmap was iterated then 0 is returned. Otherwise if
|
||||
/// the callback function f returned non-zero then non-zero is returned.
|
||||
HASHMAP_WEAK int hashmap_iterate(const struct hashmap_s *const hashmap,
|
||||
int (*iterator)(void *const context,
|
||||
void *const value),
|
||||
void *const context);
|
||||
|
||||
/// @brief Iterate over all the elements in a hashmap.
|
||||
/// @param hashmap The hashmap to iterate over.
|
||||
/// @param iterator The function pointer to call on each element.
|
||||
/// @param context The context to pass as the first argument to f.
|
||||
/// @return If the entire hashmap was iterated then 0 is returned.
|
||||
/// Otherwise if the callback function f returned positive then the positive
|
||||
/// value is returned. If the callback function returns -1, the current item
|
||||
/// is removed and iteration continues.
|
||||
HASHMAP_WEAK int hashmap_iterate_pairs(
|
||||
struct hashmap_s *const hashmap,
|
||||
int (*iterator)(void *const, struct hashmap_element_s *const),
|
||||
void *const context);
|
||||
|
||||
/// @brief Get the size of the hashmap.
|
||||
/// @param hashmap The hashmap to get the size of.
|
||||
/// @return The size of the hashmap.
|
||||
HASHMAP_ALWAYS_INLINE hashmap_uint32_t
|
||||
hashmap_num_entries(const struct hashmap_s *const hashmap);
|
||||
|
||||
/// @brief Get the capacity of the hashmap.
|
||||
/// @param hashmap The hashmap to get the size of.
|
||||
/// @return The capacity of the hashmap.
|
||||
HASHMAP_ALWAYS_INLINE hashmap_uint32_t
|
||||
hashmap_capacity(const struct hashmap_s *const hashmap);
|
||||
|
||||
/// @brief Destroy the hashmap.
|
||||
/// @param hashmap The hashmap to destroy.
|
||||
HASHMAP_WEAK void hashmap_destroy(struct hashmap_s *const hashmap);
|
||||
|
||||
static hashmap_uint32_t hashmap_crc32_hasher(const hashmap_uint32_t seed,
|
||||
const void *const s,
|
||||
const hashmap_uint32_t len);
|
||||
static int hashmap_memcmp_comparer(const void *const a,
|
||||
const hashmap_uint32_t a_len,
|
||||
const void *const b,
|
||||
const hashmap_uint32_t b_len);
|
||||
HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_hash_helper_int_helper(
|
||||
const struct hashmap_s *const m, const void *const key,
|
||||
const hashmap_uint32_t len);
|
||||
HASHMAP_ALWAYS_INLINE int
|
||||
hashmap_hash_helper(const struct hashmap_s *const m, const void *const key,
|
||||
const hashmap_uint32_t len,
|
||||
hashmap_uint32_t *const out_index);
|
||||
HASHMAP_WEAK int hashmap_rehash_iterator(void *const new_hash,
|
||||
struct hashmap_element_s *const e);
|
||||
HASHMAP_ALWAYS_INLINE int hashmap_rehash_helper(struct hashmap_s *const m);
|
||||
HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_clz(const hashmap_uint32_t x);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#define HASHMAP_CAST(type, x) static_cast<type>(x)
|
||||
#define HASHMAP_PTR_CAST(type, x) reinterpret_cast<type>(x)
|
||||
#define HASHMAP_NULL NULL
|
||||
#else
|
||||
#define HASHMAP_CAST(type, x) ((type)(x))
|
||||
#define HASHMAP_PTR_CAST(type, x) ((type)(x))
|
||||
#define HASHMAP_NULL 0
|
||||
#endif
|
||||
|
||||
int hashmap_create(const hashmap_uint32_t initial_capacity,
|
||||
struct hashmap_s *const out_hashmap) {
|
||||
struct hashmap_create_options_s options;
|
||||
memset(&options, 0, sizeof(options));
|
||||
options.initial_capacity = initial_capacity;
|
||||
|
||||
return hashmap_create_ex(options, out_hashmap);
|
||||
}
|
||||
|
||||
int hashmap_create_ex(struct hashmap_create_options_s options,
|
||||
struct hashmap_s *const out_hashmap) {
|
||||
if (2 > options.initial_capacity) {
|
||||
options.initial_capacity = 2;
|
||||
} else if (0 != (options.initial_capacity & (options.initial_capacity - 1))) {
|
||||
options.initial_capacity = 1u
|
||||
<< (32 - hashmap_clz(options.initial_capacity));
|
||||
}
|
||||
|
||||
if (HASHMAP_NULL == options.hasher) {
|
||||
options.hasher = &hashmap_crc32_hasher;
|
||||
}
|
||||
|
||||
if (HASHMAP_NULL == options.comparer) {
|
||||
options.comparer = &hashmap_memcmp_comparer;
|
||||
}
|
||||
|
||||
out_hashmap->data = HASHMAP_CAST(
|
||||
struct hashmap_element_s *,
|
||||
calloc(options.initial_capacity + HASHMAP_LINEAR_PROBE_LENGTH,
|
||||
sizeof(struct hashmap_element_s)));
|
||||
|
||||
out_hashmap->log2_capacity = 31 - hashmap_clz(options.initial_capacity);
|
||||
out_hashmap->size = 0;
|
||||
out_hashmap->hasher = options.hasher;
|
||||
out_hashmap->comparer = options.comparer;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hashmap_put(struct hashmap_s *const m, const void *const key,
|
||||
const hashmap_uint32_t len, void *const value) {
|
||||
hashmap_uint32_t index;
|
||||
|
||||
if ((HASHMAP_NULL == key) || (0 == len)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Find a place to put our value. */
|
||||
while (!hashmap_hash_helper(m, key, len, &index)) {
|
||||
if (hashmap_rehash_helper(m)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the data. */
|
||||
m->data[index].data = value;
|
||||
m->data[index].key = key;
|
||||
m->data[index].key_len = len;
|
||||
|
||||
/* If the hashmap element was not already in use, set that it is being used
|
||||
* and bump our size. */
|
||||
if (0 == m->data[index].in_use) {
|
||||
m->data[index].in_use = 1;
|
||||
m->size++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *hashmap_get(const struct hashmap_s *const m, const void *const key,
|
||||
const hashmap_uint32_t len) {
|
||||
hashmap_uint32_t i, curr;
|
||||
|
||||
if ((HASHMAP_NULL == key) || (0 == len)) {
|
||||
return HASHMAP_NULL;
|
||||
}
|
||||
|
||||
curr = hashmap_hash_helper_int_helper(m, key, len);
|
||||
|
||||
/* Linear probing, if necessary */
|
||||
for (i = 0; i < HASHMAP_LINEAR_PROBE_LENGTH; i++) {
|
||||
const hashmap_uint32_t index = curr + i;
|
||||
|
||||
if (m->data[index].in_use) {
|
||||
if (m->comparer(m->data[index].key, m->data[index].key_len, key, len)) {
|
||||
return m->data[index].data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
return HASHMAP_NULL;
|
||||
}
|
||||
|
||||
int hashmap_remove(struct hashmap_s *const m, const void *const key,
|
||||
const hashmap_uint32_t len) {
|
||||
hashmap_uint32_t i, curr;
|
||||
|
||||
if ((HASHMAP_NULL == key) || (0 == len)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
curr = hashmap_hash_helper_int_helper(m, key, len);
|
||||
|
||||
/* Linear probing, if necessary */
|
||||
for (i = 0; i < HASHMAP_LINEAR_PROBE_LENGTH; i++) {
|
||||
const hashmap_uint32_t index = curr + i;
|
||||
|
||||
if (m->data[index].in_use) {
|
||||
if (m->comparer(m->data[index].key, m->data[index].key_len, key, len)) {
|
||||
/* Blank out the fields including in_use */
|
||||
memset(&m->data[index], 0, sizeof(struct hashmap_element_s));
|
||||
|
||||
/* Reduce the size */
|
||||
m->size--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const void *hashmap_remove_and_return_key(struct hashmap_s *const m,
|
||||
const void *const key,
|
||||
const hashmap_uint32_t len) {
|
||||
hashmap_uint32_t i, curr;
|
||||
|
||||
if ((HASHMAP_NULL == key) || (0 == len)) {
|
||||
return HASHMAP_NULL;
|
||||
}
|
||||
|
||||
curr = hashmap_hash_helper_int_helper(m, key, len);
|
||||
|
||||
/* Linear probing, if necessary */
|
||||
for (i = 0; i < HASHMAP_LINEAR_PROBE_LENGTH; i++) {
|
||||
const hashmap_uint32_t index = curr + i;
|
||||
|
||||
if (m->data[index].in_use) {
|
||||
if (m->comparer(m->data[index].key, m->data[index].key_len, key, len)) {
|
||||
const void *const stored_key = m->data[index].key;
|
||||
|
||||
/* Blank out the fields */
|
||||
memset(&m->data[index], 0, sizeof(struct hashmap_element_s));
|
||||
|
||||
/* Reduce the size */
|
||||
m->size--;
|
||||
|
||||
return stored_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HASHMAP_NULL;
|
||||
}
|
||||
|
||||
int hashmap_iterate(const struct hashmap_s *const m,
|
||||
int (*f)(void *const, void *const), void *const context) {
|
||||
hashmap_uint32_t i;
|
||||
|
||||
for (i = 0; i < (hashmap_capacity(m) + HASHMAP_LINEAR_PROBE_LENGTH); i++) {
|
||||
if (m->data[i].in_use) {
|
||||
if (!f(context, m->data[i].data)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hashmap_iterate_pairs(struct hashmap_s *const m,
|
||||
int (*f)(void *const,
|
||||
struct hashmap_element_s *const),
|
||||
void *const context) {
|
||||
hashmap_uint32_t i;
|
||||
struct hashmap_element_s *p;
|
||||
int r;
|
||||
|
||||
for (i = 0; i < (hashmap_capacity(m) + HASHMAP_LINEAR_PROBE_LENGTH); i++) {
|
||||
p = &m->data[i];
|
||||
if (p->in_use) {
|
||||
r = f(context, p);
|
||||
switch (r) {
|
||||
case -1: /* remove item */
|
||||
memset(p, 0, sizeof(struct hashmap_element_s));
|
||||
m->size--;
|
||||
break;
|
||||
case 0: /* continue iterating */
|
||||
break;
|
||||
default: /* early exit */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hashmap_destroy(struct hashmap_s *const m) {
|
||||
free(m->data);
|
||||
memset(m, 0, sizeof(struct hashmap_s));
|
||||
}
|
||||
|
||||
HASHMAP_ALWAYS_INLINE hashmap_uint32_t
|
||||
hashmap_num_entries(const struct hashmap_s *const m) {
|
||||
return m->size;
|
||||
}
|
||||
|
||||
HASHMAP_ALWAYS_INLINE hashmap_uint32_t
|
||||
hashmap_capacity(const struct hashmap_s *const m) {
|
||||
return 1u << m->log2_capacity;
|
||||
}
|
||||
|
||||
hashmap_uint32_t hashmap_crc32_hasher(const hashmap_uint32_t seed,
|
||||
const void *const k,
|
||||
const hashmap_uint32_t len) {
|
||||
hashmap_uint32_t i = 0;
|
||||
hashmap_uint32_t crc32val = seed;
|
||||
const hashmap_uint8_t *const s = HASHMAP_PTR_CAST(const hashmap_uint8_t *, k);
|
||||
|
||||
#if defined(HASHMAP_X86_SSE42)
|
||||
for (; (i + sizeof(hashmap_uint32_t)) < len; i += sizeof(hashmap_uint32_t)) {
|
||||
hashmap_uint32_t next;
|
||||
memcpy(&next, &s[i], sizeof(next));
|
||||
crc32val = _mm_crc32_u32(crc32val, next);
|
||||
}
|
||||
|
||||
for (; i < len; i++) {
|
||||
crc32val = _mm_crc32_u8(crc32val, s[i]);
|
||||
}
|
||||
#elif defined(HASHMAP_ARM_CRC32)
|
||||
for (; (i + sizeof(hashmap_uint64_t)) < len; i += sizeof(hashmap_uint64_t)) {
|
||||
hashmap_uint64_t next;
|
||||
memcpy(&next, &s[i], sizeof(next));
|
||||
crc32val = __crc32d(crc32val, next);
|
||||
}
|
||||
|
||||
for (; i < len; i++) {
|
||||
crc32val = __crc32b(crc32val, s[i]);
|
||||
}
|
||||
#else
|
||||
// Using polynomial 0x11EDC6F41 to match SSE 4.2's crc function.
|
||||
static const hashmap_uint32_t crc32_tab[] = {
|
||||
0x00000000U, 0xF26B8303U, 0xE13B70F7U, 0x1350F3F4U, 0xC79A971FU,
|
||||
0x35F1141CU, 0x26A1E7E8U, 0xD4CA64EBU, 0x8AD958CFU, 0x78B2DBCCU,
|
||||
0x6BE22838U, 0x9989AB3BU, 0x4D43CFD0U, 0xBF284CD3U, 0xAC78BF27U,
|
||||
0x5E133C24U, 0x105EC76FU, 0xE235446CU, 0xF165B798U, 0x030E349BU,
|
||||
0xD7C45070U, 0x25AFD373U, 0x36FF2087U, 0xC494A384U, 0x9A879FA0U,
|
||||
0x68EC1CA3U, 0x7BBCEF57U, 0x89D76C54U, 0x5D1D08BFU, 0xAF768BBCU,
|
||||
0xBC267848U, 0x4E4DFB4BU, 0x20BD8EDEU, 0xD2D60DDDU, 0xC186FE29U,
|
||||
0x33ED7D2AU, 0xE72719C1U, 0x154C9AC2U, 0x061C6936U, 0xF477EA35U,
|
||||
0xAA64D611U, 0x580F5512U, 0x4B5FA6E6U, 0xB93425E5U, 0x6DFE410EU,
|
||||
0x9F95C20DU, 0x8CC531F9U, 0x7EAEB2FAU, 0x30E349B1U, 0xC288CAB2U,
|
||||
0xD1D83946U, 0x23B3BA45U, 0xF779DEAEU, 0x05125DADU, 0x1642AE59U,
|
||||
0xE4292D5AU, 0xBA3A117EU, 0x4851927DU, 0x5B016189U, 0xA96AE28AU,
|
||||
0x7DA08661U, 0x8FCB0562U, 0x9C9BF696U, 0x6EF07595U, 0x417B1DBCU,
|
||||
0xB3109EBFU, 0xA0406D4BU, 0x522BEE48U, 0x86E18AA3U, 0x748A09A0U,
|
||||
0x67DAFA54U, 0x95B17957U, 0xCBA24573U, 0x39C9C670U, 0x2A993584U,
|
||||
0xD8F2B687U, 0x0C38D26CU, 0xFE53516FU, 0xED03A29BU, 0x1F682198U,
|
||||
0x5125DAD3U, 0xA34E59D0U, 0xB01EAA24U, 0x42752927U, 0x96BF4DCCU,
|
||||
0x64D4CECFU, 0x77843D3BU, 0x85EFBE38U, 0xDBFC821CU, 0x2997011FU,
|
||||
0x3AC7F2EBU, 0xC8AC71E8U, 0x1C661503U, 0xEE0D9600U, 0xFD5D65F4U,
|
||||
0x0F36E6F7U, 0x61C69362U, 0x93AD1061U, 0x80FDE395U, 0x72966096U,
|
||||
0xA65C047DU, 0x5437877EU, 0x4767748AU, 0xB50CF789U, 0xEB1FCBADU,
|
||||
0x197448AEU, 0x0A24BB5AU, 0xF84F3859U, 0x2C855CB2U, 0xDEEEDFB1U,
|
||||
0xCDBE2C45U, 0x3FD5AF46U, 0x7198540DU, 0x83F3D70EU, 0x90A324FAU,
|
||||
0x62C8A7F9U, 0xB602C312U, 0x44694011U, 0x5739B3E5U, 0xA55230E6U,
|
||||
0xFB410CC2U, 0x092A8FC1U, 0x1A7A7C35U, 0xE811FF36U, 0x3CDB9BDDU,
|
||||
0xCEB018DEU, 0xDDE0EB2AU, 0x2F8B6829U, 0x82F63B78U, 0x709DB87BU,
|
||||
0x63CD4B8FU, 0x91A6C88CU, 0x456CAC67U, 0xB7072F64U, 0xA457DC90U,
|
||||
0x563C5F93U, 0x082F63B7U, 0xFA44E0B4U, 0xE9141340U, 0x1B7F9043U,
|
||||
0xCFB5F4A8U, 0x3DDE77ABU, 0x2E8E845FU, 0xDCE5075CU, 0x92A8FC17U,
|
||||
0x60C37F14U, 0x73938CE0U, 0x81F80FE3U, 0x55326B08U, 0xA759E80BU,
|
||||
0xB4091BFFU, 0x466298FCU, 0x1871A4D8U, 0xEA1A27DBU, 0xF94AD42FU,
|
||||
0x0B21572CU, 0xDFEB33C7U, 0x2D80B0C4U, 0x3ED04330U, 0xCCBBC033U,
|
||||
0xA24BB5A6U, 0x502036A5U, 0x4370C551U, 0xB11B4652U, 0x65D122B9U,
|
||||
0x97BAA1BAU, 0x84EA524EU, 0x7681D14DU, 0x2892ED69U, 0xDAF96E6AU,
|
||||
0xC9A99D9EU, 0x3BC21E9DU, 0xEF087A76U, 0x1D63F975U, 0x0E330A81U,
|
||||
0xFC588982U, 0xB21572C9U, 0x407EF1CAU, 0x532E023EU, 0xA145813DU,
|
||||
0x758FE5D6U, 0x87E466D5U, 0x94B49521U, 0x66DF1622U, 0x38CC2A06U,
|
||||
0xCAA7A905U, 0xD9F75AF1U, 0x2B9CD9F2U, 0xFF56BD19U, 0x0D3D3E1AU,
|
||||
0x1E6DCDEEU, 0xEC064EEDU, 0xC38D26C4U, 0x31E6A5C7U, 0x22B65633U,
|
||||
0xD0DDD530U, 0x0417B1DBU, 0xF67C32D8U, 0xE52CC12CU, 0x1747422FU,
|
||||
0x49547E0BU, 0xBB3FFD08U, 0xA86F0EFCU, 0x5A048DFFU, 0x8ECEE914U,
|
||||
0x7CA56A17U, 0x6FF599E3U, 0x9D9E1AE0U, 0xD3D3E1ABU, 0x21B862A8U,
|
||||
0x32E8915CU, 0xC083125FU, 0x144976B4U, 0xE622F5B7U, 0xF5720643U,
|
||||
0x07198540U, 0x590AB964U, 0xAB613A67U, 0xB831C993U, 0x4A5A4A90U,
|
||||
0x9E902E7BU, 0x6CFBAD78U, 0x7FAB5E8CU, 0x8DC0DD8FU, 0xE330A81AU,
|
||||
0x115B2B19U, 0x020BD8EDU, 0xF0605BEEU, 0x24AA3F05U, 0xD6C1BC06U,
|
||||
0xC5914FF2U, 0x37FACCF1U, 0x69E9F0D5U, 0x9B8273D6U, 0x88D28022U,
|
||||
0x7AB90321U, 0xAE7367CAU, 0x5C18E4C9U, 0x4F48173DU, 0xBD23943EU,
|
||||
0xF36E6F75U, 0x0105EC76U, 0x12551F82U, 0xE03E9C81U, 0x34F4F86AU,
|
||||
0xC69F7B69U, 0xD5CF889DU, 0x27A40B9EU, 0x79B737BAU, 0x8BDCB4B9U,
|
||||
0x988C474DU, 0x6AE7C44EU, 0xBE2DA0A5U, 0x4C4623A6U, 0x5F16D052U,
|
||||
0xAD7D5351U};
|
||||
|
||||
for (; i < len; i++) {
|
||||
crc32val = crc32_tab[(HASHMAP_CAST(hashmap_uint8_t, crc32val) ^ s[i])] ^
|
||||
(crc32val >> 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Use the mix function from murmur3.
|
||||
crc32val ^= len;
|
||||
|
||||
crc32val ^= crc32val >> 16;
|
||||
crc32val *= 0x85ebca6b;
|
||||
crc32val ^= crc32val >> 13;
|
||||
crc32val *= 0xc2b2ae35;
|
||||
crc32val ^= crc32val >> 16;
|
||||
|
||||
return crc32val;
|
||||
}
|
||||
|
||||
int hashmap_memcmp_comparer(const void *const a, const hashmap_uint32_t a_len,
|
||||
const void *const b, const hashmap_uint32_t b_len) {
|
||||
return (a_len == b_len) && (0 == memcmp(a, b, a_len));
|
||||
}
|
||||
|
||||
HASHMAP_ALWAYS_INLINE hashmap_uint32_t
|
||||
hashmap_hash_helper_int_helper(const struct hashmap_s *const m,
|
||||
const void *const k, const hashmap_uint32_t l) {
|
||||
return (m->hasher(~0u, k, l) * 2654435769u) >> (32u - m->log2_capacity);
|
||||
}
|
||||
|
||||
HASHMAP_ALWAYS_INLINE int
|
||||
hashmap_hash_helper(const struct hashmap_s *const m, const void *const key,
|
||||
const hashmap_uint32_t len,
|
||||
hashmap_uint32_t *const out_index) {
|
||||
hashmap_uint32_t curr;
|
||||
hashmap_uint32_t i;
|
||||
hashmap_uint32_t first_free;
|
||||
|
||||
/* If full, return immediately */
|
||||
if (hashmap_num_entries(m) == hashmap_capacity(m)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find the best index */
|
||||
curr = hashmap_hash_helper_int_helper(m, key, len);
|
||||
first_free = ~0u;
|
||||
|
||||
for (i = 0; i < HASHMAP_LINEAR_PROBE_LENGTH; i++) {
|
||||
const hashmap_uint32_t index = curr + i;
|
||||
|
||||
if (!m->data[index].in_use) {
|
||||
first_free = (first_free < index) ? first_free : index;
|
||||
} else if (m->comparer(m->data[index].key, m->data[index].key_len, key,
|
||||
len)) {
|
||||
*out_index = index;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't find a free element in the linear probe.
|
||||
if (~0u == first_free) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*out_index = first_free;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int hashmap_rehash_iterator(void *const new_hash,
|
||||
struct hashmap_element_s *const e) {
|
||||
int temp = hashmap_put(HASHMAP_PTR_CAST(struct hashmap_s *, new_hash), e->key,
|
||||
e->key_len, e->data);
|
||||
|
||||
if (0 < temp) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* clear old value to avoid stale pointers */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Doubles the size of the hashmap, and rehashes all the elements
|
||||
*/
|
||||
HASHMAP_ALWAYS_INLINE int hashmap_rehash_helper(struct hashmap_s *const m) {
|
||||
struct hashmap_create_options_s options;
|
||||
struct hashmap_s new_m;
|
||||
int flag;
|
||||
|
||||
memset(&options, 0, sizeof(options));
|
||||
options.initial_capacity = hashmap_capacity(m) * 2;
|
||||
options.hasher = m->hasher;
|
||||
|
||||
if (0 == options.initial_capacity) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
flag = hashmap_create_ex(options, &new_m);
|
||||
|
||||
if (0 != flag) {
|
||||
return flag;
|
||||
}
|
||||
|
||||
/* copy the old elements to the new table */
|
||||
flag = hashmap_iterate_pairs(m, hashmap_rehash_iterator,
|
||||
HASHMAP_PTR_CAST(void *, &new_m));
|
||||
|
||||
if (0 != flag) {
|
||||
return flag;
|
||||
}
|
||||
|
||||
hashmap_destroy(m);
|
||||
|
||||
/* put new hash into old hash structure by copying */
|
||||
memcpy(m, &new_m, sizeof(struct hashmap_s));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_clz(const hashmap_uint32_t x) {
|
||||
#if defined(_MSC_VER)
|
||||
unsigned long result;
|
||||
_BitScanReverse(&result, x);
|
||||
return 31 - HASHMAP_CAST(hashmap_uint32_t, result);
|
||||
#else
|
||||
return HASHMAP_CAST(hashmap_uint32_t, __builtin_clz(x));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -1,21 +0,0 @@
|
||||
//
|
||||
// Created by 12009 on 2021/1/12.
|
||||
//
|
||||
#include <libipset/ipset.h>
|
||||
|
||||
#ifndef UA2F_IPSET_HOOK_H
|
||||
#define UA2F_IPSET_HOOK_H
|
||||
|
||||
int func(struct ipset *ipset, void *p, int status, const char *msg, ...) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int func2(struct ipset *ipset, void *p) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int func3(struct ipset_session *session, void *p, const char *fmt, ...) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif //UA2F_IPSET_HOOK_H
|
||||
51
src/rwmutex.h
Normal file
51
src/rwmutex.h
Normal file
@ -0,0 +1,51 @@
|
||||
#ifndef UA2F_RWMUTEX_H
|
||||
#define UA2F_RWMUTEX_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <threads.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
typedef struct {
|
||||
mtx_t write_mtx;
|
||||
mtx_t read_mtx;
|
||||
atomic_int read_counter;
|
||||
} RWMutex;
|
||||
|
||||
void rw_mutex_init(RWMutex *rw_mutex) {
|
||||
mtx_init(&rw_mutex->write_mtx, mtx_plain);
|
||||
mtx_init(&rw_mutex->read_mtx, mtx_plain);
|
||||
rw_mutex->read_counter = ATOMIC_VAR_INIT(0);
|
||||
}
|
||||
|
||||
void rw_mutex_destroy(RWMutex *rw_mutex) {
|
||||
mtx_destroy(&rw_mutex->write_mtx);
|
||||
mtx_destroy(&rw_mutex->read_mtx);
|
||||
}
|
||||
|
||||
void rw_mutex_read_lock(RWMutex *rw_mutex) {
|
||||
mtx_lock(&rw_mutex->read_mtx);
|
||||
__auto_type read_count = atomic_fetch_add(&rw_mutex->read_counter, 1);
|
||||
if (read_count == 0) {
|
||||
mtx_lock(&rw_mutex->write_mtx);
|
||||
}
|
||||
mtx_unlock(&rw_mutex->read_mtx);
|
||||
}
|
||||
|
||||
void rw_mutex_read_unlock(RWMutex *rw_mutex) {
|
||||
mtx_lock(&rw_mutex->read_mtx);
|
||||
__auto_type read_count = atomic_fetch_sub(&rw_mutex->read_counter, 1);
|
||||
if (read_count == 1) {
|
||||
mtx_unlock(&rw_mutex->write_mtx);
|
||||
}
|
||||
mtx_unlock(&rw_mutex->read_mtx);
|
||||
}
|
||||
|
||||
void rw_mutex_write_lock(RWMutex *rw_mutex) {
|
||||
mtx_lock(&rw_mutex->write_mtx);
|
||||
}
|
||||
|
||||
void rw_mutex_write_unlock(RWMutex *rw_mutex) {
|
||||
mtx_unlock(&rw_mutex->write_mtx);
|
||||
}
|
||||
|
||||
#endif //UA2F_RWMUTEX_H
|
||||
@ -8,7 +8,7 @@ static long long UserAgentPacketCount = 0;
|
||||
static long long TcpPacketCount = 0;
|
||||
static long long PacketWithUserAgentMark = 0;
|
||||
static long long PacketWithoutUserAgentMark = 0;
|
||||
static long long HttpPacketCount = 4;
|
||||
static long long LastReportCount = 4;
|
||||
|
||||
static time_t start_t;
|
||||
|
||||
@ -32,36 +32,33 @@ void count_packet_without_user_agent_mark() {
|
||||
PacketWithoutUserAgentMark++;
|
||||
}
|
||||
|
||||
void count_http_packet() {
|
||||
HttpPacketCount++;
|
||||
}
|
||||
|
||||
static char TimeStringBuffer[60];
|
||||
|
||||
char *fill_time_string(int sec) {
|
||||
char *fill_time_string(double sec) {
|
||||
int s = (int) sec;
|
||||
memset(TimeStringBuffer, 0, sizeof(TimeStringBuffer));
|
||||
if (sec <= 60) {
|
||||
sprintf(TimeStringBuffer, "%d seconds", sec);
|
||||
} else if (sec <= 3600) {
|
||||
sprintf(TimeStringBuffer, "%d minutes and %d seconds", sec / 60, sec % 60);
|
||||
} else if (sec <= 86400) {
|
||||
sprintf(TimeStringBuffer, "%d hours, %d minutes and %d seconds", sec / 3600, sec % 3600 / 60, sec % 60);
|
||||
if (s <= 60) {
|
||||
sprintf(TimeStringBuffer, "%d seconds", s);
|
||||
} else if (s <= 3600) {
|
||||
sprintf(TimeStringBuffer, "%d minutes and %d seconds", s / 60, s % 60);
|
||||
} else if (s <= 86400) {
|
||||
sprintf(TimeStringBuffer, "%d hours, %d minutes and %d seconds", s / 3600, s % 3600 / 60, s % 60);
|
||||
} else {
|
||||
sprintf(TimeStringBuffer, "%d days, %d hours, %d minutes and %d seconds", sec / 86400, sec % 86400 / 3600,
|
||||
sec % 3600 / 60,
|
||||
sec % 60);
|
||||
sprintf(TimeStringBuffer, "%d days, %d hours, %d minutes and %d seconds", s / 86400, s % 86400 / 3600,
|
||||
s % 3600 / 60,
|
||||
s % 60);
|
||||
}
|
||||
return TimeStringBuffer;
|
||||
}
|
||||
|
||||
void try_print_statistics() {
|
||||
if (UserAgentPacketCount / HttpPacketCount == 2 || UserAgentPacketCount - HttpPacketCount >= 8192) {
|
||||
HttpPacketCount = UserAgentPacketCount;
|
||||
if (UserAgentPacketCount / LastReportCount == 2 || UserAgentPacketCount - LastReportCount >= 16384) {
|
||||
LastReportCount = UserAgentPacketCount;
|
||||
time_t current_t = time(NULL);
|
||||
syslog(LOG_INFO,
|
||||
"UA2F has handled %lld ua http, %lld tcp. Set %lld mark and %lld noUA mark in %s",
|
||||
UserAgentPacketCount, TcpPacketCount, PacketWithUserAgentMark, PacketWithoutUserAgentMark,
|
||||
fill_time_string((int) difftime(current_t, start_t)));
|
||||
fill_time_string(difftime(current_t, start_t)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
114
src/ua2f.c
114
src/ua2f.c
@ -1,5 +1,6 @@
|
||||
#include "ipset_hook.h"
|
||||
#include "statistics.h"
|
||||
#include "util.h"
|
||||
#include "child.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -13,52 +14,20 @@
|
||||
#include <arpa/inet.h>
|
||||
|
||||
|
||||
#include <libmnl/libmnl.h>
|
||||
|
||||
#include <linux/netfilter/nfnetlink_queue.h>
|
||||
#include <linux/netfilter/nfnetlink_conntrack.h>
|
||||
#include <libmnl/libmnl.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
|
||||
|
||||
int child_status;
|
||||
|
||||
static struct mnl_socket *nl;
|
||||
static const int queue_number = 10010;
|
||||
const int queue_number = 10010;
|
||||
|
||||
char *UAstr = NULL;
|
||||
|
||||
static struct ipset *Pipset;
|
||||
|
||||
void *memncasemem(const void *l, size_t l_len, const void *s, size_t s_len) {
|
||||
register char *cur, *last;
|
||||
const char *cl = (const char *) l;
|
||||
const char *cs = (const char *) s;
|
||||
|
||||
/* we need something to compare */
|
||||
if (l_len == 0 || s_len == 0)
|
||||
return NULL;
|
||||
|
||||
/* "s" must be smaller or equal to "l" */
|
||||
if (l_len < s_len)
|
||||
return NULL;
|
||||
|
||||
/* special case where s_len == 1 */
|
||||
if (s_len == 1)
|
||||
return memchr(l, (int) *cs, l_len);
|
||||
|
||||
/* the last position where its possible to find "s" in "l" */
|
||||
last = (char *) cl + l_len - s_len;
|
||||
|
||||
for (cur = (char *) cl; cur <= last; cur++)
|
||||
if (cur[0] == cs[0] && strncasecmp(cur, cs, s_len) == 0)
|
||||
return cur;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
char *replacement_user_agent_string = NULL;
|
||||
|
||||
static int parse_attrs(const struct nlattr *attr, void *data) {
|
||||
const struct nlattr **tb = data;
|
||||
@ -105,8 +74,6 @@ nfq_send_verdict(int queue_num, uint32_t id, struct pkt_buff *pktb, uint32_t mar
|
||||
mnl_attr_put_u32(nlh, CTA_MARK, htonl(43));
|
||||
mnl_attr_nest_end(nlh, nest); // 加 CONNMARK
|
||||
|
||||
ipset_parse_line(Pipset, addcmd); //加 ipset 标记
|
||||
|
||||
count_packet_without_user_agent_mark();
|
||||
}
|
||||
} else {
|
||||
@ -171,7 +138,7 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) {
|
||||
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);
|
||||
@ -249,7 +216,7 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) {
|
||||
}
|
||||
|
||||
if (ualength > 0) {
|
||||
if (nfq_tcp_mangle_ipv4(pktb, uaoffset, ualength, UAstr, ualength) == 1) {
|
||||
if (nfq_tcp_mangle_ipv4(pktb, uaoffset, ualength, replacement_user_agent_string, ualength) == 1) {
|
||||
count_user_agent_packet();
|
||||
} else {
|
||||
syslog(LOG_ERR, "Mangle packet failed.");
|
||||
@ -267,68 +234,19 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) {
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static void killChild() {
|
||||
syslog(LOG_INFO, "Received SIGTERM, kill child %d", child_status);
|
||||
kill(child_status, SIGKILL); // Not graceful, but work
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
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 errcount = 0;
|
||||
|
||||
signal(SIGTERM, killChild);
|
||||
|
||||
while (true) {
|
||||
child_status = fork();
|
||||
if (child_status < 0) {
|
||||
syslog(LOG_ERR, "Failed to give birth.");
|
||||
syslog(LOG_ERR, "Exit at fork.");
|
||||
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 suicide.");
|
||||
} else {
|
||||
syslog(LOG_ERR, "Meet fatal error.[%d] dies by %d", deadpid, deadstat);
|
||||
}
|
||||
}
|
||||
errcount++;
|
||||
if (errcount > 10) {
|
||||
syslog(LOG_ERR, "Meet too many fatal error, no longer try to recover.");
|
||||
syslog(LOG_ERR, "Exit with too many error.");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
openlog("UA2F", LOG_PID, LOG_SYSLOG);
|
||||
|
||||
works_as_child();
|
||||
|
||||
init_statistics();
|
||||
|
||||
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) {
|
||||
@ -338,21 +256,19 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
|
||||
perror("mnl_socket_bind");
|
||||
syslog(LOG_ERR, "Exit at mnl_socket_bind.");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
portid = mnl_socket_get_portid(nl);
|
||||
|
||||
buf = malloc(sizeof_buf);
|
||||
buf = malloc(MNL_SOCKET_BUFFER_SIZE);
|
||||
if (!buf) {
|
||||
perror("allocate receive buffer");
|
||||
syslog(LOG_ERR, "Exit at breakpoint 6.");
|
||||
syslog(LOG_ERR, "Failed to allocate buffer memory.");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
UAstr = malloc(sizeof_buf);
|
||||
memset(UAstr, 'F', sizeof_buf);
|
||||
replacement_user_agent_string = malloc(MNL_SOCKET_BUFFER_SIZE);
|
||||
memset(replacement_user_agent_string, 'F', MNL_SOCKET_BUFFER_SIZE);
|
||||
|
||||
nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_number);
|
||||
nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_BIND);
|
||||
|
||||
29
src/util.c
Normal file
29
src/util.c
Normal file
@ -0,0 +1,29 @@
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
void *memncasemem(const void *l, size_t l_len, const void *s, size_t s_len) {
|
||||
register char *cur, *last;
|
||||
const char *cl = (const char *) l;
|
||||
const char *cs = (const char *) s;
|
||||
|
||||
/* we need something to compare */
|
||||
if (l_len == 0 || s_len == 0)
|
||||
return NULL;
|
||||
|
||||
/* "s" must be smaller or equal to "l" */
|
||||
if (l_len < s_len)
|
||||
return NULL;
|
||||
|
||||
/* special case where s_len == 1 */
|
||||
if (s_len == 1)
|
||||
return memchr(l, (int) *cs, l_len);
|
||||
|
||||
/* the last position where its possible to find "s" in "l" */
|
||||
last = (char *) cl + l_len - s_len;
|
||||
|
||||
for (cur = (char *) cl; cur <= last; cur++)
|
||||
if (cur[0] == cs[0] && strncasecmp(cur, cs, s_len) == 0)
|
||||
return cur;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
8
src/util.h
Normal file
8
src/util.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef UA2F_UTIL_H
|
||||
#define UA2F_UTIL_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void *memncasemem(const void *l, size_t l_len, const void *s, size_t s_len);
|
||||
|
||||
#endif //UA2F_UTIL_H
|
||||
Loading…
Reference in New Issue
Block a user