/* $NetBSD: tcpmsg.c,v 1.1.2.2 2024/02/24 13:07:01 martin Exp $ */ /* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * * SPDX-License-Identifier: MPL-2.0 * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, you can obtain one at https://mozilla.org/MPL/2.0/. * * See the COPYRIGHT file distributed with this work for additional * information regarding copyright ownership. */ /*! \file */ #include #include #include #include #include #include #include #include #ifdef TCPMSG_DEBUG #include /* Required for printf. */ #define XDEBUG(x) printf x #else /* ifdef TCPMSG_DEBUG */ #define XDEBUG(x) #endif /* ifdef TCPMSG_DEBUG */ #define TCPMSG_MAGIC ISC_MAGIC('T', 'C', 'P', 'm') #define VALID_TCPMSG(foo) ISC_MAGIC_VALID(foo, TCPMSG_MAGIC) static void recv_length(isc_task_t *, isc_event_t *); static void recv_message(isc_task_t *, isc_event_t *); static void recv_length(isc_task_t *task, isc_event_t *ev_in) { isc_socketevent_t *ev = (isc_socketevent_t *)ev_in; isc_event_t *dev; dns_tcpmsg_t *tcpmsg = ev_in->ev_arg; isc_region_t region; isc_result_t result; INSIST(VALID_TCPMSG(tcpmsg)); dev = &tcpmsg->event; tcpmsg->address = ev->address; if (ev->result != ISC_R_SUCCESS) { tcpmsg->result = ev->result; goto send_and_free; } /* * Success. */ tcpmsg->size = ntohs(tcpmsg->size); if (tcpmsg->size == 0) { tcpmsg->result = ISC_R_UNEXPECTEDEND; goto send_and_free; } if (tcpmsg->size > tcpmsg->maxsize) { tcpmsg->result = ISC_R_RANGE; goto send_and_free; } region.base = isc_mem_get(tcpmsg->mctx, tcpmsg->size); region.length = tcpmsg->size; if (region.base == NULL) { tcpmsg->result = ISC_R_NOMEMORY; goto send_and_free; } XDEBUG(("Allocated %d bytes\n", tcpmsg->size)); isc_buffer_init(&tcpmsg->buffer, region.base, region.length); result = isc_socket_recv(tcpmsg->sock, ®ion, 0, task, recv_message, tcpmsg); if (result != ISC_R_SUCCESS) { tcpmsg->result = result; goto send_and_free; } isc_event_free(&ev_in); return; send_and_free: isc_task_send(tcpmsg->task, &dev); tcpmsg->task = NULL; isc_event_free(&ev_in); return; } static void recv_message(isc_task_t *task, isc_event_t *ev_in) { isc_socketevent_t *ev = (isc_socketevent_t *)ev_in; isc_event_t *dev; dns_tcpmsg_t *tcpmsg = ev_in->ev_arg; (void)task; INSIST(VALID_TCPMSG(tcpmsg)); dev = &tcpmsg->event; tcpmsg->address = ev->address; if (ev->result != ISC_R_SUCCESS) { tcpmsg->result = ev->result; goto send_and_free; } tcpmsg->result = ISC_R_SUCCESS; isc_buffer_add(&tcpmsg->buffer, ev->n); XDEBUG(("Received %u bytes (of %d)\n", ev->n, tcpmsg->size)); send_and_free: isc_task_send(tcpmsg->task, &dev); tcpmsg->task = NULL; isc_event_free(&ev_in); } void dns_tcpmsg_init(isc_mem_t *mctx, isc_socket_t *sock, dns_tcpmsg_t *tcpmsg) { REQUIRE(mctx != NULL); REQUIRE(sock != NULL); REQUIRE(tcpmsg != NULL); tcpmsg->magic = TCPMSG_MAGIC; tcpmsg->size = 0; tcpmsg->buffer.base = NULL; tcpmsg->buffer.length = 0; tcpmsg->maxsize = 65535; /* Largest message possible. */ tcpmsg->mctx = mctx; tcpmsg->sock = sock; tcpmsg->task = NULL; /* None yet. */ tcpmsg->result = ISC_R_UNEXPECTED; /* None yet. */ /* Should probably initialize the event here, but it can wait. */ } void dns_tcpmsg_setmaxsize(dns_tcpmsg_t *tcpmsg, unsigned int maxsize) { REQUIRE(VALID_TCPMSG(tcpmsg)); REQUIRE(maxsize < 65536); tcpmsg->maxsize = maxsize; } isc_result_t dns_tcpmsg_readmessage(dns_tcpmsg_t *tcpmsg, isc_task_t *task, isc_taskaction_t action, void *arg) { isc_result_t result; isc_region_t region; REQUIRE(VALID_TCPMSG(tcpmsg)); REQUIRE(task != NULL); REQUIRE(tcpmsg->task == NULL); /* not currently in use */ if (tcpmsg->buffer.base != NULL) { isc_mem_put(tcpmsg->mctx, tcpmsg->buffer.base, tcpmsg->buffer.length); tcpmsg->buffer.base = NULL; tcpmsg->buffer.length = 0; } tcpmsg->task = task; tcpmsg->action = action; tcpmsg->arg = arg; tcpmsg->result = ISC_R_UNEXPECTED; /* unknown right now */ ISC_EVENT_INIT(&tcpmsg->event, sizeof(isc_event_t), 0, 0, DNS_EVENT_TCPMSG, action, arg, tcpmsg, NULL, NULL); region.base = (unsigned char *)&tcpmsg->size; region.length = 2; /* uint16_t */ result = isc_socket_recv(tcpmsg->sock, ®ion, 0, tcpmsg->task, recv_length, tcpmsg); if (result != ISC_R_SUCCESS) { tcpmsg->task = NULL; } return (result); } void dns_tcpmsg_cancelread(dns_tcpmsg_t *tcpmsg) { REQUIRE(VALID_TCPMSG(tcpmsg)); isc_socket_cancel(tcpmsg->sock, NULL, ISC_SOCKCANCEL_RECV); } void dns_tcpmsg_keepbuffer(dns_tcpmsg_t *tcpmsg, isc_buffer_t *buffer) { REQUIRE(VALID_TCPMSG(tcpmsg)); REQUIRE(buffer != NULL); *buffer = tcpmsg->buffer; tcpmsg->buffer.base = NULL; tcpmsg->buffer.length = 0; } #if 0 void dns_tcpmsg_freebuffer(dns_tcpmsg_t *tcpmsg) { REQUIRE(VALID_TCPMSG(tcpmsg)); if (tcpmsg->buffer.base == NULL) { return; } isc_mem_put(tcpmsg->mctx, tcpmsg->buffer.base, tcpmsg->buffer.length); tcpmsg->buffer.base = NULL; tcpmsg->buffer.length = 0; } #endif /* if 0 */ void dns_tcpmsg_invalidate(dns_tcpmsg_t *tcpmsg) { REQUIRE(VALID_TCPMSG(tcpmsg)); tcpmsg->magic = 0; if (tcpmsg->buffer.base != NULL) { isc_mem_put(tcpmsg->mctx, tcpmsg->buffer.base, tcpmsg->buffer.length); tcpmsg->buffer.base = NULL; tcpmsg->buffer.length = 0; } }