import unbound 1.19.2, reminded by florian@
authorsthen <sthen@openbsd.org>
Fri, 12 Apr 2024 15:44:27 +0000 (15:44 +0000)
committersthen <sthen@openbsd.org>
Fri, 12 Apr 2024 15:44:27 +0000 (15:44 +0000)
(yes I know there is a newer one, I'll update on top)

19 files changed:
usr.sbin/unbound/cachedb/redis.c
usr.sbin/unbound/dnstap/dnstap.m4
usr.sbin/unbound/dnstap/dtstream.c
usr.sbin/unbound/dnstap/unbound-dnstap-socket.c
usr.sbin/unbound/dynlibmod/dynlibmod.c
usr.sbin/unbound/ipset/ipset.c
usr.sbin/unbound/iterator/iter_priv.c
usr.sbin/unbound/iterator/iter_resptype.h
usr.sbin/unbound/iterator/iter_scrub.h
usr.sbin/unbound/services/rpz.c
usr.sbin/unbound/testcode/dohclient.c
usr.sbin/unbound/testcode/fake_event.c
usr.sbin/unbound/testcode/streamtcp.1
usr.sbin/unbound/testcode/streamtcp.c
usr.sbin/unbound/testcode/unitauth.c
usr.sbin/unbound/util/proxy_protocol.c
usr.sbin/unbound/util/proxy_protocol.h
usr.sbin/unbound/util/rfc_1982.c
usr.sbin/unbound/util/siphash.c

index 93a575a..6cc9759 100644 (file)
@@ -59,11 +59,28 @@ struct redis_moddata {
        const char* server_path; /* server's unix path, or "", NULL if unused */
        const char* server_password; /* server's AUTH password, or "", NULL if unused */
        struct timeval timeout;  /* timeout for connection setup and commands */
+       int logical_db;         /* the redis logical database to use */
 };
 
 static redisReply* redis_command(struct module_env*, struct cachedb_env*,
        const char*, const uint8_t*, size_t);
 
+static void
+moddata_clean(struct redis_moddata** moddata) {
+       if(!moddata || !*moddata)
+               return;
+       if((*moddata)->ctxs) {
+               int i;
+               for(i = 0; i < (*moddata)->numctxs; i++) {
+                       if((*moddata)->ctxs[i])
+                               redisFree((*moddata)->ctxs[i]);
+               }
+               free((*moddata)->ctxs);
+       }
+       free(*moddata);
+       *moddata = NULL;
+}
+
 static redisContext*
 redis_connect(const struct redis_moddata* moddata)
 {
@@ -97,10 +114,21 @@ redis_connect(const struct redis_moddata* moddata)
                }
                freeReplyObject(rep);
        }
+       if(moddata->logical_db > 0) {
+               redisReply* rep;
+               rep = redisCommand(ctx, "SELECT %d", moddata->logical_db);
+               if(!rep || rep->type == REDIS_REPLY_ERROR) {
+                       log_err("failed to set logical database (%d)",
+                               moddata->logical_db);
+                       freeReplyObject(rep);
+                       goto fail;
+               }
+               freeReplyObject(rep);
+       }
        verbose(VERB_OPS, "Connection to Redis established");
        return ctx;
 
-  fail:
+fail:
        if(ctx)
                redisFree(ctx);
        return NULL;
@@ -117,14 +145,13 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
        moddata = calloc(1, sizeof(struct redis_moddata));
        if(!moddata) {
                log_err("out of memory");
-               return 0;
+               goto fail;
        }
        moddata->numctxs = env->cfg->num_threads;
        moddata->ctxs = calloc(env->cfg->num_threads, sizeof(redisContext*));
        if(!moddata->ctxs) {
                log_err("out of memory");
-               free(moddata);
-               return 0;
+               goto fail;
        }
        /* note: server_host is a shallow reference to configured string.
         * we don't have to free it in this module. */
@@ -134,8 +161,15 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
        moddata->server_password = env->cfg->redis_server_password;
        moddata->timeout.tv_sec = env->cfg->redis_timeout / 1000;
        moddata->timeout.tv_usec = (env->cfg->redis_timeout % 1000) * 1000;
-       for(i = 0; i < moddata->numctxs; i++)
-               moddata->ctxs[i] = redis_connect(moddata);
+       moddata->logical_db = env->cfg->redis_logical_db;
+       for(i = 0; i < moddata->numctxs; i++) {
+               redisContext* ctx = redis_connect(moddata);
+               if(!ctx) {
+                       log_err("redis_init: failed to init redis");
+                       goto fail;
+               }
+               moddata->ctxs[i] = ctx;
+       }
        cachedb_env->backend_data = moddata;
        if(env->cfg->redis_expire_records) {
                redisReply* rep = NULL;
@@ -148,7 +182,7 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
                        log_err("redis_init: failed to init redis, the "
                                "redis-expire-records option requires the SETEX command "
                                "(redis >= 2.0.0)");
-                       return 0;
+                       goto fail;
                }
                redis_reply_type = rep->type;
                freeReplyObject(rep);
@@ -160,11 +194,14 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
                        log_err("redis_init: failed to init redis, the "
                                "redis-expire-records option requires the SETEX command "
                                "(redis >= 2.0.0)");
-                       return 0;
+                       goto fail;
                }
        }
-
        return 1;
+
+fail:
+       moddata_clean(&moddata);
+       return 0;
 }
 
 static void
@@ -175,18 +212,7 @@ redis_deinit(struct module_env* env, struct cachedb_env* cachedb_env)
        (void)env;
 
        verbose(VERB_OPS, "Redis deinitialization");
