This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
tango-tfe/plugin/business/doh/src/doh.cpp
luwenpeng cd26e3e6c1 TSG-1531 TFE 新增 DOH 插件
1.DOH 协议解析
2.DOH 协议还原
3.DOH POST请求 early response
4.DOH 策略扫描
5.tfe plugin 支持多个 bussiness 插件调用
6.Maat_feather 的创建从 pangu 剥离(涉及pangu/doh/ssl-policy)
7.增加 kafka 日志
8.增加测试用例
2020-07-06 16:16:21 +08:00

758 lines
20 KiB
C++

#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)