Home | History | Annotate | Line # | Download | only in isccc
      1 /*	$NetBSD: ccmsg.c,v 1.8 2025/01/26 16:25:44 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0 AND ISC
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 /*
     17  * Copyright (C) 2001 Nominum, Inc.
     18  *
     19  * Permission to use, copy, modify, and/or distribute this software for any
     20  * purpose with or without fee is hereby granted, provided that the above
     21  * copyright notice and this permission notice appear in all copies.
     22  *
     23  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL
     24  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
     25  * OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY
     26  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     27  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     28  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     29  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     30  */
     31 
     32 /*! \file */
     33 
     34 #include <inttypes.h>
     35 
     36 #include <isc/mem.h>
     37 #include <isc/netmgr.h>
     38 #include <isc/result.h>
     39 #include <isc/string.h>
     40 #include <isc/util.h>
     41 
     42 #include <isccc/ccmsg.h>
     43 
     44 #define CCMSG_MAGIC	 ISC_MAGIC('C', 'C', 'm', 's')
     45 #define VALID_CCMSG(foo) ISC_MAGIC_VALID(foo, CCMSG_MAGIC)
     46 
     47 /*
     48  * Try parsing a message from the internal read_buffer and set state
     49  * accordingly. Returns true if a message was successfully parsed, false if not.
     50  * If no message could be parsed the ccmsg struct remains untouched.
     51  */
     52 static isc_result_t
     53 try_parse_message(isccc_ccmsg_t *ccmsg) {
     54 	REQUIRE(ccmsg != NULL);
     55 
     56 	uint32_t len = 0;
     57 	if (isc_buffer_peekuint32(ccmsg->buffer, &len) != ISC_R_SUCCESS) {
     58 		return ISC_R_NOMORE;
     59 	}
     60 	if (len == 0) {
     61 		return ISC_R_UNEXPECTEDEND;
     62 	}
     63 	if (len > ccmsg->maxsize) {
     64 		return ISC_R_RANGE;
     65 	}
     66 	if (isc_buffer_remaininglength(ccmsg->buffer) < sizeof(uint32_t) + len)
     67 	{
     68 		return ISC_R_NOMORE;
     69 	}
     70 	/* Skip the size we just peeked */
     71 	isc_buffer_forward(ccmsg->buffer, sizeof(uint32_t));
     72 	ccmsg->size = len;
     73 	return ISC_R_SUCCESS;
     74 }
     75 
     76 static void
     77 recv_data(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
     78 	  void *arg) {
     79 	isccc_ccmsg_t *ccmsg = arg;
     80 
     81 	REQUIRE(VALID_CCMSG(ccmsg));
     82 
     83 	REQUIRE(handle == ccmsg->handle);
     84 	if (eresult != ISC_R_SUCCESS) {
     85 		goto done;
     86 	}
     87 
     88 	REQUIRE(region != NULL);
     89 
     90 	/* Copy the received data to our reassembly buffer */
     91 	eresult = isc_buffer_copyregion(ccmsg->buffer, region);
     92 	if (eresult != ISC_R_SUCCESS) {
     93 		goto done;
     94 	}
     95 	isc_region_consume(region, region->length);
     96 
     97 	/* Try to parse a single message of the buffer */
     98 	eresult = try_parse_message(ccmsg);
     99 	/* No results from parsing, we need more data */
    100 	if (eresult == ISC_R_NOMORE) {
    101 		return;
    102 	}
    103 
    104 done:
    105 	isc_nm_read_stop(handle);
    106 	ccmsg->recv_cb(handle, eresult, ccmsg->recv_cbarg);
    107 
    108 	return;
    109 }
    110 
    111 void
    112 isccc_ccmsg_init(isc_mem_t *mctx, isc_nmhandle_t *handle,
    113 		 isccc_ccmsg_t *ccmsg) {
    114 	REQUIRE(mctx != NULL);
    115 	REQUIRE(handle != NULL);
    116 	REQUIRE(ccmsg != NULL);
    117 
    118 	*ccmsg = (isccc_ccmsg_t){
    119 		.magic = CCMSG_MAGIC,
    120 		.maxsize = 0xffffffffU, /* Largest message possible. */
    121 		.mctx = mctx,
    122 	};
    123 
    124 	/* Preallocate the buffer to maximum single TCP read */
    125 	isc_buffer_allocate(ccmsg->mctx, &ccmsg->buffer,
    126 			    UINT16_MAX + sizeof(uint16_t));
    127 
    128 	isc_nmhandle_attach(handle, &ccmsg->handle);
    129 }
    130 
    131 void
    132 isccc_ccmsg_setmaxsize(isccc_ccmsg_t *ccmsg, unsigned int maxsize) {
    133 	REQUIRE(VALID_CCMSG(ccmsg));
    134 
    135 	ccmsg->maxsize = maxsize;
    136 }
    137 
    138 void
    139 isccc_ccmsg_readmessage(isccc_ccmsg_t *ccmsg, isc_nm_cb_t cb, void *cbarg) {
    140 	REQUIRE(VALID_CCMSG(ccmsg));
    141 
    142 	if (ccmsg->size != 0) {
    143 		/* Remove the previously read message from the buffer */
    144 		isc_buffer_forward(ccmsg->buffer, ccmsg->size);
    145 		ccmsg->size = 0;
    146 		isc_buffer_trycompact(ccmsg->buffer);
    147 	}
    148 
    149 	ccmsg->recv_cb = cb;
    150 	ccmsg->recv_cbarg = cbarg;
    151 
    152 	/* If we have previous data still in the buffer, try to parse it */
    153 	isc_result_t result = try_parse_message(ccmsg);
    154 	if (result == ISC_R_NOMORE) {
    155 		/* We need to read more data */
    156 		isc_nm_read(ccmsg->handle, recv_data, ccmsg);
    157 		return;
    158 	}
    159 
    160 	ccmsg->recv_cb(ccmsg->handle, result, ccmsg->recv_cbarg);
    161 }
    162 
    163 static void
    164 ccmsg_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
    165 	isccc_ccmsg_t *ccmsg = arg;
    166 
    167 	REQUIRE(VALID_CCMSG(ccmsg));
    168 	REQUIRE(ccmsg->send_cb != NULL);
    169 
    170 	isc_nm_cb_t send_cb = ccmsg->send_cb;
    171 	ccmsg->send_cb = NULL;
    172 
    173 	send_cb(handle, eresult, ccmsg->send_cbarg);
    174 
    175 	isc_nmhandle_detach(&handle);
    176 }
    177 
    178 void
    179 isccc_ccmsg_sendmessage(isccc_ccmsg_t *ccmsg, isc_region_t *region,
    180 			isc_nm_cb_t cb, void *cbarg) {
    181 	REQUIRE(VALID_CCMSG(ccmsg));
    182 	REQUIRE(ccmsg->send_cb == NULL);
    183 
    184 	ccmsg->send_cb = cb;
    185 	ccmsg->send_cbarg = cbarg;
    186 
    187 	isc_nmhandle_ref(ccmsg->handle);
    188 	isc_nm_send(ccmsg->handle, region, ccmsg_senddone, ccmsg);
    189 }
    190 
    191 void
    192 isccc_ccmsg_disconnect(isccc_ccmsg_t *ccmsg) {
    193 	REQUIRE(VALID_CCMSG(ccmsg));
    194 
    195 	if (ccmsg->handle != NULL) {
    196 		isc_nm_read_stop(ccmsg->handle);
    197 		isc_nmhandle_close(ccmsg->handle);
    198 		isc_nmhandle_detach(&ccmsg->handle);
    199 	}
    200 }
    201 
    202 void
    203 isccc_ccmsg_invalidate(isccc_ccmsg_t *ccmsg) {
    204 	REQUIRE(VALID_CCMSG(ccmsg));
    205 	REQUIRE(ccmsg->handle == NULL);
    206 
    207 	ccmsg->magic = 0;
    208 
    209 	isc_buffer_free(&ccmsg->buffer);
    210 }
    211 
    212 void
    213 isccc_ccmsg_toregion(isccc_ccmsg_t *ccmsg, isccc_region_t *ccregion) {
    214 	REQUIRE(VALID_CCMSG(ccmsg));
    215 	REQUIRE(ccmsg->buffer);
    216 	REQUIRE(isc_buffer_remaininglength(ccmsg->buffer) >= ccmsg->size);
    217 
    218 	ccregion->rstart = isc_buffer_current(ccmsg->buffer);
    219 	ccregion->rend = ccregion->rstart + ccmsg->size;
    220 }
    221