"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.16.7/lib/isc/netmgr/tcpdns.c" (4 Sep 2020, 15317 Bytes) of package /linux/misc/dns/bind9/9.16.7/bind-9.16.7.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "tcpdns.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 9.17.3_vs_9.17.4.

    1 /*
    2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
    3  *
    4  * This Source Code Form is subject to the terms of the Mozilla Public
    5  * License, v. 2.0. If a copy of the MPL was not distributed with this
    6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
    7  *
    8  * See the COPYRIGHT file distributed with this work for additional
    9  * information regarding copyright ownership.
   10  */
   11 
   12 #include <unistd.h>
   13 #include <uv.h>
   14 
   15 #include <isc/atomic.h>
   16 #include <isc/buffer.h>
   17 #include <isc/condition.h>
   18 #include <isc/magic.h>
   19 #include <isc/mem.h>
   20 #include <isc/netmgr.h>
   21 #include <isc/random.h>
   22 #include <isc/refcount.h>
   23 #include <isc/region.h>
   24 #include <isc/result.h>
   25 #include <isc/sockaddr.h>
   26 #include <isc/thread.h>
   27 #include <isc/util.h>
   28 
   29 #include "netmgr-int.h"
   30 #include "uv-compat.h"
   31 
   32 #define TCPDNS_CLIENTS_PER_CONN 23
   33 /*%<
   34  *
   35  * Maximum number of simultaneous handles in flight supported for a single
   36  * connected TCPDNS socket. This value was chosen arbitrarily, and may be
   37  * changed in the future.
   38  */
   39 
   40 static void
   41 dnslisten_readcb(isc_nmhandle_t *handle, isc_region_t *region, void *arg);
   42 
   43 static void
   44 resume_processing(void *arg);
   45 
   46 static void
   47 tcpdns_close_direct(isc_nmsocket_t *sock);
   48 
   49 static inline size_t
   50 dnslen(unsigned char *base) {
   51     return ((base[0] << 8) + (base[1]));
   52 }
   53 
   54 /*
   55  * Regular TCP buffer, should suffice in most cases.
   56  */
   57 #define NM_REG_BUF 4096
   58 /*
   59  * Two full DNS packets with lengths.
   60  * netmgr receives 64k at most so there's no risk
   61  * of overrun.
   62  */
   63 #define NM_BIG_BUF (65535 + 2) * 2
   64 static inline void
   65 alloc_dnsbuf(isc_nmsocket_t *sock, size_t len) {
   66     REQUIRE(len <= NM_BIG_BUF);
   67 
   68     if (sock->buf == NULL) {
   69         /* We don't have the buffer at all */
   70         size_t alloc_len = len < NM_REG_BUF ? NM_REG_BUF : NM_BIG_BUF;
   71         sock->buf = isc_mem_allocate(sock->mgr->mctx, alloc_len);
   72         sock->buf_size = alloc_len;
   73     } else {
   74         /* We have the buffer but it's too small */
   75         sock->buf = isc_mem_reallocate(sock->mgr->mctx, sock->buf,
   76                            NM_BIG_BUF);
   77         sock->buf_size = NM_BIG_BUF;
   78     }
   79 }
   80 
   81 static void
   82 timer_close_cb(uv_handle_t *handle) {
   83     isc_nmsocket_t *sock = (isc_nmsocket_t *)uv_handle_get_data(handle);
   84     INSIST(VALID_NMSOCK(sock));
   85     isc_nmsocket_detach(&sock);
   86 }
   87 
   88 static void
   89 dnstcp_readtimeout(uv_timer_t *timer) {
   90     isc_nmsocket_t *sock =
   91         (isc_nmsocket_t *)uv_handle_get_data((uv_handle_t *)timer);
   92 
   93     REQUIRE(VALID_NMSOCK(sock));
   94     REQUIRE(sock->tid == isc_nm_tid());
   95     tcpdns_close_direct(sock);
   96 }
   97 
   98 /*
   99  * Accept callback for TCP-DNS connection.
  100  */
  101 static isc_result_t
  102 dnslisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
  103     isc_nmsocket_t *dnslistensock = (isc_nmsocket_t *)cbarg;
  104     isc_nmsocket_t *dnssock = NULL;
  105 
  106     REQUIRE(VALID_NMSOCK(dnslistensock));
  107     REQUIRE(dnslistensock->type == isc_nm_tcpdnslistener);
  108 
  109     if (result != ISC_R_SUCCESS) {
  110         return (result);
  111     }
  112 
  113     if (dnslistensock->accept_cb.accept != NULL) {
  114         result = dnslistensock->accept_cb.accept(
  115             handle, ISC_R_SUCCESS, dnslistensock->accept_cbarg);
  116         if (result != ISC_R_SUCCESS) {
  117             return (result);
  118         }
  119     }
  120 
  121     /* We need to create a 'wrapper' dnssocket for this connection */
  122     dnssock = isc_mem_get(handle->sock->mgr->mctx, sizeof(*dnssock));
  123     isc__nmsocket_init(dnssock, handle->sock->mgr, isc_nm_tcpdnssocket,
  124                handle->sock->iface);
  125 
  126     dnssock->extrahandlesize = dnslistensock->extrahandlesize;
  127     isc_nmsocket_attach(dnslistensock, &dnssock->listener);
  128     isc_nmsocket_attach(handle->sock, &dnssock->outer);
  129     dnssock->peer = handle->sock->peer;
  130     dnssock->read_timeout = handle->sock->mgr->init;
  131     dnssock->tid = isc_nm_tid();
  132     dnssock->closehandle_cb = resume_processing;
  133 
  134     uv_timer_init(&dnssock->mgr->workers[isc_nm_tid()].loop,
  135               &dnssock->timer);
  136     dnssock->timer.data = dnssock;
  137     dnssock->timer_initialized = true;
  138     uv_timer_start(&dnssock->timer, dnstcp_readtimeout,
  139                dnssock->read_timeout, 0);
  140 
  141     isc_nm_read(handle, dnslisten_readcb, dnssock);
  142 
  143     return (ISC_R_SUCCESS);
  144 }
  145 
  146 /*
  147  * Process a single packet from the incoming buffer.
  148  *
  149  * Return ISC_R_SUCCESS and attach 'handlep' to a handle if something
  150  * was processed; return ISC_R_NOMORE if there isn't a full message
  151  * to be processed.
  152  *
  153  * The caller will need to unreference the handle.
  154  */
  155 static isc_result_t
  156 processbuffer(isc_nmsocket_t *dnssock, isc_nmhandle_t **handlep) {
  157     size_t len;
  158 
  159     REQUIRE(VALID_NMSOCK(dnssock));
  160     REQUIRE(handlep != NULL && *handlep == NULL);
  161 
  162     /*
  163      * If we don't even have the length yet, we can't do
  164      * anything.
  165      */
  166     if (dnssock->buf_len < 2) {
  167         return (ISC_R_NOMORE);
  168     }
  169 
  170     /*
  171      * Process the first packet from the buffer, leaving
  172      * the rest (if any) for later.
  173      */
  174     len = dnslen(dnssock->buf);
  175     if (len <= dnssock->buf_len - 2) {
  176         isc_nmhandle_t *dnshandle = isc__nmhandle_get(dnssock, NULL,
  177                                   NULL);
  178         isc_nmsocket_t *listener = dnssock->listener;
  179 
  180         if (listener != NULL && listener->rcb.recv != NULL) {
  181             listener->rcb.recv(
  182                 dnshandle,
  183                 &(isc_region_t){ .base = dnssock->buf + 2,
  184                          .length = len },
  185                 listener->rcbarg);
  186         }
  187 
  188         len += 2;
  189         dnssock->buf_len -= len;
  190         if (len > 0) {
  191             memmove(dnssock->buf, dnssock->buf + len,
  192                 dnssock->buf_len);
  193         }
  194 
  195         *handlep = dnshandle;
  196         return (ISC_R_SUCCESS);
  197     }
  198 
  199     return (ISC_R_NOMORE);
  200 }
  201 
  202 /*
  203  * We've got a read on our underlying socket, need to check if we have
  204  * a complete DNS packet and, if so - call the callback
  205  */
  206 static void
  207 dnslisten_readcb(isc_nmhandle_t *handle, isc_region_t *region, void *arg) {
  208     isc_nmsocket_t *dnssock = (isc_nmsocket_t *)arg;
  209     unsigned char *base = NULL;
  210     bool done = false;
  211     size_t len;
  212 
  213     REQUIRE(VALID_NMSOCK(dnssock));
  214     REQUIRE(VALID_NMHANDLE(handle));
  215     REQUIRE(dnssock->tid == isc_nm_tid());
  216 
  217     if (region == NULL) {
  218         /* Connection closed */
  219         isc__nm_tcpdns_close(dnssock);
  220         return;
  221     }
  222 
  223     base = region->base;
  224     len = region->length;
  225 
  226     if (dnssock->buf_len + len > dnssock->buf_size) {
  227         alloc_dnsbuf(dnssock, dnssock->buf_len + len);
  228     }
  229     memmove(dnssock->buf + dnssock->buf_len, base, len);
  230     dnssock->buf_len += len;
  231 
  232     dnssock->read_timeout = (atomic_load(&dnssock->keepalive)
  233                      ? dnssock->mgr->keepalive
  234                      : dnssock->mgr->idle);
  235 
  236     do {
  237         isc_result_t result;
  238         isc_nmhandle_t *dnshandle = NULL;
  239 
  240         result = processbuffer(dnssock, &dnshandle);
  241         if (result != ISC_R_SUCCESS) {
  242             /*
  243              * There wasn't anything in the buffer to process.
  244              */
  245             return;
  246         }
  247 
  248         /*
  249          * We have a packet: stop timeout timers
  250          */
  251         atomic_store(&dnssock->outer->processing, true);
  252         if (dnssock->timer_initialized) {
  253             uv_timer_stop(&dnssock->timer);
  254         }
  255 
  256         if (atomic_load(&dnssock->sequential)) {
  257             /*
  258              * We're in sequential mode and we processed
  259              * one packet, so we're done until the next read
  260              * completes.
  261              */
  262             isc_nm_pauseread(dnssock->outer);
  263             done = true;
  264         } else {
  265             /*
  266              * We're pipelining, so we now resume processing
  267              * packets until the clients-per-connection limit
  268              * is reached (as determined by the number of
  269              * active handles on the socket). When the limit
  270              * is reached, pause reading.
  271              */
  272             if (atomic_load(&dnssock->ah) >=
  273                 TCPDNS_CLIENTS_PER_CONN) {
  274                 isc_nm_pauseread(dnssock->outer);
  275                 done = true;
  276             }
  277         }
  278 
  279         isc_nmhandle_unref(dnshandle);
  280     } while (!done);
  281 }
  282 
  283 /*
  284  * isc_nm_listentcpdns listens for connections and accepts
  285  * them immediately, then calls the cb for each incoming DNS packet
  286  * (with 2-byte length stripped) - just like for UDP packet.
  287  */
  288 isc_result_t
  289 isc_nm_listentcpdns(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb,
  290             void *cbarg, isc_nm_accept_cb_t accept_cb,
  291             void *accept_cbarg, size_t extrahandlesize, int backlog,
  292             isc_quota_t *quota, isc_nmsocket_t **sockp) {
  293     /* A 'wrapper' socket object with outer set to true TCP socket */
  294     isc_nmsocket_t *dnslistensock = isc_mem_get(mgr->mctx,
  295                             sizeof(*dnslistensock));
  296     isc_result_t result;
  297 
  298     REQUIRE(VALID_NM(mgr));
  299 
  300     isc__nmsocket_init(dnslistensock, mgr, isc_nm_tcpdnslistener, iface);
  301     dnslistensock->rcb.recv = cb;
  302     dnslistensock->rcbarg = cbarg;
  303     dnslistensock->accept_cb.accept = accept_cb;
  304     dnslistensock->accept_cbarg = accept_cbarg;
  305     dnslistensock->extrahandlesize = extrahandlesize;
  306 
  307     /* We set dnslistensock->outer to a true listening socket */
  308     result = isc_nm_listentcp(mgr, iface, dnslisten_acceptcb, dnslistensock,
  309                   extrahandlesize, backlog, quota,
  310                   &dnslistensock->outer);
  311     if (result == ISC_R_SUCCESS) {
  312         atomic_store(&dnslistensock->listening, true);
  313         *sockp = dnslistensock;
  314         return (ISC_R_SUCCESS);
  315     } else {
  316         atomic_store(&dnslistensock->closed, true);
  317         isc_nmsocket_detach(&dnslistensock);
  318         return (result);
  319     }
  320 }
  321 
  322 void
  323 isc__nm_tcpdns_stoplistening(isc_nmsocket_t *sock) {
  324     REQUIRE(VALID_NMSOCK(sock));
  325     REQUIRE(sock->type == isc_nm_tcpdnslistener);
  326 
  327     atomic_store(&sock->listening, false);
  328     atomic_store(&sock->closed, true);
  329     sock->rcb.recv = NULL;
  330     sock->rcbarg = NULL;
  331 
  332     if (sock->outer != NULL) {
  333         isc_nm_stoplistening(sock->outer);
  334         isc_nmsocket_detach(&sock->outer);
  335     }
  336 }
  337 
  338 void
  339 isc_nm_tcpdns_sequential(isc_nmhandle_t *handle) {
  340     REQUIRE(VALID_NMHANDLE(handle));
  341 
  342     if (handle->sock->type != isc_nm_tcpdnssocket ||
  343         handle->sock->outer == NULL) {
  344         return;
  345     }
  346 
  347     /*
  348      * We don't want pipelining on this connection. That means
  349      * that we need to pause after reading each request, and
  350      * resume only after the request has been processed. This
  351      * is done in resume_processing(), which is the socket's
  352      * closehandle_cb callback, called whenever a handle
  353      * is released.
  354      */
  355     isc_nm_pauseread(handle->sock->outer);
  356     atomic_store(&handle->sock->sequential, true);
  357 }
  358 
  359 void
  360 isc_nm_tcpdns_keepalive(isc_nmhandle_t *handle) {
  361     REQUIRE(VALID_NMHANDLE(handle));
  362 
  363     if (handle->sock->type != isc_nm_tcpdnssocket ||
  364         handle->sock->outer == NULL) {
  365         return;
  366     }
  367 
  368     atomic_store(&handle->sock->keepalive, true);
  369     atomic_store(&handle->sock->outer->keepalive, true);
  370 }
  371 
  372 static void
  373 resume_processing(void *arg) {
  374     isc_nmsocket_t *sock = (isc_nmsocket_t *)arg;
  375     isc_result_t result;
  376 
  377     REQUIRE(VALID_NMSOCK(sock));
  378     REQUIRE(sock->tid == isc_nm_tid());
  379 
  380     if (sock->type != isc_nm_tcpdnssocket || sock->outer == NULL) {
  381         return;
  382     }
  383 
  384     if (atomic_load(&sock->ah) == 0) {
  385         /* Nothing is active; sockets can timeout now */
  386         atomic_store(&sock->outer->processing, false);
  387         if (sock->timer_initialized) {
  388             uv_timer_start(&sock->timer, dnstcp_readtimeout,
  389                        sock->read_timeout, 0);
  390         }
  391     }
  392 
  393     /*
  394      * For sequential sockets: Process what's in the buffer, or
  395      * if there aren't any messages buffered, resume reading.
  396      */
  397     if (atomic_load(&sock->sequential)) {
  398         isc_nmhandle_t *handle = NULL;
  399 
  400         result = processbuffer(sock, &handle);
  401         if (result == ISC_R_SUCCESS) {
  402             atomic_store(&sock->outer->processing, true);
  403             if (sock->timer_initialized) {
  404                 uv_timer_stop(&sock->timer);
  405             }
  406             isc_nmhandle_unref(handle);
  407         } else if (sock->outer != NULL) {
  408             isc_nm_resumeread(sock->outer);
  409         }
  410 
  411         return;
  412     }
  413 
  414     /*
  415      * For pipelined sockets: If we're under the clients-per-connection
  416      * limit, resume processing until we reach the limit again.
  417      */
  418     do {
  419         isc_nmhandle_t *dnshandle = NULL;
  420 
  421         result = processbuffer(sock, &dnshandle);
  422         if (result != ISC_R_SUCCESS) {
  423             /*
  424              * Nothing in the buffer; resume reading.
  425              */
  426             if (sock->outer != NULL) {
  427                 isc_nm_resumeread(sock->outer);
  428             }
  429 
  430             break;
  431         }
  432 
  433         if (sock->timer_initialized) {
  434             uv_timer_stop(&sock->timer);
  435         }
  436         atomic_store(&sock->outer->processing, true);
  437         isc_nmhandle_unref(dnshandle);
  438     } while (atomic_load(&sock->ah) < TCPDNS_CLIENTS_PER_CONN);
  439 }
  440 
  441 static void
  442 tcpdnssend_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
  443     isc__nm_uvreq_t *req = (isc__nm_uvreq_t *)cbarg;
  444 
  445     UNUSED(handle);
  446 
  447     req->cb.send(req->handle, result, req->cbarg);
  448     isc_mem_put(req->sock->mgr->mctx, req->uvbuf.base, req->uvbuf.len);
  449     isc__nm_uvreq_put(&req, req->handle->sock);
  450 }
  451 
  452 void
  453 isc__nm_async_tcpdnssend(isc__networker_t *worker, isc__netievent_t *ev0) {
  454     isc_result_t result;
  455     isc__netievent_tcpdnssend_t *ievent =
  456         (isc__netievent_tcpdnssend_t *)ev0;
  457     isc__nm_uvreq_t *req = ievent->req;
  458     isc_nmsocket_t *sock = ievent->sock;
  459 
  460     REQUIRE(worker->id == sock->tid);
  461 
  462     result = ISC_R_NOTCONNECTED;
  463     if (atomic_load(&sock->active)) {
  464         isc_region_t r;
  465 
  466         r.base = (unsigned char *)req->uvbuf.base;
  467         r.length = req->uvbuf.len;
  468         result = isc__nm_tcp_send(sock->outer->tcphandle, &r,
  469                       tcpdnssend_cb, req);
  470     }
  471 
  472     if (result != ISC_R_SUCCESS) {
  473         req->cb.send(req->handle, result, req->cbarg);
  474         isc_mem_put(sock->mgr->mctx, req->uvbuf.base, req->uvbuf.len);
  475         isc__nm_uvreq_put(&req, sock);
  476     }
  477 }
  478 
  479 /*
  480  * isc__nm_tcp_send sends buf to a peer on a socket.
  481  */
  482 isc_result_t
  483 isc__nm_tcpdns_send(isc_nmhandle_t *handle, isc_region_t *region,
  484             isc_nm_cb_t cb, void *cbarg) {
  485     isc__nm_uvreq_t *uvreq = NULL;
  486 
  487     REQUIRE(VALID_NMHANDLE(handle));
  488 
  489     isc_nmsocket_t *sock = handle->sock;
  490 
  491     REQUIRE(VALID_NMSOCK(sock));
  492     REQUIRE(sock->type == isc_nm_tcpdnssocket);
  493 
  494     uvreq = isc__nm_uvreq_get(sock->mgr, sock);
  495     uvreq->handle = handle;
  496     isc_nmhandle_ref(uvreq->handle);
  497     uvreq->cb.send = cb;
  498     uvreq->cbarg = cbarg;
  499 
  500     uvreq->uvbuf.base = isc_mem_get(sock->mgr->mctx, region->length + 2);
  501     uvreq->uvbuf.len = region->length + 2;
  502     *(uint16_t *)uvreq->uvbuf.base = htons(region->length);
  503     memmove(uvreq->uvbuf.base + 2, region->base, region->length);
  504 
  505     if (sock->tid == isc_nm_tid()) {
  506         isc_region_t r;
  507 
  508         r.base = (unsigned char *)uvreq->uvbuf.base;
  509         r.length = uvreq->uvbuf.len;
  510 
  511         return (isc__nm_tcp_send(sock->outer->tcphandle, &r,
  512                      tcpdnssend_cb, uvreq));
  513     } else {
  514         isc__netievent_tcpdnssend_t *ievent = NULL;
  515 
  516         ievent = isc__nm_get_ievent(sock->mgr, netievent_tcpdnssend);
  517         ievent->req = uvreq;
  518         ievent->sock = sock;
  519 
  520         isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
  521                        (isc__netievent_t *)ievent);
  522 
  523         return (ISC_R_SUCCESS);
  524     }
  525 
  526     return (ISC_R_UNEXPECTED);
  527 }
  528 
  529 static void
  530 tcpdns_close_direct(isc_nmsocket_t *sock) {
  531     REQUIRE(sock->tid == isc_nm_tid());
  532     /* We don't need atomics here, it's all in single network thread */
  533 
  534     if (sock->timer_initialized) {
  535         /*
  536          * We need to fire the timer callback to clean it up,
  537          * it will then call us again (via detach) so that we
  538          * can finally close the socket.
  539          */
  540         sock->timer_initialized = false;
  541         uv_timer_stop(&sock->timer);
  542         uv_close((uv_handle_t *)&sock->timer, timer_close_cb);
  543     } else {
  544         /*
  545          * At this point we're certain that there are no external
  546          * references, we can close everything.
  547          */
  548         if (sock->outer != NULL) {
  549             sock->outer->rcb.recv = NULL;
  550             isc_nmsocket_detach(&sock->outer);
  551         }
  552         if (sock->listener != NULL) {
  553             isc_nmsocket_detach(&sock->listener);
  554         }
  555         atomic_store(&sock->closed, true);
  556     }
  557 }
  558 
  559 void
  560 isc__nm_tcpdns_close(isc_nmsocket_t *sock) {
  561     REQUIRE(VALID_NMSOCK(sock));
  562     REQUIRE(sock->type == isc_nm_tcpdnssocket);
  563 
  564     if (sock->tid == isc_nm_tid()) {
  565         tcpdns_close_direct(sock);
  566     } else {
  567         isc__netievent_tcpdnsclose_t *ievent =
  568             isc__nm_get_ievent(sock->mgr, netievent_tcpdnsclose);
  569 
  570         ievent->sock = sock;
  571         isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
  572                        (isc__netievent_t *)ievent);
  573     }
  574 }
  575 
  576 void
  577 isc__nm_async_tcpdnsclose(isc__networker_t *worker, isc__netievent_t *ev0) {
  578     isc__netievent_tcpdnsclose_t *ievent =
  579         (isc__netievent_tcpdnsclose_t *)ev0;
  580 
  581     REQUIRE(worker->id == ievent->sock->tid);
  582 
  583     tcpdns_close_direct(ievent->sock);
  584 }