-
-       if(!moddata)
-               return;
-       if(moddata->ctxs) {
-               int i;
-               for(i = 0; i < moddata->numctxs; i++) {
-                       if(moddata->ctxs[i])
-                               redisFree(moddata->ctxs[i]);
-               }
-               free(moddata->ctxs);
-       }
-       free(moddata);
+       moddata_clean(&moddata);
 }
 
 /*
index 1ff6c3f..be8b40c 100644 (file)
@@ -41,7 +41,7 @@ AC_DEFUN([dt_DNSTAP],
          fi
     ])
     AC_SEARCH_LIBS([protobuf_c_message_pack], [protobuf-c], [],
-      AC_MSG_ERROR([The protobuf-c library was not found. Please install protobuf-c!]))
+      AC_MSG_ERROR([The protobuf-c library was not found. Please install the development libraries for protobuf-c!]))
     $2
   else
     $3
index 9153f04..69c9512 100644 (file)
@@ -788,7 +788,7 @@ static int dtio_write_ssl(struct dt_io_thread* dtio, uint8_t* buf,
                        }
                        return -1;
                }
-               log_crypto_err("dnstap io, could not SSL_write");
+               log_crypto_err_io("dnstap io, could not SSL_write", want);
                return -1;
        }
        return r;
@@ -1029,7 +1029,7 @@ static int ssl_read_bytes(struct dt_io_thread* dtio, void* buf, size_t len)
                                "other side");
                        return 0;
                }
-               log_crypto_err("could not SSL_read");
+               log_crypto_err_io("could not SSL_read", want);
                verbose(VERB_DETAIL, "dnstap io: output closed by the "
                                "other side");
                return 0;
@@ -1431,8 +1431,8 @@ static int dtio_ssl_handshake(struct dt_io_thread* dtio,
                } else {
                        unsigned long err = ERR_get_error();
                        if(!squelch_err_ssl_handshake(err)) {
-                               log_crypto_err_code("dnstap io, ssl handshake failed",
-                                       err);
+                               log_crypto_err_io_code("dnstap io, ssl handshake failed",
+                                       want, err);
                                verbose(VERB_OPS, "dnstap io, ssl handshake failed "
                                        "from %s", dtio->ip_str);
                        }
index d172a67..04fda74 100644 (file)
@@ -708,7 +708,7 @@ static ssize_t ssl_read_bytes(struct tap_data* data, void* buf, size_t len)
                                (data->id?data->id:""));
                        return 0;
                }
-               log_crypto_err("could not SSL_read");
+               log_crypto_err_io("could not SSL_read", want);
                if(verbosity) log_info("dnstap client stream closed from %s",
                        (data->id?data->id:""));
                return 0;
@@ -760,10 +760,11 @@ static int reply_with_accept(struct tap_data* data)
        fd_set_block(data->fd);
        if(data->ssl) {
                if((r=SSL_write(data->ssl, acceptframe, len)) <= 0) {
-                       if(SSL_get_error(data->ssl, r) == SSL_ERROR_ZERO_RETURN)
+                       int r2;
+                       if((r2=SSL_get_error(data->ssl, r)) == SSL_ERROR_ZERO_RETURN)
                                log_err("SSL_write, peer closed connection");
                        else
-                               log_err("could not SSL_write");
+                               log_crypto_err_io("could not SSL_write", r2);
                        fd_set_nonblock(data->fd);
                        free(acceptframe);
                        return 0;
@@ -805,10 +806,11 @@ static int reply_with_finish(struct tap_data* data)
        if(data->ssl) {
                int r;
                if((r=SSL_write(data->ssl, finishframe, len)) <= 0) {
-                       if(SSL_get_error(data->ssl, r) == SSL_ERROR_ZERO_RETURN)
+                       int r2;
+                       if((r2=SSL_get_error(data->ssl, r)) == SSL_ERROR_ZERO_RETURN)
                                log_err("SSL_write, peer closed connection");
                        else
-                               log_err("could not SSL_write");
+                               log_crypto_err_io("could not SSL_write", r2);
                        fd_set_nonblock(data->fd);
                        free(finishframe);
                        return 0;
index ffac7ff..1e040a3 100644 (file)
@@ -75,6 +75,7 @@ int dynlibmod_init(struct module_env* env, int id) {
     struct config_strlist* cfg_item = env->cfg->dynlib_file;
     struct dynlibmod_env* de = (struct dynlibmod_env*)calloc(1, sizeof(struct dynlibmod_env));
     __DYNMOD dynamic_library;
+    int i;
     if (!de)
     {
         log_err("dynlibmod[%d]: malloc failure", dynlib_mod_idx);
@@ -84,7 +85,7 @@ int dynlibmod_init(struct module_env* env, int id) {
     env->modinfo[id] = (void*) de;
 
     de->fname = NULL;
-    for(int i = dynlib_mod_idx;
+    for(i = dynlib_mod_idx;
         i != 0 && cfg_item != NULL;
         i--, cfg_item = cfg_item->next) {}
 
index c61ebc2..af55de8 100644 (file)
@@ -158,10 +158,10 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie,
                qs = NULL;
                plen = strlen(p->str);
 
-               if (dlen >= plen) {
+               if (dlen == plen || (dlen > plen && dname[dlen - plen - 1] == '.' )) {
                        ds = dname + (dlen - plen);
                }
-               if (qlen >= plen) {
+               if (qlen == plen || (qlen > plen && qname[qlen - plen - 1] == '.' )) {
                        qs = qname + (qlen - plen);
                }
                if ((ds && strncasecmp(p->str, ds, plen) == 0)
index 90bea17..be42192 100644 (file)
@@ -207,28 +207,6 @@ size_t priv_get_mem(struct iter_priv* priv)
        return sizeof(*priv) + regional_get_mem(priv->region);
 }
 
-/** remove RR from msgparse RRset, return true if rrset is entirely bad */
-static int
-remove_rr(const char* str, sldns_buffer* pkt, struct rrset_parse* rrset,
-       struct rr_parse* prev, struct rr_parse** rr, struct sockaddr_storage* addr, socklen_t addrlen)
-{
-       if(verbosity >= VERB_QUERY && rrset->dname_len <= LDNS_MAX_DOMAINLEN && str) {
-               uint8_t buf[LDNS_MAX_DOMAINLEN+1];
-               dname_pkt_copy(pkt, buf, rrset->dname);
-               log_name_addr(VERB_QUERY, str, buf, addr, addrlen);
-       }
-       if(prev)
-               prev->next = (*rr)->next;
-       else    rrset->rr_first = (*rr)->next;
-       if(rrset->rr_last == *rr)
-               rrset->rr_last = prev;
-       rrset->rr_count --;
-       rrset->size -= (*rr)->size;
-       /* rr struct still exists, but is unlinked, so that in the for loop
-        * the rr->next works fine to continue. */
-       return rrset->rr_count == 0;
-}
-
 int priv_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt,
        struct rrset_parse* rrset)
 {
@@ -261,7 +239,7 @@ int priv_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt,
                                        INET_SIZE);
                                memmove(&addr, &sa, len);
                                if(priv_lookup_addr(priv, &addr, len)) {
-                                       if(remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, &rr, &addr, len))
+                                       if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, rr, &addr, len))
                                                return 1;
                                        continue;
                                }
