static uchar * ixfr_gobble_authority_rr(TASK *t, uchar *query, size_t querylen, uchar *current, IARR *rr){ uchar * src = current; int rdlength = 0; task_error_t errcode = TASK_FAILED; if (!(IARR_NAME(rr) = name_unencode2(query, querylen, &src, &errcode))) { formerr(t, DNS_RCODE_FORMERR, errcode, NULL); return NULL; } DNS_GET16(rr->type, src); DNS_GET16(rr->class, src); DNS_GET32(rr->ttl, src); DNS_GET16(rdlength, src); if (!(IARR_MNAME(rr) = name_unencode2(query, querylen, &src, &errcode))) { formerr(t, DNS_RCODE_FORMERR, errcode, NULL); return NULL; } if (!(IARR_RNAME(rr) = name_unencode2(query, querylen, &src, &errcode))) { formerr(t, DNS_RCODE_FORMERR, errcode, NULL); return NULL; } DNS_GET32(rr->serial, src); DNS_GET32(rr->refresh, src); DNS_GET32(rr->retry, src); DNS_GET32(rr->expire, src); DNS_GET32(rr->minimum, src); return src; }
//process a segment of data int passer_related_data(struct sockinfo *si, mbuf_type *mbuf, struct author *author) { uchar *buf = si->buf, *tail = NULL; int stype = 0/*, ret*//*, seg*/; struct rbtree *rbt; int datalen = 0; ushort n; struct hlpp hlp; dnsheader *hdr = (dnsheader *) buf; tail = buf + sizeof(dnsheader) + si->lowerdomain->label_len[0]; //domain len /* type = ntohs(*(ushort *) tail); */ /* class = ntohs(*(ushort *) (tail + 2)); */ tail = tail + 4; datalen = si->buflen; rbt = author->s->ttlexp; n = ntohs(hdr->ancount); hlp.stype = &stype; hlp.ds = author->s->datasets; hlp.rbt = rbt; hlp.buf = buf; hlp.datalen = datalen; hlp.tmpbuf = mbuf->tempbuffer; hlp.domainbuf = mbuf->tdbuffer; hlp.dmbuf = mbuf->dmbuffer; if (n > 0) { hlp.section = AN_SECTION; tail = process_rdata(&hlp, tail, n); if (tail == NULL) return -1; } n = ntohs(hdr->nscount); if (n > 0) { hlp.section = NS_SECTION; tail = process_rdata(&hlp, tail, n); if (tail == NULL) return -1; } n = ntohs(hdr->arcount); if (n > 0) { // ignore OPT(EDNS0 option) if ((tail + OPT_LEN + OPT_RDATA) <= (buf + datalen)) { uint8_t opt_owner = *(uint8_t *)tail; uint16_t opt_type = *(uint16_t *)(tail + 1); if (opt_owner == 0 && DNS_GET16(opt_type) == OPT) { return stype; } } hlp.section = AR_SECTION; tail = process_rdata(&hlp, tail, n); if (tail == NULL) return -1; } return stype; }
uchar * fill_header_in_msg(struct setheader * sh) { uchar *itor = sh->itor; dnsheader *hdr = (dnsheader *) (sh->itor); qdns *qd; hdr->flags = 0; hdr->flags = SET_QR_R(hdr->flags); hdr->flags = SET_RA(hdr->flags); hdr->flags = DNS_GET16(hdr->flags); hdr->ancount = DNS_GET16(sh->an); hdr->nscount = DNS_GET16(sh->ns); hdr->arcount = 0; //DNS_GET16(0); itor += sizeof(dnsheader); itor = itor + sh->dlen; qd = (qdns *) itor; qd->type = DNS_GET16(sh->type); qd->dclass = DNS_GET16(CLASS_IN); itor += sizeof(qdns); return itor; }
taskexec_t ixfr(TASK * t, datasection_t section, dns_qtype_t qtype, char *fqdn, int truncateonly) { MYDNS_SOA *soa = NULL; uchar *query = (uchar*)t->query; int querylen = t->len; uchar *src = query + DNS_HEADERSIZE; IQ *q = NULL; task_error_t errcode = 0; #if DEBUG_ENABLED && DEBUG_IXFR DebugX("ixfr", 1, "%s: ixfr(%s, %s, \"%s\", %d)", desctask(t), resolve_datasection_str[section], mydns_qtype_str(qtype), fqdn, truncateonly); #endif if (!dns_ixfr_enabled) { dnserror(t, DNS_RCODE_REFUSED, ERR_IXFR_NOT_ENABLED); return (TASK_FAILED); } /* * Authority section contains the SOA record for the client's version of the zone * only trust the serial number. */ if (mydns_soa_load(sql, &soa, fqdn) < 0) { dnserror(t, DNS_RCODE_SERVFAIL, ERR_DB_ERROR); return (TASK_FAILED); } if (!soa) { dnserror(t, DNS_RCODE_REFUSED, ERR_ZONE_NOT_FOUND); return (TASK_FAILED); } #if DEBUG_ENABLED && DEBUG_IXFR DebugX("ixfr", 1, _("%s: DNS IXFR: SOA id %u"), desctask(t), soa->id); DebugX("ixfr", 1, _("%s: DNS IXFR: QDCOUNT=%d (Query)"), desctask(t), t->qdcount); DebugX("ixfr", 1, _("%s: DNS IXFR: ANCOUNT=%d (Answer)"), desctask(t), t->ancount); DebugX("ixfr", 1, _("%s: DNS IXFR: AUCOUNT=%d (Authority)"), desctask(t), t->nscount); DebugX("ixfr", 1, _("%s: DNS IXFR: ADCOUNT=%d (Additional data)"), desctask(t), t->arcount); #endif if (!t->nscount) return formerr(t, DNS_RCODE_FORMERR, ERR_NO_AUTHORITY, _("ixfr query contains no authority data")); if (t->nscount != 1) return formerr(t, DNS_RCODE_FORMERR, ERR_MULTI_AUTHORITY, _("ixfr query contains multiple authority records")); if (!t->qdcount) return formerr(t, DNS_RCODE_FORMERR, ERR_NO_QUESTION, _("ixfr query does not contain question")); if (t->qdcount != 1) return formerr(t, DNS_RCODE_FORMERR, ERR_MULTI_QUESTIONS, _("ixfr query contains multiple questions")); if (t->ancount || t->arcount) return formerr(t, DNS_RCODE_FORMERR, ERR_MALFORMED_REQUEST, _("ixfr query has answer or additional data")); q = allocate_iq(); if (!(IQ_NAME(q) = name_unencode2(query, querylen, &src, &errcode))) { free_iq(q); return formerr(t, DNS_RCODE_FORMERR, errcode, NULL); } DNS_GET16(q->type, src); DNS_GET16(q->class, src); if (!(src = ixfr_gobble_authority_rr(t, query, querylen, src, &q->IR))) { free_iq(q); return (TASK_FAILED); } /* Get the serial number from the RR record in the authority section */ #if DEBUG_ENABLED && DEBUG_IXFR DebugX("ixfr", 1, _("%s: DNS IXFR Question[zone %s qclass %s qtype %s]" " Authority[zone %s qclass %s qtype %s ttl %u " "mname %s rname %s serial %u refresh %u retry %u expire %u minimum %u]"), desctask(t), q->name, mydns_class_str(q->class), mydns_qtype_str(q->type), q->IR.name, mydns_class_str(q->IR.class), mydns_qtype_str(q->IR.type), q->IR.ttl, q->IR.mname, q->IR.rname, q->IR.serial, q->IR.refresh, q->IR.retry, q->IR.expire, q->IR.minimum); #endif /* * As per RFC 1995 we have 3 options for a response if a delta exists. * * We can send a full zone transfer if it will fit in a UDP packet and is smaller * than sending deltas * * We can send a delta transfer if it will fit into a single UDP packet and we can calculate * one for the difference between the client and the current serial * * We can send a packet with a single SOA record for the latest SOA. This will force the client * to initiate an AXFR. * * We can calculate the size of the response by either building both messages * or by an estimation technique. In either case we need to look at the data. * * I have chosen to check for altered records within the database first. * * First check is to make sure that the serial held by the client is not the current one * * Next check to see if out incremental data for the transition from client serial * to current serial has not expired. * * Then retrieve the updated records between the client serial and the latest serial. * and retrieve the entire zone ... a record count is the first check. * * If the number of delta records is larger than the number of zone records then send the zone * * Calculate the size of the variable parts of the record and compare. * We assume that name encoding will have an equal effect on the data. * So having chosen to send either the zone or the deltas construct the packet. * * Check that the packet has not overflowed the UDP limit and send. If it has * that abandon the packet and send one containing just the latest SOA. * */ if (soa->serial == q->IR.serial) { /* Tell the client to do no zone transfer */ rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin); t->sort_level++; } else { /* Do we have incremental information in the database */ if (!truncateonly && mydns_rr_use_active && mydns_rr_use_stamp && mydns_rr_use_serial) { /* We can do incrementals */ /* Need to send an IXFR if available */ /* * Work out when the client SOA came into being */ MYDNS_RR *ThisRR = NULL, *rr = NULL; char *deltafilter = NULL; int deletecount, activecount, zonesize; size_t deltasize, fullsize; /* For very large zones we do not want to load all of the records just to give up */ sql_build_query(&deltafilter, "serial > %u", q->IR.serial); /* * Compare counts of changes from full zone data * ... assumes records are about the same size * approximate zone size by 2 * deleted count === actual number of delta records */ deletecount = mydns_rr_count_deleted_filtered(sql, soa->id, DNS_QTYPE_ANY, NULL, soa->origin, deltafilter); activecount = mydns_rr_count_active_filtered(sql, soa->id, DNS_QTYPE_ANY, NULL, soa->origin, deltafilter); zonesize = mydns_rr_count_active(sql, soa->id, DNS_QTYPE_ANY, NULL, soa->origin); deltasize = deletecount + activecount + 4; fullsize = zonesize + 2; if ((deletecount < 0) || (activecount < 0) || (zonesize < 0)) { RELEASE(deltafilter); dnserror(t, DNS_RCODE_SERVFAIL, ERR_DB_ERROR); return (TASK_FAILED); } if (deletecount || activecount) { if (deltasize >= fullsize) { /* Send a full zone transfer */ /* Current Serial first */ rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin); t->sort_level++; if (mydns_rr_load_active(sql, &ThisRR, soa->id, DNS_QTYPE_ANY, NULL, soa->origin) == 0) { for (rr = ThisRR; rr; rr = rr->next) { char *name = mydns_rr_append_origin(MYDNS_RR_NAME(rr), soa->origin); rrlist_add(t, ANSWER, DNS_RRTYPE_RR, (void *)rr, name); if (name != MYDNS_RR_NAME(rr)) RELEASE(name); } t->sort_level++; mydns_rr_free(ThisRR); rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin); t->sort_level++; } } else { int latest_serial = soa->serial; rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin); t->sort_level++; soa->serial = q->IR.serial; rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin); t->sort_level++; soa->serial = latest_serial; if (mydns_rr_load_deleted_filtered(sql, &ThisRR, soa->id, DNS_QTYPE_ANY, NULL, soa->origin, deltafilter) == 0) { for (rr = ThisRR; rr; rr = rr->next) { char *name = mydns_rr_append_origin(MYDNS_RR_NAME(rr), soa->origin); rrlist_add(t, ANSWER, DNS_RRTYPE_RR, (void *)rr, name); if (name != MYDNS_RR_NAME(rr)) RELEASE(name); } t->sort_level++; mydns_rr_free(ThisRR); } rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin); t->sort_level++; if (mydns_rr_load_active_filtered(sql, &ThisRR, soa->id, DNS_QTYPE_ANY, NULL, soa->origin, deltafilter) == 0) { for (rr = ThisRR; rr; rr = rr->next) { char *name = mydns_rr_append_origin(MYDNS_RR_NAME(rr), soa->origin); rrlist_add(t, ANSWER, DNS_RRTYPE_RR, (void *)rr, name); if (name != MYDNS_RR_NAME(rr)) RELEASE(name); } t->sort_level++; mydns_rr_free(ThisRR); rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin); t->sort_level++; } RELEASE(deltafilter); } goto FINISHEDIXFR; } } } /* Tell the client to do a full zone transfer or not at all */ rrlist_add(t, ANSWER, DNS_RRTYPE_SOA, (void *)soa, soa->origin); t->sort_level++; FINISHEDIXFR: mydns_soa_free(soa); free_iq(q); t->hdr.aa = 1; return (TASK_EXECUTED); }
//databuffer format //type.mvalue.data.type.mvalue.data... int // write_back_to_client(uchar * td, enum rrtype otype, uint8_t level, ushort id, int dlen, write_back_to_client(mbuf_type *mbuf, uchar * fr, int vlen) { struct setheader sh = { 0 }; //data in dns header int main_val = 0, dnslen = 0; uchar *msg = mbuf->buf, type; //if bigger, use TCP uchar *from = fr, *to = msg; struct mvalue *mv = NULL; int jump = 0; uint16_t temp = 0; struct hlpc hlp[100]; //p domians to compression hlp[0].name = mbuf->td; hlp[0].off = sizeof(dnsheader); hlp[0].level = mbuf->lowerdomain.label_count; hlp[0].ref = -1; hlp[0].mt = 0; hlp[0].len = mbuf->dlen; if (mbuf->dlen == 2) // root jump = sizeof(dnsheader) + 1 + sizeof(qdns); else jump = sizeof(dnsheader) + mbuf->dlen + sizeof(qdns); to = to + jump; while (vlen > 1) //vlen include type.mvalue.data. { type = from[0]; mv = (struct mvalue *)(from + 1); to = fill_rrset_in_msg(hlp, from, to, &main_val, msg); if (to == NULL) return -1; // *to = 0; vlen = vlen - 1 - mv->len - sizeof(struct mvalue); sh.an += mv->num; from = from + mv->len + 1 + sizeof(struct mvalue); // type.mv.len. /** * if previous record is CNAME(query type can't be ANY, that we don't support now), * next A & AAAA & CNAME use previous domain name (cname name). * We duplicate here to make it possible to refer previous one. */ if (type == CNAME && vlen > 1 /*&& mbuf->qtype != ANY*/ && (from[0] == A || from[0] == AAAA || from[0] == CNAME)) { main_val++; hlp[main_val].name = hlp[main_val - 1].name; hlp[main_val].off = hlp[main_val - 1].off; hlp[main_val].level = hlp[main_val - 1].level; hlp[main_val].len = hlp[main_val - 1].len; hlp[main_val].ref = -1; hlp[main_val].mt = 0; } } sh.itor = msg; sh.dlen = (mbuf->dlen == 2) ? 1: (mbuf->dlen); sh.od = mbuf->td; sh.id = mbuf->id; sh.type = mbuf->qtype; fill_header_in_msg(&sh); dnslen = to - msg; mbuf->buflen = dnslen; mbuf->addr = &(mbuf->caddr); if (mbuf->socktype == UDP) { if (dnslen > MAX_UDP_SIZE) send_tc_to_client(mbuf); else udp_write_info(mbuf, 0); //ignore send error } else { temp = DNS_GET16(dnslen); memcpy(msg - 2, &temp, sizeof(uint16_t)); mbuf->buflen = dnslen + 2; mbuf->buf = msg - 2; tcp_write_info(mbuf, 0); } //////////////////////////////////////////////////////////// //key, val, vallen, ttl offset //if now + TTL_UPDATE > ttl //return /* ret = transfer_record_to_msg(msgto, td, msg + 2, dnslen, ttloff); */ /* if (ret < 0) */ /* return -1; */ return 0; }
int make_dns_query(char *buf, int query_len, time_t *ttl, int *Anum) { struct sockaddr_in addr; int sockfd = -1; struct timeval timeout = { RETRANS_INTERVAL, 0 }; int i, ret = -1; int addrlen, send_len, result_len; int try_num = 0; dns_head_type *dns_head; #ifdef WIN32 WSADATA wsa; WSAStartup(MAKEWORD(2, 2), &wsa); #endif addr.sin_family = AF_INET; //addr.sin_addr.s_addr = inet_addr(PUBLIC_DNS_DEFAULT_SERVER); inet_pton(AF_INET, PUBLIC_DNS_DEFAULT_SERVER, &(addr.sin_addr.s_addr)); addr.sin_port = htons((uint16_t)PUBLIC_DNS_DEFAULT_PORT); sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (-1 == sockfd) { fprintf(stderr, "socket error\n"); goto clear; } ret = wait_writable(sockfd, timeout); if (ret != 0) { fprintf(stderr, "wait writable timeout\n"); goto clear; } while (try_num++ <= RETRANS_TRY_NUM) { send_len = sendto(sockfd, buf, query_len, 0, (struct sockaddr*)&addr, sizeof(struct sockaddr)); if (send_len != query_len) { fprintf(stderr, "sendto dns query failed\n"); ret = -1; goto clear; } ret = wait_readable(sockfd, timeout); if (ret == 0) { break; } } if (try_num > RETRANS_TRY_NUM) { fprintf(stderr, "dns query failed over try num\n"); ret = -1; goto clear; } addrlen = sizeof(struct sockaddr); result_len = recvfrom(sockfd, buf, DNS_DEFAULT_DATA_SIZE, 0/*MSG_WAITALL*/, (struct sockaddr *)&addr, (socklen_t*)&addrlen); if (result_len <= 0) { fprintf(stderr, "receve dns response failed\n"); ret = -1; goto clear; } //只支持A记录 dns_head = (dns_head_type *)buf; int off = 0; int num = DNS_GET16(dns_head->numA); for (i = 0; i < num; i++) { char *result_set = buf + query_len + off; response *rp = (response *)(result_set + 2); //2 bytes' offsets uint16_t type = DNS_GET16(rp->type); *ttl = DNS_GET32(rp->ttl); //解析A记录 if (TYPE_A == type) { memcpy(buf + (*Anum) * 4, (char *)(rp + 1), 4); (*Anum)++; off += (2 + sizeof(response) + 4); } else if (TYPE_CNAME == type) { //如果是CNAME记录则直接查找下一条记录 off += (2 + sizeof(response) + DNS_GET16(rp->length)); } else { //其他类型不支持 goto clear; } } ret = 0; clear: if (sockfd != -1) { #ifdef WIN32 closesocket(sockfd); WSACleanup(); #else close(sockfd); #endif } return ret; }
//databuffer format //type.mvalue.data.type.mvalue.data... int // write_back_to_client(uchar * td, enum rrtype otype, uint8_t level, ushort id, int dlen, write_back_to_client(mbuf_type *mbuf, uchar * fr, int vlen) { struct setheader sh = { 0 }; //data in dns header int main_val = 0, dnslen = 0; uchar *msg = mbuf->buf, type; //if bigger, use TCP uchar *from = fr, *to = msg; struct mvalue *mv = NULL; int jump = 0; uint16_t temp = 0; struct hlpc hlp[100]; //p domians to compression hlp[0].name = mbuf->td; hlp[0].off = sizeof(dnsheader); hlp[0].level = mbuf->lowerdomain.label_count; hlp[0].ref = -1; hlp[0].mt = 0; hlp[0].len = mbuf->dlen; jump = sizeof(dnsheader) + mbuf->dlen + sizeof(qdns); to = to + jump; while (vlen > 1) //vlen include type.mvalue.data. { type = from[0]; mv = (struct mvalue *)(from + 1); to = fill_rrset_in_msg(hlp, from, to, main_val, msg); if (to == NULL) return -1; // *to = 0; vlen = vlen - 1 - mv->len - sizeof(struct mvalue); sh.an += mv->num; if (type == CNAME) //cname must be 1 main_val++; //no all rdata is the cname's from = from + mv->len + 1 + sizeof(struct mvalue); // type.mv.len. } sh.itor = msg; sh.dlen = mbuf->dlen; sh.od = mbuf->td; sh.id = mbuf->id; sh.type = mbuf->qtype; fill_header_in_msg(&sh); dnslen = to - msg; mbuf->buflen = dnslen; mbuf->addr = &(mbuf->caddr); if (mbuf->socktype == UDP) { if (dnslen > MAX_UDP_SIZE) send_tc_to_client(mbuf); else udp_write_info(mbuf, 0); //ignore send error } else { temp = DNS_GET16(dnslen); memcpy(msg - 2, &temp, sizeof(uint16_t)); mbuf->buflen = dnslen + 2; mbuf->buf = msg - 2; tcp_write_info(mbuf, 0); } //////////////////////////////////////////////////////////// //key, val, vallen, ttl offset //if now + TTL_UPDATE > ttl //return /* ret = transfer_record_to_msg(msgto, td, msg + 2, dnslen, ttloff); */ /* if (ret < 0) */ /* return -1; */ return 0; }