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$");
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#include "hast_checksum.h"
48#include "hast_compression.h"
49#include "hast_proto.h"
50
51struct hast_main_header {
52	/* Protocol version. */
53	uint8_t		version;
54	/* Size of nv headers. */
55	uint32_t	size;
56} __packed;
57
58typedef int hps_send_t(const struct hast_resource *, struct nv *nv, void **,
59    size_t *, bool *);
60typedef int hps_recv_t(const struct hast_resource *, struct nv *nv, void **,
61    size_t *, bool *);
62
63struct hast_pipe_stage {
64	const char	*hps_name;
65	hps_send_t	*hps_send;
66	hps_recv_t	*hps_recv;
67};
68
69static struct hast_pipe_stage pipeline[] = {
70	{ "compression", compression_send, compression_recv },
71	{ "checksum", checksum_send, checksum_recv }
72};
73
74/*
75 * Send the given nv structure via conn.
76 * We keep headers in nv structure and pass data in separate argument.
77 * There can be no data at all (data is NULL then).
78 */
79int
80hast_proto_send(const struct hast_resource *res, struct proto_conn *conn,
81    struct nv *nv, const void *data, size_t size)
82{
83	struct hast_main_header hdr;
84	struct ebuf *eb;
85	bool freedata;
86	void *dptr, *hptr;
87	size_t hsize;
88	int ret;
89
90	dptr = (void *)(uintptr_t)data;
91	freedata = false;
92	ret = -1;
93
94	if (data != NULL) {
95		unsigned int ii;
96
97		for (ii = 0; ii < sizeof(pipeline) / sizeof(pipeline[0]);
98		    ii++) {
99			(void)pipeline[ii].hps_send(res, nv, &dptr, &size,
100			    &freedata);
101		}
102		nv_add_uint32(nv, size, "size");
103		if (nv_error(nv) != 0) {
104			errno = nv_error(nv);
105			goto end;
106		}
107	}
108
109	eb = nv_hton(nv);
110	if (eb == NULL)
111		goto end;
112
113	hdr.version = res != NULL ? res->hr_version : HAST_PROTO_VERSION;
114	hdr.size = htole32((uint32_t)ebuf_size(eb));
115	if (ebuf_add_head(eb, &hdr, sizeof(hdr)) == -1)
116		goto end;
117
118	hptr = ebuf_data(eb, &hsize);
119	if (proto_send(conn, hptr, hsize) == -1)
120		goto end;
121	if (data != NULL && proto_send(conn, dptr, size) == -1)
122		goto end;
123
124	ret = 0;
125end:
126	if (freedata)
127		free(dptr);
128	return (ret);
129}
130
131int
132hast_proto_recv_hdr(const struct proto_conn *conn, struct nv **nvp)
133{
134	struct hast_main_header hdr;
135	struct nv *nv;
136	struct ebuf *eb;
137	void *hptr;
138
139	eb = NULL;
140	nv = NULL;
141
142	if (proto_recv(conn, &hdr, sizeof(hdr)) == -1)
143		goto fail;
144
145	if (hdr.version > HAST_PROTO_VERSION) {
146		errno = ERPCMISMATCH;
147		goto fail;
148	}
149
150	hdr.size = le32toh(hdr.size);
151
152	eb = ebuf_alloc(hdr.size);
153	if (eb == NULL)
154		goto fail;
155	if (ebuf_add_tail(eb, NULL, hdr.size) == -1)
156		goto fail;
157	hptr = ebuf_data(eb, NULL);
158	PJDLOG_ASSERT(hptr != NULL);
159	if (proto_recv(conn, hptr, hdr.size) == -1)
160		goto fail;
161	nv = nv_ntoh(eb);
162	if (nv == NULL)
163		goto fail;
164
165	*nvp = nv;
166	return (0);
167fail:
168	if (eb != NULL)
169		ebuf_free(eb);
170	return (-1);
171}
172
173int
174hast_proto_recv_data(const struct hast_resource *res, struct proto_conn *conn,
175    struct nv *nv, void *data, size_t size)
176{
177	unsigned int ii;
178	bool freedata;
179	size_t dsize;
180	void *dptr;
181	int ret;
182
183	PJDLOG_ASSERT(data != NULL);
184	PJDLOG_ASSERT(size > 0);
185
186	ret = -1;
187	freedata = false;
188	dptr = data;
189
190	dsize = nv_get_uint32(nv, "size");
191	if (dsize > size) {
192		errno = EINVAL;
193		goto end;
194	} else if (dsize == 0) {
195		(void)nv_set_error(nv, 0);
196	} else {
197		if (proto_recv(conn, data, dsize) == -1)
198			goto end;
199		for (ii = sizeof(pipeline) / sizeof(pipeline[0]); ii > 0;
200		    ii--) {
201			ret = pipeline[ii - 1].hps_recv(res, nv, &dptr,
202			    &dsize, &freedata);
203			if (ret == -1)
204				goto end;
205		}
206		ret = -1;
207		if (dsize > size) {
208			errno = EINVAL;
209			goto end;
210		}
211		if (dptr != data)
212			bcopy(dptr, data, dsize);
213	}
214
215	ret = 0;
216end:
217	if (freedata)
218		free(dptr);
219	return (ret);
220}
221