]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib: cleanup
authorMarek Vavruša <marek.vavrusa@nic.cz>
Wed, 22 Apr 2015 20:55:06 +0000 (22:55 +0200)
committerMarek Vavruša <marek.vavrusa@nic.cz>
Wed, 22 Apr 2015 20:55:06 +0000 (22:55 +0200)
lib/layer.h
lib/nsrep.c
lib/resolve.c

index 2189581a8fe12748bcc6e7726dd538a7c9ebfda1..e812efc72b8dfffec4962dd42638408f5f0bbb61 100644 (file)
  * @{
  */
 
-#include <libknot/processing/layer.h>
-#include <libknot/packet/pkt.h>
-
 #include "lib/defines.h"
-
-struct kr_context;
-struct kr_rplan;
-
-/**
- * Processing module parameters.
- *
- * @note These parameters are passed to each processing layer.
- */
-struct kr_layer_param {
-       struct kr_context *ctx;
-       struct kr_rplan *rplan;
-       knot_pkt_t *answer;
-};
+#include "lib/resolve.h"
 
 #ifndef NDEBUG
 /** @internal Print a debug message related to resolution. */
index 02f9bf9af5aa45b4dc58125662d93a6819a18552..9b8fb37ed3534196d8baa8d009ebfd97d7656d21 100644 (file)
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <assert.h>
+
 #include "lib/nsrep.h"
+#include "lib/defines.h"
 #include "lib/generic/pack.h"
 
 /** @internal Macro to set address structure. */
index 014a5a180edef6a54f74d0d4289f114788bb633c..55ba4e118e0a58dc386ec672d2377debf8160049 100644 (file)
  */
 
 #include <stdio.h>
+#include <sys/fcntl.h>
 
 #include <libknot/internal/mempool.h>
 #include <libknot/processing/requestor.h>
 #include <libknot/rrtype/rdname.h>
 #include <libknot/descriptor.h>
+#include <libknot/internal/net.h>
 #include <dnssec/random.h>
 
 #include "lib/rplan.h"
@@ -27,7 +29,7 @@
 #include "lib/layer/itercache.h"
 #include "lib/layer/iterate.h"
 
-#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(param->rplan), "resl",  fmt)
+#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(rplan), "resl",  fmt)
 
 /* Defines */
 #define ITER_LIMIT 50
@@ -42,160 +44,314 @@ static int invalidate_ns(struct kr_rplan *rplan, struct kr_query *qry)
        return kr_zonecut_del(&qry->zone_cut, qry->ns.name, rdata);
 }
 