@@ -284,7 +262,7 @@ int priv_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt,
                                        INET6_SIZE);
                                memmove(&addr, &sa, len);
                                if(priv_lookup_addr(priv, &addr, len)) {
-                                       if(remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, &rr, &addr, len))
+                                       if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, rr, &addr, len))
                                                return 1;
                                        continue;
                                }
index fee9ef3..bfd4b66 100644 (file)
@@ -119,9 +119,11 @@ enum response_type response_type_from_cache(struct dns_msg* msg,
  * @param request: the request that generated the response.
  * @param dp: The delegation point that was being queried
  *          when the response was returned.
+ * @param empty_nodata_found: flag to keep track of empty nodata detection.
  * @return the response type (CNAME or ANSWER).
  */
 enum response_type response_type_from_server(int rdset, 
-       struct dns_msg* msg, struct query_info* request, struct delegpt* dp);
+       struct dns_msg* msg, struct query_info* request, struct delegpt* dp,
+       int* empty_nodata_found);
 
 #endif /* ITERATOR_ITER_RESPTYPE_H */
index cbbaf73..4d6ce71 100644 (file)
@@ -48,6 +48,7 @@ struct query_info;
 struct regional;
 struct module_env;
 struct iter_env;
+struct module_qstate;
 
 /**
  * Cleanup the passed dns message.
@@ -59,11 +60,13 @@ struct iter_env;
  *     Used to determine out of bailiwick information.
  * @param regional: where to allocate (new) parts of the message.
  * @param env: module environment with config settings and cache. 
+ * @param qstate: for setting errinf for EDE error messages.
  * @param ie: iterator module environment data.
  * @return: false if the message is total waste. true if scrubbed with success.
  */
 int scrub_message(struct sldns_buffer* pkt, struct msg_parse* msg, 
        struct query_info* qinfo, uint8_t* zonename, struct regional* regional,
-       struct module_env* env, struct iter_env* ie);
+       struct module_env* env, struct module_qstate* qstate,
+       struct iter_env* ie);
 
 #endif /* ITERATOR_ITER_SCRUB_H */
index 6ce83cb..18d76c0 100644 (file)
@@ -2162,7 +2162,7 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r,
        case RPZ_TCP_ONLY_ACTION:
                /* basically a passthru here but the tcp-only will be
                 * honored before the query gets sent. */
-               ms->respip_action_info->action = respip_truncate;
+               ms->tcp_required = 1;
                ret = NULL;
                break;
        case RPZ_DROP_ACTION:
@@ -2217,7 +2217,7 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r,
        case RPZ_TCP_ONLY_ACTION:
                /* basically a passthru here but the tcp-only will be
                 * honored before the query gets sent. */
-               ms->respip_action_info->action = respip_truncate;
+               ms->tcp_required = 1;
                ret = NULL;
                break;
        case RPZ_DROP_ACTION:
@@ -2428,7 +2428,7 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms,
        case RPZ_TCP_ONLY_ACTION:
                /* basically a passthru here but the tcp-only will be
                 * honored before the query gets sent. */
-               ms->respip_action_info->action = respip_truncate;
+               ms->tcp_required = 1;
                ret = NULL;
                break;
        case RPZ_DROP_ACTION:
@@ -2448,6 +2448,10 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms,
                        rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
                ret = NULL;
        }
+       if(r->log)
+               log_rpz_apply("qname", (z?z->name:NULL), NULL,
+                       localzone_type_to_rpz_action(lzt),
+                       &is->qchase, NULL, ms, r->log_name);
        lock_rw_unlock(&z->lock);
        lock_rw_unlock(&a->lock);
        return ret;
index de9f39d..2c12a50 100644 (file)
@@ -286,7 +286,7 @@ static ssize_t http2_recv_cb(nghttp2_session* ATTR_UNUSED(session),
                        if(want == SSL_ERROR_ZERO_RETURN) {
                                return NGHTTP2_ERR_EOF;
                        }
-                       log_crypto_err("could not SSL_read");
+                       log_crypto_err_io("could not SSL_read", want);
                        return NGHTTP2_ERR_EOF;
                }
                return r;
