xref: /netbsd-src/external/mpl/bind/dist/lib/isccc/ccmsg.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
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