1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2009-2010 The FreeBSD Foundation
5 * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
6 * All rights reserved.
7 *
8 * This software was developed by Pawel Jakub Dawidek under sponsorship from
9 * the FreeBSD Foundation.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: stable/11/sbin/hastd/hast_proto.c 330449 2018-03-05 07:26:05Z eadler $");
35
36#include <sys/endian.h>
37
38#include <errno.h>
39#include <strings.h>
40
41#include <hast.h>
42#include <ebuf.h>
43#include <nv.h>
44#include <pjdlog.h>
45#include <proto.h>
46
47#ifdef HAVE_CRYPTO
48#include "hast_checksum.h"
49#endif
50#include "hast_compression.h"
51#include "hast_proto.h"
52
53struct hast_main_header {
54	/* Protocol version. */
55	uint8_t		version;
56	/* Size of nv headers. */
57	uint32_t	size;
58} __packed;
59
60typedef int hps_send_t(const struct hast_resource *, struct nv *nv, void **,
61    size_t *, bool *);
62typedef int hps_recv_t(const struct hast_resource *, struct nv *nv, void **,
63    size_t *, bool *);
64
65struct hast_pipe_stage {
66	const char	*hps_name;
67	hps_send_t	*hps_send;
68	hps_recv_t	*hps_recv;
69};
70
71static struct hast_pipe_stage pipeline[] = {
72	{ "compression", compression_send, compression_recv },
73#ifdef HAVE_CRYPTO
74	{ "checksum", checksum_send, checksum_recv }
75#endif
76};
77
78/*
79 * Send the given nv structure via conn.
80 * We keep headers in nv structure and pass data in separate argument.
81 * There can be no data at all (data is NULL then).
82 */
83int
84hast_proto_send(const struct hast_resource *res, struct proto_conn *conn,
85    struct nv *nv, const void *data, size_t size)
86{
87	struct hast_main_header hdr;
88	struct ebuf *eb;
89	bool freedata;
90	void *dptr, *hptr;
91	size_t hsize;
92	int ret;
93
94	dptr = (void *)(uintptr_t)data;
95	freedata = false;
96	ret = -1;
97
98	if (data != NULL) {
99		unsigned int ii;
100
101		for (ii = 0; ii < sizeof(pipeline) / sizeof(pipeline[0]);
102		    ii++) {
103			(void)pipeline[ii].hps_send(res, nv, &dptr, &size,
104			    &freedata);
105		}
106		nv_add_uint32(nv, size, "size");
107		if (nv_error(nv) != 0) {
108			errno = nv_error(nv);
109			goto end;
110		}
111	}
112
113	eb = nv_hton(nv);
114	if (eb == NULL)
115		goto end;
116
117	hdr.version = res != NULL ? res->hr_version : HAST_PROTO_VERSION;
118	hdr.size = htole32((uint32_t)ebuf_size(eb));
119	if (ebuf_add_head(eb, &hdr, sizeof(hdr)) == -1)
120		goto end;
121
122	hptr = ebuf_data(eb, &hsize);
123	if (proto_send(conn, hptr, hsize) == -1)
124		goto end;
125	if (data != NULL && proto_send(conn, dptr, size) == -1)
126		goto end;
127
128	ret = 0;
129end:
130	if (freedata)
131		free(dptr);
132	return (ret);
133}
134
135int
136hast_proto_recv_hdr(const struct proto_conn *conn, struct nv **nvp)
137{
138	struct hast_main_header hdr;
139	struct nv *nv;
140	struct ebuf *eb;
141	void *hptr;
142
143	eb = NULL;
144	nv = NULL;
145
146	if (proto_recv(conn, &hdr, sizeof(hdr)) == -1)
147		goto fail;
148
149	if (hdr.version > HAST_PROTO_VERSION) {
150		errno = ERPCMISMATCH;
151		goto fail;
152	}
153
154	hdr.size = le32toh(hdr.size);
155
156	eb = ebuf_alloc(hdr.size);
157	if (eb == NULL)
158		goto fail;
159	if (ebuf_add_tail(eb, NULL, hdr.size) == -1)
160		goto fail;
161	hptr = ebuf_data(eb, NULL);
162	PJDLOG_ASSERT(hptr != NULL);
163	if (proto_recv(conn, hptr, hdr.size) == -1)
164		goto fail;
165	nv = nv_ntoh(eb);
166	if (nv == NULL)
167		goto fail;
168
169	*nvp = nv;
170	return (0);
171fail:
172	if (eb != NULL)
173		ebuf_free(eb);
174	return (-1);
175}
176
177int
178hast_proto_recv_data(const struct hast_resource *res, struct proto_conn *conn,
179    struct nv *nv, void *data, size_t size)
180{
181	unsigned int ii;
182	bool freedata;
183	size_t dsize;
184	void *dptr;
185	int ret;
186
187	PJDLOG_ASSERT(data != NULL);
188	PJDLOG_ASSERT(size > 0);
189
190	ret = -1;
191	freedata = false;
192	dptr = data;
193
194	dsize = nv_get_uint32(nv, "size");
195	if (dsize > size) {
196		errno = EINVAL;
197		goto end;
198	} else if (dsize == 0) {
199		(void)nv_set_error(nv, 0);
200	} else {
201		if (proto_recv(conn, data, dsize) == -1)
202			goto end;
203		for (ii = sizeof(pipeline) / sizeof(pipeline[0]); ii > 0;
204		    ii--) {
205			ret = pipeline[ii - 1].hps_recv(res, nv, &dptr,
206			    &dsize, &freedata);
207			if (ret == -1)
208				goto end;
209		}
210		ret = -1;
211		if (dsize > size) {
212			errno = EINVAL;
213			goto end;
214		}
215		if (dptr != data)
216			bcopy(dptr, data, dsize);
217	}
218
219	ret = 0;
220end:
221	if (freedata)
222		free(dptr);
223	return (ret);
224}
225