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