diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f1880d..b7d00ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ option(ENABLE_SANITIZE_THREAD "Enable ThreadSanitizer" FALSE) # Plugins option(ENABLE_PLUGIN_HTTP "Enable HTTP support" TRUE) option(ENABLE_PLUGIN_TRAFFIC_MIRROR "Enable traffic mirror" TRUE) +option(ENABLE_PLUGIN_DOH "Enable Doh business" TRUE) option(ENABLE_PLUGIN_PANGU_HTTP "Enable Pangu-HTTP business" TRUE) option(ENABLE_PLUGIN_HTTP2 "Enable HTTP2 business" TRUE) option(ENABLE_PLUGIN_SSL_POLICY "Enable SSL policy support" TRUE) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 69c0b8e..d65f52e 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(common src/tfe_utils.cpp src/tfe_types.cpp src/tfe_future.cpp src/tfe_http.cpp src/tfe_plugin.cpp src/tfe_rpc.cpp src/tfe_cmsg.cpp src/tfe_kafka_logger.cpp) +add_library(common src/tfe_utils.cpp src/tfe_types.cpp src/tfe_future.cpp src/tfe_http.cpp src/tfe_plugin.cpp src/tfe_rpc.cpp src/tfe_cmsg.cpp src/tfe_kafka_logger.cpp src/tfe_resource.cpp) target_include_directories(common PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) target_link_libraries(common PUBLIC libevent-static libevent-static-openssl libevent-static-pthreads) target_link_libraries(common PUBLIC MESA_handle_logger) diff --git a/common/include/tfe_http.h b/common/include/tfe_http.h index ce352cc..7b961fa 100644 --- a/common/include/tfe_http.h +++ b/common/include/tfe_http.h @@ -282,10 +282,13 @@ struct http_field_name const char * field_name; }; +#define CALL_NEXT_PLUGIN 0 +#define NO_CALL_NEXT_PLUGIN -1 + typedef void (http_session_begin_cb)(const struct tfe_stream * stream, const struct tfe_http_session * session, unsigned int thread_id, void ** pme); -typedef void (http_session_data_cb)(const struct tfe_stream * stream, +typedef int (http_session_data_cb)(const struct tfe_stream * stream, const struct tfe_http_session * session, enum tfe_http_event event, const unsigned char * data, size_t datalen, unsigned int thread_id, void ** pme); diff --git a/common/include/tfe_resource.h b/common/include/tfe_resource.h new file mode 100644 index 0000000..7a70b0e --- /dev/null +++ b/common/include/tfe_resource.h @@ -0,0 +1,10 @@ +#pragma once + +enum RESOURCE_TYPE +{ + STATIC_MAAT, + DYNAMINC_MAAT, +}; + +int tfe_bussiness_resouce_init(); +void *tfe_bussiness_resouce_get(enum RESOURCE_TYPE type); \ No newline at end of file diff --git a/common/include/tfe_utils.h b/common/include/tfe_utils.h index 079c826..acb6e12 100644 --- a/common/include/tfe_utils.h +++ b/common/include/tfe_utils.h @@ -175,4 +175,5 @@ char *tfe_read_file(const char *filename, size_t *filelen); const char * tfe_version(); int __wrapper_MESA_htable_set_opt(MESA_htable_handle table, enum MESA_htable_opt opt_type, unsigned value); int __wrapper_MESA_htable_set_opt(MESA_htable_handle table, enum MESA_htable_opt opt_type, void * val, size_t len); +int tfe_decode_base64url(u_char *dst, u_char *src); diff --git a/common/src/tfe_http.cpp b/common/src/tfe_http.cpp index e655299..78d27c3 100644 --- a/common/src/tfe_http.cpp +++ b/common/src/tfe_http.cpp @@ -153,11 +153,12 @@ int http_frame_raise_session_begin(struct http_frame_session_ctx * ht_frame, struct tfe_plugin * plugin_info_iter; TFE_PLUGIN_FOREACH(plugin_info_iter, &__for_each_iterator) { + __plugin_id++; if (plugin_info_iter->on_session_begin == NULL) continue; /* Calling ctx, in callback can fetch by calling frame_plugin_status_get_XXX */ ht_frame->calling_plugin = plugin_info_iter; - ht_frame->calling_plugin_status = &ht_frame->plugin_status[__plugin_id]; + ht_frame->calling_plugin_status = &ht_frame->plugin_status[__plugin_id - 1]; /* Call session begin */ void ** calling_pme = &ht_frame->calling_plugin_status->pme; @@ -179,11 +180,12 @@ void http_frame_raise_session_end(struct http_frame_session_ctx * ht_frame, cons TFE_PLUGIN_FOREACH(plugin_info_iter, &__for_each_iterator) { + __plugin_id++; if (plugin_info_iter->on_session_end == NULL) continue; /* Calling ctx, in callback can fetch by calling frame_plugin_status_get_XXX */ ht_frame->calling_plugin = plugin_info_iter; - ht_frame->calling_plugin_status = &ht_frame->plugin_status[__plugin_id]; + ht_frame->calling_plugin_status = &ht_frame->plugin_status[__plugin_id - 1]; /* Call session end */ void ** calling_pme = &ht_frame->calling_plugin_status->pme; @@ -207,6 +209,7 @@ void http_frame_raise_event(struct http_frame_session_ctx * ht_frame, struct tfe_plugin * plugin_info_iter; TFE_PLUGIN_FOREACH(plugin_info_iter, &__for_each_iterator) { + __plugin_id++; if (plugin_info_iter->on_session_data == NULL) { continue; @@ -214,7 +217,7 @@ void http_frame_raise_event(struct http_frame_session_ctx * ht_frame, /* Calling ctx, in callback can fetch by calling frame_plugin_status_get_XXX */ ht_frame->calling_plugin = plugin_info_iter; - ht_frame->calling_plugin_status = &ht_frame->plugin_status[__plugin_id]; + ht_frame->calling_plugin_status = &ht_frame->plugin_status[__plugin_id - 1]; if (ht_frame->calling_plugin_status->detached) { @@ -222,7 +225,10 @@ void http_frame_raise_event(struct http_frame_session_ctx * ht_frame, } void ** calling_pme = &ht_frame->calling_plugin_status->pme; - plugin_info_iter->on_session_data(stream, ht_session, event, data, datalen, thread_id, calling_pme); + if (plugin_info_iter->on_session_data(stream, ht_session, event, data, datalen, thread_id, calling_pme) == NO_CALL_NEXT_PLUGIN) + { + break; + } } ht_frame->calling_plugin = NULL; diff --git a/common/src/tfe_resource.cpp b/common/src/tfe_resource.cpp new file mode 100644 index 0000000..92a8efb --- /dev/null +++ b/common/src/tfe_resource.cpp @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include + +#define MAAT_INPUT_JSON 0 +#define MAAT_INPUT_REDIS 1 +#define MAAT_INPUT_FILE 2 + +static Maat_feather_t static_maat = NULL; +static Maat_feather_t dynamic_maat = NULL; + +static Maat_feather_t create_maat_feather(const char *instance_name, const char *profile, const char *section, int max_thread, void *logger) +{ + Maat_feather_t target; + int input_mode = 0, maat_stat_on = 0, maat_perf_on = 0; + int ret = 0, scan_detail = 0, effect_interval = 60; + char table_info[TFE_STRING_MAX] = {0}, inc_cfg_dir[TFE_STRING_MAX] = {0}, ful_cfg_dir[TFE_STRING_MAX] = {0}; + char redis_server[TFE_STRING_MAX] = {0}; + char redis_port_range[TFE_STRING_MAX] = {0}; + char accept_tags[TFE_STRING_MAX] = {0}; + int redis_port_begin = 0, redis_port_end = 0; + int redis_port_select = 0; + int redis_db_idx = 0; + char json_cfg_file[TFE_STRING_MAX] = {0}, maat_stat_file[TFE_STRING_MAX] = {0}; + + MESA_load_profile_int_def(profile, section, "maat_input_mode", &(input_mode), 0); + MESA_load_profile_int_def(profile, section, "stat_switch", &(maat_stat_on), 1); + MESA_load_profile_int_def(profile, section, "perf_switch", &(maat_perf_on), 1); + MESA_load_profile_string_def(profile, section, "table_info", table_info, sizeof(table_info), ""); + MESA_load_profile_string_def(profile, section, "accept_tags", accept_tags, sizeof(accept_tags), ""); + MESA_load_profile_string_def(profile, section, "json_cfg_file", json_cfg_file, sizeof(json_cfg_file), ""); + MESA_load_profile_string_def(profile, section, "maat_redis_server", redis_server, sizeof(redis_server), ""); + MESA_load_profile_string_def(profile, section, "maat_redis_port_range", redis_port_range, sizeof(redis_server), "6379"); + MESA_load_profile_int_def(profile, section, "maat_redis_db_index", &(redis_db_idx), 0); + MESA_load_profile_string_def(profile, section, "inc_cfg_dir", inc_cfg_dir, sizeof(inc_cfg_dir), ""); + MESA_load_profile_string_def(profile, section, "full_cfg_dir", ful_cfg_dir, sizeof(ful_cfg_dir), ""); + MESA_load_profile_string_def(profile, section, "stat_file", maat_stat_file, sizeof(maat_stat_file), ""); + MESA_load_profile_int_def(profile, section, "effect_interval_s", &(effect_interval), 60); + + effect_interval *= 1000; //convert s to ms + + target = Maat_feather(max_thread, table_info, logger); + Maat_set_feather_opt(target, MAAT_OPT_INSTANCE_NAME, instance_name, strlen(instance_name) + 1); + switch (input_mode) + { + case MAAT_INPUT_JSON: + if (!strlen(json_cfg_file)) + { + TFE_LOG_ERROR(logger, "Invalid json_cfg_file, MAAT init failed."); + goto error_out; + } + + Maat_set_feather_opt(target, MAAT_OPT_JSON_FILE_PATH, json_cfg_file, strlen(json_cfg_file) + 1); + break; + case MAAT_INPUT_REDIS: + if (!strlen(redis_server)) + { + TFE_LOG_ERROR(logger, "Invalid maat_redis_server, MAAT init failed."); + goto error_out; + } + + ret = sscanf(redis_port_range, "%d-%d", &redis_port_begin, &redis_port_end); + if (ret == 1) + { + redis_port_select = redis_port_begin; + } + else if (ret == 2) + { + srand(time(NULL)); + redis_port_select = redis_port_begin + rand() % (redis_port_end - redis_port_begin); + } + else + { + TFE_LOG_ERROR(logger, "Invalid redis port range %s, MAAT init failed.", redis_port_range); + + goto error_out; + } + + Maat_set_feather_opt(target, MAAT_OPT_REDIS_IP, redis_server, strlen(redis_server) + 1); + Maat_set_feather_opt(target, MAAT_OPT_REDIS_PORT, &redis_port_select, sizeof(redis_port_select)); + Maat_set_feather_opt(target, MAAT_OPT_REDIS_INDEX, &redis_db_idx, sizeof(redis_db_idx)); + break; + case MAAT_INPUT_FILE: + if (!strlen(ful_cfg_dir)) + { + TFE_LOG_ERROR(logger, "Invalid ful_cfg_dir, MAAT init failed."); + goto error_out; + } + + if (!strlen(inc_cfg_dir)) + { + TFE_LOG_ERROR(logger, "Invalid inc_cfg_dir, MAAT init failed."); + goto error_out; + } + + Maat_set_feather_opt(target, MAAT_OPT_FULL_CFG_DIR, ful_cfg_dir, strlen(ful_cfg_dir) + 1); + Maat_set_feather_opt(target, MAAT_OPT_INC_CFG_DIR, inc_cfg_dir, strlen(inc_cfg_dir) + 1); + break; + default: + TFE_LOG_ERROR(logger, "Invalid MAAT Input Mode: %d.", input_mode); + goto error_out; + break; + } + + Maat_set_feather_opt(target, MAAT_OPT_FOREIGN_CONT_DIR, "./pangu_files", strlen("./pangu_files") + 1); + if (maat_stat_on) + { + Maat_set_feather_opt(target, MAAT_OPT_STAT_FILE_PATH, maat_stat_file, strlen(maat_stat_file) + 1); + Maat_set_feather_opt(target, MAAT_OPT_STAT_ON, NULL, 0); + if (maat_perf_on) + { + Maat_set_feather_opt(target, MAAT_OPT_PERF_ON, NULL, 0); + } + } + + Maat_set_feather_opt(target, MAAT_OPT_EFFECT_INVERVAL_MS, &effect_interval, sizeof(effect_interval)); + Maat_set_feather_opt(target, MAAT_OPT_SCAN_DETAIL, &scan_detail, sizeof(scan_detail)); + if (strlen(accept_tags) > 0) + { + Maat_set_feather_opt(target, MAAT_OPT_ACCEPT_TAGS, &accept_tags, sizeof(accept_tags)); + } + + ret = Maat_initiate_feather(target); + if (ret < 0) + { + TFE_LOG_ERROR(logger, "%s MAAT init failed.", __FUNCTION__); + goto error_out; + } + + return target; +error_out: + Maat_burn_feather(target); + return NULL; +} + +int tfe_bussiness_resouce_init() +{ + const char *profile_path = "./conf/pangu/pangu_pxy.conf"; + unsigned int thread_num = tfe_proxy_get_work_thread_count(); + static_maat = create_maat_feather("static", profile_path, "MAAT", thread_num, g_default_logger); + if (!static_maat) + { + return -1; + } + + dynamic_maat = create_maat_feather("dyn", profile_path, "DYNAMIC_MAAT", thread_num, g_default_logger); + if (!dynamic_maat) + { + return -1; + } + + return 0; +} + +void *tfe_bussiness_resouce_get(enum RESOURCE_TYPE type) +{ + if (type == STATIC_MAAT) + { + return static_maat; + } + if (type == DYNAMINC_MAAT) + { + return dynamic_maat; + } + + return NULL; +} \ No newline at end of file diff --git a/common/src/tfe_utils.cpp b/common/src/tfe_utils.cpp index bf20fbb..2656a68 100644 --- a/common/src/tfe_utils.cpp +++ b/common/src/tfe_utils.cpp @@ -154,4 +154,76 @@ cleanup: return content; } +static int tfe_decode_base64_internal(u_char *dst, u_char *src, const u_char *basis) +{ + size_t len; + u_char *d, *s; + for (len = 0; len < strlen((char *)src); len++) + { + if (src[len] == '=') + { + break; + } + + if (basis[src[len]] == 77) + { + return 0; + } + } + + if (len % 4 == 1) + { + return 0; + } + + s = src; + d = dst; + + while (len > 3) + { + *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4); + *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2); + *d++ = (u_char) (basis[s[2]] << 6 | basis[s[3]]); + + s += 4; + len -= 4; + } + + if (len > 1) + { + *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4); + } + + if (len > 2) + { + *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2); + } + + return d - dst; +} + +int tfe_decode_base64url(u_char *dst, u_char *src) +{ + static u_char basis64[] = { + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77, + 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 63, + 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77, + + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 + }; + + return tfe_decode_base64_internal(dst, src, basis64); +} diff --git a/conf/doh/doh.conf b/conf/doh/doh.conf new file mode 100644 index 0000000..0da64de --- /dev/null +++ b/conf/doh/doh.conf @@ -0,0 +1,34 @@ +[doh] +# default 1 +enable=1 + +[log] +# default 10 +# RLOG_LV_DEBUG : 10 +# RLOG_LV_INFO : 20 +# RLOG_LV_FATAL : 30 +log_level=10 + +[maat] +# default TSG_OBJ_APP_ID +table_appid=TSG_OBJ_APP_ID +# default TSG_SECURITY_ADDR +table_addr=TSG_SECURITY_ADDR +# default TSG_FIELD_DOH_QNAME +table_qname=TSG_FIELD_DOH_QNAME +# default TSG_FIELD_HTTP_HOST +table_host=TSG_FIELD_HTTP_HOST + +[kafka] +# default NULL +device_id_filepath==/opt/tsg/etc/tsg_sn.json +# default 0 +ENTRANCE_ID=0 +# default 1 +en_sendlog=1 +# default eth0 +NIC_NAME=eth0 +# defautl empty +kafka_brokerlist=192.168.40.224:9092 +# default POLICY-DOH-LOG +kafka_topic=POLICY-DOH-LOG \ No newline at end of file diff --git a/platform/CMakeLists.txt b/platform/CMakeLists.txt index 85a76d7..f2813c3 100644 --- a/platform/CMakeLists.txt +++ b/platform/CMakeLists.txt @@ -35,6 +35,10 @@ if(ENABLE_PLUGIN_HTTP2) target_link_libraries(tfe -Wl,--whole-archive http2 -Wl,--no-whole-archive) endif() +if(ENABLE_PLUGIN_DOH) + target_link_libraries(tfe -Wl,--whole-archive doh -Wl,--no-whole-archive) +endif() + if(ENABLE_PLUGIN_PANGU_HTTP) target_link_libraries(tfe -Wl,--whole-archive pangu-http -Wl,--no-whole-archive) endif() diff --git a/platform/include/internal/resource.h b/platform/include/internal/resource.h new file mode 100644 index 0000000..7a70b0e --- /dev/null +++ b/platform/include/internal/resource.h @@ -0,0 +1,10 @@ +#pragma once + +enum RESOURCE_TYPE +{ + STATIC_MAAT, + DYNAMINC_MAAT, +}; + +int tfe_bussiness_resouce_init(); +void *tfe_bussiness_resouce_get(enum RESOURCE_TYPE type); \ No newline at end of file diff --git a/platform/src/proxy.cpp b/platform/src/proxy.cpp index 9d11484..58dfa0f 100644 --- a/platform/src/proxy.cpp +++ b/platform/src/proxy.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -729,6 +730,10 @@ int main(int argc, char * argv[]) g_default_proxy->scm_sender = sender_scm_init(main_profile, "kni", g_default_logger); CHECK_OR_EXIT(g_default_proxy->scm_sender != NULL, "Failed at creating scm sender, Exit."); + /* RESOURCE INIT */ + ret = tfe_bussiness_resouce_init(); + CHECK_OR_EXIT(ret == 0, "TFE bussiness resource init failed. Exit."); + /* PLUGIN INIT */ unsigned int plugin_iterator = 0; for (struct tfe_plugin * plugin_iter = tfe_plugin_iterate(&plugin_iterator); @@ -739,7 +744,6 @@ int main(int argc, char * argv[]) TFE_LOG_INFO(g_default_logger, "Plugin %s initialized. ", plugin_iter->symbol); } - //ugly here. g_business_maat is available after plugin initiate. g_default_proxy->ssl_ply_enforcer=ssl_policy_enforcer_create(g_default_logger); ssl_manager_set_new_upstream_cb(g_default_proxy->ssl_mgr_handler, ssl_policy_enforce, g_default_proxy->ssl_ply_enforcer); ret = tfe_proxy_work_thread_run(g_default_proxy); diff --git a/platform/src/resource.cpp b/platform/src/resource.cpp new file mode 100644 index 0000000..14c6a13 --- /dev/null +++ b/platform/src/resource.cpp @@ -0,0 +1,168 @@ +#include +#include +#include +#include + +#define MAAT_INPUT_JSON 0 +#define MAAT_INPUT_REDIS 1 +#define MAAT_INPUT_FILE 2 + +static Maat_feather_t static_maat = NULL; +static Maat_feather_t dynamic_maat = NULL; + +static Maat_feather_t create_maat_feather(const char *instance_name, const char *profile, const char *section, int max_thread, void *logger) +{ + Maat_feather_t target; + int input_mode = 0, maat_stat_on = 0, maat_perf_on = 0; + int ret = 0, scan_detail = 0, effect_interval = 60; + char table_info[TFE_STRING_MAX] = {0}, inc_cfg_dir[TFE_STRING_MAX] = {0}, ful_cfg_dir[TFE_STRING_MAX] = {0}; + char redis_server[TFE_STRING_MAX] = {0}; + char redis_port_range[TFE_STRING_MAX] = {0}; + char accept_tags[TFE_STRING_MAX] = {0}; + int redis_port_begin = 0, redis_port_end = 0; + int redis_port_select = 0; + int redis_db_idx = 0; + char json_cfg_file[TFE_STRING_MAX] = {0}, maat_stat_file[TFE_STRING_MAX] = {0}; + + MESA_load_profile_int_def(profile, section, "maat_input_mode", &(input_mode), 0); + MESA_load_profile_int_def(profile, section, "stat_switch", &(maat_stat_on), 1); + MESA_load_profile_int_def(profile, section, "perf_switch", &(maat_perf_on), 1); + MESA_load_profile_string_def(profile, section, "table_info", table_info, sizeof(table_info), ""); + MESA_load_profile_string_def(profile, section, "accept_tags", accept_tags, sizeof(accept_tags), ""); + MESA_load_profile_string_def(profile, section, "json_cfg_file", json_cfg_file, sizeof(json_cfg_file), ""); + MESA_load_profile_string_def(profile, section, "maat_redis_server", redis_server, sizeof(redis_server), ""); + MESA_load_profile_string_def(profile, section, "maat_redis_port_range", redis_port_range, sizeof(redis_server), "6379"); + MESA_load_profile_int_def(profile, section, "maat_redis_db_index", &(redis_db_idx), 0); + MESA_load_profile_string_def(profile, section, "inc_cfg_dir", inc_cfg_dir, sizeof(inc_cfg_dir), ""); + MESA_load_profile_string_def(profile, section, "full_cfg_dir", ful_cfg_dir, sizeof(ful_cfg_dir), ""); + MESA_load_profile_string_def(profile, section, "stat_file", maat_stat_file, sizeof(maat_stat_file), ""); + MESA_load_profile_int_def(profile, section, "effect_interval_s", &(effect_interval), 60); + + effect_interval *= 1000; //convert s to ms + + target = Maat_feather(max_thread, table_info, logger); + Maat_set_feather_opt(target, MAAT_OPT_INSTANCE_NAME, instance_name, strlen(instance_name) + 1); + switch (input_mode) + { + case MAAT_INPUT_JSON: + if (!strlen(json_cfg_file)) + { + TFE_LOG_ERROR(logger, "Invalid json_cfg_file, MAAT init failed."); + goto error_out; + } + + Maat_set_feather_opt(target, MAAT_OPT_JSON_FILE_PATH, json_cfg_file, strlen(json_cfg_file) + 1); + break; + case MAAT_INPUT_REDIS: + if (!strlen(redis_server)) + { + TFE_LOG_ERROR(logger, "Invalid maat_redis_server, MAAT init failed."); + goto error_out; + } + + ret = sscanf(redis_port_range, "%d-%d", &redis_port_begin, &redis_port_end); + if (ret == 1) + { + redis_port_select = redis_port_begin; + } + else if (ret == 2) + { + srand(time(NULL)); + redis_port_select = redis_port_begin + rand() % (redis_port_end - redis_port_begin); + } + else + { + TFE_LOG_ERROR(logger, "Invalid redis port range %s, MAAT init failed.", redis_port_range); + + goto error_out; + } + + Maat_set_feather_opt(target, MAAT_OPT_REDIS_IP, redis_server, strlen(redis_server) + 1); + Maat_set_feather_opt(target, MAAT_OPT_REDIS_PORT, &redis_port_select, sizeof(redis_port_select)); + Maat_set_feather_opt(target, MAAT_OPT_REDIS_INDEX, &redis_db_idx, sizeof(redis_db_idx)); + break; + case MAAT_INPUT_FILE: + if (!strlen(ful_cfg_dir)) + { + TFE_LOG_ERROR(logger, "Invalid ful_cfg_dir, MAAT init failed."); + goto error_out; + } + + if (!strlen(inc_cfg_dir)) + { + TFE_LOG_ERROR(logger, "Invalid inc_cfg_dir, MAAT init failed."); + goto error_out; + } + + Maat_set_feather_opt(target, MAAT_OPT_FULL_CFG_DIR, ful_cfg_dir, strlen(ful_cfg_dir) + 1); + Maat_set_feather_opt(target, MAAT_OPT_INC_CFG_DIR, inc_cfg_dir, strlen(inc_cfg_dir) + 1); + break; + default: + TFE_LOG_ERROR(logger, "Invalid MAAT Input Mode: %d.", input_mode); + goto error_out; + break; + } + + Maat_set_feather_opt(target, MAAT_OPT_FOREIGN_CONT_DIR, "./pangu_files", strlen("./pangu_files") + 1); + if (maat_stat_on) + { + Maat_set_feather_opt(target, MAAT_OPT_STAT_FILE_PATH, maat_stat_file, strlen(maat_stat_file) + 1); + Maat_set_feather_opt(target, MAAT_OPT_STAT_ON, NULL, 0); + if (maat_perf_on) + { + Maat_set_feather_opt(target, MAAT_OPT_PERF_ON, NULL, 0); + } + } + + Maat_set_feather_opt(target, MAAT_OPT_EFFECT_INVERVAL_MS, &effect_interval, sizeof(effect_interval)); + Maat_set_feather_opt(target, MAAT_OPT_SCAN_DETAIL, &scan_detail, sizeof(scan_detail)); + if (strlen(accept_tags) > 0) + { + Maat_set_feather_opt(target, MAAT_OPT_ACCEPT_TAGS, &accept_tags, sizeof(accept_tags)); + } + + ret = Maat_initiate_feather(target); + if (ret < 0) + { + TFE_LOG_ERROR(logger, "%s MAAT init failed.", __FUNCTION__); + goto error_out; + } + + return target; +error_out: + Maat_burn_feather(target); + return NULL; +} + +int tfe_bussiness_resouce_init() +{ + const char *profile_path = "./conf/pangu/pangu_pxy.conf"; + unsigned int thread_num = tfe_proxy_get_work_thread_count(); + static_maat = create_maat_feather("static", profile_path, "MAAT", thread_num, g_default_logger); + if (!static_maat) + { + return -1; + } + + dynamic_maat = create_maat_feather("dyn", profile_path, "DYNAMIC_MAAT", thread_num, g_default_logger); + if (!dynamic_maat) + { + return -1; + } + + return 0; +} + +void *tfe_bussiness_resouce_get(enum RESOURCE_TYPE type) +{ + if (type == STATIC) + { + return static_maat; + } + if (type == DYNAMINC) + { + return dynamic_maat; + } + + return NULL; +} diff --git a/plugin/business/CMakeLists.txt b/plugin/business/CMakeLists.txt index ded492a..229060b 100644 --- a/plugin/business/CMakeLists.txt +++ b/plugin/business/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(traffic-mirror) +add_subdirectory(doh) add_subdirectory(pangu-http) add_subdirectory(ssl-policy) diff --git a/plugin/business/doh/CMakeLists.txt b/plugin/business/doh/CMakeLists.txt new file mode 100644 index 0000000..ce37958 --- /dev/null +++ b/plugin/business/doh/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(doh src/doh.cpp src/dns.cpp src/logger.cpp src/pub.cpp) +target_link_libraries(doh PUBLIC cjson) +target_link_libraries(doh PUBLIC maatframe) +target_link_libraries(doh PUBLIC common) +target_link_libraries(doh PUBLIC http) + +add_executable(dns_test test/dns_test.cpp src/dns.cpp src/pub.cpp) +target_link_libraries(dns_test PUBLIC cjson) +target_link_libraries(dns_test PUBLIC common) \ No newline at end of file diff --git a/plugin/business/doh/src/dns.cpp b/plugin/business/doh/src/dns.cpp new file mode 100644 index 0000000..219d73d --- /dev/null +++ b/plugin/business/doh/src/dns.cpp @@ -0,0 +1,1250 @@ +// git version 5effe725 + +#include +#include +#include +#include +#include + +#include "dns.h" + +#define DNS_ERROR_LOG(msg) printf("%s, %s:%d", msg, __FILE__, __LINE__) + +///////////////////////////////////////////////////////////////////////////////// +// compress/decompress API +///////////////////////////////////////////////////////////////////////////////// + +static int dns_compress_rr_str(u_char *domain, int domain_len, u_char *result) +{ + int section_len = 0; + int result_pos = 1; + int domain_pos = 0; + + if (domain_len < 0 || domain_len > DNS_MAX_NAME + 1 || '.' == domain[0] || '.' == domain[domain_len - 1]) + { + return -1; + } + + while ((domain[domain_pos] != '\n') || (domain[domain_pos] != '\0')) + { + section_len = 0; + while ((domain[domain_pos] != '.') && (domain[domain_pos] != '\n') && (domain[domain_pos] != '\0')) + { + result[result_pos] = domain[domain_pos]; + result_pos++; + domain_pos++; + section_len++; + } + result[result_pos - section_len - 1] = section_len; + + if ((domain[domain_pos] == '\n') || (domain[domain_pos] == '\0')) + break; + result_pos++; + domain_pos++; + } + + result[result_pos] = '\0'; + + if (result_pos >= domain_len) + { + return result_pos + 1; + } + else + { + return result_pos; + } + + return 0; +} + +static int dns_name_compress(u_char *name, int name_len, char *payload) +{ + int used_len = 0; + u_int16_t compress_len = 0; + u_char compress_name[DNS_MAX_NAME + 1] = {0}; + + if (name_len) + { + compress_len = dns_compress_rr_str(name, name_len, compress_name); + if (compress_len <= 0) + { + DNS_ERROR_LOG("dns_compress_rr_str()"); + return -1; + } + NS_SETLEN(compress_name, compress_len, payload, used_len); + } + else + { + NS_SET8(0, payload, used_len); + } + + return used_len; +} + +static int dns_name_decompress(char *msg, u_char **ptr, u_char *buf, int buflen, char *end) +{ + u_char *p = NULL; + int index = 0, len = 0; + int np = 0, tot_len = 0; + + p = *ptr; + *ptr = NULL; + index = 0; + np = 0; + while (1) + { + if (0 == p[0]) + { + break; + } + + if (0x0c0 == (p[0] & 0x0c0)) + { + if (p + 2 > (u_char *)end) + return -1; + + len = ((p[0] & 0x03f) << 8) + p[1]; + + if (NULL == *ptr) + { + tot_len += 2; + *ptr = p + 2; + } + + p = (u_char *)msg + len; + if (p > (u_char *)end) + return -1; + + /* too many pointers. */ + if (np++ > 16) + return -1; + + continue; + } + + len = p[0]; + p++; + tot_len++; + + if (p + len > (u_char *)end) + return -1; + + if (index + len >= buflen - 1) + return -1; + + memcpy(buf + index, p, len); + index += len; + buf[index++] = '.'; + p += len; + tot_len += len; + } + + if (NULL == *ptr) + { + *ptr = p + 1; + tot_len++; + } + /* + * omit last '.' + */ + if (index > 0) + { + buf[index - 1] = '\0'; + } + else + { + buf[0] = '\0'; + } + + return tot_len; +} + +///////////////////////////////////////////////////////////////////////////////// +// get API +///////////////////////////////////////////////////////////////////////////////// + +static void get_dns_hdr_info(dns_hdr_t *dns_hdr, char *payload) +{ + dns_hdr_t *tmp = ((dns_hdr_t *)payload); + memset(dns_hdr, 0, sizeof(dns_hdr_t)); + + dns_hdr->qr = tmp->qr; + dns_hdr->opcode = tmp->opcode; + dns_hdr->aa = tmp->aa; + dns_hdr->tc = tmp->tc; + dns_hdr->rd = tmp->rd; + dns_hdr->ra = tmp->ra; + dns_hdr->z = tmp->z; + dns_hdr->rcode = tmp->rcode; + + dns_hdr->id = ntohs(tmp->id); + dns_hdr->qdcount = ntohs(tmp->qdcount); + dns_hdr->ancount = ntohs(tmp->ancount); + dns_hdr->aucount = ntohs(tmp->aucount); + dns_hdr->adcount = ntohs(tmp->adcount); +} + +static void get_rr_type_nsec3(char **ptr, nsec3_t *nsec3, char *end) +{ + NS_GET8(nsec3->hash_algo, *ptr); + NS_GET8(nsec3->flags, *ptr); + NS_GET16(nsec3->iteration, *ptr); + NS_GET8(nsec3->salt_len, *ptr); + nsec3->salt_value = *(u_char **)ptr; + *ptr += nsec3->salt_len; /* jump salt_value */ + + NS_GET8(nsec3->hash_len, *ptr); + nsec3->next_hash_owner = *(u_char **)ptr; + *ptr += nsec3->hash_len; /* jump next_hash_owner */ +} + +// unused +static int get_rr_signer(u_char **ptr, u_char *buf, int buflen, char *end) +{ + u_char *p = NULL; + int len = 0, i = 0; + + p = *ptr; + if (0 == p[0]) + { + len = strlen("Root"); + memcpy(buf, "Root", len); + buf[len] = '\0'; + *ptr += 1; + return 1; + } + + p = *ptr; + *ptr = NULL; + len = 0; + + while (1) + { + len++; + if (0x03 == p[0]) + { + p += 1; + continue; + } + + buf[i++] = p[0]; + p += 1; + + if (0 == p[0]) + { + len++; + p += 1; + break; + } + } + + *ptr = p; + buf[i] = '\0'; + + return len; +} + +static void get_rr_type_rrsig(char **ptr, rrsig_t *rrsig, char *end) +{ + NS_GET16(rrsig->type_covered, *ptr); + NS_GET8(rrsig->algo, *ptr); + NS_GET8(rrsig->labels, *ptr); + NS_GET32(rrsig->original_ttl, *ptr); + NS_GET32(rrsig->sig_expiration, *ptr); + NS_GET32(rrsig->sig_inception, *ptr); + NS_GET16(rrsig->key_tag, *ptr); +} + +static int get_rr_type_wks(char **ptr, wks_t *wks, char *end) +{ + if (*ptr + 4 > end) + return -1; + NS_GET32(wks->addr, *ptr); + if (*ptr + 1 > end) + return -1; + NS_GET8(wks->protocol, *ptr); + wks->bitmap = *(u_char **)ptr; + + return 0; +} + +static void get_rr_type_info(char **ptr, hinfo_t *hinfo, char *end) +{ + int len = 0; + + NS_GET8(hinfo->cpu_len, *ptr); + len = MIN(hinfo->cpu_len, sizeof(DNS_HINFO_MAX_CPU - 1)); + memcpy((char *)hinfo->cpu, *ptr, len); + hinfo->cpu[len] = '\0'; + *ptr += hinfo->cpu_len; + hinfo->cpu_len = len; + + NS_GET8(hinfo->os_len, *ptr); + len = MIN(hinfo->os_len, sizeof(DNS_HINFO_MAX_OS - 1)); + memcpy((char *)hinfo->os, *ptr, len); + hinfo->os[len] = '\0'; + *ptr += hinfo->os_len; + hinfo->os_len = len; +} + +static int get_rr_type_soa(char *msg, char **ptr, soa_t *soa, char *end) +{ + if (0 >= dns_name_decompress(msg, (u_char **)ptr, soa->mname, sizeof(soa->mname), end)) + return -1; + if (0 >= dns_name_decompress(msg, (u_char **)ptr, soa->rname, sizeof(soa->rname), end)) + return -1; + + if (*ptr + 4 > end) + return -1; + NS_GET32(soa->serial, *ptr); + if (*ptr + 4 > end) + return -1; + NS_GET32(soa->refresh, *ptr); + if (*ptr + 4 > end) + return -1; + NS_GET32(soa->retry, *ptr); + if (*ptr + 4 > end) + return -1; + NS_GET32(soa->expire, *ptr); + if (*ptr + 4 > end) + return -1; + NS_GET32(soa->minimum, *ptr); + + return 0; +} + +static int get_rr_common_field(char *msg, char **ptr, dns_rr_t *rr, char *end) +{ + char *p = NULL; + if (*ptr == NULL) + { + return -1; + } + + dns_name_decompress(msg, (u_char **)ptr, rr->name, DNS_MAX_NAME + 1, end); +#if 0 + if(0 >= dns_name_decompress(msg, (u_char**)ptr, rr->name, DNS_MAX_NAME+1, end)) + { + return -1; + } +#endif + if (*ptr == NULL || *ptr + 2 > end) + { + return -1; + } + NS_GET16(rr->type, *ptr); + + if (*ptr == NULL || *ptr + 2 > end) + { + return -1; + } + + NS_GET16(rr->rr_class, *ptr); + + if (DNS_CLASS_UNKNOWN == rr->rr_class) + { + return -1; + } + + if (*ptr == NULL || *ptr + 4 > end) + { + return -1; + } + NS_GET32(rr->ttl, *ptr); + if (*ptr == NULL || *ptr + 2 > end) + { + return -1; + } + NS_GET16(rr->rdlength, *ptr); + p = *ptr + rr->rdlength; + if (*ptr == NULL || p > end) + { + return -1; + } + + return 0; +} + +static int get_dns_query_question(char *msg, char **ptr, dns_query_question_t *q, char *end) +{ + int used_len = 0; + if (0 >= dns_name_decompress(msg, (u_char **)ptr, q->qname, DNS_MAX_NAME + 1, end)) + { + return -1; + } + + used_len = strlen((char *)q->qname); + if (q->qname[used_len - 1] == '.') + return -2; + + if (*ptr + 2 > end) + { + return -1; + } + + NS_GET16(q->qtype, *ptr); + + if (*ptr + 2 > end) + { + return -1; + } + + NS_GET16(q->qclass, *ptr); + + return 0; +} + +static int get_one_resource_record(char *msg, char **ptr, dns_rr_t *rr, char *end) +{ + u_int32_t len = 0, byte = 0; + u_char *original_ptr = NULL; + + switch (rr->type) + { + case DNS_TYPE_CNAME: + original_ptr = (u_char *)*ptr; + if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.cname, DNS_MAX_NAME + 1, end)) + { + return 0; + } + + break; + case DNS_TYPE_HINFO: + get_rr_type_info(ptr, &(rr->rdata.hinfo), end); + break; + case DNS_TYPE_MB: + if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.mb, DNS_MAX_NAME + 1, end)) + return 0; + break; + case DNS_TYPE_MD: + if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.md, DNS_MAX_NAME + 1, end)) + return 0; + break; + case DNS_TYPE_MF: + if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.mf, DNS_MAX_NAME + 1, end)) + return 0; + break; + case DNS_TYPE_MG: + if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.mg, DNS_MAX_NAME + 1, end)) + return 0; + break; + case DNS_TYPE_MINFO: + if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.minfo.rmailbx, DNS_MAX_NAME + 1, end)) + return 0; + if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.minfo.emailbx, DNS_MAX_NAME + 1, end)) + return 0; + break; + case DNS_TYPE_MR: + if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.mr, DNS_MAX_NAME + 1, end)) + return 0; + break; + case DNS_TYPE_MX: + if (*ptr + 2 > end) + return 0; + NS_GET16(rr->rdata.mx.preference, *ptr); + if (rr->rdlength - 2 < ((u_char *)*ptr)[0]) + { + if (rr->rdlength < 2) + { + *ptr += rr->rdlength; + break; + } + len = MIN(DNS_MAX_NAME - 1, rr->rdlength - 2); /* size=1byte */ + memcpy(rr->rdata.mx.exchange, *ptr, len); /* error labels */ + rr->rdata.mx.exchange[len] = '\0'; + *ptr += rr->rdlength - 2; + } + else + { + if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.mx.exchange, DNS_MAX_NAME + 1, end)) + return 0; + } + break; + case DNS_TYPE_NS: + if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.ns, DNS_MAX_NAME + 1, end)) + return 0; + break; + case DNS_TYPE_PTR: + if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.ptr, DNS_MAX_NAME + 1, end)) + return 0; + break; + case DNS_TYPE_SOA: + original_ptr = (u_char *)*ptr; + if (0 != get_rr_type_soa(msg, ptr, &(rr->rdata.soa), end)) + return 0; + if ((char *)original_ptr + rr->rdlength != *ptr) + { + *ptr = (char *)original_ptr + rr->rdlength; + } + break; + case DNS_TYPE_A: + if (*ptr + 4 > end) + return 0; + memcpy(rr->rdata.a, *ptr, NS_INT32SZ); + (*ptr) += NS_INT32SZ; + break; + case DNS_TYPE_AAAA: + if (*ptr + 16 > end) + return -1; + memcpy(rr->rdata.aaaa, *ptr, 16); + (*ptr) += 16; + break; + case DNS_TYPE_DNAME: + if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.dname, DNS_MAX_NAME + 1, end)) + return 0; + break; + case DNS_TYPE_ISDN: + memcpy(rr->rdata.isdn, *ptr, sizeof(u_char)); + (*ptr) += 1; + break; + case DNS_TYPE_TXT: + len = MIN(DNS_MAX_NAME - 1, rr->rdlength - 1); /* size=1byte */ + memcpy(rr->rdata.txt.txt, *ptr + 1, len); + rr->rdata.txt.size = len; + *ptr += rr->rdlength; + break; + case DNS_TYPE_RP: + if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.rp.mailbox, DNS_MAX_NAME + 1, end)) + return 0; + if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.rp.txt_rr, DNS_MAX_NAME + 1, end)) + return 0; + break; + case DNS_TYPE_NULL: + len = MIN(DNS_MAX_NAME - 1, rr->rdlength - 1); /* size=1byte */ + memcpy(rr->rdata.null.null, *ptr + 1, len); + rr->rdata.null.size = len; + *ptr += rr->rdlength; + break; + case DNS_TYPE_WKS: + if (0 != get_rr_type_wks(ptr, &(rr->rdata.wks), end)) + return 0; + rr->rdata.wks.size = rr->rdlength - 5; + *ptr += rr->rdlength - 5; + case DNS_TYPE_SRV: + NS_GET16(rr->rdata.srv.priority, *ptr); + NS_GET16(rr->rdata.srv.weight, *ptr); + NS_GET16(rr->rdata.srv.port, *ptr); + if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.srv.target, DNS_MAX_TARGET, end)) + return 0; + break; + case DNS_TYPE_DS: + case DNS_TYPE_DLV: + if (*ptr + 4 > end) + return 0; + NS_GET16(rr->rdata.ds.key_tag, *ptr); + NS_GET8(rr->rdata.ds.algo, *ptr); + NS_GET8(rr->rdata.ds.digest_type, *ptr); + rr->rdata.ds.digest = *(u_char **)ptr; + rr->rdata.ds.digest_len = rr->rdlength - 4; + *ptr += rr->rdlength - 4; + break; + case DNS_TYPE_RRSIG: + if (*ptr + 18 > end) + return 0; + get_rr_type_rrsig(ptr, &(rr->rdata.rrsig), end); + len = dns_name_decompress(msg, (u_char **)ptr, rr->rdata.rrsig.signer_name, DNS_MAX_SIGNER_NAME, end); + if (len <= 0) + { + return -1; + } + rr->rdata.rrsig.signature = *(u_char **)ptr; + rr->rdata.rrsig.signature_len = rr->rdlength - 18 - len; + *ptr += rr->rdlength - 18 - len; + break; + case DNS_TYPE_NSEC: + original_ptr = (u_char *)*ptr; + len = dns_name_decompress(msg, (u_char **)ptr, rr->rdata.nsec.next_domain, DNS_MAX_OWNER, end); + if (len <= 0) + { + return -1; + } + + if ((original_ptr + rr->rdlength != (u_char *)*ptr) && (*ptr != NULL)) + { + NS_GET16(len, *ptr); + byte = MIN(DNS_MAX_MAPS - 1, len); + memcpy(rr->rdata.nsec.type_bit_maps, *ptr, byte); + rr->rdata.nsec.type_bit_maps[byte] = '\0'; + *ptr += len; + rr->rdata.nsec.maps_temp_len = len; + rr->rdata.nsec.maps_len = len; + len = byte; + + byte = ((u_char *)ptr)[0]; + if ((byte & 0xFF) == 0xFF || byte == 128) + { + *ptr += 1; /* jump 0xFF */ + byte = ((u_char *)ptr)[0]; + *ptr += 1; /* jump 1 byte of len */ + len = MIN(DNS_MAX_MAPS - 1 - len, byte); + memcpy(rr->rdata.nsec.type_bit_maps + rr->rdata.nsec.maps_len, *ptr, len); + (rr->rdata.nsec.type_bit_maps + rr->rdata.nsec.maps_len)[len] = '\0'; + *ptr += byte; /* jump byte */ + rr->rdata.nsec.maps_len += len; + } + } + break; + case DNS_TYPE_DNSKEY: + if (*ptr + 4 > end) + return 0; + NS_GET16(rr->rdata.dnskey.flags, *ptr); + NS_GET8(rr->rdata.dnskey.protocol, *ptr); + NS_GET8(rr->rdata.dnskey.algo, *ptr); + rr->rdata.dnskey.public_key = *(u_char **)ptr; + rr->rdata.dnskey.public_key_len = rr->rdlength - 4; /* sizeof(flags)+sizeof(protocol)+sizeof(algo) */ + *ptr += rr->rdlength - 4; /* todo add log */ + break; + case DNS_TYPE_NSEC3: + if (*ptr + 5 > end) + return 0; + original_ptr = (u_char *)*ptr; + get_rr_type_nsec3(ptr, &(rr->rdata.nsec3), end); + + if ((original_ptr + rr->rdlength != (u_char *)*ptr) && (*ptr != NULL)) + { + NS_GET16(len, *ptr); + byte = MIN(DNS_MAX_MAPS - 1, len); + memcpy(rr->rdata.nsec3.type_bit_maps, *ptr, byte); + rr->rdata.nsec3.type_bit_maps[byte] = '\0'; + *ptr += len; + rr->rdata.nsec3.maps_temp_len = byte; + rr->rdata.nsec3.maps_len = byte; + len = byte; + + byte = ((u_char *)*ptr)[0]; + if ((byte & 0xFF) == 0xFF || byte == 128) + { + *ptr += 1; /* jump 0xFF */ + byte = ((u_char *)*ptr)[0]; + *ptr += 1; /* jump 1 byte of len */ + len = MIN(DNS_MAX_MAPS - 1 - len, byte); + memcpy(rr->rdata.nsec3.type_bit_maps + rr->rdata.nsec3.maps_len, *ptr, len); + (rr->rdata.nsec3.type_bit_maps + rr->rdata.nsec3.maps_len)[len] = '\0'; + *ptr += byte; /* jump byte */ + rr->rdata.nsec3.maps_len += len; + } + } + break; + case DNS_TYPE_NSEC3PARAM: + NS_GET8(rr->rdata.nsec3param.hash_algo, *ptr); + NS_GET8(rr->rdata.nsec3param.flags, *ptr); + NS_GET16(rr->rdata.nsec3param.iteration, *ptr); + rr->rdata.nsec3param.salt_len = rr->rdlength - 4 - 1; + *ptr += 1; + rr->rdata.nsec3param.salt_value = *(u_char **)ptr; + *ptr += rr->rdlength - 5; + break; + case DNS_TYPE_OPT: + /* fail through */ + case DNS_TYPE_UNKNOWN: + /* fail through */ + default: + memcpy(rr->rdata.unknown_data, *ptr, rr->rdlength); + rr->rdata.unknown_data[rr->rdlength] = '\0'; + (*ptr) += rr->rdlength; + printf("No support respone type, type: %d", rr->type); + break; + } + + return 1; +} + +static int parse_resource_record(dns_info_t *dns_info, char *payload, int payload_len, char **cur_pos) +{ + int i = 0; + + dns_info->rr_count = dns_info->hdr_info.ancount + dns_info->hdr_info.adcount + dns_info->hdr_info.aucount; + if (dns_info->rr_count == 0) + { + return 0; + } + + dns_info->rr_count = MIN(dns_info->rr_count, MAX_RR_NUM); + for (i = 0; i < dns_info->rr_count; i++) + { + if ((u_char *)*cur_pos >= (u_char *)payload + payload_len || (u_char *)*cur_pos < (u_char *)payload) + { + DNS_ERROR_LOG("parse_resource_record()"); + return -1; + } + + if (0 != get_rr_common_field(payload, cur_pos, &dns_info->rr[i], payload + payload_len)) + { + if (dns_info->rr[i].rr_class == DNS_CLASS_UNKNOWN) + { + DNS_ERROR_LOG("get_rr_common_field()"); + return -1; /* error packet */ + } + else + { + DNS_ERROR_LOG("get_rr_common_field()"); + dns_info->rr_count -= 1; + i -= 1; + continue; + } + } + + if (dns_info->rr[i].rdlength == 0) + { + continue; + } + + if (get_one_resource_record(payload, cur_pos, &dns_info->rr[i], payload + payload_len) != 1) + { + DNS_ERROR_LOG("get_one_resource_record()"); + dns_info->rr_count -= 1; + i -= 1; + } + } + + assert(i == dns_info->rr_count); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////// +// set API +///////////////////////////////////////////////////////////////////////////////// + +static void set_dns_hdr_info(dns_hdr_t *src, char *payload) +{ + dns_hdr_t *dst = ((dns_hdr_t *)payload); + memset(dst, 0, sizeof(dns_hdr_t)); + + dst->qr = src->qr; + dst->opcode = src->opcode; + dst->aa = src->aa; + dst->tc = src->tc; + dst->rd = src->rd; + dst->ra = src->ra; + dst->z = src->z; + dst->rcode = src->rcode; + + dst->id = htons(src->id); + dst->qdcount = htons(src->qdcount); // 16bits: QDCOUNT: number of questions + dst->ancount = htons(src->ancount); // 16bits: ANCOUNT: number of answer resource records + dst->aucount = htons(src->aucount); // 16bits: NSCOUNT: number of authority resource records + dst->adcount = htons(src->adcount); // 16bits: ARCOUNT: number of additional resource records +} + +static int set_dns_pkt_rr_header(char *payload, int payload_len, dns_rr_t *rr, int rdlength, char **rr_header_length_ptr) +{ + int ret; + int used_len = 0; + + if (rr->name) + { + ret = dns_name_compress(rr->name, strlen((char *)rr->name), payload); + if (ret == -1) + { + return -1; + } + used_len += ret; + } + else + { + NS_SET16(0xc00c, payload, used_len); + } + + NS_SET16(rr->type, payload, used_len); + NS_SET16(rr->rr_class, payload, used_len); + NS_SET32(rr->ttl, payload, used_len); + *rr_header_length_ptr = payload + used_len; + NS_SET16(rr->rdlength, payload, used_len); + + return used_len; +} + +static void reset_rr_header_length(char **rr_length_ptr, int rdlength) +{ + u_int16_t seg_16 = 0; + seg_16 = htons(rdlength); + memset(*rr_length_ptr, 0, sizeof(seg_16)); + memcpy(*rr_length_ptr, &seg_16, sizeof(seg_16)); +} + +///////////////////////////////////////////////////////////////////////////////// +// cheat API +///////////////////////////////////////////////////////////////////////////////// + +static void set_cheat_pkt_header(dns_hdr_t *dns_hdr) +{ + dns_hdr->qr = 1; // 1bit: Response + dns_hdr->opcode = 0; // 4bits: Query + dns_hdr->aa = 0; // 1bit: authoritative answer + dns_hdr->tc = 0; // 1bit: Not truncated + dns_hdr->rd = 1; // 1bit: Recursion Desired + dns_hdr->ra = 1; // 1bit: Recursion Available + dns_hdr->z = 0; // 3bits: Reserved for future use: Must be zero in all queries and responses + dns_hdr->rcode = 0; // 4bits: 0: No error condition + + dns_hdr->id = htons(dns_hdr->id); + dns_hdr->qdcount = htons(dns_hdr->qdcount); // 16bits: QDCOUNT: number of questions + dns_hdr->ancount = htons(dns_hdr->ancount); // 16bits: ANCOUNT: number of answer resource records + dns_hdr->aucount = htons(dns_hdr->aucount); // 16bits: NSCOUNT: number of authority resource records + dns_hdr->adcount = htons(dns_hdr->adcount); // 16bits: ARCOUNT: number of additional resource records +} + +static int set_cheat_pkt_question(char *payload, dns_query_question_t *query, int query_num) +{ + int ret; + int used_len = 0; + + /* 只处理一个请求 */ + ret = dns_name_compress(query->qname, strlen((char *)query->qname), payload); + if (ret == -1) + { + return -1; + } + used_len += ret; + + NS_SET16(query->qtype, payload, used_len); + NS_SET16(query->qclass, payload, used_len); + + return used_len; +} + +// NOTE 返回值 TODO +static int set_cheat_pkt_rr_pdata_type_str(char *payload, cheat_pkt_opt_t *cheat_opt) +{ + int used_len = 0; + u_int16_t q_class = 1; + u_int16_t compress_len = 0; + u_char compress_name[DNS_MAX_NAME + 1] = {0}; + + NS_SET16(0xc00c, payload, used_len); + NS_SET16(cheat_opt->res_type, payload, used_len); + NS_SET16(q_class, payload, used_len); + NS_SET32(cheat_opt->ttl, payload, used_len); + + compress_len = dns_compress_rr_str(cheat_opt->res_info, cheat_opt->res_len, compress_name); + if (compress_len <= 0) + { + DNS_ERROR_LOG("dns_compress_rr_str()"); + } + + NS_SET16(compress_len, payload, used_len); + NS_SETLEN(compress_name, compress_len, payload, used_len); + + return used_len; +} + +static int set_cheat_pkt_rr_pdata_type_ip(char *payload, cheat_pkt_opt_t *cheat_opt) +{ + int used_len = 0; + u_int16_t q_class = 1; + + NS_SET16(0xc00c, payload, used_len); + NS_SET16(cheat_opt->res_type, payload, used_len); + NS_SET16(q_class, payload, used_len); + NS_SET32(cheat_opt->ttl, payload, used_len); + NS_SET16(cheat_opt->res_len, payload, used_len); + NS_SETLEN((char *)cheat_opt->res_info, cheat_opt->res_len, payload, used_len); + + return used_len; +} + +///////////////////////////////////////////////////////////////////////////////// +// public API +///////////////////////////////////////////////////////////////////////////////// + +dns_info_t *dns_new(void) +{ + dns_info_t *dns_info = (dns_info_t *)calloc(sizeof(dns_info_t), 1); + return dns_info; +} + +void dns_free(dns_info_t *dns_info) +{ + if (dns_info) + { + free(dns_info); + dns_info = NULL; + } +} + +int dns_parser(dns_info_t *dns_info, char *in_buff, int buff_len) +{ + char *cur_pos = NULL; + + get_dns_hdr_info(&dns_info->hdr_info, in_buff); + cur_pos = in_buff + sizeof(dns_hdr_t); + + if (0 == dns_info->hdr_info.qdcount || dns_info->hdr_info.qdcount > 1) + { + return -1; + } + + if (0 != get_dns_query_question(in_buff, &cur_pos, &(dns_info->query_question), in_buff + buff_len)) + { + DNS_ERROR_LOG("get_dns_query_question()"); + return -1; + } + + if (parse_resource_record(dns_info, in_buff, buff_len, &cur_pos) != 0) + { + DNS_ERROR_LOG("parse_resource_record()"); + return -1; + } + + return 0; +} + +int dns_package(dns_info_t *dns_info, char *out_buff, int buff_size) +{ + char *rr_header_length_ptr = NULL; + int i = 0; + int rr_count = 0; + dns_hdr_t *dns_hdr = &dns_info->hdr_info; + int ret = 0; + int used_len = 0; + dns_rr_t *rr; + + set_dns_hdr_info(dns_hdr, out_buff); + used_len += sizeof(dns_hdr_t); + ret = set_cheat_pkt_question(out_buff + used_len, &dns_info->query_question, dns_hdr->qdcount); + if (ret < 0) + { + return -1; + } + used_len += ret; + + rr_count = dns_hdr->adcount + dns_hdr->ancount + dns_hdr->aucount; + for (i = 0; i < rr_count; i++) + { + rr = &dns_info->rr[i]; + ret = set_dns_pkt_rr_header(out_buff + used_len, buff_size, rr, 0, &rr_header_length_ptr); + if (ret == -1) + { + return -1; + } + used_len += ret; + + if (rr->rdlength == 0) + { + continue; + } + + int rr_header_used_len = used_len; + switch (rr->type) + { + case DNS_TYPE_A: + NS_SETLEN(rr->rdata.a, strlen((char *)rr->rdata.a), out_buff, used_len); + break; + case DNS_TYPE_AAAA: + NS_SETLEN(rr->rdata.aaaa, 16, out_buff, used_len); + break; + case DNS_TYPE_CNAME: + ret = dns_name_compress(rr->rdata.cname, strlen((char *)rr->rdata.cname), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + break; + case DNS_TYPE_MB: + ret = dns_name_compress(rr->rdata.mb, strlen((char *)rr->rdata.mb), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + break; + case DNS_TYPE_MD: + ret = dns_name_compress(rr->rdata.md, strlen((char *)rr->rdata.md), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + break; + case DNS_TYPE_MF: + ret = dns_name_compress(rr->rdata.mf, strlen((char *)rr->rdata.mf), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + break; + case DNS_TYPE_MG: + ret = dns_name_compress(rr->rdata.mg, strlen((char *)rr->rdata.mg), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + break; + case DNS_TYPE_MINFO: + ret = dns_name_compress(rr->rdata.minfo.rmailbx, strlen((char *)rr->rdata.minfo.rmailbx), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + + ret = dns_name_compress(rr->rdata.minfo.emailbx, strlen((char *)rr->rdata.minfo.emailbx), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + break; + case DNS_TYPE_MR: + ret = dns_name_compress(rr->rdata.mr, strlen((char *)rr->rdata.mr), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + break; + case DNS_TYPE_MX: + NS_SET16(rr->rdata.mx.preference, out_buff, used_len); + + ret = dns_name_compress(rr->rdata.mx.exchange, strlen((char *)rr->rdata.mx.exchange), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + break; + case DNS_TYPE_NS: + ret = dns_name_compress(rr->rdata.ns, strlen((char *)rr->rdata.ns), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + break; + case DNS_TYPE_PTR: + ret = dns_name_compress(rr->rdata.ptr, strlen((char *)rr->rdata.ptr), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + break; + case DNS_TYPE_SOA: + ret = dns_name_compress(rr->rdata.soa.mname, strlen((char *)rr->rdata.soa.mname), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + + ret = dns_name_compress(rr->rdata.soa.rname, strlen((char *)rr->rdata.soa.rname), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + + NS_SET32(rr->rdata.soa.serial, out_buff, used_len); + NS_SET32(rr->rdata.soa.refresh, out_buff, used_len); + NS_SET32(rr->rdata.soa.retry, out_buff, used_len); + NS_SET32(rr->rdata.soa.expire, out_buff, used_len); + NS_SET32(rr->rdata.soa.minimum, out_buff, used_len); + break; + case DNS_TYPE_DNAME: + ret = dns_name_compress(rr->rdata.dname, strlen((char *)rr->rdata.dname), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + break; + case DNS_TYPE_ISDN: + NS_SETLEN(rr->rdata.isdn, strlen((char *)rr->rdata.isdn), out_buff, used_len); + break; + case DNS_TYPE_RP: + ret = dns_name_compress(rr->rdata.rp.mailbox, strlen((char *)rr->rdata.rp.mailbox), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + + ret = dns_name_compress(rr->rdata.rp.txt_rr, strlen((char *)rr->rdata.rp.txt_rr), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + break; + case DNS_TYPE_SRV: + NS_SET16(rr->rdata.srv.priority, out_buff, used_len); + NS_SET16(rr->rdata.srv.weight, out_buff, used_len); + NS_SET16(rr->rdata.srv.port, out_buff, used_len); + + ret = dns_name_compress(rr->rdata.srv.target, strlen((char *)rr->rdata.srv.target), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + break; + case DNS_TYPE_RRSIG: + NS_SET16(rr->rdata.rrsig.type_covered, out_buff, used_len); + NS_SET8(rr->rdata.rrsig.algo, out_buff, used_len); + NS_SET8(rr->rdata.rrsig.labels, out_buff, used_len); + NS_SET32(rr->rdata.rrsig.original_ttl, out_buff, used_len); + NS_SET32(rr->rdata.rrsig.sig_expiration, out_buff, used_len); + NS_SET32(rr->rdata.rrsig.sig_inception, out_buff, used_len); + NS_SET16(rr->rdata.rrsig.key_tag, out_buff, used_len); + + ret = dns_name_compress(rr->rdata.rrsig.signer_name, strlen((char *)rr->rdata.rrsig.signer_name), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + NS_SETLEN(rr->rdata.rrsig.signature, rr->rdata.rrsig.signature_len, out_buff, used_len); + break; + case DNS_TYPE_DNSKEY: + NS_SET16(rr->rdata.dnskey.flags, out_buff, used_len); + NS_SET8(rr->rdata.dnskey.protocol, out_buff, used_len); + NS_SET8(rr->rdata.dnskey.algo, out_buff, used_len); + NS_SETLEN(rr->rdata.dnskey.public_key, rr->rdata.dnskey.public_key_len, out_buff, used_len); + break; + case DNS_TYPE_NSEC3PARAM: + NS_SET8(rr->rdata.nsec3param.hash_algo, out_buff, used_len); + NS_SET8(rr->rdata.nsec3param.flags, out_buff, used_len); + NS_SET16(rr->rdata.nsec3param.iteration, out_buff, used_len); + NS_SET8(rr->rdata.nsec3param.salt_len, out_buff, used_len); + NS_SETLEN(rr->rdata.nsec3param.salt_value, rr->rdata.nsec3param.salt_len, out_buff, used_len); + break; + case DNS_TYPE_DS: + /* fail through */ + case DNS_TYPE_DLV: + NS_SET16(rr->rdata.ds.key_tag, out_buff, used_len); + NS_SET8(rr->rdata.ds.algo, out_buff, used_len); + NS_SET8(rr->rdata.ds.digest_type, out_buff, used_len); + NS_SETLEN(rr->rdata.ds.digest, rr->rdata.ds.digest_len, out_buff, used_len); + break; + case DNS_TYPE_TXT: + NS_SET8(rr->rdata.txt.size, out_buff, used_len); + NS_SETLEN(rr->rdata.txt.txt, rr->rdata.txt.size, out_buff, used_len); + break; + case DNS_TYPE_NULL: + NS_SET8(rr->rdata.null.size, out_buff, used_len); + NS_SETLEN(rr->rdata.null.null, rr->rdata.null.size, out_buff, used_len); + break; + case DNS_TYPE_WKS: + NS_SET32(rr->rdata.wks.addr, out_buff, used_len); + NS_SET8(rr->rdata.wks.protocol, out_buff, used_len); + NS_SETLEN(rr->rdata.wks.bitmap, rr->rdata.wks.size, out_buff, used_len); + break; + case DNS_TYPE_HINFO: + NS_SET8(rr->rdata.hinfo.cpu_len, out_buff, used_len); + NS_SETLEN(rr->rdata.hinfo.cpu, rr->rdata.hinfo.cpu_len, out_buff, used_len); + NS_SET8(rr->rdata.hinfo.os_len, out_buff, used_len); + NS_SETLEN(rr->rdata.hinfo.os, rr->rdata.hinfo.os_len, out_buff, used_len); + break; + case DNS_TYPE_NSEC: + ret = dns_name_compress(rr->rdata.nsec.next_domain, strlen((char *)rr->rdata.nsec.next_domain), out_buff + used_len); + if (ret == -1) + { + return -1; + } + used_len += ret; + + if (rr->rdata.nsec.maps_len) + { + NS_SET16(rr->rdata.nsec.maps_temp_len, out_buff, used_len); + NS_SETLEN(rr->rdata.nsec.type_bit_maps, rr->rdata.nsec.maps_temp_len, out_buff, used_len); + + if (rr->rdata.nsec.maps_len > rr->rdata.nsec.maps_temp_len) + { + NS_SET8(0xFF, out_buff, used_len); + NS_SET8(rr->rdata.nsec.maps_len - rr->rdata.nsec.maps_temp_len, out_buff, used_len); + NS_SETLEN(rr->rdata.nsec.type_bit_maps + rr->rdata.nsec.maps_temp_len, rr->rdata.nsec.maps_len - rr->rdata.nsec.maps_temp_len, out_buff, used_len); + } + } + break; + case DNS_TYPE_NSEC3: + NS_SET8(rr->rdata.nsec3.hash_algo, out_buff, used_len); + NS_SET8(rr->rdata.nsec3.flags, out_buff, used_len); + NS_SET16(rr->rdata.nsec3.iteration, out_buff, used_len); + NS_SET8(rr->rdata.nsec3.salt_len, out_buff, used_len); + NS_SETLEN(rr->rdata.nsec3.salt_value, rr->rdata.nsec3.salt_len, out_buff, used_len); + NS_SET8(rr->rdata.nsec3.hash_len, out_buff, used_len); + NS_SETLEN(rr->rdata.nsec3.next_hash_owner, rr->rdata.nsec3.hash_len, out_buff, used_len); + + if (rr->rdata.nsec3.maps_len) + { + NS_SET16(rr->rdata.nsec3.maps_temp_len, out_buff, used_len); + NS_SETLEN(rr->rdata.nsec3.type_bit_maps, rr->rdata.nsec3.maps_temp_len, out_buff, used_len); + + if (rr->rdata.nsec3.maps_len > rr->rdata.nsec3.maps_temp_len) + { + NS_SET8(0xFF, out_buff, used_len); + NS_SET8(rr->rdata.nsec3.maps_len - rr->rdata.nsec3.maps_temp_len, out_buff, used_len); + NS_SETLEN(rr->rdata.nsec3.type_bit_maps + rr->rdata.nsec3.maps_temp_len, rr->rdata.nsec3.maps_len - rr->rdata.nsec3.maps_temp_len, out_buff, used_len); + } + } + break; + case DNS_TYPE_OPT: + /* fail through */ + case DNS_TYPE_UNKNOWN: + /* fail through */ + default: + NS_SETLEN(rr->rdata.unknown_data, rr->rdlength, out_buff, used_len); + break; + } + + if (used_len - rr_header_used_len != rr->rdlength) + { + reset_rr_header_length(&rr_header_length_ptr, used_len - rr_header_used_len); + } + if (used_len > buff_size) + { + printf("dns_package buff to short, %s, %d\n", __FILE__, __LINE__); + return -1; + } + } + + return used_len; +} + +int dns_cheat_response(dns_info_t *dns_info, cheat_pkt_opt_t *cheat_opt, int cheat_opt_num, char *out_buff, int buff_size) +{ + int i = 0; + dns_hdr_t *dns_hdr = NULL; + int ret = 0, used_len = 0; + + memset(out_buff, 0, buff_size); + dns_hdr = (dns_hdr_t *)out_buff; + dns_hdr->id = dns_info->hdr_info.id; + dns_hdr->qdcount = 1; + dns_hdr->ancount = cheat_opt_num; + used_len += sizeof(dns_hdr_t); + + ret = set_cheat_pkt_question(out_buff + used_len, &dns_info->query_question, dns_hdr->qdcount); + if (ret < 0) + { + return -1; + } + used_len += ret; + + for (i = 0; i < cheat_opt_num; i++) + { + switch (cheat_opt[i].res_type) + { + case DNS_TYPE_A: + case DNS_TYPE_AAAA: + used_len += set_cheat_pkt_rr_pdata_type_ip(out_buff + used_len, &cheat_opt[i]); + break; + case DNS_TYPE_CNAME: + case DNS_TYPE_NS: + used_len += set_cheat_pkt_rr_pdata_type_str(out_buff + used_len, &cheat_opt[i]); + break; + } + + if (used_len > buff_size) + { + return -2; + } + } + + set_cheat_pkt_header(dns_hdr); + + return used_len; +} \ No newline at end of file diff --git a/plugin/business/doh/src/dns.h b/plugin/business/doh/src/dns.h new file mode 100644 index 0000000..f56c682 --- /dev/null +++ b/plugin/business/doh/src/dns.h @@ -0,0 +1,388 @@ +#ifndef DNS_ANALYSE_H +#define DNS_ANALYSE_H + +#include + +#ifndef u_char +#define u_char unsigned char +#endif + +#ifndef u_int16_t +#define u_int16_t unsigned short +#endif + +#ifndef u_int32_t +#define u_int32_t unsigned int //adjust by lqy 20070521 long to int +#endif + +#ifndef MIN +#define MIN(a, b) ((a) > (b) ? (b) : (a)) +#endif + +#define DNS_MAX_SALT 256 +#define DNS_MAX_OWNER 256 +#define DNS_MAX_MAPS 256 +#define DNS_MAX_PUBLIC_KEY 256 +#define DNS_MAX_SIGNER_NAME 256 +#define DNS_MAX_SIGNATURE 256 +#define DNS_MAX_DIGEST 256 +#define DNS_MAX_TARGET 256 + +#define DNS_HINFO_MAX_CPU 40 +#define DNS_HINFO_MAX_OS 40 +#define DNS_MAX_NAME 255 + +#define DNS_RR_TYPE_ALL 0 +#define DNS_RR_TYPE_ANS 1 +#define DNS_RR_TYPE_AUTH 2 +#define DNS_RR_TYPE_ADD 3 + +#define NS_INT8SZ 1 +#define NS_INT16SZ 2 +#define NS_INT32SZ 4 + +#define NS_GET32(l, cp) \ + do \ + { \ + register u_char *t_cp = (u_char *)(cp); \ + (l) = ((u_int32_t)t_cp[0] << 24) | ((u_int32_t)t_cp[1] << 16) | ((u_int32_t)t_cp[2] << 8) | ((u_int32_t)t_cp[3]); \ + (cp) += NS_INT32SZ; \ + } while (0) + +#define NS_GET16(s, cp) \ + do \ + { \ + register u_char *t_cp = (u_char *)(cp); \ + (s) = ((u_int16_t)t_cp[0] << 8) | ((u_int16_t)t_cp[1]); \ + (cp) += NS_INT16SZ; \ + } while (0) + +#define NS_GET8(s, cp) \ + do \ + { \ + register u_char *t_cp = (u_char *)(cp); \ + (s) = ((u_char)t_cp[0]); \ + (cp) += NS_INT8SZ; \ + } while (0) + +#define NS_SET8(data, payload, used_len) \ + do \ + { \ + u_char seg_8 = (data); \ + memcpy(((payload) + (used_len)), &seg_8, sizeof(seg_8)); \ + (used_len) += sizeof(seg_8); \ + } while (0) + +#define NS_SET16(data, payload, used_len) \ + do \ + { \ + u_int16_t seg_16 = htons(data); \ + memcpy(((payload) + (used_len)), &seg_16, sizeof(seg_16)); \ + (used_len) += sizeof(seg_16); \ + } while (0) + +#define NS_SET32(data, payload, used_len) \ + do \ + { \ + u_int32_t seg_32 = htonl(data); \ + memcpy(((payload) + (used_len)), &seg_32, sizeof(seg_32)); \ + (used_len) += sizeof(seg_32); \ + } while (0) + +#define NS_SETLEN(data, len, payload, used_len) \ + do \ + { \ + memcpy(((payload) + (used_len)), (data), (len)); \ + (used_len) += (len); \ + } while (0) + +/* RR type */ +#define DNS_TYPE_A 1 +#define DNS_TYPE_NS 2 +#define DNS_TYPE_MD 3 +#define DNS_TYPE_MF 4 +#define DNS_TYPE_CNAME 5 +#define DNS_TYPE_SOA 6 +#define DNS_TYPE_MB 7 +#define DNS_TYPE_MG 8 +#define DNS_TYPE_MR 9 +#define DNS_TYPE_NULL 10 +#define DNS_TYPE_WKS 11 +#define DNS_TYPE_PTR 12 +#define DNS_TYPE_HINFO 13 +#define DNS_TYPE_MINFO 14 +#define DNS_TYPE_MX 15 +#define DNS_TYPE_TXT 16 +#define DNS_TYPE_RP 17 +#define DNS_TYPE_ISDN 20 +#define DNS_TYPE_AAAA 28 +#define DNS_TYPE_SRV 33 +#define DNS_TYPE_DNAME 39 +#define DNS_TYPE_OPT 41 +#define DNS_TYPE_DS 43 +#define DNS_TYPE_RRSIG 46 +#define DNS_TYPE_NSEC 47 +#define DNS_TYPE_DNSKEY 48 +#define DNS_TYPE_NSEC3 50 +#define DNS_TYPE_NSEC3PARAM 51 +#define DNS_QTYPE_AXFR 252 +#define DNS_QTYPE_MAILB 253 +#define DNS_QTYPE_MAILA 254 +#define DNS_QTYPE_ANY 255 +#define DNS_TYPE_DLV 32769 /* DSNSEC Lokkaside Validation */ +#define DNS_TYPE_UNKNOWN 65534 + +#define DNS_CLASS_UNKNOWN 0 +#define DNS_CLASS_IN 1 +#define DNS_CLASS_CS 2 +#define DNS_CLASS_CH 3 +#define DNS_CLASS_HS 4 +#define DNS_QCLASS_ANY 255 + +// 存储 DNS 头部信息的结构体 +typedef struct _dns_hdr +{ + u_int16_t id; +#if __BYTE_ORDER == __LITTLE_ENDIAN + u_char rd : 1; + u_char tc : 1; + u_char aa : 1; + u_char opcode : 4; + u_char qr : 1; + u_char rcode : 4; + u_char z : 3; + u_char ra : 1; +#elif __BYTE_ORDER == __BIG_ENDIAN + u_char qr : 1; + u_char opcode : 4; + u_char aa : 1; + u_char tc : 1; + u_char rd : 1; + u_char ra : 1; + u_char z : 3; + u_char rcode : 4; +#endif + u_int16_t qdcount; + u_int16_t ancount; + u_int16_t aucount; //authority count + u_int16_t adcount; //additional count +} dns_hdr_t; + +typedef struct _hinfo +{ + u_char os_len; + u_char cpu_len; + u_char cpu[DNS_HINFO_MAX_CPU + 1]; + u_char os[DNS_HINFO_MAX_OS + 1]; +} hinfo_t; + +typedef struct _minfo +{ + u_char rmailbx[DNS_MAX_NAME + 1]; + u_char emailbx[DNS_MAX_NAME + 1]; +} minfo_t; + +typedef struct _mx +{ + u_int16_t preference; + u_char exchange[DNS_MAX_NAME + 1]; +} mx_t; + +typedef struct _soa +{ + u_char mname[DNS_MAX_NAME + 1]; + u_char rname[DNS_MAX_NAME + 1]; + u_int32_t serial; + u_int32_t refresh; + u_int32_t retry; + u_int32_t expire; + u_int32_t minimum; +} soa_t; + +typedef struct _rp_t +{ + u_char mailbox[DNS_MAX_NAME + 1]; + u_char txt_rr[DNS_MAX_NAME + 1]; +} rp_t; + +typedef struct _txt_t +{ + u_char txt[DNS_MAX_NAME + 1]; + u_char size; +} txt_t; + +typedef struct _null +{ + u_char null[DNS_MAX_NAME + 1]; + u_char size; +} null_t; + +typedef struct _wks +{ + u_char protocol; + u_int32_t addr; + u_int32_t size; + u_char *bitmap; +} wks_t; + +typedef struct _srv +{ + u_int16_t priority; + u_int16_t weight; + u_int16_t port; + u_char target[DNS_MAX_TARGET]; +} srv_t; + +typedef struct _ds +{ + u_int16_t key_tag; + u_char algo; + u_char digest_type; + u_int32_t digest_len; + u_char *digest; +} ds_t; + +typedef struct _rrsig +{ + u_int16_t type_covered; + u_char algo; + u_char labels; + u_int32_t original_ttl; + u_int32_t sig_expiration; + u_int32_t sig_inception; + u_int32_t key_tag; + u_int32_t signature_len; + u_char signer_name[DNS_MAX_SIGNER_NAME]; + u_char *signature; +} rrsig_t; + +typedef struct _nsec +{ + u_int16_t maps_temp_len; + u_int16_t maps_len; + u_char next_domain[DNS_MAX_OWNER]; + u_char type_bit_maps[DNS_MAX_MAPS]; +} nsec_t; + +typedef struct _dnskey +{ + u_int16_t flags; + u_char protocol; + u_char algo; + u_int32_t public_key_len; + u_char *public_key; +} dnskey_t; + +typedef struct _nsec3 +{ + u_char hash_algo; + u_char flags; + u_char salt_len; + u_char hash_len; + u_int16_t iteration; + u_int16_t maps_temp_len; + u_int16_t maps_len; + u_char *salt_value; + u_char *next_hash_owner; + u_char type_bit_maps[DNS_MAX_MAPS]; +} nsec3_t; + +typedef struct _nsec3param +{ + u_char hash_algo; + u_char flags; + u_char salt_len; + u_int16_t iteration; + u_char *salt_value; +} nsec3param_t; + +// 存储 DNS 资源记录区域(回答区域 / 授权区域 / 附加区域)的结构体 +typedef struct _dns_rr +{ + u_char name[DNS_MAX_NAME + 1]; + u_int16_t type; + u_int16_t rr_class; + u_int32_t ttl; /* 1byte: extended RCODE; 1byte: version; 2bytes: Z(upper bit) if type is OPT */ + u_int16_t rdlength; + union { + u_char cname[DNS_MAX_NAME + 1]; /* cname[DNS_MAX_NAME + 1]; */ + hinfo_t hinfo; + u_char mb[DNS_MAX_NAME + 1]; /* mb[DNS_MAX_NAME + 1]; */ + u_char md[DNS_MAX_NAME + 1]; /* md[DNS_MAX_NAME + 1]; */ + u_char mf[DNS_MAX_NAME + 1]; /* mf[DNS_MAX_NAME + 1]; */ + u_char mg[DNS_MAX_NAME + 1]; /* mg[DNS_MAX_NAME + 1]; */ + minfo_t minfo; + u_char mr[DNS_MAX_NAME + 1]; /* mr[DNS_MAX_NAME + 1]; */ + mx_t mx; + u_char ns[DNS_MAX_NAME + 1]; /* ns[DNS_MAX_NAME + 1]; */ + u_char ptr[DNS_MAX_NAME + 1]; /* ptr[DNS_MAX_NAME + 1]; */ + soa_t soa; + u_char a[DNS_MAX_NAME + 1]; + u_char aaaa[DNS_MAX_NAME + 1]; /* aaaa[16]; */ + u_char dname[DNS_MAX_NAME + 1]; + u_char isdn[DNS_MAX_NAME + 1]; + u_char unknown_data[DNS_MAX_NAME + 1]; + txt_t txt; + rp_t rp; + null_t null; + wks_t wks; + srv_t srv; + ds_t ds; + rrsig_t rrsig; + nsec_t nsec; + dnskey_t dnskey; + nsec3_t nsec3; + nsec3param_t nsec3param; + } rdata; +} dns_rr_t; + +typedef struct _fake_packet_opt +{ + u_int16_t cfg_type; /* IP or STR */ + u_int16_t res_type; + u_int32_t ttl; + u_int32_t res_len; + u_char res_info[DNS_MAX_NAME + 1]; +} cheat_pkt_opt_t; + +// 存储 DNS Queries 区域的结构体 +typedef struct +{ + u_int16_t qtype; + u_int16_t qclass; + u_char qname[DNS_MAX_NAME + 1]; +} dns_query_question_t; + +#define MAX_RR_NUM 256 + +// 存储完整 DNS 信息的结构体 +typedef struct _dns_info +{ + // 存储 DNS 头部信息的结构体 + dns_hdr_t hdr_info; + + // RR 记录的个数 + int rr_count; + // 存储 DNS 资源记录区域(回答区域 / 授权区域 / 附加区域)的结构体 + dns_rr_t rr[MAX_RR_NUM]; + + // 存储 DNS Queries 区域的结构体 + dns_query_question_t query_question; +} dns_info_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +dns_info_t *dns_new(void); +void dns_free(dns_info_t *dns_info); + +int dns_parser(dns_info_t *dns_info, char *in_buff, int buff_len); +int dns_package(dns_info_t *dns_info, char *out_buff, int buff_size); +int dns_cheat_response(dns_info_t *dns_info, cheat_pkt_opt_t *cheat_opt, int cheat_opt_num, char *out_buff, int buff_size); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/plugin/business/doh/src/doh.cpp b/plugin/business/doh/src/doh.cpp new file mode 100644 index 0000000..835608f --- /dev/null +++ b/plugin/business/doh/src/doh.cpp @@ -0,0 +1,758 @@ +#include "logger.h" + +#define MAX_SCAN_RESULT 128 +#define DOH_CTX_MAGIC_NUM 20200601 + +#define REQ_METHOD_IS_GET(method) ((method == TFE_HTTP_METHOD_GET) ? 1 : 0) +#define REQ_METHOD_IS_POST(method) ((method == TFE_HTTP_METHOD_POST) ? 1 : 0) + +struct dns_str2idx +{ + int index; + int len; + char *type; +}; + +struct dns_str2idx str2index[] = { + {DNS_TYPE_CNAME, 5, (char *)"CNAME"}, + {DNS_TYPE_MX, 2, (char *)"MX"}, + {DNS_TYPE_A, 1, (char *)"A"}, + {DNS_TYPE_NS, 2, (char *)"NS"}, + {DNS_TYPE_AAAA, 4, (char *)"AAAA"}, + {DNS_TYPE_TXT, 3, (char *)"TXT"}, + {DNS_TYPE_PTR, 3, (char *)"PTR"}}; + +static struct doh_conf *g_doh_conf = NULL; + +static int doh_type2index(char *type) +{ + int i = 0; + for (i = 0; i < (int)(sizeof(str2index) / sizeof(struct dns_str2idx)); i++) + { + if (str2index[i].len == (int)strlen(type) && (strncasecmp(str2index[i].type, type, str2index[i].len)) == 0) + { + return str2index[i].index; + } + } + + return -1; +} + +static void doh_addr_tfe2sapp(const struct tfe_stream_addr *tfe_addr, struct ipaddr *sapp_addr) +{ + if (tfe_addr->addrtype == TFE_ADDR_STREAM_TUPLE4_V4 || tfe_addr->addrtype == TFE_ADDR_IPV4) + { + sapp_addr->addrtype = ADDR_TYPE_IPV4; + } + else + { + sapp_addr->addrtype = ADDR_TYPE_IPV6; + } + sapp_addr->paddr = (char *)tfe_addr->paddr; +} + +static int doh_get_answer_ttl(cJSON *object) +{ + int min = 0; + int max = 0; + cJSON *item = NULL; + + item = cJSON_GetObjectItem(object, "min"); + if (item) + { + min = item->valueint; + } + + item = cJSON_GetObjectItem(object, "max"); + if (item) + { + max = item->valueint; + } + + return (rand() % (max - min + 1) + min); +} + +static cJSON *doh_get_answer_records(cJSON *object, int qtype) +{ + int i = 0; + + cJSON *resolution = cJSON_GetObjectItem(object, "resolution"); + int size = cJSON_GetArraySize(resolution); + for (i = 0; i < size; i++) + { + cJSON *item = cJSON_GetArrayItem(resolution, i); + cJSON *tmp = cJSON_GetObjectItem(item, "qtype"); + + if (doh_type2index(tmp->valuestring) == qtype) + { + return cJSON_GetObjectItem(item, "answer"); + } + } + + return NULL; +} + +static void doh_get_cheat_data(Maat_rule_t *p_result, int qtype, struct doh_ctx *ctx) +{ + int i; + int answer_size = 0; + char *tmp = NULL; + cJSON *items = NULL; + cJSON *item = NULL; + cJSON *object = NULL; + cJSON *answer_array = NULL; + + tmp = (char *)calloc(1, p_result->serv_def_len + 1); + Maat_read_rule(g_doh_conf->maat, p_result, MAAT_RULE_SERV_DEFINE, tmp, p_result->serv_def_len); + TFE_LOG_INFO(g_doh_conf->local_logger, "%s hit %d %s", ctx->addr_string, p_result->config_id, tmp); + + object = cJSON_Parse(tmp); + if (object == NULL) + { + goto end; + } + + answer_array = doh_get_answer_records(object, qtype); + if (answer_array != NULL) + { + answer_size = cJSON_GetArraySize(answer_array); + ctx->opts = ALLOC(cheat_pkt_opt_t, answer_size); + ctx->opts_num = answer_size; + + for (i = 0; i < answer_size; i++) + { + items = cJSON_GetArrayItem(answer_array, i); + + item = cJSON_GetObjectItem(items, "atype"); + ctx->opts[i].res_type = doh_type2index(item->valuestring); + + item = cJSON_GetObjectItem(items, "ttl"); + ctx->opts[i].ttl = doh_get_answer_ttl(item); + + if (ctx->min_ttl == 0) + { + ctx->min_ttl = ctx->opts[i].ttl; + } + else + { + if (ctx->min_ttl > ctx->opts[i].ttl) + { + ctx->min_ttl = ctx->opts[i].ttl; + } + } + + item = cJSON_GetObjectItem(items, "value"); + if (item) + { + switch (ctx->opts[i].res_type) + { + case DNS_TYPE_A: + ctx->opts[i].res_len = sizeof(unsigned int); + inet_pton(AF_INET, item->valuestring, (void *)ctx->opts[i].res_info); + break; + case DNS_TYPE_AAAA: + ctx->opts[i].res_len = IPV6_ADDR_LEN; + inet_pton(AF_INET6, item->valuestring, (void *)ctx->opts[i].res_info); + break; + default: + ctx->opts[i].res_len = (strlen(item->valuestring) > sizeof(ctx->opts[i].res_info) - 1) ? sizeof(ctx->opts[i].res_info) - 1 : strlen(item->valuestring); + memcpy(ctx->opts[i].res_info, item->valuestring, ctx->opts[i].res_len); + break; + } + } + } + } + +end: + if (object) + { + cJSON_Delete(object); + object = NULL; + } + + if (tmp) + { + free(tmp); + tmp = NULL; + } +} + +static struct Maat_rule_t *doh_fetch_rule(Maat_rule_t *result, int result_num) +{ + int i = 0; + Maat_rule_t *p_result = NULL; + + for (i = 0; i < result_num; i++) + { + if (p_result == NULL) + { + p_result = &result[i]; + continue; + } + + if (result[i].config_id > p_result->config_id) + { + p_result = &result[i]; + } + } + + return p_result; +} + +static void doh_maat_scan(const struct tfe_stream *stream, const struct tfe_http_session *session, struct doh_ctx *ctx, char *qname, int qtype) +{ + int hit_cnt = 0; + int scan_ret = 0; + const char *app_id = "doh."; + struct ipaddr sapp_addr; + struct Maat_rule_t *p_result = NULL; + struct Maat_rule_t result[MAX_SCAN_RESULT]; + + // scan server host + const char *host = session->req->req_spec.host; + if (host) + { + scan_ret = Maat_full_scan_string(g_doh_conf->maat, g_doh_conf->tables[TYPE_HOST].id, CHARSET_UTF8, + host, strlen(host), result + hit_cnt, NULL, MAX_SCAN_RESULT - hit_cnt, &(ctx->scan_mid), ctx->thread_id); + if (scan_ret > 0) + { + hit_cnt += scan_ret; + TFE_LOG_INFO(g_doh_conf->local_logger, "SCAN_HOST, Hit host: %s scan ret: %d policy_id: %d service: %d action: %d addr: %s", + host, scan_ret, result[hit_cnt].config_id, result[hit_cnt].service_id, result[hit_cnt].action, ctx->addr_string); + } + else + { + TFE_LOG_INFO(g_doh_conf->local_logger, "SCAN_HOST, NO hit host: %s scan ret: %d addr: %s", + host, scan_ret, ctx->addr_string); + } + } + + // scan addr + doh_addr_tfe2sapp(stream->addr, &sapp_addr); + scan_ret = Maat_scan_proto_addr(g_doh_conf->maat, g_doh_conf->tables[TYPE_ADDR].id, &sapp_addr, + 0, result + hit_cnt, MAX_SCAN_RESULT - hit_cnt, &(ctx->scan_mid), ctx->thread_id); + if (scan_ret > 0) + { + hit_cnt += scan_ret; + TFE_LOG_INFO(g_doh_conf->local_logger, "SCAN_ADDR, Hit addr: %s scan ret: %d policy_id: %d service: %d action: %d", + ctx->addr_string, scan_ret, result[hit_cnt].config_id, result[hit_cnt].service_id, result[hit_cnt].action); + } + else + { + TFE_LOG_INFO(g_doh_conf->local_logger, "SCAN_ADDR, NO hit addr: %s scan ret: %d", + ctx->addr_string, scan_ret); + } + + // scan appid + scan_ret = Maat_full_scan_string(g_doh_conf->maat, g_doh_conf->tables[TYPE_APPID].id, CHARSET_UTF8, + app_id, strlen(app_id), result + hit_cnt, NULL, MAX_SCAN_RESULT - hit_cnt, &(ctx->scan_mid), ctx->thread_id); + if (scan_ret > 0) + { + hit_cnt += scan_ret; + TFE_LOG_INFO(g_doh_conf->local_logger, "SCAN_APPID, Hit proto: %s scan ret: %d policy_id: %d service: %d action: %d addr: %s", + app_id, scan_ret, result[hit_cnt].config_id, result[hit_cnt].service_id, result[hit_cnt].action, ctx->addr_string); + } + else + { + TFE_LOG_INFO(g_doh_conf->local_logger, "SCAN_APPID, NO hit proto: %s scan ret: %d addr: %s", + app_id, scan_ret, ctx->addr_string); + } + + // scan qname + scan_ret = Maat_full_scan_string(g_doh_conf->maat, g_doh_conf->tables[TYPE_QNAME].id, CHARSET_UTF8, + qname, strlen(qname), result + hit_cnt, NULL, MAX_SCAN_RESULT - hit_cnt, &(ctx->scan_mid), ctx->thread_id); + if (scan_ret > 0) + { + hit_cnt += scan_ret; + TFE_LOG_INFO(g_doh_conf->local_logger, "SCAN_QNAME, Hit domain: %s scan ret: %d qtype: %d policy_id: %d service: %d action: %d addr: %s", + qname, scan_ret, qtype, result[hit_cnt].config_id, result[hit_cnt].service_id, result[hit_cnt].action, ctx->addr_string); + } + else + { + TFE_LOG_INFO(g_doh_conf->local_logger, "SCAN_QNAME, NO hit domain: %s scan ret: %d addr: %s", + qname, scan_ret, ctx->addr_string); + } + + if (hit_cnt) + { + p_result = doh_fetch_rule(result, hit_cnt); + if (p_result) + { + ctx->result_num = 1; + ctx->result = ALLOC(struct Maat_rule_t, ctx->result_num); + memcpy(ctx->result, p_result, sizeof(struct Maat_rule_t)); + doh_get_cheat_data(p_result, qtype, ctx); + } + } +} + +static int doh_maat_init(const char *profile, const char *section) +{ + g_doh_conf->maat = (Maat_feather_t)tfe_bussiness_resouce_get(STATIC_MAAT); + MESA_load_profile_string_def(profile, section, "table_appid", g_doh_conf->tables[TYPE_APPID].name, TFE_STRING_MAX, "TSG_OBJ_APP_ID"); + MESA_load_profile_string_def(profile, section, "table_addr", g_doh_conf->tables[TYPE_ADDR].name, TFE_STRING_MAX, "TSG_SECURITY_ADDR"); + MESA_load_profile_string_def(profile, section, "table_qname", g_doh_conf->tables[TYPE_QNAME].name, TFE_STRING_MAX, "TSG_FIELD_DOH_QNAME"); + MESA_load_profile_string_def(profile, section, "table_host", g_doh_conf->tables[TYPE_HOST].name, TFE_STRING_MAX, "TSG_FIELD_HTTP_HOST"); + + for (int i = 0; i < TYPE_MAX; i++) + { + g_doh_conf->tables[i].id = Maat_table_register(g_doh_conf->maat, g_doh_conf->tables[i].name); + if (g_doh_conf->tables[i].id < 0) + { + TFE_LOG_ERROR(g_doh_conf->local_logger, "Maat_table_register failed, table_name: %s", g_doh_conf->tables[i].name); + return -1; + } + } + + return 0; +} + +static void doh_gc_cb(evutil_socket_t fd, short what, void *arg) +{ + int i = 0; + + for (i = 0; i < DOH_STAT_MAX; i++) + { + FS_operate(g_doh_conf->fs_handle, g_doh_conf->fs_id[i], 0, FS_OP_SET, ATOMIC_READ(&(g_doh_conf->stat_val[i]))); + } +} + +static int doh_field_init() +{ + int i = 0; + struct timeval gc_delay = {0, 500 * 1000}; //Microseconds, we set 500 miliseconds here. + const char *spec[DOH_STAT_MAX] = {0}; + + spec[STAT_SESSION] = "doh_sess"; + spec[STAT_LOG_NUM] = "doh_log"; + spec[STAT_ACTION_HIJACK] = "doh_hijack"; + + for (i = 0; i < DOH_STAT_MAX; i++) + { + if (spec[i] != NULL) + { + g_doh_conf->fs_id[i] = FS_register(g_doh_conf->fs_handle, FS_STYLE_FIELD, FS_CALC_CURRENT, spec[i]); + } + } + g_doh_conf->gcev = event_new(g_doh_conf->gc_evbase, -1, EV_PERSIST, doh_gc_cb, NULL); + evtimer_add(g_doh_conf->gcev, &gc_delay); + + return 0; +} + +static struct doh_ctx *doh_ctx_new(unsigned int thread_id) +{ + struct doh_ctx *ctx = ALLOC(struct doh_ctx, 1); + assert(ctx); + + ctx->magic_num = DOH_CTX_MAGIC_NUM; + ctx->thread_id = (int)thread_id; + ctx->scan_mid = NULL; + ctx->opts_num = 0; + ctx->opts = NULL; + ctx->min_ttl = 0; + ctx->doh_req = NULL; + + return ctx; +} + +static void doh_ctx_free(struct doh_ctx *ctx) +{ + assert(ctx->magic_num == DOH_CTX_MAGIC_NUM); + + if (ctx->result) + { + free(ctx->result); + ctx->result = NULL; + } + if (ctx->doh_req) + { + dns_free(ctx->doh_req); + ctx->doh_req = NULL; + } + + if (ctx->opts_num) + { + free(ctx->opts); + ctx->opts = NULL; + } + + if (ctx->http_req_body) + { + evbuffer_free(ctx->http_req_body); + ctx->http_req_body = NULL; + } + + if (ctx->addr_string) + { + free(ctx->addr_string); + ctx->addr_string = NULL; + } + + FREE(&ctx); +} + +static int req_session_is_doh(const struct tfe_http_session *session, struct doh_ctx *ctx) +{ + // https://tools.ietf.org/html/rfc8484 + + // https://tools.ietf.org/id/draft-ietf-doh-dns-over-https-10.html + // Registration of application/dns-message Media Type + + // https://tools.ietf.org/id/draft-ietf-doh-dns-over-https-05.html + // Registration of application/dns-udpwireformat Media Type + + // POST + const char *cont_type_val = tfe_http_std_field_read(session->req, TFE_HTTP_CONT_TYPE); + if (cont_type_val && strncasecmp(cont_type_val, "application/dns-message", strlen("application/dns-message")) == 0) + { + ctx->type = DOH_TYPE_MESSAGE; + return 1; + } + if (cont_type_val && strncasecmp(cont_type_val, "application/dns-udpwireformat", strlen("application/dns-udpwireformat")) == 0) + { + ctx->type = DOH_TYPE_UDPWIREFORMAT; + return 1; + } + + // GET + const char *accept_type_val = tfe_http_nonstd_field_read(session->req, "Accept"); + if (accept_type_val && strncasecmp(accept_type_val, "application/dns-message", strlen("application/dns-message")) == 0) + { + ctx->type = DOH_TYPE_MESSAGE; + return 1; + } + if (accept_type_val && strncasecmp(accept_type_val, "application/dns-udpwireformat", strlen("application/dns-udpwireformat")) == 0) + { + ctx->type = DOH_TYPE_UDPWIREFORMAT; + return 1; + } + + return 0; +} + +static void doh_process_req(const struct tfe_stream *stream, const struct tfe_http_session *session, void **pme) +{ + struct doh_ctx *ctx = *(struct doh_ctx **)pme; + struct tfe_http_half *response = NULL; + struct tfe_http_session *to_write = NULL; + + int rsp_len; + int rsp_size; + char *rsp_buff = NULL; + char cont_len_str[20] = {0}; + char max_age_str[20] = {0}; + + char *req_data = (char *)evbuffer_pullup(ctx->http_req_body, -1); + size_t req_len = evbuffer_get_length(ctx->http_req_body); + + if (req_data && req_len) + { + ctx->doh_req = dns_new(); + assert(ctx->doh_req); + + if (dns_parser(ctx->doh_req, req_data, req_len) == -1) + { + // base64_len = (str_len / 3 + 1) * 4 + int temp_size = (req_len / 3 + 1) * 4; + char *temp = (char *)ALLOC(char, temp_size); + int len = base64_encode(temp, temp_size - 1, req_data, req_len); + TFE_LOG_ERROR(g_doh_conf->local_logger, "%s Doh parser request failed, PASSTHROUGH, data:%s", ctx->addr_string, len > 0 ? temp : ""); + free(temp); + goto end; + } + TFE_LOG_DEBUG(g_doh_conf->local_logger, "%s qtype %d qname:%s", + ctx->addr_string, ctx->doh_req->query_question.qtype, ctx->doh_req->query_question.qname); + + if (ctx->doh_req->query_question.qtype != DNS_TYPE_A && ctx->doh_req->query_question.qtype != DNS_TYPE_AAAA) + { + TFE_LOG_INFO(g_doh_conf->local_logger, "%s Doh qtype not A/AAAA, PASSTHROUGH", ctx->addr_string); + goto end; + } + if (strlen((char *)ctx->doh_req->query_question.qname) == 0) + { + TFE_LOG_INFO(g_doh_conf->local_logger, "%s Doh qname is empty, PASSTHROUGH", ctx->addr_string); + goto end; + } + + doh_maat_scan(stream, session, ctx, (char *)ctx->doh_req->query_question.qname, ctx->doh_req->query_question.qtype); + if (!ctx->opts_num) + { + TFE_LOG_INFO(g_doh_conf->local_logger, "%s Doh no hit answer type, PASSTHROUGH", ctx->addr_string); + goto end; + } + + rsp_size = sizeof(cheat_pkt_opt_t) * ctx->opts_num * 16; + rsp_buff = (char *)ALLOC(char, rsp_size); + rsp_len = dns_cheat_response(ctx->doh_req, ctx->opts, ctx->opts_num, rsp_buff, rsp_size - 1); + if (rsp_len < 0) + { + TFE_LOG_ERROR(g_doh_conf->local_logger, "%s Doh cheat response failed: %d, PASSTHROUGH", ctx->addr_string, rsp_len); + goto end; + } + + to_write = tfe_http_session_allow_write(session); + if (to_write == NULL) + { + assert(0); + } + + ctx->manipulate = 1; + ATOMIC_INC(&(g_doh_conf->stat_val[STAT_ACTION_HIJACK])); + + snprintf(cont_len_str, sizeof(cont_len_str), "%d", rsp_len); + response = tfe_http_session_response_create(to_write, 200); + tfe_http_std_field_write(response, TFE_HTTP_CONT_LENGTH, cont_len_str); + if (ctx->type == DOH_TYPE_MESSAGE) + tfe_http_std_field_write(response, TFE_HTTP_CONT_TYPE, "application/dns-message"); + else + tfe_http_std_field_write(response, TFE_HTTP_CONT_TYPE, "application/dns-udpwireformat"); + + /* The assigned freshness lifetime of a DoH HTTP response MUST be less + * than or equal to the smallest TTL in the Answer section of the DNS + * response. */ + snprintf(max_age_str, sizeof(max_age_str), "max-age=%d", ctx->min_ttl); + tfe_http_std_field_write(response, TFE_HTTP_CACHE_CONTROL, max_age_str); + tfe_http_half_append_body(response, rsp_buff, rsp_len, 0); + tfe_http_half_append_body(response, NULL, 0, 0); + + tfe_http_session_response_set(to_write, response); + tfe_http_session_detach(session); + end: + if (rsp_buff) + { + free(rsp_buff); + rsp_buff = NULL; + } + } +} + +int doh_on_init(struct tfe_proxy *proxy) +{ + const char *profile = "./conf/doh/doh.conf"; + + g_doh_conf = ALLOC(struct doh_conf, 1); + assert(g_doh_conf); + + MESA_load_profile_int_def(profile, "doh", "enable", &(g_doh_conf->enable), 1); + MESA_load_profile_int_def(profile, "log", "log_level", &(g_doh_conf->local_level), 10); + + if (!g_doh_conf->enable) + { + TFE_LOG_INFO(NULL, "Doh disabled."); + goto success; + } + + g_doh_conf->thread_num = tfe_proxy_get_work_thread_count(); + g_doh_conf->local_logger = MESA_create_runtime_log_handle("./log/doh_pxy.log", g_doh_conf->local_level); + + g_doh_conf->gc_evbase = tfe_proxy_get_gc_evbase(); + g_doh_conf->fs_handle = tfe_proxy_get_fs_handle(); + if (doh_field_init() != 0) + { + TFE_LOG_ERROR(NULL, "Doh init field stat failed."); + goto error; + } + + if (doh_kafka_init(profile, g_doh_conf) != 0) + { + TFE_LOG_ERROR(NULL, "Doh init kafka failed."); + goto error; + } + + if (doh_maat_init(profile, "maat") != 0) + { + TFE_LOG_ERROR(NULL, "Doh init maat failed."); + goto error; + } + +success: + return 0; + +error: + TFE_LOG_ERROR(NULL, "Doh init failed."); + return -1; +} + +void doh_on_begin(const struct tfe_stream *stream, const struct tfe_http_session *session, unsigned int thread_id, void **pme) +{ + struct doh_ctx *ctx = NULL; + + if (!g_doh_conf->enable) + { + return; + } + + ctx = *(struct doh_ctx **)pme; + assert(ctx == NULL); + ctx = doh_ctx_new(thread_id); + + ctx->addr_string = tfe_stream_addr_to_str(stream->addr); + + *pme = ctx; +} + +// return : NO_CALL_NEXT_PLUGIN +// return : CALL_NEXT_PLUGIN +int doh_on_data(const struct tfe_stream *stream, const struct tfe_http_session *session, enum tfe_http_event events, const unsigned char *body_frag, size_t frag_size, unsigned int thread_id, void **pme) +{ + if (!g_doh_conf->enable) + { + return CALL_NEXT_PLUGIN; + } + + struct doh_ctx *ctx = *(struct doh_ctx **)pme; + assert(ctx); + + if (!req_session_is_doh(session, ctx)) + { + return CALL_NEXT_PLUGIN; + } + + if (!ctx->count) + { + ctx->count = 1; + ATOMIC_INC(&(g_doh_conf->stat_val[STAT_SESSION])); + TFE_LOG_DEBUG(g_doh_conf->local_logger, "%s method:%s content-type:%s accept:%s url:%s", + ctx->addr_string, + http_std_method_to_string(session->req->req_spec.method), + tfe_http_std_field_read(session->req, TFE_HTTP_CONT_TYPE), + tfe_http_nonstd_field_read(session->req, "Accept"), + session->req->req_spec.url); + } + + // request get + if (REQ_METHOD_IS_GET(session->req->req_spec.method) && (events & EV_HTTP_REQ_HDR)) + { + /* https://tools.ietf.org/html/draft-ietf-doh-dns-over-https-04 + * + * When using the GET method the URI path MUST contain a query parameter + * name-value pair [QUERYPARAMETER] with the name of "ct" and a value + * indicating the media-format used for the dns parameter. The value + * may either be an explicit media type (e.g. ct=application/dns- + * udpwireformat&dns=...) or it may be empty. An empty value indicates + * the default application/dns-udpwireformat type (e.g. ct&dns=...). + * + * NOTE: evhttp_parse_query_str() + * support "ct=x&dns=xxx" + * support "ct=&dns=xxx" + * not support "ct&dns=xxx" + * So we parser "dns=" by self. + */ + int len; + u_char *temp = NULL; + u_char *dns_data = NULL; + const char *uri_begin = session->req->req_spec.uri; + const char *uri_end = session->req->req_spec.uri + strlen(session->req->req_spec.uri) - 1; + char *dns_end = NULL; + char *dns_begin = (char *)strstr(uri_begin, "dns="); + if (dns_begin == NULL) + { + goto error; + } + + if ((dns_begin == uri_begin || *(dns_begin - 1) == '&' || *(dns_begin - 1) == '?') && dns_begin + 4 < uri_end) + { + dns_begin = dns_begin + 4; + dns_end = strstr(dns_begin, "&"); + if (dns_end == NULL) + { + dns_end = (char *)uri_end; + } + else + { + dns_end -= 1; + } + + if (dns_end <= dns_begin) + { + goto error; + } + + dns_data = (u_char *)calloc(1, dns_end - dns_begin + 1 + 1); + memcpy(dns_data, dns_begin, dns_end - dns_begin + 1); + } + + temp = (u_char *)ALLOC(u_char, strlen((char *)dns_data) * 2); + len = tfe_decode_base64url(temp, dns_data); + if (len == 0) + { + TFE_LOG_ERROR(g_doh_conf->local_logger, "%s Doh base64 decode uri failed:%s, PASSTHROUGH", ctx->addr_string, session->req->req_spec.uri); + goto error; + } + + assert(ctx->http_req_body == NULL); + ctx->http_req_body = evbuffer_new(); + evbuffer_add(ctx->http_req_body, temp, len); + doh_process_req(stream, session, pme); + + error: + if (dns_data) + { + free(dns_data); + dns_data = NULL; + } + + if (temp) + { + free(temp); + temp = NULL; + } + } + + // request post + if (REQ_METHOD_IS_POST(session->req->req_spec.method)) + { + if (events & EV_HTTP_REQ_BODY_BEGIN) + { + assert(ctx->http_req_body == NULL); + ctx->http_req_body = evbuffer_new(); + } + + if (events & EV_HTTP_REQ_BODY_CONT) + { + evbuffer_add(ctx->http_req_body, body_frag, frag_size); + } + + if (events & EV_HTTP_REQ_BODY_END) + { + doh_process_req(stream, session, pme); + } + } + + return NO_CALL_NEXT_PLUGIN; +} + +void doh_on_end(const struct tfe_stream *stream, const struct tfe_http_session *session, unsigned int thread_id, void **pme) +{ + if (!g_doh_conf->enable) + { + return; + } + + struct doh_ctx *ctx = *(struct doh_ctx **)pme; + if (ctx->manipulate) + { + int ret = doh_send_log(g_doh_conf, session, stream, ctx); + if (ret > 0) + { + ATOMIC_ADD(&(g_doh_conf->stat_val[STAT_LOG_NUM]), ret); + } + } + + doh_ctx_free(ctx); + *pme = NULL; +} + +struct tfe_plugin doh_spec = { + .symbol = NULL, + .type = TFE_PLUGIN_TYPE_BUSINESS, + .on_init = doh_on_init, + .on_deinit = NULL, + .on_open = NULL, + .on_data = NULL, + .on_close = NULL, + .on_session_begin = doh_on_begin, + .on_session_data = doh_on_data, + .on_session_end = doh_on_end}; +TFE_PLUGIN_REGISTER(doh, doh_spec) \ No newline at end of file diff --git a/plugin/business/doh/src/logger.cpp b/plugin/business/doh/src/logger.cpp new file mode 100644 index 0000000..91762b3 --- /dev/null +++ b/plugin/business/doh/src/logger.cpp @@ -0,0 +1,519 @@ +#include "logger.h" + +struct json_spec +{ + const char *log_filed_name; + enum tfe_http_std_field field_id; +}; + +enum _log_action //Bigger action number is prior. +{ + LG_ACTION_NONE = 0x00, + LG_ACTION_MONIT = 0x01, + LG_ACTION_FORWARD = 0x02, /* N/A */ + LG_ACTION_REJECT = 0x10, + LG_ACTION_DROP = 0x20, /* N/A */ + LG_ACTION_MANIPULATE = 0x30, + LG_ACTION_RATELIMIT = 0x40, /* N/A */ + LG_ACTION_LOOP = 0x60, /* N/A */ + LG_ACTION_WHITELIST = 0x80, + __LG_ACTION_MAX +}; + +static int get_rr_str2json(cJSON *object, dns_info_t *dns_info, int *dns_sec) +{ + int i = 0; + char ip_str[128]; + dns_rr_t *dns_rr = NULL; + cJSON *one_rr_object = NULL; + cJSON *dns_rr_array = NULL; + + if (object == NULL || dns_info == NULL || dns_sec == NULL) + { + return -1; + } + + dns_rr_array = cJSON_CreateArray(); + + for (i = 0; i < dns_info->rr_count; i++) + { + one_rr_object = cJSON_CreateObject(); + dns_rr = &(dns_info->rr[i]); + + if (dns_rr->type == DNS_TYPE_OPT) + { + cJSON_AddStringToObject(one_rr_object, "name", (const char *)(dns_rr->name)); + cJSON_AddNumberToObject(one_rr_object, "type", dns_rr->type); + cJSON_AddNumberToObject(one_rr_object, "udp_payload", dns_rr->rr_class); + cJSON_AddNumberToObject(one_rr_object, "rcode", (int)(dns_rr->ttl >> 24)); + cJSON_AddNumberToObject(one_rr_object, "version", (int)((dns_rr->ttl >> 16) & 0xFF)); + cJSON_AddNumberToObject(one_rr_object, "Z", (int)(dns_rr->ttl && 0xFFFF)); + cJSON_AddNumberToObject(one_rr_object, "rdlength", dns_rr->rdlength); + } + else + { + cJSON_AddStringToObject(one_rr_object, "name", (const char *)(dns_rr->name)); + cJSON_AddNumberToObject(one_rr_object, "type", dns_rr->type); + cJSON_AddNumberToObject(one_rr_object, "class", dns_rr->rr_class); + cJSON_AddNumberToObject(one_rr_object, "ttl", dns_rr->ttl); + cJSON_AddNumberToObject(one_rr_object, "rdlength", dns_rr->rdlength); + } + + if (dns_rr->rdata.a == NULL) + { + cJSON_AddItemToArray(dns_rr_array, one_rr_object); + continue; + } + + switch (dns_rr->type) + { + case DNS_TYPE_A: + inet_ntop(AF_INET, (void *)(dns_rr->rdata.a), ip_str, sizeof(ip_str)); + cJSON_AddStringToObject(one_rr_object, "a", ip_str); + break; + case DNS_TYPE_NS: + cJSON_AddStringToObject(one_rr_object, "ns", (const char *)(dns_rr->rdata.ns)); + break; + case DNS_TYPE_MD: + cJSON_AddStringToObject(one_rr_object, "md", (const char *)(dns_rr->rdata.md)); + break; + case DNS_TYPE_MF: + cJSON_AddStringToObject(one_rr_object, "mf", (const char *)(dns_rr->rdata.mf)); + break; + case DNS_TYPE_CNAME: + cJSON_AddStringToObject(one_rr_object, "cname", (const char *)(dns_rr->rdata.cname)); + break; + case DNS_TYPE_SOA: + cJSON_AddStringToObject(one_rr_object, "mname", (const char *)(dns_rr->rdata.soa.mname)); + cJSON_AddStringToObject(one_rr_object, "rname", (const char *)(dns_rr->rdata.soa.rname)); + cJSON_AddNumberToObject(one_rr_object, "serial", dns_rr->rdata.soa.serial); + cJSON_AddNumberToObject(one_rr_object, "refresh", dns_rr->rdata.soa.refresh); + cJSON_AddNumberToObject(one_rr_object, "retry", dns_rr->rdata.soa.retry); + cJSON_AddNumberToObject(one_rr_object, "cname", dns_rr->rdata.soa.expire); + cJSON_AddNumberToObject(one_rr_object, "minimum", dns_rr->rdata.soa.minimum); + break; + case DNS_TYPE_MB: + cJSON_AddStringToObject(one_rr_object, "mb", (const char *)(dns_rr->rdata.mb)); + break; + case DNS_TYPE_MG: + cJSON_AddStringToObject(one_rr_object, "mg", (const char *)(dns_rr->rdata.mg)); + break; + case DNS_TYPE_MR: + cJSON_AddStringToObject(one_rr_object, "mr", (const char *)(dns_rr->rdata.mr)); + break; + case DNS_TYPE_NULL: + cJSON_AddNumberToObject(one_rr_object, "size", dns_rr->rdata.null.size); + cJSON_AddStringToObject(one_rr_object, "null", (const char *)(dns_rr->rdata.null.null)); + break; + case DNS_TYPE_WKS: + cJSON_AddStringToObject(one_rr_object, "addr", ip_str); + cJSON_AddNumberToObject(one_rr_object, "protocol", dns_rr->rdata.wks.protocol); + cJSON_AddStringToObject(one_rr_object, "bitmap", (const char *)(dns_rr->rdata.wks.bitmap)); + cJSON_AddNumberToObject(one_rr_object, "size", dns_rr->rdata.wks.size); + break; + case DNS_TYPE_PTR: + cJSON_AddStringToObject(one_rr_object, "ptr", (const char *)(dns_rr->rdata.ptr)); + break; + case DNS_TYPE_HINFO: + cJSON_AddStringToObject(one_rr_object, "cpu", (const char *)(dns_rr->rdata.hinfo.cpu)); + cJSON_AddStringToObject(one_rr_object, "os", (const char *)(dns_rr->rdata.hinfo.os)); + break; + case DNS_TYPE_MINFO: + cJSON_AddStringToObject(one_rr_object, "rmailbx", (const char *)(dns_rr->rdata.minfo.rmailbx)); + cJSON_AddStringToObject(one_rr_object, "emailbx", (const char *)(dns_rr->rdata.minfo.emailbx)); + break; + case DNS_TYPE_MX: + cJSON_AddStringToObject(one_rr_object, "exchange", (const char *)(dns_rr->rdata.mx.exchange)); + cJSON_AddNumberToObject(one_rr_object, "preference", dns_rr->rdata.mx.preference); + break; + case DNS_TYPE_TXT: + cJSON_AddStringToObject(one_rr_object, "txt", (char *)(dns_rr->rdata.txt.txt)); + cJSON_AddNumberToObject(one_rr_object, "size", dns_rr->rdata.txt.size); + break; + case DNS_TYPE_RP: + cJSON_AddStringToObject(one_rr_object, "mailbox", (char *)(dns_rr->rdata.rp.mailbox)); + cJSON_AddStringToObject(one_rr_object, "txt_rr", (char *)(dns_rr->rdata.rp.txt_rr)); + break; + case DNS_TYPE_AAAA: + inet_ntop(AF_INET6, dns_rr->rdata.aaaa, ip_str, sizeof(ip_str)); + cJSON_AddStringToObject(one_rr_object, "aaaa", ip_str); + break; + case DNS_TYPE_OPT: + break; + case DNS_TYPE_DS: + *dns_sec = 2; + cJSON_AddNumberToObject(one_rr_object, "key_tag", dns_rr->rdata.ds.key_tag); + cJSON_AddNumberToObject(one_rr_object, "algo", dns_rr->rdata.ds.algo); + cJSON_AddNumberToObject(one_rr_object, "digest_type", dns_rr->rdata.ds.digest_type); + cJSON_AddStringToObject(one_rr_object, "digest", (char *)(dns_rr->rdata.ds.digest)); + break; + case DNS_TYPE_RRSIG: + *dns_sec = 2; + cJSON_AddNumberToObject(one_rr_object, "type_covered", dns_rr->rdata.rrsig.type_covered); + cJSON_AddNumberToObject(one_rr_object, "algo", dns_rr->rdata.rrsig.algo); + cJSON_AddNumberToObject(one_rr_object, "labels", dns_rr->rdata.rrsig.labels); + cJSON_AddNumberToObject(one_rr_object, "original_ttl", dns_rr->rdata.rrsig.original_ttl); + cJSON_AddNumberToObject(one_rr_object, "sig_expiration", dns_rr->rdata.rrsig.sig_expiration); + cJSON_AddNumberToObject(one_rr_object, "sig_inception", dns_rr->rdata.rrsig.sig_inception); + cJSON_AddNumberToObject(one_rr_object, "key_tag", dns_rr->rdata.rrsig.key_tag); + cJSON_AddStringToObject(one_rr_object, "signer_name", (const char *)(dns_rr->rdata.rrsig.signer_name)); + cJSON_AddStringToObject(one_rr_object, "signature", (char *)(dns_rr->rdata.rrsig.signature)); + break; + case DNS_TYPE_NSEC: + *dns_sec = 2; + cJSON_AddStringToObject(one_rr_object, "next_domain", (const char *)(dns_rr->rdata.nsec.next_domain)); + cJSON_AddStringToObject(one_rr_object, "type_bit_maps", (char *)(dns_rr->rdata.nsec.type_bit_maps)); + break; + case DNS_TYPE_DNSKEY: + *dns_sec = 2; + cJSON_AddNumberToObject(one_rr_object, "flags", dns_rr->rdata.dnskey.flags); + cJSON_AddNumberToObject(one_rr_object, "protocol", dns_rr->rdata.dnskey.protocol); + cJSON_AddNumberToObject(one_rr_object, "algo", dns_rr->rdata.dnskey.algo); + cJSON_AddStringToObject(one_rr_object, "public_key", (char *)(dns_rr->rdata.dnskey.public_key)); + break; + case DNS_TYPE_NSEC3: + *dns_sec = 2; + cJSON_AddNumberToObject(one_rr_object, "hash_algo", dns_rr->rdata.nsec3.hash_algo); + cJSON_AddNumberToObject(one_rr_object, "flags", dns_rr->rdata.nsec3.flags); + cJSON_AddNumberToObject(one_rr_object, "iteration", dns_rr->rdata.nsec3.iteration); + cJSON_AddNumberToObject(one_rr_object, "salt_len", dns_rr->rdata.nsec3.salt_len); + cJSON_AddNumberToObject(one_rr_object, "hash_len", dns_rr->rdata.nsec3.hash_len); + cJSON_AddStringToObject(one_rr_object, "salt_value", (char *)(dns_rr->rdata.nsec3.salt_value)); + cJSON_AddStringToObject(one_rr_object, "next_hash_owner", (char *)(dns_rr->rdata.nsec3.next_hash_owner)); + cJSON_AddStringToObject(one_rr_object, "type_bit_maps", (char *)(dns_rr->rdata.nsec3.type_bit_maps)); + break; + case DNS_TYPE_NSEC3PARAM: + cJSON_AddNumberToObject(one_rr_object, "hash_algo", dns_rr->rdata.nsec3param.hash_algo); + cJSON_AddNumberToObject(one_rr_object, "flags", dns_rr->rdata.nsec3param.flags); + cJSON_AddNumberToObject(one_rr_object, "iteration", dns_rr->rdata.nsec3param.iteration); + cJSON_AddNumberToObject(one_rr_object, "salt_len", dns_rr->rdata.nsec3param.salt_len); + cJSON_AddStringToObject(one_rr_object, "salt_value", (char *)(dns_rr->rdata.nsec3param.salt_value)); + break; + case DNS_QTYPE_AXFR: + break; + case DNS_QTYPE_MAILB: + continue; + break; + case DNS_QTYPE_MAILA: + break; + case DNS_QTYPE_ANY: + break; + default: + break; + } + + cJSON_AddItemToArray(dns_rr_array, one_rr_object); + } + + cJSON_AddItemToObject(object, "rr", dns_rr_array); + + return 0; +} + +static void add_dns_info_to_log(cJSON *common_obj, dns_info_t *dns_info) +{ + int i = 0; + dns_rr_t *rr = NULL; + int dns_sec = 1; + char *cname = NULL, *rr_buf = NULL; + + cJSON_AddNumberToObject(common_obj, "doh_qr", dns_info->hdr_info.qr); + cJSON_AddNumberToObject(common_obj, "doh_aa", dns_info->hdr_info.aa); + cJSON_AddNumberToObject(common_obj, "doh_message_id", dns_info->hdr_info.id); + cJSON_AddNumberToObject(common_obj, "doh_opcode", dns_info->hdr_info.opcode); + cJSON_AddNumberToObject(common_obj, "doh_ra", dns_info->hdr_info.ra); + cJSON_AddNumberToObject(common_obj, "doh_rcode", dns_info->hdr_info.rcode); + cJSON_AddNumberToObject(common_obj, "doh_rd", dns_info->hdr_info.rd); + cJSON_AddNumberToObject(common_obj, "doh_tc", dns_info->hdr_info.tc); + cJSON_AddNumberToObject(common_obj, "doh_qdcount", dns_info->hdr_info.qdcount); + cJSON_AddNumberToObject(common_obj, "doh_ancount", dns_info->hdr_info.ancount); + cJSON_AddNumberToObject(common_obj, "doh_nscount", dns_info->hdr_info.aucount); + cJSON_AddNumberToObject(common_obj, "doh_arcount", dns_info->hdr_info.adcount); + + if ((strlen((char *)dns_info->query_question.qname)) > 0) + { + cJSON_AddStringToObject(common_obj, "doh_qname", (char *)dns_info->query_question.qname); + cJSON_AddNumberToObject(common_obj, "doh_qtype", dns_info->query_question.qtype); + cJSON_AddNumberToObject(common_obj, "doh_qclass", dns_info->query_question.qclass); + } + + cJSON *item = NULL; + cJSON *cname_array = cJSON_CreateArray(); + for (i = 0; i < dns_info->rr_count && dns_info->rr != NULL; i++) + { + rr = &dns_info->rr[i]; + if (rr != NULL && rr->type == DNS_TYPE_CNAME) + { + if (rr->rdata.cname != NULL) + { + item = cJSON_CreateString((const char *)rr->rdata.cname); + cJSON_AddItemToArray(cname_array, item); + } + } + } + + cname = cJSON_PrintUnformatted(cname_array); + if (cname) + { + if (strlen(cname) > 0) + { + cJSON_AddStringToObject(common_obj, "doh_cname", cname); + } + free(cname); + } + cJSON_Delete(cname_array); + cname_array = NULL; + + cJSON *object = cJSON_CreateObject(); + get_rr_str2json(object, dns_info, &dns_sec); + rr_buf = cJSON_PrintUnformatted(object); + cJSON_AddStringToObject(common_obj, "doh_rr", rr_buf); + free(rr_buf); + rr_buf = NULL; + cJSON_Delete(object); + object = NULL; + + cJSON_AddNumberToObject(common_obj, "doh_sub", dns_sec); +} + +static const char *tfe_device_id_create(const char *profile, const char *section, void *local_logger) +{ + int ret = -1; + size_t device_id_size = 0; + char *tsg_sn_file = NULL, *device_id; + const char *device_def_id = "DFT2201925000001"; + cJSON *json = NULL, *item = NULL; + char device_id_filepath[TFE_STRING_MAX] = {0}; + + ret = MESA_load_profile_string_def(profile, section, "device_id_filepath", device_id_filepath, sizeof(device_id_filepath), NULL); + if (ret < 0) + { + TFE_LOG_ERROR(local_logger, "Doh log init failed, no device_path in profile %s section %s.", profile, section); + goto finish; + } + tsg_sn_file = tfe_read_file(device_id_filepath, &device_id_size); + if (tsg_sn_file == NULL) + { + TFE_LOG_ERROR(local_logger, "Doh log init failed, %s not existed.", tsg_sn_file); + goto finish; + } + json = cJSON_Parse(tsg_sn_file); + if (json == NULL) + { + TFE_LOG_ERROR(local_logger, "invalid device parameter: file = %s", tsg_sn_file); + goto finish; + } + item = cJSON_GetObjectItem(json, "sn"); + if (unlikely(!item || !cJSON_IsString(item))) + { + TFE_LOG_ERROR(local_logger, "Invalid device parameter: %s invalid json format", tsg_sn_file); + } + device_id = tfe_strdup(item->valuestring); + + cJSON_Delete(json); + return device_id; +finish: + return device_def_id; +} + +int doh_kafka_init(const char *profile, struct doh_conf *conf) +{ + char nic_name[64] = {0}; + char brokerlist[TFE_STRING_MAX] = {0}; + char topic_name[TFE_STRING_MAX] = {0}; + const char *section = "kafka"; + + MESA_load_profile_int_def(profile, section, "ENTRANCE_ID", &(conf->entry_id), 0); + MESA_load_profile_int_def(profile, section, "en_sendlog", &conf->en_sendlog, 1); + MESA_load_profile_string_def(profile, section, "NIC_NAME", nic_name, sizeof(nic_name), "eth0"); + MESA_load_profile_string_def(profile, section, "KAFKA_BROKERLIST", brokerlist, sizeof(brokerlist), ""); + MESA_load_profile_string_def(profile, section, "KAFKA_TOPIC", topic_name, sizeof(topic_name), "POLICY-DOH-LOG"); + + TFE_LOG_INFO(conf->local_logger, "Doh sendlog : %s", conf->en_sendlog ? "ENABLE" : "DISABLE"); + if (!conf->en_sendlog) + { + return 0; + } + conf->device_id = tfe_device_id_create(profile, section, conf->local_logger); + if (!strlen(brokerlist)) + { + TFE_LOG_ERROR(conf->local_logger, "Doh log init failed, no brokerlist in profile %s section %s.", profile, section); + return -1; + } + conf->kafka_logger = tfe_kafka_logger_create(conf->en_sendlog, nic_name, brokerlist, topic_name, conf->local_logger); + if (conf->kafka_logger == NULL) + { + TFE_LOG_ERROR(conf->local_logger, "Doh kafka init failed, error to create kafka logger."); + return -1; + } + + TFE_LOG_INFO(conf->local_logger, "Doh device id : %s", conf->device_id); + TFE_LOG_INFO(conf->local_logger, "Doh kafka topic : %s", topic_name); + TFE_LOG_INFO(conf->local_logger, "Doh kafka brokerlist : %s", brokerlist); + + return 0; +} + +int doh_send_log(struct doh_conf *handle, const struct tfe_http_session *http, const struct tfe_stream *stream, struct doh_ctx *ctx) +{ + Maat_rule_t *result = ctx->result; + size_t result_num = ctx->result_num; + dns_info_t *dns_info = ctx->doh_req; + const struct tfe_stream_addr *addr = stream->addr; + const char *tmp_val = NULL; + cJSON *common_obj = NULL, *per_hit_obj = NULL; + char *log_payload = NULL; + int kafka_status = 0; + int send_cnt = 0; + time_t cur_time; + char src_ip_str[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)] = {0}; + char dst_ip_str[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)] = {0}; + + const char *app_proto[] = {"unkonw", "http1", "http2"}; + struct json_spec req_fields[] = {{"doh_cookie", TFE_HTTP_COOKIE}, + {"doh_referer", TFE_HTTP_REFERER}, + {"doh_user_agent", TFE_HTTP_USER_AGENT}}; + struct json_spec resp_fields[] = {{"doh_content_type", TFE_HTTP_CONT_TYPE}, + {"doh_content_length", TFE_HTTP_CONT_LENGTH}, + {"doh_set_cookie", TFE_HTTP_SET_COOKIE}}; + + if (!handle->en_sendlog) + { + return 0; + } + + common_obj = cJSON_CreateObject(); + cur_time = time(NULL); + + cJSON_AddNumberToObject(common_obj, "common_start_time", cur_time); + cJSON_AddNumberToObject(common_obj, "common_end_time", cur_time); + cJSON_AddStringToObject(common_obj, "doh_version", app_proto[http->major_version]); + cJSON_AddStringToObject(common_obj, "common_schema_type", "DOH"); + + char opt_val[24]; + uint16_t opt_out_size; + struct tfe_cmsg *cmsg = tfe_stream_get0_cmsg(stream); + if (cmsg != NULL) + { + int ret = tfe_cmsg_get_value(cmsg, TFE_CMSG_STREAM_TRACE_ID, (unsigned char *)opt_val, sizeof(opt_val), &opt_out_size); + if (ret == 0) + { + cJSON_AddNumberToObject(common_obj, "common_stream_trace_id", atoll(opt_val)); + } + } + + if (http->req) + { + char *request_line = NULL; + struct tfe_http_req_spec req_spec = http->req->req_spec; + asprintf(&request_line, "%s %s HTTP/%d.%d", http_std_method_to_string(req_spec.method), req_spec.url, http->major_version, http->minor_version); + cJSON_AddStringToObject(common_obj, "doh_request_line", request_line); + free(request_line); + } + + if (http->resp) + { + char *response_line = NULL; + struct tfe_http_resp_spec resp_spec = http->resp->resp_spec; + asprintf(&response_line, "HTTP/%d.%d %d OK", http->major_version, http->minor_version, resp_spec.resp_code); + cJSON_AddStringToObject(common_obj, "doh_response_line", response_line); + free(response_line); + } + + switch (addr->addrtype) + { + case TFE_ADDR_STREAM_TUPLE4_V4: + cJSON_AddNumberToObject(common_obj, "common_address_type", 4); + inet_ntop(AF_INET, &addr->tuple4_v4->saddr, src_ip_str, sizeof(src_ip_str)); + inet_ntop(AF_INET, &addr->tuple4_v4->daddr, dst_ip_str, sizeof(dst_ip_str)); + cJSON_AddStringToObject(common_obj, "common_client_ip", src_ip_str); + cJSON_AddStringToObject(common_obj, "common_server_ip", dst_ip_str); + cJSON_AddNumberToObject(common_obj, "common_client_port", ntohs(addr->tuple4_v4->source)); + cJSON_AddNumberToObject(common_obj, "common_server_port", ntohs(addr->tuple4_v4->dest)); + cJSON_AddStringToObject(common_obj, "common_l4_protocol", "IPv4_TCP"); + break; + case TFE_ADDR_STREAM_TUPLE4_V6: + cJSON_AddNumberToObject(common_obj, "common_address_type", 6); + inet_ntop(AF_INET6, &addr->tuple4_v6->saddr, src_ip_str, sizeof(src_ip_str)); + inet_ntop(AF_INET6, &addr->tuple4_v6->daddr, dst_ip_str, sizeof(dst_ip_str)); + cJSON_AddStringToObject(common_obj, "common_client_ip", src_ip_str); + cJSON_AddStringToObject(common_obj, "common_server_ip", dst_ip_str); + cJSON_AddNumberToObject(common_obj, "common_client_port", ntohs(addr->tuple4_v6->source)); + cJSON_AddNumberToObject(common_obj, "common_server_port", ntohs(addr->tuple4_v6->dest)); + cJSON_AddStringToObject(common_obj, "common_l4_protocol", "IPv6_TCP"); + break; + default: + break; + } + size_t c2s_byte_num = 0, s2c_byte_num = 0; + tfe_stream_info_get(stream, INFO_FROM_DOWNSTREAM_RX_OFFSET, &c2s_byte_num, sizeof(c2s_byte_num)); + tfe_stream_info_get(stream, INFO_FROM_UPSTREAM_RX_OFFSET, &s2c_byte_num, sizeof(s2c_byte_num)); + + cJSON_AddNumberToObject(common_obj, "common_direction", 0); //0锛氬煙鍐->鍩熷锛1锛氬煙澶->鍩熷唴锛屾弿杩扮殑鏄疌LIENT_IP淇℃伅 + cJSON_AddNumberToObject(common_obj, "common_link_id", 0); + cJSON_AddNumberToObject(common_obj, "common_stream_dir", 3); //1:c2s, 2:s2c, 3:double + cJSON_AddStringToObject(common_obj, "common_sled_ip", handle->kafka_logger->local_ip_str); + cJSON_AddNumberToObject(common_obj, "common_entrance_id", handle->entry_id); + cJSON_AddStringToObject(common_obj, "common_device_id", handle->device_id); + cJSON_AddNumberToObject(common_obj, "common_c2s_byte_num", c2s_byte_num); + cJSON_AddNumberToObject(common_obj, "common_s2c_byte_num", s2c_byte_num); + cJSON_AddStringToObject(common_obj, "doh_url", http->req->req_spec.url); + cJSON_AddStringToObject(common_obj, "doh_host", http->req->req_spec.host); + for (size_t i = 0; i < sizeof(req_fields) / sizeof(struct json_spec); i++) + { + tmp_val = tfe_http_std_field_read(http->req, req_fields[i].field_id); + if (tmp_val != NULL) + { + cJSON_AddStringToObject(common_obj, req_fields[i].log_filed_name, tmp_val); + } + } + for (size_t i = 0; i < sizeof(resp_fields) / sizeof(struct json_spec) && http->resp != NULL; i++) + { + tmp_val = tfe_http_std_field_read(http->resp, resp_fields[i].field_id); + if (tmp_val != NULL) + { + cJSON_AddStringToObject(common_obj, resp_fields[i].log_filed_name, tmp_val); + } + } + + add_dns_info_to_log(common_obj, dns_info); + for (size_t i = 0; i < result_num; i++) + { + + TFE_LOG_DEBUG(handle->local_logger, "URL: %s, policy_id: %d, service: %d, do_log:%d", + http->req->req_spec.url, + result[i].config_id, + result[i].service_id, + result[i].do_log); + + if (result[i].do_log == 0) + { + continue; + } + + per_hit_obj = cJSON_Duplicate(common_obj, 1); + cJSON_AddNumberToObject(per_hit_obj, "common_policy_id", result[i].config_id); + cJSON_AddNumberToObject(per_hit_obj, "common_service", result[i].service_id); + cJSON_AddNumberToObject(per_hit_obj, "common_action", LG_ACTION_MANIPULATE); + cJSON_AddStringToObject(per_hit_obj, "common_sub_action", "redirect"); + + log_payload = cJSON_PrintUnformatted(per_hit_obj); + + TFE_LOG_DEBUG(handle->local_logger, "%s", log_payload); + + kafka_status = tfe_kafka_logger_send(handle->kafka_logger, log_payload, strlen(log_payload)); + free(log_payload); + cJSON_Delete(per_hit_obj); + if (kafka_status < 0) + { + TFE_LOG_ERROR(handle->local_logger, "Kafka produce failed: %s", rd_kafka_err2name(rd_kafka_last_error())); + } + else + { + send_cnt++; + } + } + + cJSON_Delete(common_obj); + return send_cnt; +} diff --git a/plugin/business/doh/src/logger.h b/plugin/business/doh/src/logger.h new file mode 100644 index 0000000..b7da6d4 --- /dev/null +++ b/plugin/business/doh/src/logger.h @@ -0,0 +1,18 @@ +#ifndef _LOGGER_H +#define _LOGGER_H + +#ifdef __cpluscplus +extern "C" +{ +#endif + +#include "pub.h" + +int doh_kafka_init(const char *profile, struct doh_conf *conf); +int doh_send_log(struct doh_conf *handle, const struct tfe_http_session *http, const struct tfe_stream *stream, struct doh_ctx *ctx); + +#ifdef __cpluscplus +} +#endif + +#endif \ No newline at end of file diff --git a/plugin/business/doh/src/pub.cpp b/plugin/business/doh/src/pub.cpp new file mode 100644 index 0000000..36c98c5 --- /dev/null +++ b/plugin/business/doh/src/pub.cpp @@ -0,0 +1,151 @@ +#include "pub.h" + +static const char base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static const char index_64[128] = +{ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, +}; + +int base64_encode(char *dst, int dstlen, const char *src, int srclen) +{ + char *to = dst; + const unsigned char *from; + unsigned char c1, c2; + int dst_needed; + + // 鍙傛暟妫娴 + if (dst == NULL || dstlen <= 0 || src == NULL || srclen <= 0) + { + return MS_ERROR; + } + + /* dst 缂撳啿鍖虹殑绌洪棿涓嶈冻銆 + * Base64 缂栫爜鏄瘡 3 涓師濮嬪瓧绗︾紪鐮佹垚 4 涓瓧绗︼紝 + * 濡傛灉鍘熷瀛楃涓查暱搴︿笉鑳借 3 鏁撮櫎锛屼娇鐢 0 鏉ヨˉ鍏呭師濮嬪瓧绗︿覆銆*/ + dst_needed = (srclen + 2) / 3; + dst_needed *= 4; + if (dstlen < dst_needed + 1) + { + return MS_ERROR; + } + + from = (unsigned char *)src; + while (srclen > 0) + { + c1 = *from++; + srclen--; + *to++ = base64[c1 >> 2]; + c1 = (c1 & 0x03) << 4; + if (srclen <= 0) + { + *to++ = base64[c1]; + *to++ = '='; + *to++ = '='; + break; + } + + c2 = *from++; + srclen--; + c1 |= (c2 >> 4) & 0x0f; + *to++ = base64[c1]; + c1 = (c2 & 0x0f) << 2; + if (srclen <= 0) + { + *to++ = base64[c1]; + *to++ = '='; + break; + } + + c2 = *from++; + srclen--; + c1 |= (c2 >> 6) & 0x03; + *to++ = base64[c1]; + *to++ = base64[c2 & 0x3f]; + } + *to = '\0'; + return to - dst; +} + +int base64_decode(char *dst, int dstlen, const char *src, int srclen) +{ + const unsigned char *p, *q; + unsigned char *t; + int c1, c2; + + // 鍙傛暟妫娴 + if (dst == NULL || dstlen <= 0 || src == NULL || srclen <= 0) + { + return MS_ERROR; + } + + // 绉婚櫎棣栧熬鐨勭┖鐧藉瓧绗 + for (p = (const unsigned char *) src; srclen > 0 && isspace(*p); p++, srclen--) + { + /* void */ ; + } + for (q = p + srclen - 1; q >= p && isspace(*q); q--, srclen--) + { + /* void */ ; + } + + // 闀垮害蹇呴』涓 4 鐨勫嶆暟 + if (srclen % 4 != 0) + { + return MS_ERROR; + } + + // 鐩爣缂撳啿鍖哄繀椤昏冻澶熼暱 + if (srclen / 4 * 3 + 1 > dstlen) + { + return MS_ERROR; + } + + t = (unsigned char *)dst; + while (srclen > 0) + { + srclen -= 4; + if (*p >= 128 || (c1 = index_64[*p++]) == -1) + { + return MS_ERROR; + } + + if (*p >= 128 || (c2 = index_64[*p++]) == -1) + { + return MS_ERROR; + } + *t++ = (c1 << 2) | ((c2 & 0x30) >> 4); + + if (p[0] == '=' && p[1] == '=') + { + break; + } + + if (*p >= 128 || (c1 = index_64[*p++]) == -1) + { + return MS_ERROR; + } + *t++ = ((c2 & 0x0f) << 4) | ((c1 & 0x3c) >> 2); + + if (p[0] == '=') + { + break; + } + + if (*p >= 128 || (c2 = index_64[*p++]) == -1) + { + return MS_ERROR; + } + *t++ = ((c1 & 0x03) << 6) | c2; + } + + return t - (unsigned char *) dst; +} diff --git a/plugin/business/doh/src/pub.h b/plugin/business/doh/src/pub.h new file mode 100644 index 0000000..36a013a --- /dev/null +++ b/plugin/business/doh/src/pub.h @@ -0,0 +1,100 @@ +#ifndef _PUB_H +#define _PUB_H + +#ifdef __cpluscplus +extern "C" +{ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dns.h" + +#define MS_OK 0 +#define MS_ERROR -1 + +enum pangu_http_stat +{ + STAT_SESSION, + STAT_LOG_NUM, + STAT_ACTION_HIJACK, + DOH_STAT_MAX +}; + +enum table_type +{ + TYPE_ADDR, + TYPE_APPID, + TYPE_QNAME, + TYPE_HOST, + TYPE_MAX +}; + +enum doh_content_type +{ + DOH_TYPE_MESSAGE, + DOH_TYPE_UDPWIREFORMAT, +}; + +struct maat_table +{ + int id; + char name[TFE_STRING_MAX]; +}; + +struct doh_conf +{ + int enable; + int thread_num; + + int local_level; + void *local_logger; + + int entry_id; + int en_sendlog; + const char *device_id; + tfe_kafka_logger_t *kafka_logger; + + int fs_id[DOH_STAT_MAX]; + long long stat_val[DOH_STAT_MAX]; + struct event *gcev; + struct event_base *gc_evbase; + screen_stat_handle_t fs_handle; + + Maat_feather_t maat; + struct maat_table tables[TYPE_MAX]; +}; + +struct doh_ctx +{ + int count; + u_int32_t min_ttl; + int thread_id; + int magic_num; + int manipulate; + int opts_num; + char *addr_string; + size_t result_num; + Maat_rule_t *result; + cheat_pkt_opt_t *opts; + scan_status_t scan_mid; + enum doh_content_type type; + struct evbuffer *http_req_body; + dns_info_t *doh_req; +}; + +int base64_encode(char *dst, int dstlen, const char *src, int srclen); +int base64_decode(char *dst, int dstlen, const char *src, int srclen); + +#ifdef __cpluscplus +} +#endif +#endif \ No newline at end of file diff --git a/plugin/business/doh/test/dns_test.cpp b/plugin/business/doh/test/dns_test.cpp new file mode 100644 index 0000000..246f7ad --- /dev/null +++ b/plugin/business/doh/test/dns_test.cpp @@ -0,0 +1,898 @@ +#include +#include +#include +#include +#include + +#include "../src/pub.h" +#include "../src/dns.h" + +#define ERROR(str) \ + { \ + printf(str); \ + exit(1); \ + } + +typedef struct +{ + int len; + const char *data; +} str_t; + +typedef struct +{ + const char *desc; + str_t req; + str_t rsp; +} package_t; + +static package_t package_arry[] = { + { + "dig @127.0.0.1 www.baidu.com a", + {56, "TDwBIAABAAAAAAABA3d3dwViYWlkdQNjb20AAAEAAQAAKRAAAAAAAAAA"}, + {136, "TDyBgAABAAMAAAABA3d3dwViYWlkdQNjb20AAAEAAcAMAAUAAQAAACoADwN3d3cBYQZzaGlmZW7AFsArAAEAAQAAACoABLY9yAfAKwABAAEAAAAqAAS2PcgGAAApEAAAAAAAAAA="}, + }, + { + "dig @127.0.0.1 baidu.com ns", + {52, "M00BIAABAAAAAAABBWJhaWR1A2NvbQAAAgABAAApEAAAAAAAAAA="}, + {172, "M02BgAABAAUAAAABBWJhaWR1A2NvbQAAAgABwAwAAgABAAAcJwAGA25zNMAMwAwAAgABAAAcJwAGA25zMsAMwAwAAgABAAAcJwAGA25zN8AMwAwAAgABAAAcJwAGA2Ruc8AMwAwAAgABAAAcJwAGA25zM8AMAAApEAAAAAAAAAA="}, + }, + { + "dig @127.0.0.1 www.baidu.com cname", + {56, "LAMBIAABAAAAAAABA3d3dwViYWlkdQNjb20AAAUAAQAAKRAAAAAAAAAA"}, + {92, "LAOBgAABAAEAAAABA3d3dwViYWlkdQNjb20AAAUAAcAMAAUAAQAAAGYADwN3d3cBYQZzaGlmZW7AFgAAKRAAAAAAAAAA"}, + }, + { + "dig @127.0.0.1 www.baidu.com soa", + {56, "9D8BIAABAAAAAAABA3d3dwViYWlkdQNjb20AAAYAAQAAKRAAAAAAAAAA"}, + {168, "9D+BgAABAAEAAQABA3d3dwViYWlkdQNjb20AAAYAAcAMAAUAAQAAAhcADwN3d3cBYQZzaGlmZW7AFsAvAAYAAQAAAmIALQNuczHALxBiYWlkdV9kbnNfbWFzdGVywBB3hAIjAAAABQAAAAUAJ40AAAAOEAAAKRAAAAAAAAAA"}, + }, + { + "dig @127.0.0.1 -x 192.30.252.153", + {76, "G+4BIAABAAAAAAABAzE1MwMyNTICMzADMTkyB2luLWFkZHIEYXJwYQAADAABAAApEAAAAAAAAAA="}, + {136, "G+6BgAABAAEAAAABAzE1MwMyNTICMzADMTkyB2luLWFkZHIEYXJwYQAADAABwAwADAABAAAOEAAiFWxiLTE5Mi0zMC0yNTItMTUzLWlhZAZnaXRodWIDY29tAAAAKRAAAAAAAAAA"}, + }, + { + "dig @127.0.0.1 163.com mx", + {48, "iRMBIAABAAAAAAABAzE2MwNjb20AAA8AAQAAKRAAAAAAAAAA"}, + {196, "iROBgAABAAQAAAABAzE2MwNjb20AAA8AAcAMAA8AAQAADnQAGwAKBzE2M214MDEGbXhtYWlsB25ldGVhc2XAEMAMAA8AAQAADnQADAAyBzE2M214MDDAL8AMAA8AAQAADnQADAAKBzE2M214MDPAL8AMAA8AAQAADnQADAAKBzE2M214MDLALwAAKRAAAAAAAAAA"}, + }, + { + "dig @127.0.0.1 baidu.com txt", + {52, "A6kBIAABAAAAAAABBWJhaWR1A2NvbQAAEAABAAApEAAAAAAAAAA="}, + {296, "A6mBgAABAAIAAAABBWJhaWR1A2NvbQAAEAABwAwAEAABAAAIKgBFRGdvb2dsZS1zaXRlLXZlcmlmaWNhdGlvbj1HSGI5OC02bXNxeXhfcXFqR2w1ZVJhdEQzUVRIeVZCNi14UTNnSkI1VXdNwAwAEAABAAAIKgBaWXY9c3BmMSBpbmNsdWRlOnNwZjEuYmFpZHUuY29tIGluY2x1ZGU6c3BmMi5iYWlkdS5jb20gaW5jbHVkZTpzcGYzLmJhaWR1LmNvbSBhIG14IHB0ciAtYWxsAAApEAAAAAAAAAA="}, + }, + { + "dig @127.0.0.1 www.taobao.com aaaa", + {60, "0qQBIAABAAAAAAABA3d3dwZ0YW9iYW8DY29tAAAcAAEAACkQAAAAAAAAAA=="}, + {640, "0qSBgAABAA8AAAABA3d3dwZ0YW9iYW8DY29tAAAcAAHADAAFAAEAAAG2ACEDd3d3BnRhb2JhbwNjb20HZGFudW95aQd0YmNhY2hlwBfALAAcAAEAAAA8ABAkDgCxmAEEAAADAAAAAAP5wCwAHAABAAAAPAAQJA4AsZgBBAkAAwAAAAAD+cAsABwAAQAAADwAECQOALGYAQQAAAMAAAAAA/rALAAcAAEAAAA8ABAkDglA4AEAAQADAAAAAAP5wCwAHAABAAAAPAAQJA4AsZgBBAgAAwAAAAAD+sAsABwAAQAAADwAECQOCUDgAQABAAMAAAAAA/rALAAcAAEAAAA8ABAkDglA4AEABQADAAAAAAP5wCwAHAABAAAAPAAQJA4AsaggAAAAAwAAAAAD9cAsABwAAQAAADwAECQOALGoIAAAAAMAAAAAA/bALAAcAAEAAAA8ABAkDgCxmAEECQADAAAAAAP6wCwAHAABAAAAPAAQJA4JQOABAAQAAwAAAAAD+cAsABwAAQAAADwAECQOCUDgAQAEAAMAAAAAA/rALAAcAAEAAAA8ABAkDgCxmAEECAADAAAAAAP5wCwAHAABAAAAPAAQJA4JQOABAAUAAwAAAAAD+gAAKRAAAAAAAAAA"}, + }, + { + "dig @127.0.0.1 com ds", + {44, "94UBIAABAAAAAAABA2NvbQAAKwABAAApEAAAAAAAAAA="}, + {108, "94WBgAABAAEAAAABA2NvbQAAKwABwAwAKwABAAB/zgAkeL0IAuLTyRb23urHMpToJo+1iFBEqDP8VFlYj0qRhM/EGldmAAApEAAAAAAAAAA="}, + }, + { + "dig @127.0.0.1 com rrsig", + {44, "ETMBIAABAAAAAAABA2NvbQAALgABAAApEAAAAAAAAAA="}, + {948, "ETOBgAABAAMAAAABA2NvbQAALgABwAwALgABAAABjgC3AAYIAQAAA4RexfcOXrysJpukA2NvbQAJCiOw6MPHup5mJVrn65fULwvBJoy5mR1IYTJSEzv7PXsT+B/3lp+5Gnfds89MvligsnUWkQvOUiCOkY5ahPwO5fQSB4dDPZJXahG44IR2QtNDHLfFNxcRROwdLmmSe9F+sU5ZL4YtmXu4+trwUycbo34f19o3xRS5D6UBV1clWPp0yK9rQafhMY5UBIZz83txHKumVVjwlnsA2d7w1Qi7wDMALgABAAEBrwETACsIAQABUYBezYNQXrxRwL8HAF4VVK323NSvS//r44sD6QQDSa9aNBCPlimQWZUbjmAh2Z/RkwTAuK0T3ngOw5flW8rHaopbNfIQ4UZSNqzcbzXtJ/F2u2DG4/Xo26LRbF1jHBZOGChGMeug4kVVepPuQvxEoSBUY9FgdGXMZ551kxO/xf3kK6xMhmjQjOQn1xJzZEGtcqV+5pKymT4MNdfPdhpJIDQRnN33bvk8su7wPiaJmGsKGdGHBKlsyMJgLCoJrMvSQIEiQj560Rwy1n1qq3Mlk6CkydQ62mZp2u32d4wBTE3UTs+aDBRfTSQmg6Dofh9x2DJgK5mFISt7rYgvacxpRfqBkD4tDdKUQlrszbjAMwAuAAEAAk6gALcAAggBAAKjAF7AwkJet3dam6QDY29tAJK2SHQgAOJ0UOxt+9f55rJHx5vzvSuuuTsaM06kaK8zhK6Xo0IRqWtu12EHehn3UiPoTGEdUK/KK2ZXmdb8yJ/H+L4BRG9AafN7xaHPAf4GbXr0pV4yOl+HoTEstZmL3JgXHPFMRLl66UYbNGBDFE6B1f1MNZlBvk6c3NjeMSN29DOIZRoajZlDKv3IRhOrRJghwLr5vhlODIwm58lTbI0AACkQAAAAAAAAAA=="}, + }, + { + "dig @127.0.0.1 com dnskey", + {44, "FJwBIAABAAAAAAABA2NvbQAAMAABAAApEAAAAAAAAAA="}, + {648, "FJyBgAABAAIAAAABA2NvbQAAMAABwAwAMAABAAAL5wEGAQEDCAEDw85XTZjL2RV+DXDSdLhJyg4O7Zr/xdzMkEdJaQZlXDXLCLM8TRcbAXyjVvSWAmKqYpPN+uixO1WyHDUc36dofTjvB0Zfh/hNPM2rivJO3r1hJrv+qHftm6IID6IhHxjcrzT2kiOxTiK6A7J8P7WoIMx0V9We0jojoj1jzSMElMljme/VZnENRi5AujZWLxtx8GJsp0L+qBcBr/yhC0sO2Una200NB172W6jFCOwWjLJJr4JtRu6CmdWIhezvYqFTXNPuwEm6pmTe2ffBBlP0IdivwYFHvB7NF1XHTyq7cmJ6EB3dspyj3DDJUxIodv9hwx40TydmssCKSjZ7+KD6P8AMADAAAQAAC+cAqAEAAwgDAQABtsVzV+OQEJLJyG3tqH155FqW2csMraxXKjGSMLrMHOb/lIaB8Mb8fpcS6lPty2AK+ep7FqKdMh9ryecFgI60B5zsZ3W9voT/dJeSCINoxQleZ23wTuhu1VmqMWU3ZA2dOWN8yIQKfXqtleW9ciLbnlPXAWRa2nn4tQKK5+wtN4pz3FtDDvLS5yxH4YrPjIRK1tiL3x+cHmc7PNaFFRyV/QAAKRAAAAAAAAAA"}, + }, + { + "dig @127.0.0.1 com nsec3param", + {44, "s48BIAABAAAAAAABA2NvbQAAMwABAAApEAAAAAAAAAA="}, + {68, "s4+BgAABAAEAAAABA2NvbQAAMwABwAwAMwABAABFmwAFAQAAAAAAACkQAAAAAAAAAA=="}, + }, + { + "dig @127.0.0.1 _sip._udp.sip.voice.google.com srv", + {80, "9OoBIAABAAAAAAABBF9zaXAEX3VkcANzaXAFdm9pY2UGZ29vZ2xlA2NvbQAAIQABAAApEAAAAAAAAAA="}, + {212, "9OqBgAABAAIAAAABBF9zaXAEX3VkcANzaXAFdm9pY2UGZ29vZ2xlA2NvbQAAIQABwAwAIQABAAAAxAAmAAoAARPEDXNpcC1hbnljYXN0LTEFdm9pY2UGZ29vZ2xlA2NvbQDADAAhAAEAAADEACYAFAABE8QNc2lwLWFueWNhc3QtMgV2b2ljZQZnb29nbGUDY29tAAAAKRAAAAAAAAAA"}, + }, + { + "dig @127.0.0.1 . nsec", + {40, "3HUBIAABAAAAAAABAAAvAAEAACkQAAAAAAAAAA=="}, + {72, "3HWBgAABAAEAAAABAAAvAAEAAC8AAQABUSUADgNhYWEAAAciAAAAAAOAAAApEAAAAAAAAAA="}, + }}; + +static int package_arry_size = sizeof(package_arry) / sizeof(package_t); + +static int dns_print(dns_info_t *dns_info, const char *filename) +{ + int i = 0, j = 0; + int used_len = 0, len = 0; + FILE *fp = NULL; + char dns_header[256]; + char question[1024]; + char rr_buf[4096]; + char ip_str[128]; + char tmp_buf[2048]; + char maps[2048]; + char salt_value[2048]; + dns_rr_t *dns_rr = NULL; + char *buf = NULL; + int buflen = sizeof(rr_buf); + buf = rr_buf; + cJSON *dns_info_object = cJSON_CreateObject(); + cJSON *dns_hdr_object = cJSON_CreateObject(); + cJSON *dns_flags_object = cJSON_CreateObject(); + cJSON *dns_question_array = cJSON_CreateArray(); + cJSON *dns_rr_array = cJSON_CreateArray(); + + memset(dns_header, 0, sizeof(dns_header)); + + fp = fopen(filename, "a+"); + if (fp == NULL) + { + return 0; + } + + used_len = snprintf(dns_header, sizeof(dns_header), + "hdr_info: \n", + dns_info->hdr_info.id, + dns_info->hdr_info.qr, + dns_info->hdr_info.opcode, + dns_info->hdr_info.aa, + dns_info->hdr_info.tc, + dns_info->hdr_info.rd, + dns_info->hdr_info.ra, + dns_info->hdr_info.z, + dns_info->hdr_info.rcode, + dns_info->hdr_info.qdcount, + dns_info->hdr_info.ancount, + dns_info->hdr_info.aucount, + dns_info->hdr_info.adcount); + + cJSON_AddNumberToObject(dns_hdr_object, "id", dns_info->hdr_info.id); + cJSON_AddNumberToObject(dns_hdr_object, "qdcount", dns_info->hdr_info.qdcount); + cJSON_AddNumberToObject(dns_hdr_object, "ancount", dns_info->hdr_info.ancount); + cJSON_AddNumberToObject(dns_hdr_object, "aucount", dns_info->hdr_info.aucount); + cJSON_AddNumberToObject(dns_hdr_object, "adcount", dns_info->hdr_info.adcount); + + cJSON_AddNumberToObject(dns_flags_object, "qr", dns_info->hdr_info.qr); + cJSON_AddNumberToObject(dns_flags_object, "opcode", dns_info->hdr_info.opcode); + cJSON_AddNumberToObject(dns_flags_object, "aa", dns_info->hdr_info.aa); + cJSON_AddNumberToObject(dns_flags_object, "tc", dns_info->hdr_info.tc); + cJSON_AddNumberToObject(dns_flags_object, "rd", dns_info->hdr_info.rd); + cJSON_AddNumberToObject(dns_flags_object, "ra", dns_info->hdr_info.ra); + cJSON_AddNumberToObject(dns_flags_object, "z", dns_info->hdr_info.z); + cJSON_AddNumberToObject(dns_flags_object, "rcode", dns_info->hdr_info.rcode); + + fwrite(dns_header, used_len, 1, fp); + + cJSON_AddItemToObject(dns_info_object, "hdr", dns_hdr_object); + cJSON_AddItemToObject(dns_info_object, "flags", dns_flags_object); + cJSON_AddItemToObject(dns_info_object, "question", dns_question_array); + + used_len = snprintf(question, sizeof(question), "question: \n", + dns_info->query_question.qtype, + dns_info->query_question.qclass, + dns_info->query_question.qname); + + cJSON *dns_question_object = cJSON_CreateObject(); + cJSON_AddItemToArray(dns_question_array, dns_question_object); + + cJSON_AddNumberToObject(dns_question_object, "qtype", dns_info->query_question.qtype); + cJSON_AddNumberToObject(dns_question_object, "qclass", dns_info->query_question.qclass); + cJSON_AddStringToObject(dns_question_object, "qname", (const char *)dns_info->query_question.qname); + + fwrite(question, used_len, 1, fp); + + used_len = snprintf(rr_buf, sizeof(rr_buf), "RRS count: %d\n", dns_info->rr_count); + fwrite(rr_buf, used_len, 1, fp); + + cJSON_AddItemToObject(dns_info_object, "rr", dns_rr_array); + + for (i = 0; i < dns_info->rr_count; i++) + { + cJSON *dns_rr_object = cJSON_CreateObject(); + cJSON_AddItemToArray(dns_rr_array, dns_rr_object); + + used_len = 0; + dns_rr = &(dns_info->rr[i]); + memset(rr_buf, 0, sizeof(rr_buf)); + + if (dns_rr->type == DNS_TYPE_OPT) + { + used_len += snprintf(rr_buf, sizeof(rr_buf), "RRS%d OPT \n", + i, + dns_rr->name, + dns_rr->type, + dns_rr->rr_class, + dns_rr->ttl >> 24, + (dns_rr->ttl >> 16) & 0xFF, + dns_rr->ttl && 0xFFFF, + dns_rr->rdlength); + + cJSON_AddStringToObject(dns_rr_object, "name", (const char *)(dns_rr->name)); + cJSON_AddNumberToObject(dns_rr_object, "type", dns_rr->type); + cJSON_AddNumberToObject(dns_rr_object, "udp_payload", dns_rr->rr_class); + cJSON_AddNumberToObject(dns_rr_object, "rcode", (int)(dns_rr->ttl >> 24)); + cJSON_AddNumberToObject(dns_rr_object, "version", (int)((dns_rr->ttl >> 16) & 0xFF)); + cJSON_AddNumberToObject(dns_rr_object, "Z", (int)(dns_rr->ttl && 0xFFFF)); + cJSON_AddNumberToObject(dns_rr_object, "rdlength", dns_rr->rdlength); + } + else + { + used_len += snprintf(rr_buf, sizeof(rr_buf), "RRS%d ", + i, + dns_rr->name, + dns_rr->type, + dns_rr->rr_class, + dns_rr->ttl, + dns_rr->rdlength); + + cJSON_AddStringToObject(dns_rr_object, "name", (const char *)(dns_rr->name)); + cJSON_AddNumberToObject(dns_rr_object, "type", dns_rr->type); + cJSON_AddNumberToObject(dns_rr_object, "class", dns_rr->rr_class); + cJSON_AddNumberToObject(dns_rr_object, "ttl", dns_rr->ttl); + cJSON_AddNumberToObject(dns_rr_object, "rdlength", dns_rr->rdlength); + } + + switch (dns_rr->type) + { + case DNS_TYPE_A: + inet_ntop(AF_INET, (void *)(dns_rr->rdata.a), ip_str, sizeof(ip_str)); + used_len += snprintf(buf + used_len, buflen - used_len, "[A: %s]\n", ip_str); + cJSON_AddStringToObject(dns_rr_object, "a", ip_str); + break; + case DNS_TYPE_NS: + used_len += snprintf(buf + used_len, buflen - used_len, "[NS: %s]\n", dns_rr->rdata.ns); + cJSON_AddStringToObject(dns_rr_object, "ns", (const char *)(dns_rr->rdata.ns)); + break; + case DNS_TYPE_MD: + used_len += snprintf(buf + used_len, buflen - used_len, "[MD: %s]\n", dns_rr->rdata.md); + cJSON_AddStringToObject(dns_rr_object, "md", (const char *)(dns_rr->rdata.md)); + break; + case DNS_TYPE_MF: + used_len += snprintf(buf + used_len, buflen - used_len, "[MF: %s]\n", dns_rr->rdata.mf); + cJSON_AddStringToObject(dns_rr_object, "mf", (const char *)(dns_rr->rdata.mf)); + break; + case DNS_TYPE_CNAME: + used_len += snprintf(buf + used_len, buflen - used_len, "[CNAME: %s]\n", dns_rr->rdata.cname); + cJSON_AddStringToObject(dns_rr_object, "cname", (const char *)(dns_rr->rdata.cname)); + break; + case DNS_TYPE_SOA: + used_len += snprintf(buf + used_len, buflen - used_len, + "[SOA mname: %s, rname: %s, serial: %u, refresh: %u, retry: %u, expire: %u, minimum: %u]\n", + dns_rr->rdata.soa.mname, + dns_rr->rdata.soa.rname, + dns_rr->rdata.soa.serial, + dns_rr->rdata.soa.refresh, + dns_rr->rdata.soa.retry, + dns_rr->rdata.soa.expire, + dns_rr->rdata.soa.minimum); + + cJSON_AddStringToObject(dns_rr_object, "mname", (const char *)(dns_rr->rdata.soa.mname)); + cJSON_AddStringToObject(dns_rr_object, "rname", (const char *)(dns_rr->rdata.soa.rname)); + cJSON_AddNumberToObject(dns_rr_object, "serial", dns_rr->rdata.soa.serial); + cJSON_AddNumberToObject(dns_rr_object, "refresh", dns_rr->rdata.soa.refresh); + cJSON_AddNumberToObject(dns_rr_object, "retry", dns_rr->rdata.soa.retry); + cJSON_AddNumberToObject(dns_rr_object, "cname", dns_rr->rdata.soa.expire); + cJSON_AddNumberToObject(dns_rr_object, "minimum", dns_rr->rdata.soa.minimum); + break; + case DNS_TYPE_MB: + used_len += snprintf(buf + used_len, buflen - used_len, "[MB: %s]\n", dns_rr->rdata.mb); + cJSON_AddStringToObject(dns_rr_object, "mb", (const char *)(dns_rr->rdata.mb)); + break; + case DNS_TYPE_MG: + used_len += snprintf(buf + used_len, buflen - used_len, "[MG: %s]\n", dns_rr->rdata.mg); + cJSON_AddStringToObject(dns_rr_object, "mg", (const char *)(dns_rr->rdata.mg)); + break; + case DNS_TYPE_MR: + used_len += snprintf(buf + used_len, buflen - used_len, "[MR: %s]\n", dns_rr->rdata.mr); + cJSON_AddStringToObject(dns_rr_object, "mr", (const char *)(dns_rr->rdata.mr)); + break; + case DNS_TYPE_NULL: + used_len += snprintf(buf + used_len, buflen - used_len, "[null size: %u, null: %s]\n", + dns_rr->rdata.null.size, dns_rr->rdata.null.null); + cJSON_AddNumberToObject(dns_rr_object, "size", dns_rr->rdata.null.size); + cJSON_AddStringToObject(dns_rr_object, "null", (const char *)(dns_rr->rdata.null.null)); + break; + case DNS_TYPE_WKS: + inet_ntop(AF_INET, &(dns_rr->rdata.wks.addr), ip_str, sizeof(ip_str)); + used_len += snprintf(buf + used_len, buflen - used_len, "[WKS addr: %s, protocol: %u, bitmap: %s, size: %u]\n", + ip_str, dns_rr->rdata.wks.protocol, dns_rr->rdata.wks.bitmap, dns_rr->rdata.wks.size); + + cJSON_AddStringToObject(dns_rr_object, "addr", ip_str); + cJSON_AddNumberToObject(dns_rr_object, "protocol", dns_rr->rdata.wks.protocol); + cJSON_AddStringToObject(dns_rr_object, "bitmap", (const char *)(dns_rr->rdata.wks.bitmap)); + cJSON_AddNumberToObject(dns_rr_object, "size", dns_rr->rdata.wks.size); + break; + case DNS_TYPE_PTR: + used_len += snprintf(buf + used_len, buflen - used_len, "[PTR: %s]\n", dns_rr->rdata.ptr); + cJSON_AddStringToObject(dns_rr_object, "ptr", (const char *)(dns_rr->rdata.ptr)); + break; + case DNS_TYPE_HINFO: + used_len += snprintf(buf + used_len, buflen - used_len, "[HINFO cpu: %s, os: %s]\n", + dns_rr->rdata.hinfo.cpu, dns_rr->rdata.hinfo.os); + + cJSON_AddStringToObject(dns_rr_object, "cpu", (const char *)(dns_rr->rdata.hinfo.cpu)); + cJSON_AddStringToObject(dns_rr_object, "os", (const char *)(dns_rr->rdata.hinfo.os)); + break; + case DNS_TYPE_MINFO: + used_len += snprintf(buf + used_len, buflen - used_len, "[MINFO rmailbx: %s, emailbx: %s]\n", + dns_rr->rdata.minfo.rmailbx, dns_rr->rdata.minfo.emailbx); + + cJSON_AddStringToObject(dns_rr_object, "rmailbx", (const char *)(dns_rr->rdata.minfo.rmailbx)); + cJSON_AddStringToObject(dns_rr_object, "emailbx", (const char *)(dns_rr->rdata.minfo.emailbx)); + break; + case DNS_TYPE_MX: + used_len += snprintf(buf + used_len, buflen - used_len, "[MX preference: %u, exchange: %s]\n", + dns_rr->rdata.mx.preference, dns_rr->rdata.mx.exchange); + + cJSON_AddStringToObject(dns_rr_object, "exchange", (const char *)(dns_rr->rdata.mx.exchange)); + cJSON_AddNumberToObject(dns_rr_object, "preference", dns_rr->rdata.mx.preference); + break; + case DNS_TYPE_TXT: + used_len += snprintf(buf + used_len, buflen - used_len, "[TXT size: %u, txt: %s]\n", + dns_rr->rdata.txt.size, dns_rr->rdata.txt.txt); + + cJSON_AddStringToObject(dns_rr_object, "txt", (const char *)(dns_rr->rdata.txt.txt)); + cJSON_AddNumberToObject(dns_rr_object, "size", dns_rr->rdata.txt.size); + break; + case DNS_TYPE_RP: + used_len += snprintf(buf + used_len, buflen - used_len, "[mailbox: %s, txt_rr: %s]\n", + dns_rr->rdata.rp.mailbox, dns_rr->rdata.rp.txt_rr); + + cJSON_AddStringToObject(dns_rr_object, "mailbox", (const char *)(dns_rr->rdata.rp.mailbox)); + cJSON_AddStringToObject(dns_rr_object, "txt_rr", (const char *)(dns_rr->rdata.rp.txt_rr)); + break; + case DNS_TYPE_AAAA: + inet_ntop(AF_INET6, dns_rr->rdata.aaaa, ip_str, sizeof(ip_str)); + used_len += snprintf(buf + used_len, buflen - used_len, "[AAAA: %s]\n", ip_str); + cJSON_AddStringToObject(dns_rr_object, "aaaa", ip_str); + break; + case DNS_TYPE_OPT: + break; + case DNS_TYPE_DS: + len = 0; + assert(dns_rr->rdata.ds.digest_len * 2 < sizeof(tmp_buf)); + for (j = 0; j < (int)(dns_rr->rdata.ds.digest_len); j++) + { + len += snprintf(tmp_buf + len, sizeof(tmp_buf) - len, "%02x", dns_rr->rdata.ds.digest[j]); + } + used_len += snprintf(buf + used_len, buflen - used_len, + "[DS key_tag: %u, algo: %u, digest_type: %u, digest: %s]\n", + dns_rr->rdata.ds.key_tag, dns_rr->rdata.ds.algo, + dns_rr->rdata.ds.digest_type, tmp_buf); + + cJSON_AddNumberToObject(dns_rr_object, "key_tag", dns_rr->rdata.ds.key_tag); + cJSON_AddNumberToObject(dns_rr_object, "algo", dns_rr->rdata.ds.algo); + cJSON_AddNumberToObject(dns_rr_object, "digest_type", dns_rr->rdata.ds.digest_type); + cJSON_AddStringToObject(dns_rr_object, "digest", tmp_buf); + break; + case DNS_TYPE_RRSIG: + len = 0; + assert(dns_rr->rdata.rrsig.signature_len * 2 < sizeof(tmp_buf)); + for (j = 0; j < (int)(dns_rr->rdata.rrsig.signature_len); j++) + { + len += snprintf(tmp_buf + len, sizeof(tmp_buf) - len, "%02x", dns_rr->rdata.rrsig.signature[j]); + } + used_len += snprintf(buf + used_len, buflen - used_len, + "[RRSIG type_covered: %u, algo: %u, labels: %u, original_ttl: %u, sig_expiration: %u, sig_inception: %u, key_tag: %u, signer_name: %s, signature: %s]\n", + dns_rr->rdata.rrsig.type_covered, dns_rr->rdata.rrsig.algo, + dns_rr->rdata.rrsig.labels, dns_rr->rdata.rrsig.original_ttl, + dns_rr->rdata.rrsig.sig_expiration, dns_rr->rdata.rrsig.sig_inception, + dns_rr->rdata.rrsig.key_tag, dns_rr->rdata.rrsig.signer_name, tmp_buf); + + cJSON_AddNumberToObject(dns_rr_object, "type_covered", dns_rr->rdata.rrsig.type_covered); + cJSON_AddNumberToObject(dns_rr_object, "algo", dns_rr->rdata.rrsig.algo); + cJSON_AddNumberToObject(dns_rr_object, "labels", dns_rr->rdata.rrsig.labels); + cJSON_AddNumberToObject(dns_rr_object, "original_ttl", dns_rr->rdata.rrsig.original_ttl); + cJSON_AddNumberToObject(dns_rr_object, "sig_expiration", dns_rr->rdata.rrsig.sig_expiration); + cJSON_AddNumberToObject(dns_rr_object, "sig_inception", dns_rr->rdata.rrsig.sig_inception); + cJSON_AddNumberToObject(dns_rr_object, "key_tag", dns_rr->rdata.rrsig.key_tag); + cJSON_AddStringToObject(dns_rr_object, "signer_name", (const char *)(dns_rr->rdata.rrsig.signer_name)); + cJSON_AddStringToObject(dns_rr_object, "signature", tmp_buf); + break; + case DNS_TYPE_NSEC: + len = 0; + for (j = 0; j < (int)(dns_rr->rdata.nsec.maps_len); j++) + { + len += snprintf(maps + len, sizeof(maps) - len, "%02x", dns_rr->rdata.nsec.type_bit_maps[j]); + } + used_len += snprintf(buf + used_len, buflen - used_len, "[NSEC next_domain: %s, type_bit_maps: %s]\n", dns_rr->rdata.nsec.next_domain, maps); + + cJSON_AddStringToObject(dns_rr_object, "next_domain", (const char *)(dns_rr->rdata.nsec.next_domain)); + cJSON_AddStringToObject(dns_rr_object, "type_bit_maps", maps); + break; + case DNS_TYPE_DNSKEY: + len = 0; + assert(dns_rr->rdata.dnskey.public_key_len * 2 < sizeof(tmp_buf)); + for (j = 0; j < (int)(dns_rr->rdata.dnskey.public_key_len); j++) + { + len += snprintf(tmp_buf + len, sizeof(tmp_buf) - len, "%02x", dns_rr->rdata.dnskey.public_key[j]); + } + used_len += snprintf(buf + used_len, buflen - used_len, "[DNSKEY flags: %u, protocol: %u, algo: %u, public_key: %s]\n", + dns_rr->rdata.dnskey.flags, dns_rr->rdata.dnskey.protocol, dns_rr->rdata.dnskey.algo, tmp_buf); + + cJSON_AddNumberToObject(dns_rr_object, "flags", dns_rr->rdata.dnskey.flags); + cJSON_AddNumberToObject(dns_rr_object, "protocol", dns_rr->rdata.dnskey.protocol); + cJSON_AddNumberToObject(dns_rr_object, "algo", dns_rr->rdata.dnskey.algo); + cJSON_AddStringToObject(dns_rr_object, "public_key", tmp_buf); + break; + case DNS_TYPE_NSEC3: + memset(tmp_buf, 0, sizeof(tmp_buf)); + memset(maps, 0, sizeof(maps)); + len = 0; + assert(dns_rr->rdata.nsec3.hash_len * 2 < sizeof(tmp_buf)); + for (j = 0; j < (int)(dns_rr->rdata.nsec3.hash_len); j++) + { + len += snprintf(tmp_buf + len, sizeof(tmp_buf) - len, "%02x", dns_rr->rdata.nsec3.next_hash_owner[j]); + } + len = 0; + for (j = 0; j < (int)(dns_rr->rdata.nsec3.maps_len); j++) + { + len += snprintf(maps + len, sizeof(maps) - len, "%02x", dns_rr->rdata.nsec3.type_bit_maps[j]); + } + len = 0; + for (j = 0; j < (int)(dns_rr->rdata.nsec3.salt_len); j++) + { + len += snprintf(salt_value + len, sizeof(salt_value) - len, "%02x", dns_rr->rdata.nsec3.salt_value[j]); + } + + used_len += snprintf(buf + used_len, buflen - used_len, + "[NSEC3 hash_algo: %u, flags: %u, iteration: %u, salt_len: %u, hash_len: %u, salt_value: %s, next_hash_owner: %s, type_bit_maps: %s]\n", + dns_rr->rdata.nsec3.hash_algo, dns_rr->rdata.nsec3.flags, + dns_rr->rdata.nsec3.iteration, dns_rr->rdata.nsec3.salt_len, + dns_rr->rdata.nsec3.hash_len, salt_value, tmp_buf, maps); + + cJSON_AddNumberToObject(dns_rr_object, "hash_algo", dns_rr->rdata.nsec3.hash_algo); + cJSON_AddNumberToObject(dns_rr_object, "flags", dns_rr->rdata.nsec3.flags); + cJSON_AddNumberToObject(dns_rr_object, "iteration", dns_rr->rdata.nsec3.iteration); + cJSON_AddNumberToObject(dns_rr_object, "salt_len", dns_rr->rdata.nsec3.salt_len); + cJSON_AddNumberToObject(dns_rr_object, "hash_len", dns_rr->rdata.nsec3.hash_len); + cJSON_AddStringToObject(dns_rr_object, "salt_value", salt_value); + cJSON_AddStringToObject(dns_rr_object, "next_hash_owner", tmp_buf); + cJSON_AddStringToObject(dns_rr_object, "type_bit_maps", maps); + break; + case DNS_TYPE_NSEC3PARAM: + len = 0; + assert(dns_rr->rdata.nsec3param.salt_len * 2 < sizeof(tmp_buf)); + for (j = 0; j < (int)(dns_rr->rdata.nsec3param.salt_len); j++) + { + len += snprintf(tmp_buf + len, sizeof(tmp_buf) - len, "%02x", dns_rr->rdata.nsec3param.salt_value[j]); + } + + used_len += snprintf(buf + used_len, buflen - used_len, + "[NSEC3PARAM hash_algo: %u, flags: %u, iteration: %u, salt_len: %u, salt_value: %s]\n", + dns_rr->rdata.nsec3param.hash_algo, dns_rr->rdata.nsec3param.flags, + dns_rr->rdata.nsec3param.iteration, dns_rr->rdata.nsec3param.salt_len, + tmp_buf); + + cJSON_AddNumberToObject(dns_rr_object, "hash_algo", dns_rr->rdata.nsec3param.hash_algo); + cJSON_AddNumberToObject(dns_rr_object, "flags", dns_rr->rdata.nsec3param.flags); + cJSON_AddNumberToObject(dns_rr_object, "iteration", dns_rr->rdata.nsec3param.iteration); + cJSON_AddNumberToObject(dns_rr_object, "salt_len", dns_rr->rdata.nsec3param.salt_len); + cJSON_AddStringToObject(dns_rr_object, "salt_value", tmp_buf); + break; + case DNS_TYPE_UNKNOWN: + used_len += snprintf(buf + used_len, buflen - used_len, "[data: %s]\n", dns_rr->rdata.unknown_data); + cJSON_AddStringToObject(dns_rr_object, "data", (const char *)(dns_rr->rdata.unknown_data)); + break; + case DNS_QTYPE_AXFR: + continue; + break; + case DNS_QTYPE_MAILB: + continue; + break; + case DNS_QTYPE_MAILA: + continue; + break; + case DNS_QTYPE_ANY: + continue; + break; + default: + continue; + break; + } + fwrite(buf, used_len, 1, fp); + } + + fclose(fp); + fp = NULL; + + //char *question_array=cJSON_Print(dns_info_object); + //char *rr_array=cJSON_Print(dns_info_object); + + //cJSON_AddArrayToObject(dns_info_object, question_array); + //cJSON_AddArrayToObject(dns_info_object, rr_array); + + char *outout1 = cJSON_PrintUnformatted(dns_info_object); + char *outout2 = cJSON_Print(dns_info_object); + + printf("%s\n", outout1); + printf("%s\n", outout2); + free(outout1); + free(outout2); + cJSON_Delete(dns_info_object); + + return 0; +} + +static void util_dump_dns(const char *data, int len, int is_req) +{ + int en_len; + int de_len; + int out_len; + char en_buff[102400] = {0}; + char de_buff[102400] = {0}; + char out_buff[102400 * 2] = {0}; + + en_len = base64_encode(en_buff, sizeof(en_buff), data, len); + if (en_len == MS_ERROR) + { + ERROR("base64_encode() failed\n"); + } + + de_len = base64_decode(de_buff, sizeof(de_buff), en_buff, en_len); + if (de_len == MS_ERROR) + { + ERROR("base64_encode() failed\n"); + } + + if (de_len != len || strncmp(data, de_buff, de_len) != 0) + { + ERROR("base64_encode() != base64_decode()\n"); + } + + FILE *fp = fopen("dns_test_base64.log", "a+"); + if (fp == NULL) + { + ERROR("fopen() failed\n"); + } + out_len = snprintf(out_buff, sizeof(out_buff), "dir:%s len:%04d, data:%s\n", is_req ? "req" : "rsp", en_len, en_buff); + printf("%s", out_buff); + fwrite(out_buff, out_len, 1, fp); + fclose(fp); +} + +static int util_proxy_dns(char *req_buff, int req_len, char *rsp_buff, int rsp_size) +{ + int ret; + int clientfd; + struct sockaddr_in saddr; + socklen_t addrlen = sizeof(struct sockaddr_in); + + clientfd = socket(AF_INET, SOCK_DGRAM, 0); + if (clientfd == -1) + { + ERROR("socket() failed\n"); + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = PF_INET; + saddr.sin_port = htons(53); + saddr.sin_addr.s_addr = inet_addr("114.114.114.114"); + + ret = sendto(clientfd, req_buff, req_len, 0, (struct sockaddr *)&saddr, addrlen); + if (ret < 0) + { + ERROR("sendto() failed\n"); + } + + ret = recvfrom(clientfd, rsp_buff, rsp_size, 0, (struct sockaddr *)&saddr, &addrlen); + if (ret < 0) + { + ERROR("recvfrom() failed\n"); + } + + close(clientfd); + + return ret; +} + +static int util_parser_dns(const char *buff, int len, char *out_buff, int size) +{ + int ret; + + dns_info_t *parser = dns_new(); + dns_info_t *package = dns_new(); + assert(parser); + assert(package); + + // buff to struct + ret = dns_parser(parser, (char *)buff, len); + if (ret == -1) + { + ERROR("dns_parser() failed\n"); + } + + // struct to buff + ret = dns_package(parser, out_buff, size); + if (ret == -1) + { + ERROR("dns_package() failed\n"); + } + + if (dns_parser(package, out_buff, ret) == -1) + { + ERROR("dns_parser() failed\n"); + } + + printf("dns first parser\n"); + dns_print(parser, "./dns_test_parser1.log"); + printf("dns second parser\n"); + dns_print(package, "./dns_test_parser2.log"); + + if (len != ret || strncmp(buff, out_buff, len) != 0) + { + printf("==================================\n" + "first parser != second parser\n" + "==================================\n"); + } + + dns_free(parser); + dns_free(package); + + return ret; +} + +static void test_as_dns_proxy_not_cheat_respone(void) +{ + int len; + int ret; + int listenfd = -1; + int reuseaddr = 1; + char req_buff[10240]; + char rsp_buff[10240]; + char req_pacakge[10240]; + char rsp_package[10240]; + + struct sockaddr_in saddr; + struct sockaddr_in caddr; + socklen_t addrlen = sizeof(struct sockaddr_in); + + listenfd = socket(AF_INET, SOCK_DGRAM, 0); + if (listenfd == -1) + { + ERROR("socket() failed\n"); + } + + if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) == -1) + { + ERROR("setsockopt() failed\n"); + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = PF_INET; + saddr.sin_port = htons(53); + saddr.sin_addr.s_addr = INADDR_ANY; + if (bind(listenfd, (struct sockaddr *)&saddr, sizeof(struct sockaddr)) == -1) + { + ERROR("bind() failed\n"); + } + + while (1) + { + memset(req_buff, 0, sizeof(req_buff)); + memset(rsp_buff, 0, sizeof(rsp_buff)); + ret = recvfrom(listenfd, req_buff, sizeof(req_buff), 0, (struct sockaddr *)&caddr, &addrlen); + if (ret < 0) + { + ERROR("recvfrom() failed\n"); + } + + // DNS 璇锋眰鍖 dump + util_dump_dns(req_buff, ret, 1); + + // DNS 璇锋眰鍖 瑙f瀽/灏佽 娴嬭瘯 + printf("============ parser req ============\n"); + len = util_parser_dns(req_buff, ret, req_pacakge, sizeof(req_pacakge)); + + ret = util_proxy_dns(req_buff, ret, rsp_buff, sizeof(rsp_buff)); + + // DNS 鍝嶅簲鍖 dump + util_dump_dns(rsp_buff, ret, 0); + + // DNS 鍝嶅簲鍖 瑙f瀽/灏佽 娴嬭瘯 + printf("============ parser rsp ============\n"); + len = util_parser_dns(rsp_buff, ret, rsp_package, sizeof(rsp_package)); + + ret = sendto(listenfd, rsp_package, len, 0, (struct sockaddr *)&caddr, addrlen); + if (ret < 0) + { + ERROR("sendto() failed\n"); + } + } +} + +static void test_as_dns_proxy_but_cheat_respone(void) +{ + int ret; + int listenfd = -1; + int reuseaddr = 1; + char req_buff[10240]; + char rsp_buff[10240]; + + struct sockaddr_in saddr; + struct sockaddr_in caddr; + socklen_t addrlen = sizeof(struct sockaddr_in); + + listenfd = socket(AF_INET, SOCK_DGRAM, 0); + if (listenfd == -1) + { + ERROR("socket() failed\n"); + } + + if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) == -1) + { + ERROR("setsockopt() failed\n"); + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = PF_INET; + saddr.sin_port = htons(53); + saddr.sin_addr.s_addr = INADDR_ANY; + if (bind(listenfd, (struct sockaddr *)&saddr, sizeof(struct sockaddr)) == -1) + { + ERROR("bind() failed\n"); + } + + cheat_pkt_opt_t cheat_opt[32]; + memset(cheat_opt, 0, sizeof(cheat_opt)); + + // DNS_TYPE_A + cheat_opt[0].res_type = DNS_TYPE_A; + cheat_opt[0].ttl = 30; + cheat_opt[0].res_len = 4; + inet_pton(AF_INET, "192.168.1.1", cheat_opt[0].res_info); + + // DNS_TYPE_AAAA + cheat_opt[1].res_type = DNS_TYPE_AAAA; + cheat_opt[1].ttl = 60; + cheat_opt[1].res_len = 16; + inet_pton(AF_INET6, "fe80::230:64ff:fe57:4a15", cheat_opt[1].res_info); + + // DNS_TYPE_CNAME + cheat_opt[2].res_type = DNS_TYPE_CNAME; + cheat_opt[2].ttl = 90; + cheat_opt[2].res_len = sizeof("www.cname1.com"); + memcpy(cheat_opt[2].res_info, "www.cname1.com", cheat_opt[2].res_len); + + // DNS_TYPE_NS + cheat_opt[3].res_type = DNS_TYPE_NS; + cheat_opt[3].ttl = 90; + cheat_opt[3].res_len = sizeof("www.ns.com"); + memcpy(cheat_opt[3].res_info, "www.ns.com", cheat_opt[3].res_len); + + while (1) + { + memset(req_buff, 0, sizeof(req_buff)); + memset(rsp_buff, 0, sizeof(rsp_buff)); + ret = recvfrom(listenfd, req_buff, sizeof(req_buff), 0, (struct sockaddr *)&caddr, &addrlen); + if (ret < 0) + { + ERROR("recvfrom() failed\n"); + } + + // DNS 璇锋眰鍖 dump + util_dump_dns(req_buff, ret, 1); + + // 瑙f瀽 DNS 璇锋眰鍖 + dns_info_t *parser = dns_new(); + assert(parser); + ret = dns_parser(parser, req_buff, ret); + if (ret == -1) + { + ERROR("dns_parser() failed\n"); + } + + // 鎵弿绛栫暐 + + // 鑻ュ懡涓瓥鐣ワ紝鏍规嵁绛栫暐淇℃伅浼 DNS 鍝嶅簲 + + // 浼 DNS 鍝嶅簲鍖 + ret = dns_cheat_response(parser, cheat_opt, 4, rsp_buff, sizeof(rsp_buff)); + if (ret == -1) + { + ERROR("dns_cheat_response() failed\n"); + } + dns_free(parser); + + // DNS 鍝嶅簲鍖 dump + util_dump_dns(rsp_buff, ret, 0); + + ret = sendto(listenfd, rsp_buff, ret, 0, (struct sockaddr *)&caddr, addrlen); + if (ret < 0) + { + ERROR("sendto() failed\n"); + } + } +} + +static void test_use_offline_package(void) +{ + int i; + int req_len; + int rsp_len; + char req_buff[102400] = {0}; + char rsp_buff[102400] = {0}; + char out_buff[102400 * 2] = {0}; + + for (i = 0; i < package_arry_size; i++) + { + printf("test[%d]: %s\n", i, package_arry[i].desc); + req_len = base64_decode(req_buff, sizeof(req_buff), package_arry[i].req.data, package_arry[i].req.len); + if (req_len == MS_ERROR) + { + ERROR("base64_encode() failed\n"); + } + printf("============ parser req ============\n"); + util_parser_dns(req_buff, req_len, out_buff, sizeof(out_buff)); + + rsp_len = base64_decode(rsp_buff, sizeof(rsp_buff), package_arry[i].rsp.data, package_arry[i].rsp.len); + if (rsp_len == MS_ERROR) + { + ERROR("base64_encode() failed\n"); + } + printf("============ parser rsp ============\n"); + util_parser_dns(rsp_buff, rsp_len, out_buff, sizeof(out_buff)); + } +} + +static void test_use_stdin_package(char *package) +{ + int req_len; + char req_buff[102400] = {0}; + char out_buff[102400 * 2] = {0}; + + req_len = base64_decode(req_buff, sizeof(req_buff), package, strlen(package)); + if (req_len == MS_ERROR) + { + ERROR("base64_encode() failed\n"); + } + util_parser_dns(req_buff, req_len, out_buff, sizeof(out_buff)); +} + +static void usage(char *name) +{ + printf("Usage: %s [option]\n" + " -p : test as dns proxy not cheat respone (dig @127.0.0.1 www.baidu.com a) dump data in base64\n" + " -c : test as dns proxy but cheat respone (dig @127.0.0.1 www.baidu.com a) dump data in base64\n" + "\n" + " -o : test use offline dns package\n" + " -b ${base64_pack} : test use stdin dns package\n", + name); + exit(0); +} + +int main(int argc, char **argv) +{ + if (argc != 2 && argc != 3) + { + usage(argv[0]); + } + + if (argc == 2 && (strncmp(argv[1], "-o", 2) != 0 && strncmp(argv[1], "-p", 2) != 0 && strncmp(argv[1], "-c", 2) != 0)) + { + usage(argv[0]); + } + if (argc == 3 && strncmp(argv[1], "-b", 2) != 0) + { + usage(argv[0]); + } + + if (strncmp(argv[1], "-p", 2) == 0) + { + test_as_dns_proxy_not_cheat_respone(); + } + else if (strncmp(argv[1], "-c", 2) == 0) + { + test_as_dns_proxy_but_cheat_respone(); + } + else if (strncmp(argv[1], "-o", 2) == 0) + { + test_use_offline_package(); + } + else if (strncmp(argv[1], "-b", 2) == 0) + { + test_use_stdin_package(argv[2]); + } + + return 0; +} diff --git a/plugin/business/pangu-http/src/pangu_http.cpp b/plugin/business/pangu-http/src/pangu_http.cpp index b2aada9..4dfa5b2 100644 --- a/plugin/business/pangu-http/src/pangu_http.cpp +++ b/plugin/business/pangu-http/src/pangu_http.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -182,116 +183,6 @@ struct pangu_rt }; struct pangu_rt * g_pangu_rt; -Maat_feather_t g_business_maat; - - -#define MAAT_INPUT_JSON 0 -#define MAAT_INPUT_REDIS 1 -#define MAAT_INPUT_FILE 2 - -static Maat_feather_t create_maat_feather(const char * instance_name, const char * profile, const char * section, int max_thread, void * logger) -{ - Maat_feather_t target; - int input_mode = 0, maat_stat_on = 0, maat_perf_on = 0; - int ret = 0, scan_detail = 0, effect_interval = 60; - char table_info[TFE_STRING_MAX] = {0}, inc_cfg_dir[TFE_STRING_MAX] = {0}, ful_cfg_dir[TFE_STRING_MAX] = {0}; - char redis_server[TFE_STRING_MAX] = {0}; - char redis_port_range[TFE_STRING_MAX] = {0}; - char accept_tags[TFE_STRING_MAX] = {0}; - int redis_port_begin=0, redis_port_end=0; - int redis_port_select=0, deferred_load_on=0; - int redis_db_idx = 0; - char json_cfg_file[TFE_STRING_MAX] = {0}, maat_stat_file[TFE_STRING_MAX] = {0}; - MESA_load_profile_int_def(profile, section, "maat_input_mode", &(input_mode), 0); - MESA_load_profile_int_def(profile, section, "stat_switch", &(maat_stat_on), 1); - MESA_load_profile_int_def(profile, section, "perf_switch", &(maat_perf_on), 1); - - MESA_load_profile_string_def(profile, section, "table_info", table_info, sizeof(table_info), ""); - MESA_load_profile_string_def(profile, section, "accept_tags", accept_tags, sizeof(accept_tags), ""); - - MESA_load_profile_string_def(profile, section, "json_cfg_file", json_cfg_file, sizeof(json_cfg_file), ""); - - MESA_load_profile_string_def(profile, section, "maat_redis_server", redis_server, sizeof(redis_server), ""); - MESA_load_profile_string_def(profile, section, "maat_redis_port_range", redis_port_range, sizeof(redis_server), "6379"); - ret=sscanf(redis_port_range,"%d-%d", &redis_port_begin, &redis_port_end); - if(ret==1) - { - redis_port_select=redis_port_begin; - } - else if(ret==2) - { - srand(time(NULL)); - redis_port_select=redis_port_begin+rand()%(redis_port_end-redis_port_begin); - } - else - { - TFE_LOG_ERROR(logger, "Invalid redis port range %s, MAAT init failed.", redis_port_range); - } - MESA_load_profile_int_def(profile, section, "maat_redis_db_index", &(redis_db_idx), 0); - - MESA_load_profile_string_def(profile, section, "inc_cfg_dir", inc_cfg_dir, sizeof(inc_cfg_dir), ""); - MESA_load_profile_string_def(profile, section, "full_cfg_dir", ful_cfg_dir, sizeof(ful_cfg_dir), ""); - - MESA_load_profile_string_def(profile, section, "stat_file", maat_stat_file, sizeof(maat_stat_file), ""); - MESA_load_profile_int_def(profile, section, "effect_interval_s", &(effect_interval), 60); - MESA_load_profile_int_def(profile, section, "deferred_load_on", &(deferred_load_on), 0); - - effect_interval *= 1000;//convert s to ms - assert(strlen(inc_cfg_dir) != 0 || strlen(ful_cfg_dir) != 0 || strlen(redis_server)!=0 || strlen(json_cfg_file)!=0); - - target = Maat_feather(max_thread, table_info, logger); - Maat_set_feather_opt(target, MAAT_OPT_INSTANCE_NAME, instance_name, strlen(instance_name) + 1); - switch (input_mode) - { - case MAAT_INPUT_JSON: - Maat_set_feather_opt(target, MAAT_OPT_JSON_FILE_PATH, json_cfg_file, strlen(json_cfg_file) + 1); - break; - case MAAT_INPUT_REDIS: - Maat_set_feather_opt(target, MAAT_OPT_REDIS_IP, redis_server, strlen(redis_server) + 1); - Maat_set_feather_opt(target, MAAT_OPT_REDIS_PORT, &redis_port_select, sizeof(redis_port_select)); - Maat_set_feather_opt(target, MAAT_OPT_REDIS_INDEX, &redis_db_idx, sizeof(redis_db_idx)); - break; - case MAAT_INPUT_FILE: Maat_set_feather_opt(target, MAAT_OPT_FULL_CFG_DIR, ful_cfg_dir, strlen(ful_cfg_dir) + 1); - Maat_set_feather_opt(target, MAAT_OPT_INC_CFG_DIR, inc_cfg_dir, strlen(inc_cfg_dir) + 1); - break; - default: TFE_LOG_ERROR(logger, "Invalid MAAT Input Mode: %d.", input_mode); - goto error_out; - break; - } - - - Maat_set_feather_opt(target, MAAT_OPT_FOREIGN_CONT_DIR, "./pangu_files", strlen("./pangu_files")+1); - if (maat_stat_on) - { - Maat_set_feather_opt(target, MAAT_OPT_STAT_FILE_PATH, maat_stat_file, strlen(maat_stat_file) + 1); - Maat_set_feather_opt(target, MAAT_OPT_STAT_ON, NULL, 0); - if (maat_perf_on) - { - Maat_set_feather_opt(target, MAAT_OPT_PERF_ON, NULL, 0); - } - } - Maat_set_feather_opt(target, MAAT_OPT_DEFERRED_LOAD, &deferred_load_on, sizeof(deferred_load_on)); - - Maat_set_feather_opt(target, MAAT_OPT_EFFECT_INVERVAL_MS, &effect_interval, sizeof(effect_interval)); - Maat_set_feather_opt(target, MAAT_OPT_SCAN_DETAIL, &scan_detail, sizeof(scan_detail)); - if(strlen(accept_tags)>0) - { - Maat_set_feather_opt(target, MAAT_OPT_ACCEPT_TAGS, &accept_tags, sizeof(accept_tags)); - } - - ret = Maat_initiate_feather(target); - if (ret < 0) - { - TFE_LOG_ERROR(logger, "%s MAAT init failed.", __FUNCTION__); - goto error_out; - } - - return target; -error_out: - Maat_burn_feather(target); - return NULL; -} - static void pangu_http_gc_cb(evutil_socket_t fd, short what, void * arg) { int i=0; @@ -1095,12 +986,7 @@ int maat_ip_table_init(int profile_idx, int pangu_policy_init(const char* profile_path, const char* static_section, const char* dynamic_section) { int ret = 0; - - g_pangu_rt->maat = create_maat_feather("static", profile_path, static_section, g_pangu_rt->thread_num, g_pangu_rt->local_logger); - if (!g_pangu_rt->maat) - { - goto error_out; - } + g_pangu_rt->maat = (Maat_feather_t)tfe_bussiness_resouce_get(STATIC_MAAT); const char * table_name[__SCAN_TABLE_MAX]; table_name[PXY_CTRL_IP] = "TSG_SECURITY_ADDR"; @@ -1170,11 +1056,8 @@ int pangu_policy_init(const char* profile_path, const char* static_section, cons goto error_out; } } - g_pangu_rt->dyn_maat = create_maat_feather("dyn", profile_path, dynamic_section, g_pangu_rt->thread_num, g_pangu_rt->local_logger); - if (!g_pangu_rt->maat) - { - goto error_out; - } + + g_pangu_rt->dyn_maat = (Maat_feather_t)tfe_bussiness_resouce_get(DYNAMINC_MAAT); g_pangu_rt->subscriber_id_table_id=Maat_table_register(g_pangu_rt->dyn_maat, "TSG_DYN_SUBSCRIBER_IP"); ret=Maat_plugin_EX_register(g_pangu_rt->dyn_maat, g_pangu_rt->subscriber_id_table_id, @@ -1304,7 +1187,6 @@ int pangu_http_init(struct tfe_proxy * proxy) TFE_LOG_INFO(NULL, "Tango Cache Enabled."); } TFE_LOG_INFO(NULL, "Pangu HTTP init success."); - g_business_maat=g_pangu_rt->maat; return 0; error_out: @@ -3078,7 +2960,7 @@ void pangu_on_http_end(const struct tfe_stream * stream, return; } -void pangu_on_http_data(const struct tfe_stream * stream, const struct tfe_http_session * session, +int pangu_on_http_data(const struct tfe_stream * stream, const struct tfe_http_session * session, enum tfe_http_event events, const unsigned char * body_frag, size_t frag_size, unsigned int thread_id, void ** pme) { struct pangu_http_ctx * ctx = *(struct pangu_http_ctx **) pme; @@ -3090,7 +2972,7 @@ void pangu_on_http_data(const struct tfe_stream * stream, const struct tfe_http_ { ctx->resumed_cb=NULL; } - return; + return NO_CALL_NEXT_PLUGIN; } enforce_control_policy(stream, session, events, body_frag, frag_size,thread_id, ctx); @@ -3118,7 +3000,7 @@ void pangu_on_http_data(const struct tfe_stream * stream, const struct tfe_http_ cache_write(session, events, body_frag, frag_size, thread_id, ctx); } } - return; + return NO_CALL_NEXT_PLUGIN; } struct tfe_plugin pangu_http_spec = { diff --git a/plugin/business/ssl-policy/src/ssl_policy.cpp b/plugin/business/ssl-policy/src/ssl_policy.cpp index 2e1f0ad..b01db0f 100644 --- a/plugin/business/ssl-policy/src/ssl_policy.cpp +++ b/plugin/business/ssl-policy/src/ssl_policy.cpp @@ -4,7 +4,7 @@ #include #include #include -extern Maat_feather_t g_business_maat; +#include struct ssl_policy_enforcer { @@ -378,7 +378,7 @@ error_out: struct ssl_policy_enforcer* ssl_policy_enforcer_create(void* logger) { struct ssl_policy_enforcer* enforcer=ALLOC(struct ssl_policy_enforcer, 1); - enforcer->maat=g_business_maat; + enforcer->maat=(Maat_feather_t)tfe_bussiness_resouce_get(STATIC_MAAT);; enforcer->logger=logger; enforcer->policy_table_id=Maat_table_register(enforcer->maat, "TSG_SECURITY_COMPILE"); assert(enforcer->policy_table_id >= 0); diff --git a/resource/pangu/doh.json b/resource/pangu/doh.json new file mode 100644 index 0000000..47ea0c2 --- /dev/null +++ b/resource/pangu/doh.json @@ -0,0 +1,170 @@ +{ + "compile_table": "PXY_CTRL_COMPILE", + "group_table": "GROUP_COMPILE_RELATION", + "rules": [ + { + "compile_id": 1021, + "service": 1, + "action": 48, + "do_blacklist": 1, + "do_log": 1, + "effective_range": 0, + "user_region": "{\"protocol\":\"http\",\"method\":\"redirect\",\"code\":302,\"to\":\"https://www.jd.com\"}", + "is_valid": "yes", + "groups": [ + { + "group_name":"http_url", + "not_flag":0, + "regions": [ + { + "table_name": "TSG_OBJ_URL", + "table_type": "string", + "table_content": { + "keywords": "baidu.com", + "expr_type": "regex", + "match_method": "sub", + "format": "uncase plain" + } + } + ] + } + ] + }, + { + "compile_id": 1022, + "service": 1, + "action": 48, + "do_blacklist": 1, + "do_log": 1, + "effective_range": 0, + "user_region": "{\"protocol\":\"http\",\"method\":\"redirect\",\"code\":302,\"to\":\"https://www.jd.com\"}", + "is_valid": "yes", + "groups": [ + { + "group_name":"http_url", + "virtual_table":"TSG_FIELD_HTTP_URL", + "not_flag":0 + } + ] + }, + { + "compile_id": 1023, + "service": 1, + "action": 48, + "do_blacklist": 1, + "do_log": 1, + "effective_range": 0, + "user_region": "{\"protocol\":\"http\",\"method\":\"replace\",\"rules\":[{\"search_in\":\"http_resp_body\",\"find\":\"閭\",\"replace_with\":\"test\"}]}", + "is_valid": "yes", + "groups": [ + { + "group_name":"http_fqdn", + "not_flag":0, + "regions": [ + { + "table_name": "TSG_OBJ_FQDN", + "table_type": "string", + "table_content": { + "keywords": "www.126.com", + "expr_type": "regex", + "match_method": "sub", + "format": "uncase plain" + } + } + ] + } + ] + }, + { + "compile_id": 1024, + "service": 1, + "action": 48, + "do_blacklist": 1, + "do_log": 1, + "effective_range": 0, + "user_region":"{\"protocol\":\"http\",\"method\":\"replace\",\"rules\":[{\"search_in\":\"http_resp_body\",\"find\":\"澶у笀\",\"replace_with\":\"灏忓皬\"}]}", + "is_valid": "yes", + "groups": [ + { + "group_name":"http_fqdn", + "virtual_table":"TSG_FIELD_HTTP_HOST", + "not_flag":0 + } + ] + }, + { + "compile_id": 1025, + "service": 1, + "action": 48, + "do_blacklist": 1, + "do_log": 1, + "effective_range": 0, + "user_region": "{\"protocol\":\"DoH\",\"method\":\"redirect\",\"resolution\":[{\"qtype\":\"A\",\"answer\":[{\"atype\":\"CNAME\",\"value\":\"www.abc.com\",\"ttl\":{\"min\":60,\"max\":300}},{\"atype\":\"A\",\"value\":\"1.1.1.1\",\"ttl\":{\"min\":60,\"max\":300}}]},{\"qtype\":\"AAAA\",\"answer\":[{\"atype\":\"AAAA\",\"value\":\"aaaa:ffff:00\",\"ttl\":{\"min\":60,\"max\":300}},{\"atype\":\"CNAME\",\"value\":\"abc.com.cn\",\"ttl\":{\"min\":60,\"max\":300}}]}]}", + "is_valid": "yes", + "groups": [ + { + "group_name":"http_fqdn", + "virtual_table":"TSG_FIELD_DOH_QNAME", + "not_flag":0 + } + ] + }, + { + "compile_id": 1026, + "service": 1, + "action": 48, + "do_blacklist": 1, + "do_log": 1, + "effective_range": 0, + "user_region": "{\"protocol\":\"DoH\",\"method\":\"redirect\",\"resolution\":[{\"qtype\":\"A\",\"answer\":[{\"atype\":\"CNAME\",\"value\":\"www.a.shifen.com\",\"ttl\":{\"min\":10,\"max\":10}},{\"atype\":\"A\",\"value\":\"182.61.200.6\",\"ttl\":{\"min\":20,\"max\":30}},{\"atype\":\"A\",\"value\":\"182.61.200.7\",\"ttl\":{\"min\":60,\"max\":61}}]},{\"qtype\":\"AAAA\",\"answer\":[{\"atype\":\"CNAME\",\"value\":\"www.taobao.com.danuoyi.tbcache.com\",\"ttl\":{\"min\":100,\"max\":100}},{\"atype\":\"AAAA\",\"value\":\"2408:871a:2800:4:3::3fa\",\"ttl\":{\"min\":200,\"max\":300}},{\"atype\":\"AAAA\",\"value\":\"2408:871a:2800:2:3::3fa\",\"ttl\":{\"min\":600,\"max\":310}}]}]}", + "is_valid": "yes", + "groups": [ + { + "group_name":"http_fqdn", + "virtual_table":"TSG_FIELD_DOH_QNAME", + "not_flag":0 + } + ] + } + ], + "plugin_table": [ + { + "table_name": "TSG_PROFILE_RESPONSE_PAGES", + "table_content": [ + "101\t404\thtml\t./resource/pangu/policy_file/404.html\t1", + "102\tHTTP403\ttemplate\t./resource/pangu/HTTP403.html\t1" + ] + }, + { + "table_name": "PXY_PROFILE_HIJACK_FILES", + "table_content": [ + "201\tchakanqi\tchakanqi-947KB.exe\tapplication/x-msdos-program\t./resource/pangu/policy_file/chakanqi-947KB.exe\t1", + "202\tWPS8648\tWPS8648-132M.exe\tapplication/x-msdos-program\t./resource/pangu/policy_file/WPS8648-132M.exe\t1" + ] + }, + { + "table_name": "PXY_PROFILE_INSERT_SCRIPTS", + "table_content": [ + "301\ttime\tjs\t./resource/pangu/policy_file/time.js\tbefore_page_load\t1", + "302\tu1\tcss\t./resource/pangu/policy_file/u1.css\tbefore_page_load\t1", + "303\tu1\tjs\t./resource/pangu/policy_file/alert.js\tbefore_page_load\t1" + ] + }, + { + "table_name": "TSG_PROFILE_DECRYPTION", + "table_content": [ + "0\ttest\t{\"dynamic_bypass\":{\"ev_cert\":0,\"cert_transparency\":0,\"mutual_authentication\":1,\"cert_pinning\":1,\"protocol_errors\":1,\"trusted_root_cert_is_not_installed_on_client\":1},\"protocol_version\":{\"min\":\"ssl3\",\"max\":\"ssl3\",\"mirror_client\":1,\"allow_http2\":1},\"certificate_checks\":{\"approach\":{\"cn\":1,\"issuer\":1,\"self-signed\":1,\"expiration\":0},\"fail_action\":\"pass-through\"}}\t1", + "3\ttest\t{\"dynamic_bypass\":{\"ev_cert\":1,\"cert_transparency\":1,\"mutual_authentication\":1,\"cert_pinning\":1,\"protocol_errors\":1,\"trusted_root_cert_is_not_installed_on_client\":0},\"protocol_version\":{\"min\":\"ssl3\",\"max\":\"tls13\",\"mirror_client\":1,\"allow_http2\":1},\"certificate_checks\":{\"approach\":{\"cn\":1,\"issuer\":1,\"self-signed\":1,\"expiration\":1},\"fail_action\":\"fail-close\"}}\t1", + "4\ttest\t{\"dynamic_bypass\":{\"ev_cert\":0,\"cert_transparency\":0,\"mutual_authentication\":0,\"cert_pinning\":0,\"protocol_errors\":0,\"trusted_root_cert_is_not_installed_on_client\":0},\"protocol_version\":{\"min\":\"ssl3\",\"max\":\"ssl3\",\"mirror_client\":0,\"allow_http2\":0},\"certificate_checks\":{\"approach\":{\"cn\":0,\"issuer\":0,\"self-signed\":0,\"expiration\":0},\"fail_action\":\"pass-through\"}}\t1" + ] + }, + { + "table_name": "TSG_SECURITY_COMPILE", + "table_content": [ + "0\t0\t2\t1\t1\t{}\t{\"protocol\":\"SSL\",\"keyring\":765,\"decryption\":0},\"decrypt_mirror\":{\"enable\":0}}\t1\t2", + "656\t0\t2\t1\t1\t{}\t{\"protocol\":\"SSL\",\"keyring\":1,\"decryption\":0},\"decrypt_mirror\":{\"enable\":0}}\t1\t2", + "49\t0\t2\t1\t1\t{}\t{\"protocol\":\"SSL\",\"keyring\":1,\"decryption\":0},\"decrypt_mirror\":{\"enable\":0}}\t1\t2" + ] + } + ] +} \ No newline at end of file diff --git a/resource/pangu/table_info.conf b/resource/pangu/table_info.conf index a6b3b6b..b2a2b27 100644 --- a/resource/pangu/table_info.conf +++ b/resource/pangu/table_info.conf @@ -58,4 +58,5 @@ 38 TSG_OBJ_GEO_LOCATION expr UTF8 UTF8/GBK yes 0 39 TSG_SECURITY_SOURCE_LOCATION virtual TSG_OBJ_GEO_LOCATION -- 40 TSG_SECURITY_DESTINATION_LOCATION virtual TSG_OBJ_GEO_LOCATION -- - +41 TSG_FIELD_DOH_QNAME virtual TSG_OBJ_FQDN -- +42 TSG_FIELD_DOH_HOST virtual TSG_OBJ_FQDN --