static int deny_refuse(struct comm_point* c, enum acl_access acl, enum acl_access deny, enum acl_access refuse, struct worker* worker, struct comm_reply* repinfo) { if(acl == deny) { comm_point_drop_reply(repinfo); if(worker->stats.extended) worker->stats.unwanted_queries++; return 0; } else if(acl == refuse) { log_addr(VERB_ALGO, "refused query from", &repinfo->addr, repinfo->addrlen); log_buf(VERB_ALGO, "refuse", c->buffer); if(worker->stats.extended) worker->stats.unwanted_queries++; if(worker_check_request(c->buffer, worker) == -1) { comm_point_drop_reply(repinfo); return 0; /* discard this */ } sldns_buffer_set_limit(c->buffer, LDNS_HEADER_SIZE); sldns_buffer_write_at(c->buffer, 4, (uint8_t*)"\0\0\0\0\0\0\0\0", 8); LDNS_QR_SET(sldns_buffer_begin(c->buffer)); LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), LDNS_RCODE_REFUSED); return 1; } return -1; }
/** convert data from return_msg into the data buffer */ static int prep_data(struct module_qstate* qstate, struct sldns_buffer* buf) { uint64_t timestamp, expiry; size_t oldlim; struct edns_data edns; memset(&edns, 0, sizeof(edns)); edns.edns_present = 1; edns.bits = EDNS_DO; edns.ext_rcode = 0; edns.edns_version = EDNS_ADVERTISED_VERSION; edns.udp_size = EDNS_ADVERTISED_SIZE; if(!qstate->return_msg || !qstate->return_msg->rep) return 0; /* We don't store the reply if its TTL is 0 unless serve-expired is * enabled. Such a reply won't be reusable and simply be a waste for * the backend. It's also compatible with the default behavior of * dns_cache_store_msg(). */ if(qstate->return_msg->rep->ttl == 0 && !qstate->env->cfg->serve_expired) return 0; if(verbosity >= VERB_ALGO) log_dns_msg("cachedb encoding", &qstate->return_msg->qinfo, qstate->return_msg->rep); if(!reply_info_answer_encode(&qstate->return_msg->qinfo, qstate->return_msg->rep, 0, qstate->query_flags, buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0)) return 0; /* TTLs in the return_msg are relative to time(0) so we have to * store that, we also store the smallest ttl in the packet+time(0) * as the packet expiry time */ /* qstate->return_msg->rep->ttl contains that relative shortest ttl */ timestamp = (uint64_t)*qstate->env->now; expiry = timestamp + (uint64_t)qstate->return_msg->rep->ttl; timestamp = htobe64(timestamp); expiry = htobe64(expiry); oldlim = sldns_buffer_limit(buf); if(oldlim + sizeof(timestamp)+sizeof(expiry) >= sldns_buffer_capacity(buf)) return 0; /* doesn't fit. */ sldns_buffer_set_limit(buf, oldlim + sizeof(timestamp)+sizeof(expiry)); sldns_buffer_write_at(buf, oldlim, ×tamp, sizeof(timestamp)); sldns_buffer_write_at(buf, oldlim+sizeof(timestamp), &expiry, sizeof(expiry)); return 1; }
/** get entry from ringbuffer */ static int ring_pop(struct ringbuf* r, sldns_buffer* pkt, struct timeval* tv, struct proxy** p) { /* time -- proxy* -- 16bitlen -- message */ uint16_t len; uint8_t* where = NULL; size_t done; if(r->low == r->high) return 0; where = r->buf + r->low; memmove(tv, where, sizeof(*tv)); memmove(p, where+sizeof(*tv), sizeof(*p)); memmove(&len, where+sizeof(*tv)+sizeof(*p), sizeof(len)); memmove(sldns_buffer_begin(pkt), where+sizeof(*tv)+sizeof(*p)+sizeof(len), len); sldns_buffer_set_limit(pkt, (size_t)len); done = sizeof(*tv)+sizeof(*p)+sizeof(len)+len; /* move lowmark */ if(r->low < r->high) { /* used part in middle */ log_assert(r->high - r->low >= done); r->low += done; } else { /* unused part in middle */ log_assert(r->size - r->low >= done); r->low += done; if(r->size - r->low > sizeof(*tv)+sizeof(*p)) { /* see if it is zeroed; means end of buffer */ struct proxy* pz; memmove(&pz, r->buf+r->low+sizeof(*tv), sizeof(pz)); if(pz == NULL) r->low = 0; } else r->low = 0; } if(r->low == r->high) { r->low = 0; /* reset if empty */ r->high = 0; } return 1; }
/** recv new waiting packets */ static void service_recv(int s, struct ringbuf* ring, sldns_buffer* pkt, fd_set* rorig, int* max, struct proxy** proxies, struct sockaddr_storage* srv_addr, socklen_t srv_len, struct timeval* now, struct timeval* delay, struct timeval* reuse) { int i; struct sockaddr_storage from; socklen_t from_len; ssize_t len; struct proxy* p; for(i=0; i<TRIES_PER_SELECT; i++) { from_len = (socklen_t)sizeof(from); len = recvfrom(s, (void*)sldns_buffer_begin(pkt), sldns_buffer_capacity(pkt), 0, (struct sockaddr*)&from, &from_len); if(len < 0) { #ifndef USE_WINSOCK if(errno == EAGAIN || errno == EINTR) return; fatal_exit("recvfrom: %s", strerror(errno)); #else if(WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINPROGRESS) return; fatal_exit("recvfrom: %s", wsa_strerror(WSAGetLastError())); #endif } sldns_buffer_set_limit(pkt, (size_t)len); /* find its proxy element */ p = find_create_proxy(&from, from_len, rorig, max, proxies, addr_is_ip6(srv_addr, srv_len), now, reuse); if(!p) fatal_exit("error: cannot find or create proxy"); p->lastuse = *now; ring_add(ring, pkt, now, delay, p); p->numwait++; log_addr(1, "recv from client", &p->addr, p->addr_len); } }
/** do proxy for one readable client */ static void do_proxy(struct proxy* p, int retsock, sldns_buffer* pkt) { int i; ssize_t r; for(i=0; i<TRIES_PER_SELECT; i++) { r = recv(p->s, (void*)sldns_buffer_begin(pkt), sldns_buffer_capacity(pkt), 0); if(r == -1) { #ifndef USE_WINSOCK if(errno == EAGAIN || errno == EINTR) return; log_err("recv: %s", strerror(errno)); #else if(WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK) return; log_err("recv: %s", wsa_strerror(WSAGetLastError())); #endif return; } sldns_buffer_set_limit(pkt, (size_t)r); log_addr(1, "return reply to client", &p->addr, p->addr_len); /* send reply back to the real client */ p->numreturn++; r = sendto(retsock, (void*)sldns_buffer_begin(pkt), (size_t)r, 0, (struct sockaddr*)&p->addr, p->addr_len); if(r == -1) { #ifndef USE_WINSOCK log_err("sendto: %s", strerror(errno)); #else log_err("sendto: %s", wsa_strerror(WSAGetLastError())); #endif } } }
int reply_info_encode(struct query_info* qinfo, struct reply_info* rep, uint16_t id, uint16_t flags, sldns_buffer* buffer, time_t timenow, struct regional* region, uint16_t udpsize, int dnssec) { uint16_t ancount=0, nscount=0, arcount=0; struct compress_tree_node* tree = 0; int r; size_t rr_offset; sldns_buffer_clear(buffer); if(udpsize < sldns_buffer_limit(buffer)) sldns_buffer_set_limit(buffer, udpsize); if(sldns_buffer_remaining(buffer) < LDNS_HEADER_SIZE) return 0; sldns_buffer_write(buffer, &id, sizeof(uint16_t)); sldns_buffer_write_u16(buffer, flags); sldns_buffer_write_u16(buffer, rep->qdcount); /* set an, ns, ar counts to zero in case of small packets */ sldns_buffer_write(buffer, "\000\000\000\000\000\000", 6); /* insert query section */ if(rep->qdcount) { if((r=insert_query(qinfo, &tree, buffer, region)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 4, 0); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } } /* roundrobin offset. using query id for random number. With ntohs * for different roundrobins for sequential id client senders. */ rr_offset = RRSET_ROUNDROBIN?ntohs(id):0; /* "prepend" any local alias records in the answer section if this * response is supposed to be authoritative. Currently it should * be a single CNAME record (sanity-checked in worker_handle_request()) * but it can be extended if and when we support more variations of * aliases. */ if(qinfo->local_alias && (flags & BIT_AA)) { struct reply_info arep; time_t timezero = 0; /* to use the 'authoritative' TTL */ memset(&arep, 0, sizeof(arep)); arep.flags = rep->flags; arep.an_numrrsets = 1; arep.rrset_count = 1; arep.rrsets = &qinfo->local_alias->rrset; if((r=insert_section(&arep, 1, &ancount, buffer, 0, timezero, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 6, ancount); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } } /* insert answer section */ if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer, 0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 6, ancount); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 6, ancount); /* if response is positive answer, auth/add sections are not required */ if( ! (MINIMAL_RESPONSES && positive_answer(rep, qinfo->qtype)) ) { /* insert auth section */ if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer, rep->an_numrrsets, timenow, region, &tree, LDNS_SECTION_AUTHORITY, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 8, nscount); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 8, nscount); /* insert add section */ if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer, rep->an_numrrsets + rep->ns_numrrsets, timenow, region, &tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* no need to set TC bit, this is the additional */ sldns_buffer_write_u16_at(buffer, 10, arcount); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 10, arcount); } sldns_buffer_flip(buffer); return 1; }
/** receive DNS datagram over TCP and print it */ static void recv_one(int fd, int udp, SSL* ssl, sldns_buffer* buf) { char* pktstr; uint16_t len; if(!udp) { if(ssl) { if(SSL_read(ssl, (void*)&len, (int)sizeof(len)) <= 0) { log_crypto_err("could not SSL_read"); exit(1); } } else { if(recv(fd, (void*)&len, sizeof(len), 0) < (ssize_t)sizeof(len)) { #ifndef USE_WINSOCK perror("read() len failed"); #else printf("read len: %s\n", wsa_strerror(WSAGetLastError())); #endif exit(1); } } len = ntohs(len); sldns_buffer_clear(buf); sldns_buffer_set_limit(buf, len); if(ssl) { int r = SSL_read(ssl, (void*)sldns_buffer_begin(buf), (int)len); if(r <= 0) { log_crypto_err("could not SSL_read"); exit(1); } if(r != (int)len) fatal_exit("ssl_read %d of %d", r, len); } else { if(recv(fd, (void*)sldns_buffer_begin(buf), len, 0) < (ssize_t)len) { #ifndef USE_WINSOCK perror("read() data failed"); #else printf("read data: %s\n", wsa_strerror(WSAGetLastError())); #endif exit(1); } } } else { ssize_t l; sldns_buffer_clear(buf); if((l=recv(fd, (void*)sldns_buffer_begin(buf), sldns_buffer_capacity(buf), 0)) < 0) { #ifndef USE_WINSOCK perror("read() data failed"); #else printf("read data: %s\n", wsa_strerror(WSAGetLastError())); #endif exit(1); } sldns_buffer_set_limit(buf, (size_t)l); len = (size_t)l; } printf("\nnext received packet\n"); log_buf(0, "data", buf); pktstr = sldns_wire2str_pkt(sldns_buffer_begin(buf), len); printf("%s", pktstr); free(pktstr); }
int reply_info_encode(struct query_info* qinfo, struct reply_info* rep, uint16_t id, uint16_t flags, sldns_buffer* buffer, time_t timenow, struct regional* region, uint16_t udpsize, int dnssec) { uint16_t ancount=0, nscount=0, arcount=0; struct compress_tree_node* tree = 0; int r; size_t rr_offset; sldns_buffer_clear(buffer); if(udpsize < sldns_buffer_limit(buffer)) sldns_buffer_set_limit(buffer, udpsize); if(sldns_buffer_remaining(buffer) < LDNS_HEADER_SIZE) return 0; sldns_buffer_write(buffer, &id, sizeof(uint16_t)); sldns_buffer_write_u16(buffer, flags); sldns_buffer_write_u16(buffer, rep->qdcount); /* set an, ns, ar counts to zero in case of small packets */ sldns_buffer_write(buffer, "\000\000\000\000\000\000", 6); /* insert query section */ if(rep->qdcount) { if((r=insert_query(qinfo, &tree, buffer, region)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 4, 0); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } } /* roundrobin offset. using query id for random number. With ntohs * for different roundrobins for sequential id client senders. */ rr_offset = RRSET_ROUNDROBIN?ntohs(id):0; /* insert answer section */ if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer, 0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 6, ancount); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 6, ancount); /* if response is positive answer, auth/add sections are not required */ if( ! (MINIMAL_RESPONSES && positive_answer(rep, qinfo->qtype)) ) { /* insert auth section */ if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer, rep->an_numrrsets, timenow, region, &tree, LDNS_SECTION_AUTHORITY, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 8, nscount); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 8, nscount); /* insert add section */ if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer, rep->an_numrrsets + rep->ns_numrrsets, timenow, region, &tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* no need to set TC bit, this is the additional */ sldns_buffer_write_u16_at(buffer, 10, arcount); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 10, arcount); } sldns_buffer_flip(buffer); return 1; }
/** convert dns message in buffer to return_msg */ static int parse_data(struct module_qstate* qstate, struct sldns_buffer* buf) { struct msg_parse* prs; struct edns_data edns; uint64_t timestamp, expiry; time_t adjust; size_t lim = sldns_buffer_limit(buf); if(lim < LDNS_HEADER_SIZE+sizeof(timestamp)+sizeof(expiry)) return 0; /* too short */ /* remove timestamp and expiry from end */ sldns_buffer_read_at(buf, lim-sizeof(expiry), &expiry, sizeof(expiry)); sldns_buffer_read_at(buf, lim-sizeof(expiry)-sizeof(timestamp), ×tamp, sizeof(timestamp)); expiry = be64toh(expiry); timestamp = be64toh(timestamp); /* parse DNS packet */ regional_free_all(qstate->env->scratch); prs = (struct msg_parse*)regional_alloc(qstate->env->scratch, sizeof(struct msg_parse)); if(!prs) return 0; /* out of memory */ memset(prs, 0, sizeof(*prs)); memset(&edns, 0, sizeof(edns)); sldns_buffer_set_limit(buf, lim - sizeof(expiry)-sizeof(timestamp)); if(parse_packet(buf, prs, qstate->env->scratch) != LDNS_RCODE_NOERROR) { sldns_buffer_set_limit(buf, lim); return 0; } if(parse_extract_edns(prs, &edns, qstate->env->scratch) != LDNS_RCODE_NOERROR) { sldns_buffer_set_limit(buf, lim); return 0; } qstate->return_msg = dns_alloc_msg(buf, prs, qstate->region); sldns_buffer_set_limit(buf, lim); if(!qstate->return_msg) return 0; qstate->return_rcode = LDNS_RCODE_NOERROR; /* see how much of the TTL expired, and remove it */ if(*qstate->env->now <= (time_t)timestamp) { verbose(VERB_ALGO, "cachedb msg adjust by zero"); return 1; /* message from the future (clock skew?) */ } adjust = *qstate->env->now - (time_t)timestamp; if(qstate->return_msg->rep->ttl < adjust) { verbose(VERB_ALGO, "cachedb msg expired"); /* If serve-expired is enabled, we still use an expired message * setting the TTL to 0. */ if(qstate->env->cfg->serve_expired) adjust = -1; else return 0; /* message expired */ } verbose(VERB_ALGO, "cachedb msg adjusted down by %d", (int)adjust); adjust_msg_ttl(qstate->return_msg, adjust); /* Similar to the unbound worker, if serve-expired is enabled and * the msg would be considered to be expired, mark the state so a * refetch will be scheduled. The comparison between 'expiry' and * 'now' should be redundant given how these values were calculated, * but we check it just in case as does good_expiry_and_qinfo(). */ if(qstate->env->cfg->serve_expired && (adjust == -1 || (time_t)expiry < *qstate->env->now)) { qstate->need_refetch = 1; } return 1; }