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