#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); } int doh_kafka_init(const char *profile, struct doh_conf *conf) { 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); if (!conf->en_sendlog) { return 0; } conf->device_id = (const char *)tfe_bussiness_resouce_get(DEVICE_ID); conf->effective_device_tag = (const char *)tfe_bussiness_resouce_get(EFFECTIVE_DEVICE_TAG); conf->kafka_logger = (tfe_kafka_logger_t *)tfe_bussiness_resouce_get(KAFKA_LOGGER); if (conf->kafka_logger && !conf->kafka_logger->enable) { TFE_LOG_ERROR(conf->local_logger, "Doh sendlog ENABLE, but tfe kafka logger DISABLED."); return -1; } 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] = { 0 }; uint16_t opt_out_size; unsigned int common_direction=0; 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_AddStringToObject(common_obj, "common_stream_trace_id", opt_val); } ret = tfe_cmsg_get_value(cmsg, TFE_CMSG_COMMON_DIRECTION, (unsigned char *)&common_direction, sizeof(common_direction), &opt_out_size); if (ret==0) { cJSON_AddNumberToObject(common_obj, "common_direction", common_direction); //0:域内->域外,1:域外->域内,描述的是CLIENT_IP信息 } } 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_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_t_vsys_id", handle->kafka_logger->t_vsys_id); cJSON_AddNumberToObject(common_obj, "common_vsys_id", ctx->vsys_id); 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); if(handle->effective_device_tag) { cJSON_AddStringToObject(common_obj, "common_device_tag", handle->effective_device_tag); } 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); } } if (ctx->location_client) { cJSON_AddStringToObject(common_obj, "common_client_location", ctx->location_client); } if (ctx->location_server) { cJSON_AddStringToObject(common_obj, "common_server_location", ctx->location_server); } if (ctx->asn_client) { cJSON_AddStringToObject(common_obj, "common_client_asn", ctx->asn_client); } if (ctx->asn_server) { cJSON_AddStringToObject(common_obj, "common_server_asn", ctx->asn_server); } 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; }