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 https://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 /*! \file */
13 
14 /**
15  *    These are low-level routines for creating and parsing lightweight
16  *    resolver no-op request and response messages.
17  *
18  *    The no-op message is analogous to a ping packet: a packet is sent to
19  *    the resolver daemon and is simply echoed back. The opcode is intended
20  *    to allow a client to determine if the server is operational or not.
21  *
22  *    There are four main functions for the no-op opcode. One render
23  *    function converts a no-op request structure -- lwres_nooprequest_t --
24  *    to the lighweight resolver's canonical format. It is complemented by a
25  *    parse function that converts a packet in this canonical format to a
26  *    no-op request structure. Another render function converts the no-op
27  *    response structure -- lwres_noopresponse_t to the canonical format.
28  *    This is complemented by a parse function which converts a packet in
29  *    canonical format to a no-op response structure.
30  *
31  *    These structures are defined in \link lwres.h <lwres/lwres.h.> \endlink They are shown below.
32  *
33  * \code
34  * #define LWRES_OPCODE_NOOP       0x00000000U
35  *
36  * typedef struct {
37  *         uint16_t  datalength;
38  *         unsigned char   *data;
39  * } lwres_nooprequest_t;
40  *
41  * typedef struct {
42  *         uint16_t  datalength;
43  *         unsigned char   *data;
44  * } lwres_noopresponse_t;
45  * \endcode
46  *
47  *    Although the structures have different types, they are identical. This
48  *    is because the no-op opcode simply echos whatever data was sent: the
49  *    response is therefore identical to the request.
50  *
51  *    lwres_nooprequest_render() uses resolver context ctx to convert no-op
52  *    request structure req to canonical format. The packet header structure
53  *    pkt is initialised and transferred to buffer b. The contents of *req
54  *    are then appended to the buffer in canonical format.
55  *    lwres_noopresponse_render() performs the same task, except it converts
56  *    a no-op response structure lwres_noopresponse_t to the lightweight
57  *    resolver's canonical format.
58  *
59  *    lwres_nooprequest_parse() uses context ctx to convert the contents of
60  *    packet pkt to a lwres_nooprequest_t structure. Buffer b provides space
61  *    to be used for storing this structure. When the function succeeds, the
62  *    resulting lwres_nooprequest_t is made available through *structp.
63  *    lwres_noopresponse_parse() offers the same semantics as
64  *    lwres_nooprequest_parse() except it yields a lwres_noopresponse_t
65  *    structure.
66  *
67  *    lwres_noopresponse_free() and lwres_nooprequest_free() release the
68  *    memory in resolver context ctx that was allocated to the
69  *    lwres_noopresponse_t or lwres_nooprequest_t structures referenced via
70  *    structp.
71  *
72  * \section lwres_noop_return Return Values
73  *
74  *    The no-op opcode functions lwres_nooprequest_render(),
75  *    lwres_noopresponse_render() lwres_nooprequest_parse() and
76  *    lwres_noopresponse_parse() all return #LWRES_R_SUCCESS on success. They
77  *    return #LWRES_R_NOMEMORY if memory allocation fails.
78  *    #LWRES_R_UNEXPECTEDEND is returned if the available space in the buffer
79  *    b is too small to accommodate the packet header or the
80  *    lwres_nooprequest_t and lwres_noopresponse_t structures.
81  *    lwres_nooprequest_parse() and lwres_noopresponse_parse() will return
82  *    #LWRES_R_UNEXPECTEDEND if the buffer is not empty after decoding the
83  *    received packet. These functions will return #LWRES_R_FAILURE if
84  *    pktflags in the packet header structure #lwres_lwpacket_t indicate that
85  *    the packet is not a response to an earlier query.
86  *
87  * \section lwres_noop_see See Also
88  *
89  *    lwpacket.c
90  */
91 
92 #include <config.h>
93 
94 #include <assert.h>
95 #include <inttypes.h>
96 #include <stdlib.h>
97 #include <string.h>
98 
99 #include <lwres/lwbuffer.h>
100 #include <lwres/lwpacket.h>
101 #include <lwres/lwres.h>
102 #include <lwres/result.h>
103 
104 #include "context_p.h"
105 #include "assert_p.h"
106 
107 /*% Uses resolver context ctx to convert no-op request structure req to canonical format. */
108 lwres_result_t
lwres_nooprequest_render(lwres_context_t * ctx,lwres_nooprequest_t * req,lwres_lwpacket_t * pkt,lwres_buffer_t * b)109 lwres_nooprequest_render(lwres_context_t *ctx, lwres_nooprequest_t *req,
110 			 lwres_lwpacket_t *pkt, lwres_buffer_t *b)
111 {
112 	unsigned char *buf;
113 	size_t buflen;
114 	int ret;
115 	size_t payload_length;
116 
117 	REQUIRE(ctx != NULL);
118 	REQUIRE(req != NULL);
119 	REQUIRE(pkt != NULL);
120 	REQUIRE(b != NULL);
121 
122 	payload_length = sizeof(uint16_t) + req->datalength;
123 
124 	buflen = LWRES_LWPACKET_LENGTH + payload_length;
125 	buf = CTXMALLOC(buflen);
126 	if (buf == NULL)
127 		return (LWRES_R_NOMEMORY);
128 	lwres_buffer_init(b, buf, (unsigned int)buflen);
129 
130 	pkt->length = (uint32_t)buflen;
131 	pkt->version = LWRES_LWPACKETVERSION_0;
132 	pkt->pktflags &= ~LWRES_LWPACKETFLAG_RESPONSE;
133 	pkt->opcode = LWRES_OPCODE_NOOP;
134 	pkt->result = 0;
135 	pkt->authtype = 0;
136 	pkt->authlength = 0;
137 
138 	ret = lwres_lwpacket_renderheader(b, pkt);
139 	if (ret != LWRES_R_SUCCESS) {
140 		lwres_buffer_invalidate(b);
141 		CTXFREE(buf, buflen);
142 		return (ret);
143 	}
144 
145 	INSIST(SPACE_OK(b, payload_length));
146 
147 	/*
148 	 * Put the length and the data.  We know this will fit because we
149 	 * just checked for it.
150 	 */
151 	lwres_buffer_putuint16(b, req->datalength);
152 	lwres_buffer_putmem(b, req->data, req->datalength);
153 
154 	INSIST(LWRES_BUFFER_AVAILABLECOUNT(b) == 0);
155 
156 	return (LWRES_R_SUCCESS);
157 }
158 
159 /*% Converts a no-op response structure lwres_noopresponse_t to the lightweight resolver's canonical format. */
160 
161 lwres_result_t
lwres_noopresponse_render(lwres_context_t * ctx,lwres_noopresponse_t * req,lwres_lwpacket_t * pkt,lwres_buffer_t * b)162 lwres_noopresponse_render(lwres_context_t *ctx, lwres_noopresponse_t *req,
163 			  lwres_lwpacket_t *pkt, lwres_buffer_t *b)
164 {
165 	unsigned char *buf;
166 	size_t buflen;
167 	int ret;
168 	size_t payload_length;
169 
170 	REQUIRE(ctx != NULL);
171 	REQUIRE(req != NULL);
172 	REQUIRE(pkt != NULL);
173 	REQUIRE(b != NULL);
174 
175 	payload_length = sizeof(uint16_t) + req->datalength;
176 
177 	buflen = LWRES_LWPACKET_LENGTH + payload_length;
178 	buf = CTXMALLOC(buflen);
179 	if (buf == NULL)
180 		return (LWRES_R_NOMEMORY);
181 	lwres_buffer_init(b, buf, (unsigned int)buflen);
182 
183 	pkt->length = (uint32_t)buflen;
184 	pkt->version = LWRES_LWPACKETVERSION_0;
185 	pkt->pktflags |= LWRES_LWPACKETFLAG_RESPONSE;
186 	pkt->opcode = LWRES_OPCODE_NOOP;
187 	pkt->authtype = 0;
188 	pkt->authlength = 0;
189 
190 	ret = lwres_lwpacket_renderheader(b, pkt);
191 	if (ret != LWRES_R_SUCCESS) {
192 		lwres_buffer_invalidate(b);
193 		CTXFREE(buf, buflen);
194 		return (ret);
195 	}
196 
197 	INSIST(SPACE_OK(b, payload_length));
198 
199 	/*
200 	 * Put the length and the data.  We know this will fit because we
201 	 * just checked for it.
202 	 */
203 	lwres_buffer_putuint16(b, req->datalength);
204 	lwres_buffer_putmem(b, req->data, req->datalength);
205 
206 	INSIST(LWRES_BUFFER_AVAILABLECOUNT(b) == 0);
207 
208 	return (LWRES_R_SUCCESS);
209 }
210 
211 /*% Uses context ctx to convert the contents of packet pkt to a lwres_nooprequest_t structure. */
212 lwres_result_t
lwres_nooprequest_parse(lwres_context_t * ctx,lwres_buffer_t * b,lwres_lwpacket_t * pkt,lwres_nooprequest_t ** structp)213 lwres_nooprequest_parse(lwres_context_t *ctx, lwres_buffer_t *b,
214 			lwres_lwpacket_t *pkt, lwres_nooprequest_t **structp)
215 {
216 	int ret;
217 	lwres_nooprequest_t *req;
218 
219 	REQUIRE(ctx != NULL);
220 	REQUIRE(b != NULL);
221 	REQUIRE(pkt != NULL);
222 	REQUIRE(structp != NULL && *structp == NULL);
223 
224 	if ((pkt->pktflags & LWRES_LWPACKETFLAG_RESPONSE) != 0)
225 		return (LWRES_R_FAILURE);
226 
227 	req = CTXMALLOC(sizeof(lwres_nooprequest_t));
228 	if (req == NULL)
229 		return (LWRES_R_NOMEMORY);
230 
231 	if (!SPACE_REMAINING(b, sizeof(uint16_t))) {
232 		ret = LWRES_R_UNEXPECTEDEND;
233 		goto out;
234 	}
235 	req->datalength = lwres_buffer_getuint16(b);
236 
237 	if (!SPACE_REMAINING(b, req->datalength)) {
238 		ret = LWRES_R_UNEXPECTEDEND;
239 		goto out;
240 	}
241 	req->data = b->base + b->current;
242 	lwres_buffer_forward(b, req->datalength);
243 
244 	if (LWRES_BUFFER_REMAINING(b) != 0) {
245 		ret = LWRES_R_TRAILINGDATA;
246 		goto out;
247 	}
248 
249 	/* success! */
250 	*structp = req;
251 	return (LWRES_R_SUCCESS);
252 
253 	/* Error return */
254  out:
255 	CTXFREE(req, sizeof(lwres_nooprequest_t));
256 	return (ret);
257 }
258 
259 /*% Offers the same semantics as lwres_nooprequest_parse() except it yields a lwres_noopresponse_t structure. */
260 lwres_result_t
lwres_noopresponse_parse(lwres_context_t * ctx,lwres_buffer_t * b,lwres_lwpacket_t * pkt,lwres_noopresponse_t ** structp)261 lwres_noopresponse_parse(lwres_context_t *ctx, lwres_buffer_t *b,
262 			 lwres_lwpacket_t *pkt, lwres_noopresponse_t **structp)
263 {
264 	int ret;
265 	lwres_noopresponse_t *req;
266 
267 	REQUIRE(ctx != NULL);
268 	REQUIRE(b != NULL);
269 	REQUIRE(pkt != NULL);
270 	REQUIRE(structp != NULL && *structp == NULL);
271 
272 	if ((pkt->pktflags & LWRES_LWPACKETFLAG_RESPONSE) == 0)
273 		return (LWRES_R_FAILURE);
274 
275 	req = CTXMALLOC(sizeof(lwres_noopresponse_t));
276 	if (req == NULL)
277 		return (LWRES_R_NOMEMORY);
278 
279 	if (!SPACE_REMAINING(b, sizeof(uint16_t))) {
280 		ret = LWRES_R_UNEXPECTEDEND;
281 		goto out;
282 	}
283 	req->datalength = lwres_buffer_getuint16(b);
284 
285 	if (!SPACE_REMAINING(b, req->datalength)) {
286 		ret = LWRES_R_UNEXPECTEDEND;
287 		goto out;
288 	}
289 	req->data = b->base + b->current;
290 
291 	lwres_buffer_forward(b, req->datalength);
292 	if (LWRES_BUFFER_REMAINING(b) != 0) {
293 		ret = LWRES_R_TRAILINGDATA;
294 		goto out;
295 	}
296 
297 	/* success! */
298 	*structp = req;
299 	return (LWRES_R_SUCCESS);
300 
301 	/* Error return */
302  out:
303 	CTXFREE(req, sizeof(lwres_noopresponse_t));
304 	return (ret);
305 }
306 
307 /*% Release the memory in resolver context ctx. */
308 void
lwres_noopresponse_free(lwres_context_t * ctx,lwres_noopresponse_t ** structp)309 lwres_noopresponse_free(lwres_context_t *ctx, lwres_noopresponse_t **structp)
310 {
311 	lwres_noopresponse_t *noop;
312 
313 	REQUIRE(ctx != NULL);
314 	REQUIRE(structp != NULL && *structp != NULL);
315 
316 	noop = *structp;
317 	*structp = NULL;
318 
319 	CTXFREE(noop, sizeof(lwres_noopresponse_t));
320 }
321 
322 /*% Release the memory in resolver context ctx. */
323 void
lwres_nooprequest_free(lwres_context_t * ctx,lwres_nooprequest_t ** structp)324 lwres_nooprequest_free(lwres_context_t *ctx, lwres_nooprequest_t **structp)
325 {
326 	lwres_nooprequest_t *noop;
327 
328 	REQUIRE(ctx != NULL);
329 	REQUIRE(structp != NULL && *structp != NULL);
330 
331 	noop = *structp;
332 	*structp = NULL;
333 
334 	CTXFREE(noop, sizeof(lwres_nooprequest_t));
335 }
336