-static int ns_resolve_addr(struct kr_query *cur, struct kr_layer_param *param)
+static int ns_resolve_addr(struct kr_query *qry, struct kr_request *param)
 {
-       if (kr_rplan_satisfies(cur, cur->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_A) ||
-           kr_rplan_satisfies(cur, cur->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_AAAA) ||
-           cur->flags & QUERY_AWAIT_ADDR) {
+       struct kr_rplan *rplan = &param->rplan;
+       if (kr_rplan_satisfies(qry, qry->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_A) ||
+           kr_rplan_satisfies(qry, qry->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_AAAA) ||
+           qry->flags & QUERY_AWAIT_ADDR) {
                DEBUG_MSG("=> dependency loop, bailing out\n");
-               kr_rplan_pop(param->rplan, cur);
-               return KNOT_EOK;
+               kr_rplan_pop(rplan, qry);
+               return KNOT_STATE_PRODUCE;
        }
 
-       (void) kr_rplan_push(param->rplan, cur, cur->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_AAAA);
-       (void) kr_rplan_push(param->rplan, cur, cur->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_A);
-       cur->flags |= QUERY_AWAIT_ADDR;
-       return KNOT_EOK;
+       (void) kr_rplan_push(rplan, qry, qry->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_AAAA);
+       (void) kr_rplan_push(rplan, qry, qry->ns.name, KNOT_CLASS_IN, KNOT_RRTYPE_A);
+       qry->flags |= QUERY_AWAIT_ADDR;
+       return KNOT_STATE_PRODUCE;
 }
 
-static int iterate(struct knot_requestor *requestor, struct kr_layer_param *param)
+static void prepare_layers(struct kr_request *param)
+{
+       struct kr_context *ctx = param->ctx;
+       for (size_t i = 0; i < ctx->modules->len; ++i) {
+               struct kr_module *mod = &ctx->modules->at[i];
+               if (mod->layer) {
+                       knot_overlay_add(&param->overlay, mod->layer(), param);
+               }
+       }
+}
+
+static int connected(struct sockaddr *addr, int proto, struct timeval *timeout)
+{
+       unsigned flags = (proto == SOCK_STREAM) ? O_NONBLOCK : 0;
+       int fd = net_connected_socket(proto, (struct sockaddr_storage *)addr, NULL, flags);
+       if (fd < 0) {
+               return kr_error(ECONNREFUSED);
+       }
+
+       /* Workaround for timeout, as we have no control over
+        * connect() time limit in blocking mode. */
+       if (proto == SOCK_STREAM) {
+               fd_set set;
+               FD_ZERO(&set);
+               FD_SET(fd, &set);
+               int ret = select(fd + 1, NULL, &set, NULL, timeout);
+               if (ret == 0) {
+                       close(fd);
+                       return kr_error(ETIMEDOUT);
+               }
+               if (ret < 0) {
+                       close(fd);
+                       return kr_error(ECONNREFUSED);
+               }
+               fcntl(fd, F_SETFL, 0);
+       }
+
+       return fd;
+}
+
+static int sendrecv(struct sockaddr *addr, int proto, const knot_pkt_t *query, knot_pkt_t *resp)
 {
-       int ret = KNOT_EOK;
        struct timeval timeout = { KR_CONN_RTT_MAX / 1000, 0 };
-       struct kr_rplan *rplan = param->rplan;
-       struct kr_query *cur = kr_rplan_current(rplan);
+       auto_close int fd = connected(addr, proto, &timeout);
+       if (fd < 0) {
+               return fd;
+       }
 
-#ifndef NDEBUG
-       char name_str[KNOT_DNAME_MAXLEN], type_str[16];
-       knot_dname_to_str(name_str, cur->sname, sizeof(name_str));
-       knot_rrtype_to_string(cur->stype, type_str, sizeof(type_str));
-       DEBUG_MSG("query '%s %s'\n", name_str, type_str);
-#endif
+       /* Send packet */
+       int ret = 0;
+       if (proto == SOCK_STREAM) {
+               ret = tcp_send_msg(fd, query->wire, query->size, &timeout);
+       } else {
+               ret = udp_send_msg(fd, query->wire, query->size, NULL);
+       }
+       if (ret != query->size) {
+               return kr_error(EIO);
+       }
 
-       /* Elect best nameserver candidate. */
-       kr_nsrep_elect(&cur->ns, &cur->zone_cut.nsset);
-       if (cur->ns.score < KR_NS_VALID) {
-               DEBUG_MSG("=> no valid NS left\n");
-               kr_rplan_pop(param->rplan, cur);
-               return KNOT_EOK;
+       /* Receive it */
+       if (proto == SOCK_STREAM) {
+               ret = tcp_recv_msg(fd, resp->wire, resp->max_size, &timeout);
        } else {
-               if (cur->ns.addr.ip.sa_family == AF_UNSPEC) {
-                       DEBUG_MSG("=> ns missing A/AAAA, fetching\n");
-                       return ns_resolve_addr(cur, param);
-               }
+               ret = udp_recv_msg(fd, resp->wire, resp->max_size, &timeout);
+       }
+       if (ret <= 0) {
+               return kr_error(ETIMEDOUT);
        }
 
-       /* Prepare query resolution. */
-       int mode = (cur->flags & QUERY_TCP) ? 0 : KNOT_RQ_UDP;
-       knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MIN_PKTSIZE, requestor->mm);
-       struct knot_request *tx = knot_request_make(requestor->mm, &cur->ns.addr.ip, NULL, query, mode);
-       knot_requestor_enqueue(requestor, tx);
+       /* Parse and return */
+       resp->size = ret;
+       if (knot_pkt_parse(resp, 0) != 0) {
+               return kr_error(EBADMSG);
+       }
 
-       /* Resolve and check status. */
-       ret = knot_requestor_exec(requestor, &timeout);
-       if (ret != KNOT_EOK) {
-               /* Network error, retry over TCP. */
-               if (ret != KNOT_LAYER_ERROR && !(cur->flags & QUERY_TCP)) {
-                       DEBUG_MSG("=> ns unreachable, retrying over TCP\n");
-                       cur->flags |= QUERY_TCP;
-                       return iterate(requestor, param);
+       return kr_ok();
+}
+
+int kr_resolve(struct kr_context* ctx, knot_pkt_t *answer,
+               const knot_dname_t *qname, uint16_t qclass, uint16_t qtype)
+{
+       if (ctx == NULL || answer == NULL || qname == NULL) {
+               return kr_error(EINVAL);
+       }
+
+       /* Create memory pool */
+       mm_ctx_t pool;
+       mm_ctx_mempool(&pool, MM_DEFAULT_BLKSIZE);
+       knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MIN_PKTSIZE, &pool);
+       knot_pkt_t *resp = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, &pool);
+       if (!query || !resp) {
+               mp_delete(pool.ctx);
+               return kr_error(ENOMEM);
+       }
+
+       /* Initialize context. */
+       struct kr_request request;
+       request.pool = pool;
+       kr_resolve_begin(&request, ctx, answer);
+#ifndef NDEBUG
+       struct kr_rplan *rplan = &request.rplan; /* for DEBUG_MSG */
+#endif
+
+       /* Resolve query, iteratively */
+       int proto = 0;
+       struct sockaddr *addr = NULL;
+       unsigned iter_count = 0;
+       int state = kr_resolve_query(&request, qname, qclass, qtype);
+       while (state == KNOT_STATE_PRODUCE) {
+               /* Hardlimit on iterative queries */
+               if (++iter_count > ITER_LIMIT) {
+                       DEBUG_MSG("iteration limit %d reached\n", ITER_LIMIT);
+                       state = KNOT_STATE_FAIL;
+                       break;
                }
-               /* Resolution failed, invalidate current NS and reset to UDP. */
-               DEBUG_MSG("=> resolution failed: '%s', invalidating\n", knot_strerror(ret));
-               if (invalidate_ns(rplan, cur) == 0) {
-                       cur->flags &= ~QUERY_TCP;
+               /* Produce next query or finish */
+               state = kr_resolve_produce(&request, &addr, &proto, query);
+               while (state == KNOT_STATE_CONSUME) {
+                       /* Get answer from nameserver and consume it */
+                       int ret = sendrecv(addr, proto, query, resp);
+                       if (ret != 0) {
+                               DEBUG_MSG("sendrecv: %s\n", kr_strerror(ret));
+                               resp->size = 0;
+                       }
+                       state = kr_resolve_consume(&request, resp);
+                       knot_pkt_clear(resp);
                }
-               return KNOT_EOK;
+               knot_pkt_clear(query);
        }
 
-       /* Pop query if resolved. */
-       if (cur->flags & QUERY_RESOLVED) {
-               kr_rplan_pop(rplan, cur);
-       }
+       /* Cleanup */
+       kr_resolve_finish(&request, state);
+       mp_delete(pool.ctx);
+       return state == KNOT_STATE_DONE ? 0 : kr_error(EIO);
+}
+
+
+int kr_resolve_begin(struct kr_request *request, struct kr_context *ctx, knot_pkt_t *answer)
+{
+       /* Initialize request */
+       kr_rplan_init(&request->rplan, ctx, &request->pool);
+       knot_overlay_init(&request->overlay, &request->pool);
+       request->ctx = ctx;
+       request->answer = answer;
+       prepare_layers(request);
 
-       return ret;
+       /* Expect first query */
+       return KNOT_STATE_CONSUME;
 }
 
-static void prepare_layers(struct knot_requestor *req, struct kr_layer_param *param)
+int kr_resolve_query(struct kr_request *request, const knot_dname_t *qname, uint16_t qclass, uint16_t qtype)
 {
-       struct kr_context *ctx = param->ctx;
-       for (size_t i = 0; i < ctx->modules->len; ++i) {
-               struct kr_module *mod = &ctx->modules->at[i];
-               if (mod->layer) {
-                       knot_requestor_overlay(req, mod->layer(), param);
-               }
+       struct kr_rplan *rplan = &request->rplan;
+       struct kr_query *qry = kr_rplan_push(rplan, NULL, qname, qclass, qtype);
+       if (!qry) {
+               return KNOT_STATE_FAIL;
        }
+
+       /* Create answer packet */
+       knot_pkt_t *answer = request->answer;
+       knot_wire_set_qr(answer->wire);
+       knot_wire_clear_aa(answer->wire);
+       knot_wire_set_ra(answer->wire);
+       knot_wire_set_rcode(answer->wire, KNOT_RCODE_NOERROR);
+
+       /* Expect answer */
+       return KNOT_STATE_PRODUCE;
 }
 
-static int resolve_iterative(struct kr_layer_param *param, mm_ctx_t *pool)
+int kr_resolve_consume(struct kr_request *request, knot_pkt_t *packet)
 {
-/* Initialize requestor. */
-       struct knot_requestor requestor;
-       knot_requestor_init(&requestor, pool);
-       prepare_layers(&requestor, param);
+       struct kr_rplan *rplan = &request->rplan;
+       struct kr_query *qry = kr_rplan_current(rplan);
 
-       /* Iteratively solve the query. */
-       int ret = KNOT_EOK;
-       unsigned iter_count = 0;
-       while((ret == KNOT_EOK) && !kr_rplan_empty(param->rplan)) {
-               ret = iterate(&requestor, param);
-               if (++iter_count > ITER_LIMIT) {
-                       DEBUG_MSG("iteration limit %d reached\n", ITER_LIMIT);
-                       ret = KNOT_ELIMIT;
+       /* Empty resolution plan, push packet as the new query */
+       if (kr_rplan_empty(&request->rplan)) {
+               const knot_dname_t *qname = knot_pkt_qname(packet);
+               uint16_t qclass = knot_pkt_qclass(packet);
+               uint16_t qtype = knot_pkt_qtype(packet);
+               return kr_resolve_query(request, qname, qclass, qtype);
+       }
+
+       /* Different processing for network error */
+       int state = KNOT_STATE_FAIL;
+       if (packet->size == 0) {
+               /* Network error, retry over TCP. */
+               if (!(qry->flags & QUERY_TCP)) {
+                       /** @todo This should just penalize UDP and elect next best. */
+                       DEBUG_MSG("=> ns unreachable, retrying over TCP\n");
+                       qry->flags |= QUERY_TCP;
+                       return KNOT_STATE_CONSUME; /* Try again */
                }
+       } else {
+               state = knot_overlay_consume(&request->overlay, packet);
        }
 
-       /* Set RCODE on internal failure. */
-       if (ret != KNOT_EOK) {
-               if (knot_wire_get_rcode(param->answer->wire) == KNOT_RCODE_NOERROR) {
-                       knot_wire_set_rcode(param->answer->wire, KNOT_RCODE_SERVFAIL);
+       /* Resolution failed, invalidate current NS and reset to UDP. */
+       if (state == KNOT_STATE_FAIL) {
+               DEBUG_MSG("=> resolution failed, invalidating\n");
+               if (invalidate_ns(rplan, qry) == 0) {
+                       qry->flags &= ~QUERY_TCP;
                }
        }
 
-       DEBUG_MSG("finished: %s, mempool: %zu B\n", knot_strerror(ret), (size_t) mp_total_size(pool->ctx));
-       knot_requestor_clear(&requestor);
-       return ret;
+       /* Pop query if resolved. */
+       if (qry->flags & QUERY_RESOLVED) {
+               kr_rplan_pop(rplan, qry);
+
+       }
+
+       knot_overlay_reset(&request->overlay);
+       return kr_rplan_empty(&request->rplan) ? KNOT_STATE_DONE : KNOT_STATE_PRODUCE;
 }
 
-int kr_resolve(struct kr_context* ctx, knot_pkt_t *answer,
-               const knot_dname_t *qname, uint16_t qclass, uint16_t qtype)
+int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *proto, knot_pkt_t *packet)
 {
-       if (ctx == NULL || answer == NULL || qname == NULL) {
-               return KNOT_EINVAL;
+       struct kr_rplan *rplan = &request->rplan;
+       struct kr_query *qry = kr_rplan_current(rplan);
+       
+       /* No query left for resolution */
+       if (kr_rplan_empty(rplan)) {
+               return KNOT_STATE_FAIL;
        }
 
-       /* Initialize context. */
-       int ret = KNOT_EOK;
-       mm_ctx_t rplan_pool;
-       mm_ctx_mempool(&rplan_pool, MM_DEFAULT_BLKSIZE);
-       struct kr_rplan rplan;
-       kr_rplan_init(&rplan, ctx, &rplan_pool);
-       struct kr_layer_param param;
-       param.ctx = ctx;
-       param.rplan = &rplan;
-       param.answer = answer;
-
-       /* Push query to resolution plan. */
-       struct kr_query *qry = kr_rplan_push(&rplan, NULL, qname, qclass, qtype);
-       if (qry != NULL) {
-               ret = resolve_iterative(&param, &rplan_pool);
+#ifndef NDEBUG
+       char name_str[KNOT_DNAME_MAXLEN], type_str[16];
+       knot_dname_to_str(name_str, qry->sname, sizeof(name_str));
+       knot_rrtype_to_string(qry->stype, type_str, sizeof(type_str));
+       DEBUG_MSG("query '%s %s'\n", type_str, name_str);
+#endif
+
+       /* Resolve current query and produce dependent or finish */
+       int state = knot_overlay_produce(&request->overlay, packet);
+       switch(state) {
+       case KNOT_STATE_FAIL: return state; break;
+       case KNOT_STATE_CONSUME: break;
+       default: /* Current query is done */
+               knot_overlay_reset(&request->overlay);
+               kr_rplan_pop(rplan, qry);
+               return kr_rplan_empty(rplan) ? KNOT_STATE_DONE : KNOT_STATE_PRODUCE;
+       }
+
+       /* Elect best nameserver candidate */
+       kr_nsrep_elect(&qry->ns, &qry->zone_cut.nsset);
+       if (qry->ns.score < KR_NS_VALID) {
+               DEBUG_MSG("=> no valid NS left\n");
+               knot_overlay_reset(&request->overlay);
+               kr_rplan_pop(rplan, qry);
+               return KNOT_STATE_PRODUCE;
        } else {
-               ret = KNOT_ENOMEM;
+               if (qry->ns.addr.ip.sa_family == AF_UNSPEC) {
+                       DEBUG_MSG("=> ns missing A/AAAA, fetching\n");
+                       knot_overlay_reset(&request->overlay);
+                       return ns_resolve_addr(qry, request);
+               }
        }
 
-       /* Check flags. */
-       knot_wire_set_qr(answer->wire);
-       knot_wire_clear_aa(answer->wire);
-       knot_wire_set_ra(answer->wire);
+#ifndef NDEBUG
+       char qname_str[KNOT_DNAME_MAXLEN], zonecut_str[KNOT_DNAME_MAXLEN], ns_str[SOCKADDR_STRLEN];
+       knot_dname_to_str(qname_str, knot_pkt_qname(packet), sizeof(qname_str));
+       struct sockaddr *addr = &qry->ns.addr.ip;
+       inet_ntop(addr->sa_family, kr_nsrep_inaddr(qry->ns.addr), ns_str, sizeof(ns_str));
+       knot_dname_to_str(zonecut_str, qry->zone_cut.name, sizeof(zonecut_str));
+       DEBUG_MSG("=> querying: '%s' zone cut: '%s' m12n: '%s'\n", ns_str, zonecut_str, qname_str);
+#endif
+
+       /* Issue dependent query to this address */
+       *dst = &qry->ns.addr.ip;
+       *proto = (qry->flags & QUERY_TCP) ? SOCK_STREAM : SOCK_DGRAM;
+       return state;
+}
+
+int kr_resolve_finish(struct kr_request *request, int state)
+{
+       struct kr_rplan *rplan = &request->rplan;
+       DEBUG_MSG("finished: %d, mempool: %zu B\n", state, (size_t) mp_total_size(request->pool.ctx));
 
        /* Resolution success, commit cache transaction. */
-       if (ret == KNOT_EOK) {
-               kr_rplan_txn_commit(&rplan);
+       if (state == KNOT_STATE_DONE) {
+               kr_rplan_txn_commit(rplan);
+       } else {
+               /* Error during procesing, internal failure */
+               knot_pkt_t *answer = request->answer;
+               if (knot_wire_get_rcode(answer->wire) == KNOT_RCODE_NOERROR) {
+                       knot_wire_set_rcode(answer->wire, KNOT_RCODE_SERVFAIL);
+               }
        }
 
        /* Clean up. */
-       kr_rplan_deinit(&rplan);
-       mp_delete(rplan_pool.ctx);
-
-       return ret;
+       knot_overlay_reset(&request->overlay);
+       knot_overlay_deinit(&request->overlay);
+       kr_rplan_deinit(&request->rplan);
+       return KNOT_STATE_DONE;
 }