@@ -317,7 +317,7 @@ static ssize_t http2_send_cb(nghttp2_session* ATTR_UNUSED(session),
                        if(want == SSL_ERROR_ZERO_RETURN) {
                                return NGHTTP2_ERR_CALLBACK_FAILURE;
                        }
-                       log_crypto_err("could not SSL_write");
+                       log_crypto_err_io("could not SSL_write", want);
                        return NGHTTP2_ERR_CALLBACK_FAILURE;
                }
                return r;
@@ -526,7 +526,7 @@ run(struct http2_session* h2_session, int port, int no_tls, int count, char** q)
                        r = SSL_get_error(ssl, r);
                        if(r != SSL_ERROR_WANT_READ &&
                                r != SSL_ERROR_WANT_WRITE) {
-                               log_crypto_err("could not ssl_handshake");
+                               log_crypto_err_io("could not ssl_handshake", r);
                                exit(1);
                        }
                }
index 2140b21..13970c3 100644 (file)
@@ -1249,7 +1249,7 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
                edns.edns_version = EDNS_ADVERTISED_VERSION;
                edns.udp_size = EDNS_ADVERTISED_SIZE;
                edns.bits = 0;
-               if(dnssec)
+               if((dnssec & EDNS_DO))
                        edns.bits = EDNS_DO;
                edns.padding_block_size = 0;
                edns.cookie_present = 0;
index f02b168..55ed4a2 100644 (file)
@@ -61,6 +61,17 @@ Specify the server to send the queries to. If not specified localhost (127.0.0.1
 .B \-d \fIsecs
 Delay after the connection before sending query.  This tests the timeout
 on the other side, eg. if shorter the connection is closed.
+.TP
+.B \-p \fIclient
+Use proxy protocol to send the query. Specify the ipaddr@portnr of the client
+to include in PROXYv2.
+.TP
+.B IXFR=serial
+Pass the type of the query as IXFR=N to send an IXFR query with serial N.
+.TP
+.B NOTIFY[=serial]
+Pass the type of the query as NOTIFY[=N] to send a notify packet. The serial N
+of the new zone can be included.
 .SH "EXAMPLES"
 .LP
 Some examples of use.
index 84d2b65..037bcfd 100644 (file)
@@ -79,6 +79,8 @@ static void usage(char* argv[])
        printf("-d secs         delay after connection before sending query\n");
        printf("-s              use ssl\n");
        printf("-h              this help text\n");
+       printf("IXFR=N          for the type, sends ixfr query with serial N.\n");
+       printf("NOTIFY[=N]      for the type, sends notify. Can set new zone serial N.\n");
        exit(1);
 }
 
@@ -115,6 +117,29 @@ open_svr(const char* svr, int udp, struct sockaddr_storage* addr,
        return fd;
 }
 
+/** Append a SOA record with serial number */
+static void
+write_soa_serial_to_buf(sldns_buffer* buf, struct query_info* qinfo,
+       uint32_t serial)
+{
+       sldns_buffer_set_position(buf, sldns_buffer_limit(buf));
+       sldns_buffer_set_limit(buf, sldns_buffer_capacity(buf));
+       /* Write compressed reference to the query */
+       sldns_buffer_write_u16(buf, PTR_CREATE(LDNS_HEADER_SIZE));
+       sldns_buffer_write_u16(buf, LDNS_RR_TYPE_SOA);
+       sldns_buffer_write_u16(buf, qinfo->qclass);
+       sldns_buffer_write_u32(buf, 3600); /* TTL */
+       sldns_buffer_write_u16(buf, 1+1+4*5); /* rdatalen */
+       sldns_buffer_write_u8(buf, 0); /* primary "." */
+       sldns_buffer_write_u8(buf, 0); /* email "." */
+       sldns_buffer_write_u32(buf, serial); /* serial */
+       sldns_buffer_write_u32(buf, 0); /* refresh */
+       sldns_buffer_write_u32(buf, 0); /* retry */
+       sldns_buffer_write_u32(buf, 0); /* expire */
+       sldns_buffer_write_u32(buf, 0); /* minimum */
+       sldns_buffer_flip(buf);
+}
+
 /** write a query over the TCP fd */
 static void
 write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id,
