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.增加测试用例
This commit is contained in:
1250
plugin/business/doh/src/dns.cpp
Normal file
1250
plugin/business/doh/src/dns.cpp
Normal file
File diff suppressed because it is too large
Load Diff
388
plugin/business/doh/src/dns.h
Normal file
388
plugin/business/doh/src/dns.h
Normal file
@@ -0,0 +1,388 @@
|
||||
#ifndef DNS_ANALYSE_H
|
||||
#define DNS_ANALYSE_H
|
||||
|
||||
#include <MESA/cJSON.h>
|
||||
|
||||
#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
|
||||
|
||||
// <20>洢 DNS ͷ<><CDB7><EFBFBD><EFBFBD>Ϣ<EFBFBD>Ľṹ<C4BD><E1B9B9>
|
||||
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;
|
||||
|
||||
// <20>洢 DNS <20><>Դ<EFBFBD><D4B4>¼<EFBFBD><C2BC><EFBFBD><EFBFBD>(<28>ش<EFBFBD><D8B4><EFBFBD><EFBFBD><EFBFBD> / <20><>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD> / <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)<29>Ľṹ<C4BD><E1B9B9>
|
||||
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;
|
||||
|
||||
// <20>洢 DNS Queries <20><><EFBFBD><EFBFBD><EFBFBD>Ľṹ<C4BD><E1B9B9>
|
||||
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
|
||||
|
||||
// <20>洢<EFBFBD><E6B4A2><EFBFBD><EFBFBD> DNS <20><>Ϣ<EFBFBD>Ľṹ<C4BD><E1B9B9>
|
||||
typedef struct _dns_info
|
||||
{
|
||||
// <20>洢 DNS ͷ<><CDB7><EFBFBD><EFBFBD>Ϣ<EFBFBD>Ľṹ<C4BD><E1B9B9>
|
||||
dns_hdr_t hdr_info;
|
||||
|
||||
// RR <20><>¼<EFBFBD>ĸ<EFBFBD><C4B8><EFBFBD>
|
||||
int rr_count;
|
||||
// <20>洢 DNS <20><>Դ<EFBFBD><D4B4>¼<EFBFBD><C2BC><EFBFBD><EFBFBD>(<28>ش<EFBFBD><D8B4><EFBFBD><EFBFBD><EFBFBD> / <20><>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD> / <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)<29>Ľṹ<C4BD><E1B9B9>
|
||||
dns_rr_t rr[MAX_RR_NUM];
|
||||
|
||||
// <20>洢 DNS Queries <20><><EFBFBD><EFBFBD><EFBFBD>Ľṹ<C4BD><E1B9B9>
|
||||
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
|
||||
758
plugin/business/doh/src/doh.cpp
Normal file
758
plugin/business/doh/src/doh.cpp
Normal file
@@ -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)
|
||||
519
plugin/business/doh/src/logger.cpp
Normal file
519
plugin/business/doh/src/logger.cpp
Normal file
@@ -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:域外->域内,描述的是CLIENT_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;
|
||||
}
|
||||
18
plugin/business/doh/src/logger.h
Normal file
18
plugin/business/doh/src/logger.h
Normal file
@@ -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
|
||||
151
plugin/business/doh/src/pub.cpp
Normal file
151
plugin/business/doh/src/pub.cpp
Normal file
@@ -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;
|
||||
}
|
||||
100
plugin/business/doh/src/pub.h
Normal file
100
plugin/business/doh/src/pub.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef _PUB_H
|
||||
#define _PUB_H
|
||||
|
||||
#ifdef __cpluscplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <event2/buffer.h>
|
||||
#include <tfe_resource.h>
|
||||
#include <tfe_proxy.h>
|
||||
#include <tfe_plugin.h>
|
||||
#include <MESA/Maat_rule.h>
|
||||
#include <MESA/MESA_prof_load.h>
|
||||
#include <tfe_kafka_logger.h>
|
||||
|
||||
#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
|
||||
Reference in New Issue
Block a user