struct outbound_entry*
worker_send_query(uint8_t* qname, size_t qnamelen, uint16_t qtype,
uint16_t qclass, uint16_t flags, int dnssec, int want_dnssec,
- int nocaps, struct sockaddr_storage* addr, socklen_t addrlen,
- uint8_t* zone, size_t zonelen, struct module_qstate* q)
+ int nocaps, struct edns_option* opt_list,
+ struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
+ size_t zonelen, struct module_qstate* q)
{
struct worker* worker = q->env->worker;
struct outbound_entry* e = (struct outbound_entry*)regional_alloc(
e->qstate = q;
e->qsent = outnet_serviced_query(worker->back, qname,
qnamelen, qtype, qclass, flags, dnssec, want_dnssec, nocaps,
- q->env->cfg->tcp_upstream, q->env->cfg->ssl_upstream, addr,
- addrlen, zone, zonelen, worker_handle_service_reply, e,
+ q->env->cfg->tcp_upstream, q->env->cfg->ssl_upstream, opt_list,
+ addr, addrlen, zone, zonelen, worker_handle_service_reply, e,
worker->back->udp_buff);
if(!e->qsent) {
return NULL;
size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype),
uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags),
int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
- int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr),
+ int ATTR_UNUSED(nocaps), struct edns_option* ATTR_UNUSED(opt_list),
+ struct sockaddr_storage* ATTR_UNUSED(addr),
socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone),
size_t ATTR_UNUSED(zonelen), struct module_qstate* ATTR_UNUSED(q))
{
network service account, from Mario Turschmann.
- compat strsep implementation.
- generic edns option parse and store code.
+ - and also generic edns options for upstream messages (and replies).
+ after parse use edns_opt_find(edns.opt_list, LDNS_EDNS_NSID),
+ to insert use edns_opt_append(edns, region, code, len, bindata) on
+ the opt_list passed to send_query, or in edns_opt_inplace_reply.
30 May 2016: Wouter
- Fix time in case answer comes from cache in ub_resolve_event().
int tf_policy;
struct delegpt_addr* target;
struct outbound_entry* outq;
+ /* EDNS options to set on outgoing packet */
+ struct edns_option* opt_list = NULL;
/* NOTE: a request will encounter this state for each target it
* needs to send a query to. That is, at least one per referral,
!qstate->blacklist&&(!iter_indicates_dnssec_fwd(qstate->env,
&iq->qinfo_out)||target->attempts==1)?0:BIT_CD),
iq->dnssec_expected, iq->caps_fallback || is_caps_whitelisted(
- ie, iq), &target->addr, target->addrlen, iq->dp->name,
- iq->dp->namelen, qstate);
+ ie, iq), opt_list, &target->addr, target->addrlen,
+ iq->dp->name, iq->dp->namelen, qstate);
if(!outq) {
log_addr(VERB_DETAIL, "error sending query to auth server",
&target->addr, target->addrlen);
struct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen,
uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec,
- int want_dnssec, int nocaps, struct sockaddr_storage* addr,
- socklen_t addrlen, uint8_t* zone, size_t zonelen,
- struct module_qstate* q)
+ int want_dnssec, int nocaps, struct edns_option* opt_list,
+ struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
+ size_t zonelen, struct module_qstate* q)
{
struct libworker* w = (struct libworker*)q->env->worker;
struct outbound_entry* e = (struct outbound_entry*)regional_alloc(
e->qstate = q;
e->qsent = outnet_serviced_query(w->back, qname,
qnamelen, qtype, qclass, flags, dnssec, want_dnssec, nocaps,
- q->env->cfg->tcp_upstream, q->env->cfg->ssl_upstream, addr,
- addrlen, zone, zonelen, libworker_handle_service_reply, e,
- w->back->udp_buff);
+ q->env->cfg->tcp_upstream, q->env->cfg->ssl_upstream, opt_list,
+ addr, addrlen, zone, zonelen, libworker_handle_service_reply,
+ e, w->back->udp_buff);
if(!e->qsent) {
return NULL;
}
size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype),
uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags),
int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
- int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr),
+ int ATTR_UNUSED(nocaps), struct edns_option* ATTR_UNUSED(opt_list),
+ struct sockaddr_storage* ATTR_UNUSED(addr),
socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone),
size_t ATTR_UNUSED(zonelen), struct module_qstate* ATTR_UNUSED(q))
{
struct comm_point;
struct module_qstate;
struct tube;
+struct edns_option;
/**
* Worker service routine to send serviced queries to authoritative servers.
* @param dnssec: if set, EDNS record will have DO bit set.
* @param want_dnssec: signatures needed.
* @param nocaps: ignore capsforid(if in config), do not perturb qname.
+ * @param opt_list: EDNS options on outgoing packet.
* @param addr: where to.
* @param addrlen: length of addr.
* @param zone: delegation point name.
*/
struct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen,
uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec,
- int want_dnssec, int nocaps, struct sockaddr_storage* addr,
- socklen_t addrlen, uint8_t* zone, size_t zonelen,
- struct module_qstate* q);
+ int want_dnssec, int nocaps, struct edns_option* opt_list,
+ struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
+ size_t zonelen, struct module_qstate* q);
/** process incoming replies from the network */
int libworker_handle_reply(struct comm_point* c, void* arg, int error,
* @param dnssec: if set, EDNS record will have DO bit set.
* @param want_dnssec: signatures needed.
* @param nocaps: ignore capsforid(if in config), do not perturb qname.
+ * @param opt_list: EDNS options on outgoing packet.
* @param addr: where to.
* @param addrlen: length of addr.
* @param zone: wireformat dname of the zone.
*/
struct outbound_entry* worker_send_query(uint8_t* qname, size_t qnamelen,
uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec,
- int want_dnssec, int nocaps, struct sockaddr_storage* addr,
- socklen_t addrlen, uint8_t* zone, size_t zonelen,
- struct module_qstate* q);
+ int want_dnssec, int nocaps, struct edns_option* opt_list,
+ struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
+ size_t zonelen, struct module_qstate* q);
/**
* process control messages from the main thread. Frees the control
prev->edns.edns_present == r->edns.edns_present &&
prev->edns.bits == r->edns.bits &&
prev->edns.udp_size == r->edns.udp_size &&
- edns_opt_list_equal(prev->edns.opt_list, r->edns.opt_list)) {
+ edns_opt_list_compare(prev->edns.opt_list, r->edns.opt_list)
+ == 0) {
/* if the previous reply is identical to this one, fix ID */
if(prev->query_reply.c->buffer != r->query_reply.c->buffer)
sldns_buffer_copy(r->query_reply.c->buffer,
}
if((r = query_dname_compare(q1->qbuf+10, q2->qbuf+10)) != 0)
return r;
+ if((r = edns_opt_list_compare(q1->opt_list, q2->opt_list)) != 0)
+ return r;
return sockaddr_cmp(&q1->addr, q1->addrlen, &q2->addr, q2->addrlen);
}
struct service_callback* p = sq->cblist, *np;
free(sq->qbuf);
free(sq->zone);
+ edns_opt_list_free(sq->opt_list);
while(p) {
np = p->next;
free(p);
/** lookup serviced query in serviced query rbtree */
static struct serviced_query*
lookup_serviced(struct outside_network* outnet, sldns_buffer* buff, int dnssec,
- struct sockaddr_storage* addr, socklen_t addrlen)
+ struct sockaddr_storage* addr, socklen_t addrlen,
+ struct edns_option* opt_list)
{
struct serviced_query key;
key.node.key = &key;
memcpy(&key.addr, addr, addrlen);
key.addrlen = addrlen;
key.outnet = outnet;
+ key.opt_list = opt_list;
return (struct serviced_query*)rbtree_search(outnet->serviced, &key);
}
serviced_create(struct outside_network* outnet, sldns_buffer* buff, int dnssec,
int want_dnssec, int nocaps, int tcp_upstream, int ssl_upstream,
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
- size_t zonelen, int qtype)
+ size_t zonelen, int qtype, struct edns_option* opt_list)
{
struct serviced_query* sq = (struct serviced_query*)malloc(sizeof(*sq));
#ifdef UNBOUND_DEBUG
sq->ssl_upstream = ssl_upstream;
memcpy(&sq->addr, addr, addrlen);
sq->addrlen = addrlen;
+ sq->opt_list = NULL;
+ if(opt_list) {
+ sq->opt_list = edns_opt_copy_alloc(opt_list);
+ if(!sq->opt_list) {
+ free(sq->zone);
+ free(sq->qbuf);
+ free(sq);
+ return NULL;
+ }
+ }
sq->outnet = outnet;
sq->cblist = NULL;
sq->pending = NULL;
edns.edns_present = 1;
edns.ext_rcode = 0;
edns.edns_version = EDNS_ADVERTISED_VERSION;
- /* insert EDNS options here for upstream messages,
- * stored from sq */
- edns.opt_list = NULL;
+ edns.opt_list = sq->opt_list;
if(sq->status == serviced_query_UDP_EDNS_FRAG) {
if(addr_is_ip6(&sq->addr, sq->addrlen)) {
if(EDNS_FRAG_SIZE_IP6 < EDNS_ADVERTISED_SIZE)
outnet_serviced_query(struct outside_network* outnet,
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
uint16_t flags, int dnssec, int want_dnssec, int nocaps,
- int tcp_upstream, int ssl_upstream, struct sockaddr_storage* addr,
- socklen_t addrlen, uint8_t* zone, size_t zonelen,
- comm_point_callback_t* callback, void* callback_arg,
+ int tcp_upstream, int ssl_upstream, struct edns_option* opt_list,
+ struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
+ size_t zonelen, comm_point_callback_t* callback, void* callback_arg,
sldns_buffer* buff)
{
struct serviced_query* sq;
struct service_callback* cb;
serviced_gen_query(buff, qname, qnamelen, qtype, qclass, flags);
- sq = lookup_serviced(outnet, buff, dnssec, addr, addrlen);
+ sq = lookup_serviced(outnet, buff, dnssec, addr, addrlen, opt_list);
/* duplicate entries are included in the callback list, because
* there is a counterpart registration by our caller that needs to
* be doubly-removed (with callbacks perhaps). */
/* make new serviced query entry */
sq = serviced_create(outnet, buff, dnssec, want_dnssec, nocaps,
tcp_upstream, ssl_upstream, addr, addrlen, zone,
- zonelen, (int)qtype);
+ zonelen, (int)qtype, opt_list);
if(!sq) {
free(cb);
return NULL;
struct sldns_buffer;
struct serviced_query;
struct dt_env;
+struct edns_option;
/**
* Send queries to outside servers and wait for answers from servers.
int last_rtt;
/** do we know edns probe status already, for UDP_EDNS queries */
int edns_lame_known;
+ /** edns options to use for sending upstream packet */
+ struct edns_option* opt_list;
/** outside network this is part of */
struct outside_network* outnet;
/** list of interested parties that need callback on results. */
* @param nocaps: ignore use_caps_for_id and use unperturbed qname.
* @param tcp_upstream: use TCP for upstream queries.
* @param ssl_upstream: use SSL for upstream queries.
+ * @param opt_list: pass edns option list (deep copied into serviced query)
+ * these options are set on the outgoing packets.
* @param callback: callback function.
* @param callback_arg: user argument to callback function.
* @param addr: to which server to send the query.
struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
uint16_t flags, int dnssec, int want_dnssec, int nocaps,
- int tcp_upstream, int ssl_upstream, struct sockaddr_storage* addr,
- socklen_t addrlen, uint8_t* zone, size_t zonelen,
- comm_point_callback_t* callback, void* callback_arg,
+ int tcp_upstream, int ssl_upstream, struct edns_option* opt_list,
+ struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
+ size_t zonelen, comm_point_callback_t* callback, void* callback_arg,
struct sldns_buffer* buff);
/**
size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype),
uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags),
int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
- int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr),
+ int ATTR_UNUSED(nocaps), struct edns_option* ATTR_UNUSED(opt_list),
+ struct sockaddr_storage* ATTR_UNUSED(addr),
socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone),
size_t ATTR_UNUSED(zonelen), struct module_qstate* ATTR_UNUSED(q))
{
size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype),
uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags),
int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
- int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr),
+ int ATTR_UNUSED(nocaps), struct edns_option* ATTR_UNUSED(opt_list),
+ struct sockaddr_storage* ATTR_UNUSED(addr),
socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone),
size_t ATTR_UNUSED(zonelen), struct module_qstate* ATTR_UNUSED(q))
{
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
uint16_t flags, int dnssec, int ATTR_UNUSED(want_dnssec),
int ATTR_UNUSED(nocaps), int ATTR_UNUSED(tcp_upstream),
- int ATTR_UNUSED(ssl_upstream), struct sockaddr_storage* addr,
- socklen_t addrlen, uint8_t* zone, size_t zonelen,
- comm_point_callback_t* callback, void* callback_arg,
+ int ATTR_UNUSED(ssl_upstream), struct edns_option* opt_list,
+ struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
+ size_t zonelen, comm_point_callback_t* callback, void* callback_arg,
sldns_buffer* ATTR_UNUSED(buff))
{
struct replay_runtime* runtime = (struct replay_runtime*)outnet->base;
edns.edns_version = EDNS_ADVERTISED_VERSION;
edns.udp_size = EDNS_ADVERTISED_SIZE;
edns.bits = 0;
- edns.opt_list = NULL;
+ edns.opt_list = opt_list;
if(dnssec)
edns.bits = EDNS_DO;
attach_edns_record(pend->buffer, &edns);
return result;
}
-int edns_opt_list_equal(struct edns_option* p, struct edns_option* q)
+int edns_opt_compare(struct edns_option* p, struct edns_option* q)
+{
+ if(!p && !q) return 0;
+ if(!p) return -1;
+ if(!q) return 1;
+ log_assert(p && q);
+ if(p->opt_code != q->opt_code)
+ return (int)q->opt_code - (int)p->opt_code;
+ if(p->opt_len != q->opt_len)
+ return (int)q->opt_len - (int)p->opt_len;
+ if(p->opt_len != 0)
+ return memcmp(p->opt_data, q->opt_data, p->opt_len);
+ return 0;
+}
+
+int edns_opt_list_compare(struct edns_option* p, struct edns_option* q)
{
+ int r;
while(p && q) {
- /* compare elements */
- if(p->opt_code != q->opt_code ||
- p->opt_len != q->opt_len)
- return 0;
- if(p->opt_len > 0 && q->opt_len > 0) {
- if(memcmp(p->opt_data, q->opt_data, p->opt_len) != 0)
- return 0;
- }
-
+ r = edns_opt_compare(p, q);
+ if(r != 0)
+ return r;
p = p->next;
q = q->next;
}
- if(p || q)
- return 0; /* uneven length lists */
- return 1;
+ if(p || q) {
+ /* uneven length lists */
+ if(p) return 1;
+ if(q) return -1;
+ }
+ return 0;
+}
+
+void edns_opt_list_free(struct edns_option* list)
+{
+ struct edns_option* n;
+ while(list) {
+ free(list->opt_data);
+ n = list->next;
+ free(list);
+ list = n;
+ }
+}
+
+struct edns_option* edns_opt_copy_alloc(struct edns_option* list)
+{
+ struct edns_option* result = NULL, *cur = NULL, *s;
+ while(list) {
+ /* copy edns option structure */
+ s = memdup(list, sizeof(*list));
+ if(!s) {
+ edns_opt_list_free(result);
+ return NULL;
+ }
+ s->next = NULL;
+
+ /* copy option data */
+ if(s->opt_data) {
+ s->opt_data = memdup(s->opt_data, s->opt_len);
+ if(!s->opt_data) {
+ edns_opt_list_free(result);
+ return NULL;
+ }
+ }
+
+ /* link into list */
+ if(cur)
+ cur->next = s;
+ else result = s;
+ cur = s;
+
+ /* examine next element */
+ list = list->next;
+ }
+ return result;
+}
+
+struct edns_option* edns_opt_find(struct edns_option* list, uint16_t code)
+{
+ struct edns_option* p;
+ for(p=list; p; p=p->next) {
+ if(p->opt_code == code)
+ return p;
+ }
+ return NULL;
}
int edns_opt_append(struct edns_data* edns, struct regional* region,
uint16_t code, size_t len, uint8_t* data);
+/**
+ * Find edns option in edns list
+ * @param list: list of edns options (eg. edns.opt_list)
+ * @param code: opt code to find.
+ * @return NULL or the edns_option element.
+ */
+struct edns_option* edns_opt_find(struct edns_option* list, uint16_t code);
+
/**
* Transform edns data structure from query structure into reply structure.
* In place transform, for errors and cache replies.
struct regional* region);
/**
- * See if edns option lists are equal, also order and contents of options.
+ * Copy edns option list allocated with malloc
+ */
+struct edns_option* edns_opt_copy_alloc(struct edns_option* list);
+
+/**
+ * Free edns option list allocated with malloc
+ */
+void edns_opt_list_free(struct edns_option* list);
+
+/**
+ * Compare an edns option. (not entire list). Also compares contents.
+ */
+int edns_opt_compare(struct edns_option* p, struct edns_option* q);
+
+/**
+ * Compare edns option lists, also the order and contents of edns-options.
*/
-int edns_opt_list_equal(struct edns_option* p, struct edns_option* q);
+int edns_opt_list_compare(struct edns_option* p, struct edns_option* q);
#endif /* UTIL_DATA_MSGREPLY_H */
fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)(
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
uint16_t flags, int dnssec, int want_dnssec, int nocaps,
- struct sockaddr_storage* addr, socklen_t addrlen,
- uint8_t* zone, size_t zonelen,
+ struct edns_option* opt_list, struct sockaddr_storage* addr,
+ socklen_t addrlen, uint8_t* zone, size_t zonelen,
struct module_qstate* q))
{
if(fptr == &worker_send_query) return 1;
int fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)(
uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
uint16_t flags, int dnssec, int want_dnssec, int nocaps,
- struct sockaddr_storage* addr, socklen_t addrlen,
+ struct edns_option*, struct sockaddr_storage* addr, socklen_t addrlen,
uint8_t* zone, size_t zonelen,
struct module_qstate* q));
* EDNS, the answer is likely to be useless for this domain.
* @param nocaps: do not use caps_for_id, use the qname as given.
* (ignored if caps_for_id is disabled).
+ * @param opt_list: set these EDNS options on the outgoing packet.
+ * or NULL if none (the list is deep-copied).
* @param addr: where to.
* @param addrlen: length of addr.
* @param zone: delegation point name.
*/
struct outbound_entry* (*send_query)(uint8_t* qname, size_t qnamelen,
uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec,
- int want_dnssec, int nocaps, struct sockaddr_storage* addr,
- socklen_t addrlen, uint8_t* zone, size_t zonelen,
- struct module_qstate* q);
+ int want_dnssec, int nocaps, struct edns_option* opt_list,
+ struct sockaddr_storage* addr, socklen_t addrlen,
+ uint8_t* zone, size_t zonelen, struct module_qstate* q);
/**
* Detach-subqueries.