@@ -123,6 +148,8 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id,
 {
        struct query_info qinfo;
        size_t proxy_buf_limit = sldns_buffer_limit(proxy_buf);
+       int have_serial = 0, is_notify = 0;
+       uint32_t serial = 0;
        /* qname */
        qinfo.qname = sldns_str2wire_dname(strname, &qinfo.qname_len);
        if(!qinfo.qname) {
@@ -130,12 +157,27 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id,
                exit(1);
        }
 
-       /* qtype and qclass */
-       qinfo.qtype = sldns_get_rr_type_by_name(strtype);
-       if(qinfo.qtype == 0 && strcmp(strtype, "TYPE0") != 0) {
-               printf("cannot parse query type: '%s'\n", strtype);
-               exit(1);
+       /* qtype */
+       if(strncasecmp(strtype, "IXFR=", 5) == 0) {
+               serial = (uint32_t)atoi(strtype+5);
+               have_serial = 1;
+               qinfo.qtype = LDNS_RR_TYPE_IXFR;
+       } else if(strcasecmp(strtype, "NOTIFY") == 0) {
+               is_notify = 1;
+               qinfo.qtype = LDNS_RR_TYPE_SOA;
+       } else if(strncasecmp(strtype, "NOTIFY=", 7) == 0) {
+               serial = (uint32_t)atoi(strtype+7);
+               have_serial = 1;
+               is_notify = 1;
+               qinfo.qtype = LDNS_RR_TYPE_SOA;
+       } else {
+               qinfo.qtype = sldns_get_rr_type_by_name(strtype);
+               if(qinfo.qtype == 0 && strcmp(strtype, "TYPE0") != 0) {
+                       printf("cannot parse query type: '%s'\n", strtype);
+                       exit(1);
+               }
        }
+       /* qclass */
        qinfo.qclass = sldns_get_rr_class_by_name(strclass);
        if(qinfo.qclass == 0 && strcmp(strclass, "CLASS0") != 0) {
                printf("cannot parse query class: '%s'\n", strclass);
@@ -150,6 +192,21 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id,
        sldns_buffer_write_u16_at(buf, 0, id);
        sldns_buffer_write_u16_at(buf, 2, BIT_RD);
 
+       if(have_serial && qinfo.qtype == LDNS_RR_TYPE_IXFR) {
+               /* Attach serial to SOA record in the authority section. */
+               write_soa_serial_to_buf(buf, &qinfo, serial);
+               LDNS_NSCOUNT_SET(sldns_buffer_begin(buf), 1);
+       }
+       if(is_notify) {
+               LDNS_OPCODE_SET(sldns_buffer_begin(buf), LDNS_PACKET_NOTIFY);
+               LDNS_RD_CLR(sldns_buffer_begin(buf));
+               LDNS_AA_SET(sldns_buffer_begin(buf));
+               if(have_serial) {
+                       write_soa_serial_to_buf(buf, &qinfo, serial);
+                       LDNS_ANCOUNT_SET(sldns_buffer_begin(buf), 1);
+               }
+       }
+
        if(1) {
                /* add EDNS DO */
                struct edns_data edns;
@@ -361,6 +418,7 @@ static int parse_pp2_client(const char* pp2_client, int udp,
        sldns_buffer* proxy_buf)
 {
        struct sockaddr_storage pp2_addr;
+       size_t bytes_written;
        socklen_t pp2_addrlen = 0;
        memset(&pp2_addr, 0, sizeof(pp2_addr));
        if(*pp2_client == 0) return 0;
@@ -369,7 +427,9 @@ static int parse_pp2_client(const char* pp2_client, int udp,
                exit(1);
        }
        sldns_buffer_clear(proxy_buf);
-       pp2_write_to_buf(proxy_buf, &pp2_addr, !udp);
+       bytes_written = pp2_write_to_buf(sldns_buffer_begin(proxy_buf),
+               sldns_buffer_remaining(proxy_buf), &pp2_addr, !udp);
+       sldns_buffer_set_position(proxy_buf, bytes_written);
        sldns_buffer_flip(proxy_buf);
        return 1;
 }
@@ -406,7 +466,7 @@ send_em(const char* svr, const char* pp2_client, int udp, int usessl,
                        r = SSL_get_error(ssl, r);
                        if(r != SSL_ERROR_WANT_READ &&
                                r != SSL_ERROR_WANT_WRITE) {
-                               log_crypto_err("could not ssl_handshake");
+                               log_crypto_err_io("could not ssl_handshake", r);
                                exit(1);
                        }
                }
@@ -541,6 +601,8 @@ int main(int argc, char** argv)
                                break;
                        case 'p':
                                pp2_client = optarg;
+                               pp_init(&sldns_write_uint16,
+                                       &sldns_write_uint32);
                                break;
                        case 'a':
                                onarrival = 1;
index d193526..11eeb43 100644 (file)
@@ -76,10 +76,18 @@ static const char* zone_example_com =
 "out.example.com.      3600    IN      CNAME   www.example.com.\n"
 "plan.example.com.     3600    IN      CNAME   nonexist.example.com.\n"
 "redir.example.com.    3600    IN      DNAME   redir.example.org.\n"
+"redir2.example.com.   3600    IN      DNAME   redir2.example.org.\n"
+"obscured.redir2.example.com.  3600    IN      A       10.0.0.12\n"
+"under2.redir2.example.com.    3600    IN      DNAME   redir3.example.net.\n"
+"doubleobscured.under2.redir2.example.com.     3600    IN      A       10.0.0.13\n"
 "sub.example.com.      3600    IN      NS      ns1.sub.example.com.\n"
 "sub.example.com.      3600    IN      NS      ns2.sub.example.com.\n"
 "ns1.sub.example.com.  3600    IN      A       10.0.0.6\n"
 "ns2.sub.example.com.  3600    IN      AAAA    2001::7\n"
+"sub2.example.com.     3600    IN      NS      ns1.sub.example.com.\n"
+"obscured.sub2.example.com.    3600    IN      A       10.0.0.10\n"
+"under.sub2.example.com.       3600    IN      NS      ns.under.sub2.example.com.\n"
+"doubleobscured.under.sub2.example.com.        3600    IN      A       10.0.0.11\n"
 "*.wild.example.com.   3600    IN      A       10.0.0.8\n"
 "*.wild2.example.com.  3600    IN      CNAME   www.example.com.\n"
 "*.wild3.example.com.  3600    IN      A       10.0.0.8\n"
@@ -281,6 +289,54 @@ static struct q_ans example_com_queries[] = {
 "foo.abc.redir.example.com.    0       IN      CNAME   foo.abc.redir.example.org.\n"
        },
 
+       { "example.com", "redir2.example.com. DNAME", "",
+";flags QR AA rcode NOERROR\n"
+";answer section\n"
+"redir2.example.com.   3600    IN      DNAME   redir2.example.org.\n"
+       },
+
+       { "example.com", "abc.redir2.example.com. A", "",
+";flags QR AA rcode NOERROR\n"
+";answer section\n"
+"redir2.example.com.   3600    IN      DNAME   redir2.example.org.\n"
+"abc.redir2.example.com.       0       IN      CNAME   abc.redir2.example.org.\n"
+       },
+
+       { "example.com", "obscured.redir2.example.com. A", "",
+";flags QR AA rcode NOERROR\n"
+";answer section\n"
+"redir2.example.com.   3600    IN      DNAME   redir2.example.org.\n"
+"obscured.redir2.example.com.  0       IN      CNAME   obscured.redir2.example.org.\n"
+       },
+
+       { "example.com", "under2.redir2.example.com. A", "",
+";flags QR AA rcode NOERROR\n"
+";answer section\n"
+"redir2.example.com.   3600    IN      DNAME   redir2.example.org.\n"
+"under2.redir2.example.com.    0       IN      CNAME   under2.redir2.example.org.\n"
+       },
+
+       { "example.com", "doubleobscured.under2.redir2.example.com. A", "",
+";flags QR AA rcode NOERROR\n"
+";answer section\n"
+"redir2.example.com.   3600    IN      DNAME   redir2.example.org.\n"
+"doubleobscured.under2.redir2.example.com.     0       IN      CNAME   doubleobscured.under2.redir2.example.org.\n"
+       },
+
+       { "example.com", "foo.doubleobscured.under2.redir2.example.com. A", "",
+";flags QR AA rcode NOERROR\n"
+";answer section\n"
+"redir2.example.com.   3600    IN      DNAME   redir2.example.org.\n"
+"foo.doubleobscured.under2.redir2.example.com. 0       IN      CNAME   foo.doubleobscured.under2.redir2.example.org.\n"
+       },
+
+       { "example.com", "foo.under2.redir2.example.com. A", "",
+";flags QR AA rcode NOERROR\n"
+";answer section\n"
+"redir2.example.com.   3600    IN      DNAME   redir2.example.org.\n"
+"foo.under2.redir2.example.com.        0       IN      CNAME   foo.under2.redir2.example.org.\n"
+       },
+
        { "example.com", "sub.example.com. NS", "",
 ";flags QR rcode NOERROR\n"
 ";authority section\n"
@@ -357,6 +413,78 @@ static struct q_ans example_com_queries[] = {
 "ns2.sub.example.com.  3600    IN      AAAA    2001::7\n"
        },
 
+       { "example.com", "sub2.example.com. A", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com.     3600    IN      NS      ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com.  3600    IN      A       10.0.0.6\n"
+       },
+
+       { "example.com", "sub2.example.com. NS", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com.     3600    IN      NS      ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com.  3600    IN      A       10.0.0.6\n"
+       },
+
+       { "example.com", "obscured.sub2.example.com. A", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com.     3600    IN      NS      ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com.  3600    IN      A       10.0.0.6\n"
+       },
+
+       { "example.com", "abc.obscured.sub2.example.com. A", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com.     3600    IN      NS      ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com.  3600    IN      A       10.0.0.6\n"
+       },
+
+       { "example.com", "under.sub2.example.com. A", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com.     3600    IN      NS      ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com.  3600    IN      A       10.0.0.6\n"
+       },
+
+       { "example.com", "under.sub2.example.com. NS", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com.     3600    IN      NS      ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com.  3600    IN      A       10.0.0.6\n"
+       },
+
+       { "example.com", "abc.under.sub2.example.com. A", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com.     3600    IN      NS      ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com.  3600    IN      A       10.0.0.6\n"
+       },
+
+       { "example.com", "doubleobscured.under.sub2.example.com. A", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com.     3600    IN      NS      ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com.  3600    IN      A       10.0.0.6\n"
+       },
+
+       { "example.com", "abc.doubleobscured.under.sub2.example.com. A", "",
+";flags QR rcode NOERROR\n"
+";authority section\n"
+"sub2.example.com.     3600    IN      NS      ns1.sub.example.com.\n"
+";additional section\n"
+"ns1.sub.example.com.  3600    IN      A       10.0.0.6\n"
+       },
+
        { "example.com", "wild.example.com. A", "",
 ";flags QR AA rcode NOERROR\n"
 ";authority section\n"
index 757c514..a188049 100644 (file)
  *
  * This file contains PROXY protocol functions.
  */
-#include "config.h"
-#include "util/log.h"
 #include "util/proxy_protocol.h"
 
-int
-pp2_write_to_buf(struct sldns_buffer* buf, struct sockaddr_storage* src,
+/**
+ * Internal struct initialized with function pointers for writing uint16 and
+ * uint32.
+ */
+struct proxy_protocol_data {
+       void (*write_uint16)(void* buf, uint16_t data);
+       void (*write_uint32)(void* buf, uint32_t data);
+};
+struct proxy_protocol_data pp_data;
+
+/**
+ * Internal lookup table; could be further generic like sldns_lookup_table
+ * for all the future generic stuff.
+ */
+struct proxy_protocol_lookup_table {
+       int id;
+       const char *text;
+};
+
+/**
+ * Internal parsing error text; could be exposed with pp_lookup_error.
+ */
+static struct proxy_protocol_lookup_table pp_parse_errors_data[] = {
+       { PP_PARSE_NOERROR, "no parse error" },
+       { PP_PARSE_SIZE, "not enough space for header" },
+       { PP_PARSE_WRONG_HEADERv2, "could not match PROXYv2 header" },
+       { PP_PARSE_UNKNOWN_CMD, "unknown command" },
+       { PP_PARSE_UNKNOWN_FAM_PROT, "unknown family and protocol" },
+};
+
+void
+pp_init(void (*write_uint16)(void* buf, uint16_t data),
+       void (*write_uint32)(void* buf, uint32_t data)) {
+       pp_data.write_uint16 = write_uint16;
+       pp_data.write_uint32 = write_uint32;
+}
+
+const char*
+pp_lookup_error(enum pp_parse_errors error) {
+       return pp_parse_errors_data[error].text;
+}
+
+size_t
+pp2_write_to_buf(uint8_t* buf, size_t buflen,
+#ifdef INET6
+       struct sockaddr_storage* src,
+#else
+       struct sockaddr_in* src,
+#endif
        int stream)
 {
        int af;
+       size_t expected_size;
        if(!src) return 0;
        af = (int)((struct sockaddr_in*)src)->sin_family;
-       if(sldns_buffer_remaining(buf) <
-               PP2_HEADER_SIZE + (af==AF_INET?12:36)) {
+       expected_size = PP2_HEADER_SIZE + (af==AF_INET?12:36);
+       if(buflen < expected_size) {
                return 0;
        }
        /* sig */
-       sldns_buffer_write(buf, PP2_SIG, PP2_SIG_LEN);
+       memcpy(buf, PP2_SIG, PP2_SIG_LEN);
+       buf += PP2_SIG_LEN;
        /* version and command */
-       sldns_buffer_write_u8(buf, (PP2_VERSION << 4) | PP2_CMD_PROXY);
-       if(af==AF_INET) {
+       *buf = (PP2_VERSION << 4) | PP2_CMD_PROXY;
+       buf++;
+       switch(af) {
+       case AF_INET:
                /* family and protocol */
-               sldns_buffer_write_u8(buf,
-                       (PP2_AF_INET<<4) |
-                       (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM));
+               *buf = (PP2_AF_INET<<4) |
+                       (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM);
+               buf++;
                /* length */
-               sldns_buffer_write_u16(buf, 12);
+               (*pp_data.write_uint16)(buf, 12);
+               buf += 2;
                /* src addr */
-               sldns_buffer_write(buf,
+               memcpy(buf,
                        &((struct sockaddr_in*)src)->sin_addr.s_addr, 4);
+               buf += 4;
                /* dst addr */
-               sldns_buffer_write_u32(buf, 0);
+               (*pp_data.write_uint32)(buf, 0);
+               buf += 4;
                /* src port */
-               sldns_buffer_write(buf,
+               memcpy(buf,
                        &((struct sockaddr_in*)src)->sin_port, 2);
+               buf += 2;
+               /* dst addr */
                /* dst port */
-               sldns_buffer_write_u16(buf, 0);
-       } else {
+               (*pp_data.write_uint16)(buf, 12);
+               break;
+#ifdef INET6
+       case AF_INET6:
                /* family and protocol */
-               sldns_buffer_write_u8(buf,
-                       (PP2_AF_INET6<<4) |
-                       (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM));
+               *buf = (PP2_AF_INET6<<4) |
+                       (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM);
+               buf++;
                /* length */
-               sldns_buffer_write_u16(buf, 36);
+               (*pp_data.write_uint16)(buf, 36);
+               buf += 2;
                /* src addr */
-               sldns_buffer_write(buf,
+               memcpy(buf,
                        &((struct sockaddr_in6*)src)->sin6_addr, 16);
+               buf += 16;
                /* dst addr */
-               sldns_buffer_set_at(buf,
-                       sldns_buffer_position(buf), 0, 16);
-               sldns_buffer_skip(buf, 16);
+               memset(buf, 0, 16);
+               buf += 16;
                /* src port */
-               sldns_buffer_write(buf,
-                       &((struct sockaddr_in6*)src)->sin6_port, 2);
+               memcpy(buf, &((struct sockaddr_in6*)src)->sin6_port, 2);
+               buf += 2;
                /* dst port */
-               sldns_buffer_write_u16(buf, 0);
+               (*pp_data.write_uint16)(buf, 0);
+               break;
+#endif /* INET6 */
+       case AF_UNIX:
+               /* fallthrough */
+       default:
+               return 0;
        }
-       return 1;
+       return expected_size;
 }
 
-struct pp2_header*
-pp2_read_header(struct sldns_buffer* buf)
+int
+pp2_read_header(uint8_t* buf, size_t buflen)
 {
        size_t size;
-       struct pp2_header* header = (struct pp2_header*)sldns_buffer_begin(buf);
+       struct pp2_header* header = (struct pp2_header*)buf;
        /* Try to fail all the unsupported cases first. */
-       if(sldns_buffer_remaining(buf) < PP2_HEADER_SIZE) {
-               log_err("proxy_protocol: not enough space for header");
-               return NULL;
+       if(buflen < PP2_HEADER_SIZE) {
+               return PP_PARSE_SIZE;
        }
        /* Check for PROXYv2 header */
        if(memcmp(header, PP2_SIG, PP2_SIG_LEN) != 0 ||
                ((header->ver_cmd & 0xF0)>>4) != PP2_VERSION) {
-               log_err("proxy_protocol: could not match PROXYv2 header");
-               return NULL;
+               return PP_PARSE_WRONG_HEADERv2;
        }
        /* Check the length */
        size = PP2_HEADER_SIZE + ntohs(header->len);
-       if(sldns_buffer_remaining(buf) < size) {
-               log_err("proxy_protocol: not enough space for header");
-               return NULL;
+       if(buflen < size) {
+               return PP_PARSE_SIZE;
        }
        /* Check for supported commands */
        if((header->ver_cmd & 0xF) != PP2_CMD_LOCAL &&
                (header->ver_cmd & 0xF) != PP2_CMD_PROXY) {
-               log_err("proxy_protocol: unsupported command");
-               return NULL;
+               return PP_PARSE_UNKNOWN_CMD;
        }
        /* Check for supported family and protocol */
-       if(header->fam_prot != 0x00 /* AF_UNSPEC|UNSPEC */ &&
-               header->fam_prot != 0x11 /* AF_INET|STREAM */ &&
-               header->fam_prot != 0x12 /* AF_INET|DGRAM */ &&
-               header->fam_prot != 0x21 /* AF_INET6|STREAM */ &&
-               header->fam_prot != 0x22 /* AF_INET6|DGRAM */) {
-               log_err("proxy_protocol: unsupported family and protocol");
-               return NULL;
+       if(header->fam_prot != PP2_UNSPEC_UNSPEC &&
+               header->fam_prot != PP2_INET_STREAM &&
+               header->fam_prot != PP2_INET_DGRAM &&
+               header->fam_prot != PP2_INET6_STREAM &&
+               header->fam_prot != PP2_INET6_DGRAM &&
+               header->fam_prot != PP2_UNIX_STREAM &&
+               header->fam_prot != PP2_UNIX_DGRAM) {
+               return PP_PARSE_UNKNOWN_FAM_PROT;
        }
        /* We have a correct header */
-       return header;
+       return PP_PARSE_NOERROR;
 }
index 13cab9d..ca81065 100644 (file)
@@ -42,7 +42,7 @@
 #ifndef PROXY_PROTOCOL_H
 #define PROXY_PROTOCOL_H
 
-#include "sldns/sbuffer.h"
+#include "config.h"
 
 /** PROXYv2 minimum header size */
 #define PP2_HEADER_SIZE 16
 #define PP2_SIG "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
 #define PP2_SIG_LEN 12
 
-/** PROXYv2 version */
+/** PROXYv2 version (protocol value) */
 #define PP2_VERSION 0x2
 
 /**
- * PROXYv2 command.
+ * PROXYv2 command (protocol value).
  */
 enum pp2_command {
        PP2_CMD_LOCAL = 0x0,
@@ -63,7 +63,7 @@ enum pp2_command {
 };
 
 /**
- * PROXYv2 address family.
+ * PROXYv2 address family (protocol value).
  */
 enum pp2_af {
        PP2_AF_UNSPEC = 0x0,
@@ -73,7 +73,7 @@ enum pp2_af {
 };
 
 /**
- * PROXYv2 protocol.
+ * PROXYv2 protocol (protocol value).
  */
 enum pp2_protocol {
        PP2_PROT_UNSPEC = 0x0,
@@ -81,6 +81,19 @@ enum pp2_protocol {
        PP2_PROT_DGRAM = 0x2
 };
 
+/**
+ * Expected combinations of address family and protocol values used in checks.
+ */
+enum pp2_af_protocol_combination {
+       PP2_UNSPEC_UNSPEC = (PP2_AF_UNSPEC<<4)|PP2_PROT_UNSPEC,
+       PP2_INET_STREAM = (PP2_AF_INET<<4)|PP2_PROT_STREAM,
+       PP2_INET_DGRAM = (PP2_AF_INET<<4)|PP2_PROT_DGRAM,
+       PP2_INET6_STREAM = (PP2_AF_INET6<<4)|PP2_PROT_STREAM,
+       PP2_INET6_DGRAM = (PP2_AF_INET6<<4)|PP2_PROT_DGRAM,
+       PP2_UNIX_STREAM = (PP2_AF_UNIX<<4)|PP2_PROT_STREAM,
+       PP2_UNIX_DGRAM = (PP2_AF_UNIX<<4)|PP2_PROT_DGRAM
+};
+
 /**
  * PROXYv2 header.
  */
@@ -109,23 +122,56 @@ struct pp2_header {
        } addr;
 };
 
+/**
+ * PROXY parse errors.
+ */
+enum pp_parse_errors {
+       PP_PARSE_NOERROR = 0,
+       PP_PARSE_SIZE,
+       PP_PARSE_WRONG_HEADERv2,
+       PP_PARSE_UNKNOWN_CMD,
+       PP_PARSE_UNKNOWN_FAM_PROT,
+};
+
+/**
+ * Initialize the internal proxy structure.
+ * @param write_uint16: pointer to a function that can write uint16.
+ * @param write_uint32: pointer to a function that can write uint32.
+ */
+void pp_init(void (*write_uint16)(void* buf, uint16_t data),
+       void (*write_uint32)(void* buf, uint32_t data));
+
+/**
+ * Lookup the parsing error description.
+ * @param error: parsing error from pp2_read_header.
+ * @return the description.
+ */
+const char* pp_lookup_error(enum pp_parse_errors error);
+
 /**
  * Write a PROXYv2 header at the current position of the buffer.
- * @param buf: the buffer to write to.
+ * @param buf: pointer to the buffer to write data to.
+ * @param buflen: available size on the buffer.
  * @param src: the source address.
  * @param stream: if the protocol is stream or datagram.
  * @return 1 on success, 0 on failure.
  */
-int pp2_write_to_buf(struct sldns_buffer* buf, struct sockaddr_storage* src,
+size_t pp2_write_to_buf(uint8_t* buf, size_t buflen,
+#ifdef INET6
+       struct sockaddr_storage* src,
+#else
+       struct sockaddr_in* src,
+#endif
        int stream);
 
 /**
  * Read a PROXYv2 header from the current position of the buffer.
  * It does initial validation and returns a pointer to the buffer position on
  * success.
- * @param buf: the buffer to read from.
- * @return the pointer to the buffer position on success, NULL on error.
+ * @param buf: pointer to the buffer data to read from.
+ * @param buflen: available size on the buffer.
+ * @return parsing error, 0 on success.
  */
-struct pp2_header* pp2_read_header(struct sldns_buffer* buf);
+int pp2_read_header(uint8_t* buf, size_t buflen);
 
 #endif /* PROXY_PROTOCOL_H */
index c28dede..cf64e21 100644 (file)
@@ -39,6 +39,7 @@
  * This file contains functions for RFC 1982 serial number arithmetic.
  */
 #include "config.h"
+#include "util/rfc_1982.h"
 
 int
 compare_1982(uint32_t a, uint32_t b)
index 0e1b597..32797df 100644 (file)
  */
 #include "config.h"
 
+/** EDIT
+  * prevent warning from -Wmissing-prototypes
+  */
+#include "util/siphash.h"
+
 /* default: SipHash-2-4 */
 #define cROUNDS 2
 #define dROUNDS 4