Exemple #1
0
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;
}
Exemple #2
0
/** 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, &timestamp, sizeof(timestamp));
	sldns_buffer_write_at(buf, oldlim+sizeof(timestamp), &expiry,
		sizeof(expiry));

	return 1;
}
Exemple #3
0
/** 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;
}
Exemple #4
0
/** 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);
	}
}
Exemple #5
0
/** 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
		}
	}
}
Exemple #6
0
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;
}
Exemple #7
0
/** 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);
}
Exemple #8
0
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;
}
Exemple #9
0
/** 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),
		&timestamp, 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;
}