// git version 5effe725 #include #include #include #include #include #include "dns.h" #define DNS_ERROR_LOG(msg) printf("%s, %s:%d", msg, __FILE__, __LINE__) ///////////////////////////////////////////////////////////////////////////////// // compress/decompress API ///////////////////////////////////////////////////////////////////////////////// static int dns_compress_rr_str(u_char *domain, int domain_len, u_char *result) { int section_len = 0; int result_pos = 1; int domain_pos = 0; if (domain_len < 0 || domain_len > DNS_MAX_NAME + 1 || '.' == domain[0] || '.' == domain[domain_len - 1]) { return -1; } while ((domain[domain_pos] != '\n') || (domain[domain_pos] != '\0')) { section_len = 0; while ((domain[domain_pos] != '.') && (domain[domain_pos] != '\n') && (domain[domain_pos] != '\0')) { result[result_pos] = domain[domain_pos]; result_pos++; domain_pos++; section_len++; } result[result_pos - section_len - 1] = section_len; if ((domain[domain_pos] == '\n') || (domain[domain_pos] == '\0')) break; result_pos++; domain_pos++; } result[result_pos] = '\0'; if (result_pos >= domain_len) { return result_pos + 1; } else { return result_pos; } return 0; } static int dns_name_compress(u_char *name, int name_len, char *payload) { int used_len = 0; u_int16_t compress_len = 0; u_char compress_name[DNS_MAX_NAME + 1] = {0}; if (name_len) { compress_len = dns_compress_rr_str(name, name_len, compress_name); if (compress_len <= 0) { DNS_ERROR_LOG("dns_compress_rr_str()"); return -1; } NS_SETLEN(compress_name, compress_len, payload, used_len); } else { NS_SET8(0, payload, used_len); } return used_len; } static int dns_name_decompress(char *msg, u_char **ptr, u_char *buf, int buflen, char *end) { u_char *p = NULL; int index = 0, len = 0; int np = 0, tot_len = 0; p = *ptr; *ptr = NULL; index = 0; np = 0; while (1) { if (0 == p[0]) { break; } if (0x0c0 == (p[0] & 0x0c0)) { if (p + 2 > (u_char *)end) return -1; len = ((p[0] & 0x03f) << 8) + p[1]; if (NULL == *ptr) { tot_len += 2; *ptr = p + 2; } p = (u_char *)msg + len; if (p > (u_char *)end) return -1; /* too many pointers. */ if (np++ > 16) return -1; continue; } len = p[0]; p++; tot_len++; if (p + len > (u_char *)end) return -1; if (index + len >= buflen - 1) return -1; memcpy(buf + index, p, len); index += len; buf[index++] = '.'; p += len; tot_len += len; } if (NULL == *ptr) { *ptr = p + 1; tot_len++; } /* * omit last '.' */ if (index > 0) { buf[index - 1] = '\0'; } else { buf[0] = '\0'; } return tot_len; } ///////////////////////////////////////////////////////////////////////////////// // get API ///////////////////////////////////////////////////////////////////////////////// static void get_dns_hdr_info(dns_hdr_t *dns_hdr, char *payload) { dns_hdr_t *tmp = ((dns_hdr_t *)payload); memset(dns_hdr, 0, sizeof(dns_hdr_t)); dns_hdr->qr = tmp->qr; dns_hdr->opcode = tmp->opcode; dns_hdr->aa = tmp->aa; dns_hdr->tc = tmp->tc; dns_hdr->rd = tmp->rd; dns_hdr->ra = tmp->ra; dns_hdr->z = tmp->z; dns_hdr->rcode = tmp->rcode; dns_hdr->id = ntohs(tmp->id); dns_hdr->qdcount = ntohs(tmp->qdcount); dns_hdr->ancount = ntohs(tmp->ancount); dns_hdr->aucount = ntohs(tmp->aucount); dns_hdr->adcount = ntohs(tmp->adcount); } static void get_rr_type_nsec3(char **ptr, nsec3_t *nsec3, char *end) { NS_GET8(nsec3->hash_algo, *ptr); NS_GET8(nsec3->flags, *ptr); NS_GET16(nsec3->iteration, *ptr); NS_GET8(nsec3->salt_len, *ptr); nsec3->salt_value = *(u_char **)ptr; *ptr += nsec3->salt_len; /* jump salt_value */ NS_GET8(nsec3->hash_len, *ptr); nsec3->next_hash_owner = *(u_char **)ptr; *ptr += nsec3->hash_len; /* jump next_hash_owner */ } // unused __attribute__((unused))static int get_rr_signer(u_char **ptr, u_char *buf, int buflen, char *end) { u_char *p = NULL; int len = 0, i = 0; p = *ptr; if (0 == p[0]) { len = strlen("Root"); memcpy(buf, "Root", len); buf[len] = '\0'; *ptr += 1; return 1; } p = *ptr; *ptr = NULL; len = 0; while (1) { len++; if (0x03 == p[0]) { p += 1; continue; } buf[i++] = p[0]; p += 1; if (0 == p[0]) { len++; p += 1; break; } } *ptr = p; buf[i] = '\0'; return len; } static void get_rr_type_rrsig(char **ptr, rrsig_t *rrsig, char *end) { NS_GET16(rrsig->type_covered, *ptr); NS_GET8(rrsig->algo, *ptr); NS_GET8(rrsig->labels, *ptr); NS_GET32(rrsig->original_ttl, *ptr); NS_GET32(rrsig->sig_expiration, *ptr); NS_GET32(rrsig->sig_inception, *ptr); NS_GET16(rrsig->key_tag, *ptr); } static int get_rr_type_wks(char **ptr, wks_t *wks, char *end) { if (*ptr + 4 > end) return -1; NS_GET32(wks->addr, *ptr); if (*ptr + 1 > end) return -1; NS_GET8(wks->protocol, *ptr); wks->bitmap = *(u_char **)ptr; return 0; } static void get_rr_type_info(char **ptr, hinfo_t *hinfo, char *end) { int len = 0; NS_GET8(hinfo->cpu_len, *ptr); len = MIN(hinfo->cpu_len, sizeof(DNS_HINFO_MAX_CPU - 1)); memcpy((char *)hinfo->cpu, *ptr, len); hinfo->cpu[len] = '\0'; *ptr += hinfo->cpu_len; hinfo->cpu_len = len; NS_GET8(hinfo->os_len, *ptr); len = MIN(hinfo->os_len, sizeof(DNS_HINFO_MAX_OS - 1)); memcpy((char *)hinfo->os, *ptr, len); hinfo->os[len] = '\0'; *ptr += hinfo->os_len; hinfo->os_len = len; } static int get_rr_type_soa(char *msg, char **ptr, soa_t *soa, char *end) { if (0 >= dns_name_decompress(msg, (u_char **)ptr, soa->mname, sizeof(soa->mname), end)) return -1; if (0 >= dns_name_decompress(msg, (u_char **)ptr, soa->rname, sizeof(soa->rname), end)) return -1; if (*ptr + 4 > end) return -1; NS_GET32(soa->serial, *ptr); if (*ptr + 4 > end) return -1; NS_GET32(soa->refresh, *ptr); if (*ptr + 4 > end) return -1; NS_GET32(soa->retry, *ptr); if (*ptr + 4 > end) return -1; NS_GET32(soa->expire, *ptr); if (*ptr + 4 > end) return -1; NS_GET32(soa->minimum, *ptr); return 0; } static int get_rr_common_field(char *msg, char **ptr, dns_rr_t *rr, char *end) { char *p = NULL; if (*ptr == NULL) { return -1; } dns_name_decompress(msg, (u_char **)ptr, rr->name, DNS_MAX_NAME + 1, end); #if 0 if(0 >= dns_name_decompress(msg, (u_char**)ptr, rr->name, DNS_MAX_NAME+1, end)) { return -1; } #endif if (*ptr == NULL || *ptr + 2 > end) { return -1; } NS_GET16(rr->type, *ptr); if (*ptr == NULL || *ptr + 2 > end) { return -1; } NS_GET16(rr->rr_class, *ptr); if (DNS_CLASS_UNKNOWN == rr->rr_class) { return -1; } if (*ptr == NULL || *ptr + 4 > end) { return -1; } NS_GET32(rr->ttl, *ptr); if (*ptr == NULL || *ptr + 2 > end) { return -1; } NS_GET16(rr->rdlength, *ptr); p = *ptr + rr->rdlength; if (*ptr == NULL || p > end) { return -1; } return 0; } static int get_dns_query_question(char *msg, char **ptr, dns_query_question_t *q, char *end) { int used_len = 0; if (0 >= dns_name_decompress(msg, (u_char **)ptr, q->qname, DNS_MAX_NAME + 1, end)) { return -1; } used_len = strlen((char *)q->qname); if (q->qname[used_len - 1] == '.') return -2; if (*ptr + 2 > end) { return -1; } NS_GET16(q->qtype, *ptr); if (*ptr + 2 > end) { return -1; } NS_GET16(q->qclass, *ptr); return 0; } static int get_one_resource_record(char *msg, char **ptr, dns_rr_t *rr, char *end) { u_int32_t len = 0, byte = 0; u_char *original_ptr = NULL; switch (rr->type) { case DNS_TYPE_CNAME: original_ptr = (u_char *)*ptr; if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.cname, DNS_MAX_NAME + 1, end)) { return 0; } break; case DNS_TYPE_HINFO: get_rr_type_info(ptr, &(rr->rdata.hinfo), end); break; case DNS_TYPE_MB: if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.mb, DNS_MAX_NAME + 1, end)) return 0; break; case DNS_TYPE_MD: if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.md, DNS_MAX_NAME + 1, end)) return 0; break; case DNS_TYPE_MF: if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.mf, DNS_MAX_NAME + 1, end)) return 0; break; case DNS_TYPE_MG: if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.mg, DNS_MAX_NAME + 1, end)) return 0; break; case DNS_TYPE_MINFO: if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.minfo.rmailbx, DNS_MAX_NAME + 1, end)) return 0; if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.minfo.emailbx, DNS_MAX_NAME + 1, end)) return 0; break; case DNS_TYPE_MR: if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.mr, DNS_MAX_NAME + 1, end)) return 0; break; case DNS_TYPE_MX: if (*ptr + 2 > end) return 0; NS_GET16(rr->rdata.mx.preference, *ptr); if (rr->rdlength - 2 < ((u_char *)*ptr)[0]) { if (rr->rdlength < 2) { *ptr += rr->rdlength; break; } len = MIN(DNS_MAX_NAME - 1, rr->rdlength - 2); /* size=1byte */ memcpy(rr->rdata.mx.exchange, *ptr, len); /* error labels */ rr->rdata.mx.exchange[len] = '\0'; *ptr += rr->rdlength - 2; } else { if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.mx.exchange, DNS_MAX_NAME + 1, end)) return 0; } break; case DNS_TYPE_NS: if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.ns, DNS_MAX_NAME + 1, end)) return 0; break; case DNS_TYPE_PTR: if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.ptr, DNS_MAX_NAME + 1, end)) return 0; break; case DNS_TYPE_SOA: original_ptr = (u_char *)*ptr; if (0 != get_rr_type_soa(msg, ptr, &(rr->rdata.soa), end)) return 0; if ((char *)original_ptr + rr->rdlength != *ptr) { *ptr = (char *)original_ptr + rr->rdlength; } break; case DNS_TYPE_A: if (*ptr + 4 > end) return 0; memcpy(rr->rdata.a, *ptr, NS_INT32SZ); (*ptr) += NS_INT32SZ; break; case DNS_TYPE_AAAA: if (*ptr + 16 > end) return -1; memcpy(rr->rdata.aaaa, *ptr, 16); (*ptr) += 16; break; case DNS_TYPE_DNAME: if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.dname, DNS_MAX_NAME + 1, end)) return 0; break; case DNS_TYPE_ISDN: memcpy(rr->rdata.isdn, *ptr, sizeof(u_char)); (*ptr) += 1; break; case DNS_TYPE_TXT: len = MIN(DNS_MAX_NAME - 1, rr->rdlength - 1); /* size=1byte */ memcpy(rr->rdata.txt.txt, *ptr + 1, len); rr->rdata.txt.size = len; *ptr += rr->rdlength; break; case DNS_TYPE_RP: if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.rp.mailbox, DNS_MAX_NAME + 1, end)) return 0; if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.rp.txt_rr, DNS_MAX_NAME + 1, end)) return 0; break; case DNS_TYPE_NULL: len = MIN(DNS_MAX_NAME - 1, rr->rdlength - 1); /* size=1byte */ memcpy(rr->rdata.null.null, *ptr + 1, len); rr->rdata.null.size = len; *ptr += rr->rdlength; break; case DNS_TYPE_WKS: if (0 != get_rr_type_wks(ptr, &(rr->rdata.wks), end)) return 0; rr->rdata.wks.size = rr->rdlength - 5; *ptr += rr->rdlength - 5; case DNS_TYPE_SRV: NS_GET16(rr->rdata.srv.priority, *ptr); NS_GET16(rr->rdata.srv.weight, *ptr); NS_GET16(rr->rdata.srv.port, *ptr); if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.srv.target, DNS_MAX_TARGET, end)) return 0; break; case DNS_TYPE_DS: case DNS_TYPE_DLV: if (*ptr + 4 > end) return 0; NS_GET16(rr->rdata.ds.key_tag, *ptr); NS_GET8(rr->rdata.ds.algo, *ptr); NS_GET8(rr->rdata.ds.digest_type, *ptr); rr->rdata.ds.digest = *(u_char **)ptr; rr->rdata.ds.digest_len = rr->rdlength - 4; *ptr += rr->rdlength - 4; break; case DNS_TYPE_RRSIG: if (*ptr + 18 > end) return 0; get_rr_type_rrsig(ptr, &(rr->rdata.rrsig), end); len = dns_name_decompress(msg, (u_char **)ptr, rr->rdata.rrsig.signer_name, DNS_MAX_SIGNER_NAME, end); if (len <= 0) { return -1; } rr->rdata.rrsig.signature = *(u_char **)ptr; rr->rdata.rrsig.signature_len = rr->rdlength - 18 - len; *ptr += rr->rdlength - 18 - len; break; case DNS_TYPE_NSEC: original_ptr = (u_char *)*ptr; len = dns_name_decompress(msg, (u_char **)ptr, rr->rdata.nsec.next_domain, DNS_MAX_OWNER, end); if (len <= 0) { return -1; } if ((original_ptr + rr->rdlength != (u_char *)*ptr) && (*ptr != NULL)) { NS_GET16(len, *ptr); byte = MIN(DNS_MAX_MAPS - 1, len); memcpy(rr->rdata.nsec.type_bit_maps, *ptr, byte); rr->rdata.nsec.type_bit_maps[byte] = '\0'; *ptr += len; rr->rdata.nsec.maps_temp_len = len; rr->rdata.nsec.maps_len = len; len = byte; byte = ((u_char *)ptr)[0]; if ((byte & 0xFF) == 0xFF || byte == 128) { *ptr += 1; /* jump 0xFF */ byte = ((u_char *)ptr)[0]; *ptr += 1; /* jump 1 byte of len */ len = MIN(DNS_MAX_MAPS - 1 - len, byte); memcpy(rr->rdata.nsec.type_bit_maps + rr->rdata.nsec.maps_len, *ptr, len); (rr->rdata.nsec.type_bit_maps + rr->rdata.nsec.maps_len)[len] = '\0'; *ptr += byte; /* jump byte */ rr->rdata.nsec.maps_len += len; } } break; case DNS_TYPE_DNSKEY: if (*ptr + 4 > end) return 0; NS_GET16(rr->rdata.dnskey.flags, *ptr); NS_GET8(rr->rdata.dnskey.protocol, *ptr); NS_GET8(rr->rdata.dnskey.algo, *ptr); rr->rdata.dnskey.public_key = *(u_char **)ptr; rr->rdata.dnskey.public_key_len = rr->rdlength - 4; /* sizeof(flags)+sizeof(protocol)+sizeof(algo) */ *ptr += rr->rdlength - 4; /* todo add log */ break; case DNS_TYPE_NSEC3: if (*ptr + 5 > end) return 0; original_ptr = (u_char *)*ptr; get_rr_type_nsec3(ptr, &(rr->rdata.nsec3), end); if ((original_ptr + rr->rdlength != (u_char *)*ptr) && (*ptr != NULL)) { NS_GET16(len, *ptr); byte = MIN(DNS_MAX_MAPS - 1, len); memcpy(rr->rdata.nsec3.type_bit_maps, *ptr, byte); rr->rdata.nsec3.type_bit_maps[byte] = '\0'; *ptr += len; rr->rdata.nsec3.maps_temp_len = byte; rr->rdata.nsec3.maps_len = byte; len = byte; byte = ((u_char *)*ptr)[0]; if ((byte & 0xFF) == 0xFF || byte == 128) { *ptr += 1; /* jump 0xFF */ byte = ((u_char *)*ptr)[0]; *ptr += 1; /* jump 1 byte of len */ len = MIN(DNS_MAX_MAPS - 1 - len, byte); memcpy(rr->rdata.nsec3.type_bit_maps + rr->rdata.nsec3.maps_len, *ptr, len); (rr->rdata.nsec3.type_bit_maps + rr->rdata.nsec3.maps_len)[len] = '\0'; *ptr += byte; /* jump byte */ rr->rdata.nsec3.maps_len += len; } } break; case DNS_TYPE_NSEC3PARAM: NS_GET8(rr->rdata.nsec3param.hash_algo, *ptr); NS_GET8(rr->rdata.nsec3param.flags, *ptr); NS_GET16(rr->rdata.nsec3param.iteration, *ptr); rr->rdata.nsec3param.salt_len = rr->rdlength - 4 - 1; *ptr += 1; rr->rdata.nsec3param.salt_value = *(u_char **)ptr; *ptr += rr->rdlength - 5; break; case DNS_TYPE_OPT: /* fail through */ case DNS_TYPE_UNKNOWN: /* fail through */ default: memcpy(rr->rdata.unknown_data, *ptr, rr->rdlength); rr->rdata.unknown_data[rr->rdlength] = '\0'; (*ptr) += rr->rdlength; // printf("No support respone type, type: %d", rr->type); break; } return 1; } static int parse_resource_record(dns_info_t *dns_info, char *payload, int payload_len, char **cur_pos) { int i = 0; dns_info->rr_count = dns_info->hdr_info.ancount + dns_info->hdr_info.adcount + dns_info->hdr_info.aucount; if (dns_info->rr_count == 0) { return 0; } dns_info->rr_count = MIN(dns_info->rr_count, MAX_RR_NUM); for (i = 0; i < dns_info->rr_count; i++) { if ((u_char *)*cur_pos >= (u_char *)payload + payload_len || (u_char *)*cur_pos < (u_char *)payload) { DNS_ERROR_LOG("parse_resource_record()"); return -1; } if (0 != get_rr_common_field(payload, cur_pos, &dns_info->rr[i], payload + payload_len)) { if (dns_info->rr[i].rr_class == DNS_CLASS_UNKNOWN) { DNS_ERROR_LOG("get_rr_common_field()"); return -1; /* error packet */ } else { DNS_ERROR_LOG("get_rr_common_field()"); dns_info->rr_count -= 1; i -= 1; continue; } } if (dns_info->rr[i].rdlength == 0) { continue; } if (get_one_resource_record(payload, cur_pos, &dns_info->rr[i], payload + payload_len) != 1) { DNS_ERROR_LOG("get_one_resource_record()"); dns_info->rr_count -= 1; i -= 1; } } assert(i == dns_info->rr_count); return 0; } ///////////////////////////////////////////////////////////////////////////////// // set API ///////////////////////////////////////////////////////////////////////////////// static void set_dns_hdr_info(dns_hdr_t *src, char *payload) { dns_hdr_t *dst = ((dns_hdr_t *)payload); memset(dst, 0, sizeof(dns_hdr_t)); dst->qr = src->qr; dst->opcode = src->opcode; dst->aa = src->aa; dst->tc = src->tc; dst->rd = src->rd; dst->ra = src->ra; dst->z = src->z; dst->rcode = src->rcode; dst->id = htons(src->id); dst->qdcount = htons(src->qdcount); // 16bits: QDCOUNT: number of questions dst->ancount = htons(src->ancount); // 16bits: ANCOUNT: number of answer resource records dst->aucount = htons(src->aucount); // 16bits: NSCOUNT: number of authority resource records dst->adcount = htons(src->adcount); // 16bits: ARCOUNT: number of additional resource records } static int set_dns_pkt_rr_header(char *payload, int payload_len, dns_rr_t *rr, int rdlength, char **rr_header_length_ptr) { int ret; int used_len = 0; if (rr->name) { ret = dns_name_compress(rr->name, strlen((char *)rr->name), payload); if (ret == -1) { return -1; } used_len += ret; } else { NS_SET16(0xc00c, payload, used_len); } NS_SET16(rr->type, payload, used_len); NS_SET16(rr->rr_class, payload, used_len); NS_SET32(rr->ttl, payload, used_len); *rr_header_length_ptr = payload + used_len; NS_SET16(rr->rdlength, payload, used_len); return used_len; } static void reset_rr_header_length(char **rr_length_ptr, int rdlength) { u_int16_t seg_16 = 0; seg_16 = htons(rdlength); memset(*rr_length_ptr, 0, sizeof(seg_16)); memcpy(*rr_length_ptr, &seg_16, sizeof(seg_16)); } ///////////////////////////////////////////////////////////////////////////////// // cheat API ///////////////////////////////////////////////////////////////////////////////// static void set_cheat_pkt_header(dns_hdr_t *dns_hdr) { dns_hdr->qr = 1; // 1bit: Response dns_hdr->opcode = 0; // 4bits: Query dns_hdr->aa = 0; // 1bit: authoritative answer dns_hdr->tc = 0; // 1bit: Not truncated dns_hdr->rd = 1; // 1bit: Recursion Desired dns_hdr->ra = 1; // 1bit: Recursion Available dns_hdr->z = 0; // 3bits: Reserved for future use: Must be zero in all queries and responses dns_hdr->rcode = 0; // 4bits: 0: No error condition dns_hdr->id = htons(dns_hdr->id); dns_hdr->qdcount = htons(dns_hdr->qdcount); // 16bits: QDCOUNT: number of questions dns_hdr->ancount = htons(dns_hdr->ancount); // 16bits: ANCOUNT: number of answer resource records dns_hdr->aucount = htons(dns_hdr->aucount); // 16bits: NSCOUNT: number of authority resource records dns_hdr->adcount = htons(dns_hdr->adcount); // 16bits: ARCOUNT: number of additional resource records } static int set_cheat_pkt_question(char *payload, dns_query_question_t *query, int query_num) { int ret; int used_len = 0; /* 只处理一个请求 */ ret = dns_name_compress(query->qname, strlen((char *)query->qname), payload); if (ret == -1) { return -1; } used_len += ret; NS_SET16(query->qtype, payload, used_len); NS_SET16(query->qclass, payload, used_len); return used_len; } // NOTE 返回值 TODO static int set_cheat_pkt_rr_pdata_type_str(char *payload, cheat_pkt_opt_t *cheat_opt) { int used_len = 0; u_int16_t q_class = 1; u_int16_t compress_len = 0; u_char compress_name[DNS_MAX_NAME + 1] = {0}; NS_SET16(0xc00c, payload, used_len); NS_SET16(cheat_opt->res_type, payload, used_len); NS_SET16(q_class, payload, used_len); NS_SET32(cheat_opt->ttl, payload, used_len); compress_len = dns_compress_rr_str(cheat_opt->res_info, cheat_opt->res_len, compress_name); if (compress_len <= 0) { DNS_ERROR_LOG("dns_compress_rr_str()"); } NS_SET16(compress_len, payload, used_len); NS_SETLEN(compress_name, compress_len, payload, used_len); return used_len; } static int set_cheat_pkt_rr_pdata_type_ip(char *payload, cheat_pkt_opt_t *cheat_opt) { int used_len = 0; u_int16_t q_class = 1; NS_SET16(0xc00c, payload, used_len); NS_SET16(cheat_opt->res_type, payload, used_len); NS_SET16(q_class, payload, used_len); NS_SET32(cheat_opt->ttl, payload, used_len); NS_SET16(cheat_opt->res_len, payload, used_len); NS_SETLEN((char *)cheat_opt->res_info, cheat_opt->res_len, payload, used_len); return used_len; } ///////////////////////////////////////////////////////////////////////////////// // public API ///////////////////////////////////////////////////////////////////////////////// dns_info_t *dns_new(void) { dns_info_t *dns_info = (dns_info_t *)calloc(sizeof(dns_info_t), 1); return dns_info; } void dns_free(dns_info_t *dns_info) { if (dns_info) { free(dns_info); dns_info = NULL; } } int dns_parser(dns_info_t *dns_info, char *in_buff, int buff_len) { char *cur_pos = NULL; get_dns_hdr_info(&dns_info->hdr_info, in_buff); cur_pos = in_buff + sizeof(dns_hdr_t); if (0 == dns_info->hdr_info.qdcount || dns_info->hdr_info.qdcount > 1) { return -1; } if (0 != get_dns_query_question(in_buff, &cur_pos, &(dns_info->query_question), in_buff + buff_len)) { DNS_ERROR_LOG("get_dns_query_question()"); return -1; } if (parse_resource_record(dns_info, in_buff, buff_len, &cur_pos) != 0) { DNS_ERROR_LOG("parse_resource_record()"); return -1; } return 0; } int dns_package(dns_info_t *dns_info, char *out_buff, int buff_size) { char *rr_header_length_ptr = NULL; int i = 0; int rr_count = 0; dns_hdr_t *dns_hdr = &dns_info->hdr_info; int ret = 0; int used_len = 0; dns_rr_t *rr; set_dns_hdr_info(dns_hdr, out_buff); used_len += sizeof(dns_hdr_t); ret = set_cheat_pkt_question(out_buff + used_len, &dns_info->query_question, dns_hdr->qdcount); if (ret < 0) { return -1; } used_len += ret; rr_count = dns_hdr->adcount + dns_hdr->ancount + dns_hdr->aucount; for (i = 0; i < rr_count; i++) { rr = &dns_info->rr[i]; ret = set_dns_pkt_rr_header(out_buff + used_len, buff_size, rr, 0, &rr_header_length_ptr); if (ret == -1) { return -1; } used_len += ret; if (rr->rdlength == 0) { continue; } int rr_header_used_len = used_len; switch (rr->type) { case DNS_TYPE_A: NS_SETLEN(rr->rdata.a, strlen((char *)rr->rdata.a), out_buff, used_len); break; case DNS_TYPE_AAAA: NS_SETLEN(rr->rdata.aaaa, 16, out_buff, used_len); break; case DNS_TYPE_CNAME: ret = dns_name_compress(rr->rdata.cname, strlen((char *)rr->rdata.cname), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; break; case DNS_TYPE_MB: ret = dns_name_compress(rr->rdata.mb, strlen((char *)rr->rdata.mb), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; break; case DNS_TYPE_MD: ret = dns_name_compress(rr->rdata.md, strlen((char *)rr->rdata.md), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; break; case DNS_TYPE_MF: ret = dns_name_compress(rr->rdata.mf, strlen((char *)rr->rdata.mf), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; break; case DNS_TYPE_MG: ret = dns_name_compress(rr->rdata.mg, strlen((char *)rr->rdata.mg), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; break; case DNS_TYPE_MINFO: ret = dns_name_compress(rr->rdata.minfo.rmailbx, strlen((char *)rr->rdata.minfo.rmailbx), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; ret = dns_name_compress(rr->rdata.minfo.emailbx, strlen((char *)rr->rdata.minfo.emailbx), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; break; case DNS_TYPE_MR: ret = dns_name_compress(rr->rdata.mr, strlen((char *)rr->rdata.mr), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; break; case DNS_TYPE_MX: NS_SET16(rr->rdata.mx.preference, out_buff, used_len); ret = dns_name_compress(rr->rdata.mx.exchange, strlen((char *)rr->rdata.mx.exchange), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; break; case DNS_TYPE_NS: ret = dns_name_compress(rr->rdata.ns, strlen((char *)rr->rdata.ns), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; break; case DNS_TYPE_PTR: ret = dns_name_compress(rr->rdata.ptr, strlen((char *)rr->rdata.ptr), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; break; case DNS_TYPE_SOA: ret = dns_name_compress(rr->rdata.soa.mname, strlen((char *)rr->rdata.soa.mname), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; ret = dns_name_compress(rr->rdata.soa.rname, strlen((char *)rr->rdata.soa.rname), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; NS_SET32(rr->rdata.soa.serial, out_buff, used_len); NS_SET32(rr->rdata.soa.refresh, out_buff, used_len); NS_SET32(rr->rdata.soa.retry, out_buff, used_len); NS_SET32(rr->rdata.soa.expire, out_buff, used_len); NS_SET32(rr->rdata.soa.minimum, out_buff, used_len); break; case DNS_TYPE_DNAME: ret = dns_name_compress(rr->rdata.dname, strlen((char *)rr->rdata.dname), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; break; case DNS_TYPE_ISDN: NS_SETLEN(rr->rdata.isdn, strlen((char *)rr->rdata.isdn), out_buff, used_len); break; case DNS_TYPE_RP: ret = dns_name_compress(rr->rdata.rp.mailbox, strlen((char *)rr->rdata.rp.mailbox), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; ret = dns_name_compress(rr->rdata.rp.txt_rr, strlen((char *)rr->rdata.rp.txt_rr), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; break; case DNS_TYPE_SRV: NS_SET16(rr->rdata.srv.priority, out_buff, used_len); NS_SET16(rr->rdata.srv.weight, out_buff, used_len); NS_SET16(rr->rdata.srv.port, out_buff, used_len); ret = dns_name_compress(rr->rdata.srv.target, strlen((char *)rr->rdata.srv.target), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; break; case DNS_TYPE_RRSIG: NS_SET16(rr->rdata.rrsig.type_covered, out_buff, used_len); NS_SET8(rr->rdata.rrsig.algo, out_buff, used_len); NS_SET8(rr->rdata.rrsig.labels, out_buff, used_len); NS_SET32(rr->rdata.rrsig.original_ttl, out_buff, used_len); NS_SET32(rr->rdata.rrsig.sig_expiration, out_buff, used_len); NS_SET32(rr->rdata.rrsig.sig_inception, out_buff, used_len); NS_SET16(rr->rdata.rrsig.key_tag, out_buff, used_len); ret = dns_name_compress(rr->rdata.rrsig.signer_name, strlen((char *)rr->rdata.rrsig.signer_name), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; NS_SETLEN(rr->rdata.rrsig.signature, rr->rdata.rrsig.signature_len, out_buff, used_len); break; case DNS_TYPE_DNSKEY: NS_SET16(rr->rdata.dnskey.flags, out_buff, used_len); NS_SET8(rr->rdata.dnskey.protocol, out_buff, used_len); NS_SET8(rr->rdata.dnskey.algo, out_buff, used_len); NS_SETLEN(rr->rdata.dnskey.public_key, rr->rdata.dnskey.public_key_len, out_buff, used_len); break; case DNS_TYPE_NSEC3PARAM: NS_SET8(rr->rdata.nsec3param.hash_algo, out_buff, used_len); NS_SET8(rr->rdata.nsec3param.flags, out_buff, used_len); NS_SET16(rr->rdata.nsec3param.iteration, out_buff, used_len); NS_SET8(rr->rdata.nsec3param.salt_len, out_buff, used_len); NS_SETLEN(rr->rdata.nsec3param.salt_value, rr->rdata.nsec3param.salt_len, out_buff, used_len); break; case DNS_TYPE_DS: /* fail through */ case DNS_TYPE_DLV: NS_SET16(rr->rdata.ds.key_tag, out_buff, used_len); NS_SET8(rr->rdata.ds.algo, out_buff, used_len); NS_SET8(rr->rdata.ds.digest_type, out_buff, used_len); NS_SETLEN(rr->rdata.ds.digest, rr->rdata.ds.digest_len, out_buff, used_len); break; case DNS_TYPE_TXT: NS_SET8(rr->rdata.txt.size, out_buff, used_len); NS_SETLEN(rr->rdata.txt.txt, rr->rdata.txt.size, out_buff, used_len); break; case DNS_TYPE_NULL: NS_SET8(rr->rdata.null.size, out_buff, used_len); NS_SETLEN(rr->rdata.null.null, rr->rdata.null.size, out_buff, used_len); break; case DNS_TYPE_WKS: NS_SET32(rr->rdata.wks.addr, out_buff, used_len); NS_SET8(rr->rdata.wks.protocol, out_buff, used_len); NS_SETLEN(rr->rdata.wks.bitmap, rr->rdata.wks.size, out_buff, used_len); break; case DNS_TYPE_HINFO: NS_SET8(rr->rdata.hinfo.cpu_len, out_buff, used_len); NS_SETLEN(rr->rdata.hinfo.cpu, rr->rdata.hinfo.cpu_len, out_buff, used_len); NS_SET8(rr->rdata.hinfo.os_len, out_buff, used_len); NS_SETLEN(rr->rdata.hinfo.os, rr->rdata.hinfo.os_len, out_buff, used_len); break; case DNS_TYPE_NSEC: ret = dns_name_compress(rr->rdata.nsec.next_domain, strlen((char *)rr->rdata.nsec.next_domain), out_buff + used_len); if (ret == -1) { return -1; } used_len += ret; if (rr->rdata.nsec.maps_len) { NS_SET16(rr->rdata.nsec.maps_temp_len, out_buff, used_len); NS_SETLEN(rr->rdata.nsec.type_bit_maps, rr->rdata.nsec.maps_temp_len, out_buff, used_len); if (rr->rdata.nsec.maps_len > rr->rdata.nsec.maps_temp_len) { NS_SET8(0xFF, out_buff, used_len); NS_SET8(rr->rdata.nsec.maps_len - rr->rdata.nsec.maps_temp_len, out_buff, used_len); NS_SETLEN(rr->rdata.nsec.type_bit_maps + rr->rdata.nsec.maps_temp_len, rr->rdata.nsec.maps_len - rr->rdata.nsec.maps_temp_len, out_buff, used_len); } } break; case DNS_TYPE_NSEC3: NS_SET8(rr->rdata.nsec3.hash_algo, out_buff, used_len); NS_SET8(rr->rdata.nsec3.flags, out_buff, used_len); NS_SET16(rr->rdata.nsec3.iteration, out_buff, used_len); NS_SET8(rr->rdata.nsec3.salt_len, out_buff, used_len); NS_SETLEN(rr->rdata.nsec3.salt_value, rr->rdata.nsec3.salt_len, out_buff, used_len); NS_SET8(rr->rdata.nsec3.hash_len, out_buff, used_len); NS_SETLEN(rr->rdata.nsec3.next_hash_owner, rr->rdata.nsec3.hash_len, out_buff, used_len); if (rr->rdata.nsec3.maps_len) { NS_SET16(rr->rdata.nsec3.maps_temp_len, out_buff, used_len); NS_SETLEN(rr->rdata.nsec3.type_bit_maps, rr->rdata.nsec3.maps_temp_len, out_buff, used_len); if (rr->rdata.nsec3.maps_len > rr->rdata.nsec3.maps_temp_len) { NS_SET8(0xFF, out_buff, used_len); NS_SET8(rr->rdata.nsec3.maps_len - rr->rdata.nsec3.maps_temp_len, out_buff, used_len); NS_SETLEN(rr->rdata.nsec3.type_bit_maps + rr->rdata.nsec3.maps_temp_len, rr->rdata.nsec3.maps_len - rr->rdata.nsec3.maps_temp_len, out_buff, used_len); } } break; case DNS_TYPE_OPT: /* fail through */ case DNS_TYPE_UNKNOWN: /* fail through */ default: NS_SETLEN(rr->rdata.unknown_data, rr->rdlength, out_buff, used_len); break; } if (used_len - rr_header_used_len != rr->rdlength) { reset_rr_header_length(&rr_header_length_ptr, used_len - rr_header_used_len); } if (used_len > buff_size) { // printf("dns_package buff to short, %s, %d\n", __FILE__, __LINE__); return -1; } } return used_len; } int dns_cheat_response(dns_info_t *dns_info, cheat_pkt_opt_t *cheat_opt, int cheat_opt_num, char *out_buff, int buff_size) { int i = 0; dns_hdr_t *dns_hdr = NULL; int ret = 0, used_len = 0; memset(out_buff, 0, buff_size); dns_hdr = (dns_hdr_t *)out_buff; dns_hdr->id = dns_info->hdr_info.id; dns_hdr->qdcount = 1; dns_hdr->ancount = cheat_opt_num; used_len += sizeof(dns_hdr_t); ret = set_cheat_pkt_question(out_buff + used_len, &dns_info->query_question, dns_hdr->qdcount); if (ret < 0) { return -1; } used_len += ret; for (i = 0; i < cheat_opt_num; i++) { switch (cheat_opt[i].res_type) { case DNS_TYPE_A: case DNS_TYPE_AAAA: used_len += set_cheat_pkt_rr_pdata_type_ip(out_buff + used_len, &cheat_opt[i]); break; case DNS_TYPE_CNAME: case DNS_TYPE_NS: used_len += set_cheat_pkt_rr_pdata_type_str(out_buff + used_len, &cheat_opt[i]); break; } if (used_len > buff_size) { return -2; } } set_cheat_pkt_header(dns_hdr); return used_len; }