1204076Spjd/*-
2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation
3219351Spjd * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
4204076Spjd * All rights reserved.
5204076Spjd *
6204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from
7204076Spjd * the FreeBSD Foundation.
8204076Spjd *
9204076Spjd * Redistribution and use in source and binary forms, with or without
10204076Spjd * modification, are permitted provided that the following conditions
11204076Spjd * are met:
12204076Spjd * 1. Redistributions of source code must retain the above copyright
13204076Spjd *    notice, this list of conditions and the following disclaimer.
14204076Spjd * 2. Redistributions in binary form must reproduce the above copyright
15204076Spjd *    notice, this list of conditions and the following disclaimer in the
16204076Spjd *    documentation and/or other materials provided with the distribution.
17204076Spjd *
18204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21204076Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28204076Spjd * SUCH DAMAGE.
29204076Spjd */
30204076Spjd
31204076Spjd#include <sys/cdefs.h>
32204076Spjd__FBSDID("$FreeBSD$");
33204076Spjd
34204076Spjd#include <sys/endian.h>
35204076Spjd
36204076Spjd#include <errno.h>
37204076Spjd#include <strings.h>
38204076Spjd
39204076Spjd#include <hast.h>
40204076Spjd#include <ebuf.h>
41204076Spjd#include <nv.h>
42204076Spjd#include <pjdlog.h>
43204076Spjd#include <proto.h>
44204076Spjd
45219351Spjd#ifdef HAVE_CRYPTO
46219351Spjd#include "hast_checksum.h"
47219351Spjd#endif
48219354Spjd#include "hast_compression.h"
49204076Spjd#include "hast_proto.h"
50204076Spjd
51204076Spjdstruct hast_main_header {
52204076Spjd	/* Protocol version. */
53204076Spjd	uint8_t		version;
54204076Spjd	/* Size of nv headers. */
55204076Spjd	uint32_t	size;
56204076Spjd} __packed;
57204076Spjd
58212033Spjdtypedef int hps_send_t(const struct hast_resource *, struct nv *nv, void **,
59212033Spjd    size_t *, bool *);
60212033Spjdtypedef int hps_recv_t(const struct hast_resource *, struct nv *nv, void **,
61212033Spjd    size_t *, bool *);
62204076Spjd
63204076Spjdstruct hast_pipe_stage {
64204076Spjd	const char	*hps_name;
65204076Spjd	hps_send_t	*hps_send;
66204076Spjd	hps_recv_t	*hps_recv;
67204076Spjd};
68204076Spjd
69204076Spjdstatic struct hast_pipe_stage pipeline[] = {
70219354Spjd	{ "compression", compression_send, compression_recv },
71221078Strociny#ifdef HAVE_CRYPTO
72204076Spjd	{ "checksum", checksum_send, checksum_recv }
73221078Strociny#endif
74204076Spjd};
75204076Spjd
76204076Spjd/*
77204076Spjd * Send the given nv structure via conn.
78204076Spjd * We keep headers in nv structure and pass data in separate argument.
79204076Spjd * There can be no data at all (data is NULL then).
80204076Spjd */
81204076Spjdint
82212033Spjdhast_proto_send(const struct hast_resource *res, struct proto_conn *conn,
83204076Spjd    struct nv *nv, const void *data, size_t size)
84204076Spjd{
85204076Spjd	struct hast_main_header hdr;
86204076Spjd	struct ebuf *eb;
87204076Spjd	bool freedata;
88204076Spjd	void *dptr, *hptr;
89204076Spjd	size_t hsize;
90204076Spjd	int ret;
91204076Spjd
92204076Spjd	dptr = (void *)(uintptr_t)data;
93204076Spjd	freedata = false;
94204076Spjd	ret = -1;
95204076Spjd
96204076Spjd	if (data != NULL) {
97204076Spjd		unsigned int ii;
98204076Spjd
99204076Spjd		for (ii = 0; ii < sizeof(pipeline) / sizeof(pipeline[0]);
100204076Spjd		    ii++) {
101219351Spjd			(void)pipeline[ii].hps_send(res, nv, &dptr, &size,
102204076Spjd			    &freedata);
103204076Spjd		}
104204076Spjd		nv_add_uint32(nv, size, "size");
105204076Spjd		if (nv_error(nv) != 0) {
106204076Spjd			errno = nv_error(nv);
107204076Spjd			goto end;
108204076Spjd		}
109204076Spjd	}
110204076Spjd
111204076Spjd	eb = nv_hton(nv);
112204076Spjd	if (eb == NULL)
113204076Spjd		goto end;
114204076Spjd
115249236Strociny	hdr.version = res != NULL ? res->hr_version : HAST_PROTO_VERSION;
116204076Spjd	hdr.size = htole32((uint32_t)ebuf_size(eb));
117231017Strociny	if (ebuf_add_head(eb, &hdr, sizeof(hdr)) == -1)
118204076Spjd		goto end;
119204076Spjd
120204076Spjd	hptr = ebuf_data(eb, &hsize);
121231017Strociny	if (proto_send(conn, hptr, hsize) == -1)
122204076Spjd		goto end;
123231017Strociny	if (data != NULL && proto_send(conn, dptr, size) == -1)
124204076Spjd		goto end;
125204076Spjd
126204076Spjd	ret = 0;
127204076Spjdend:
128204076Spjd	if (freedata)
129204076Spjd		free(dptr);
130204076Spjd	return (ret);
131204076Spjd}
132204076Spjd
133204076Spjdint
134212033Spjdhast_proto_recv_hdr(const struct proto_conn *conn, struct nv **nvp)
135204076Spjd{
136204076Spjd	struct hast_main_header hdr;
137204076Spjd	struct nv *nv;
138204076Spjd	struct ebuf *eb;
139204076Spjd	void *hptr;
140204076Spjd
141204076Spjd	eb = NULL;
142204076Spjd	nv = NULL;
143204076Spjd
144231017Strociny	if (proto_recv(conn, &hdr, sizeof(hdr)) == -1)
145204076Spjd		goto fail;
146204076Spjd
147249236Strociny	if (hdr.version > HAST_PROTO_VERSION) {
148204076Spjd		errno = ERPCMISMATCH;
149204076Spjd		goto fail;
150204076Spjd	}
151204076Spjd
152204076Spjd	hdr.size = le32toh(hdr.size);
153204076Spjd
154204076Spjd	eb = ebuf_alloc(hdr.size);
155204076Spjd	if (eb == NULL)
156204076Spjd		goto fail;
157231017Strociny	if (ebuf_add_tail(eb, NULL, hdr.size) == -1)
158204076Spjd		goto fail;
159204076Spjd	hptr = ebuf_data(eb, NULL);
160229509Strociny	PJDLOG_ASSERT(hptr != NULL);
161231017Strociny	if (proto_recv(conn, hptr, hdr.size) == -1)
162204076Spjd		goto fail;
163204076Spjd	nv = nv_ntoh(eb);
164204076Spjd	if (nv == NULL)
165204076Spjd		goto fail;
166204076Spjd
167204076Spjd	*nvp = nv;
168204076Spjd	return (0);
169204076Spjdfail:
170209175Spjd	if (eb != NULL)
171204076Spjd		ebuf_free(eb);
172204076Spjd	return (-1);
173204076Spjd}
174204076Spjd
175204076Spjdint
176212033Spjdhast_proto_recv_data(const struct hast_resource *res, struct proto_conn *conn,
177204076Spjd    struct nv *nv, void *data, size_t size)
178204076Spjd{
179204076Spjd	unsigned int ii;
180204076Spjd	bool freedata;
181204076Spjd	size_t dsize;
182204076Spjd	void *dptr;
183204076Spjd	int ret;
184204076Spjd
185229509Strociny	PJDLOG_ASSERT(data != NULL);
186229509Strociny	PJDLOG_ASSERT(size > 0);
187204076Spjd
188204076Spjd	ret = -1;
189204076Spjd	freedata = false;
190204076Spjd	dptr = data;
191204076Spjd
192204076Spjd	dsize = nv_get_uint32(nv, "size");
193220522Strociny	if (dsize > size) {
194220522Strociny		errno = EINVAL;
195220522Strociny		goto end;
196220522Strociny	} else if (dsize == 0) {
197204076Spjd		(void)nv_set_error(nv, 0);
198220522Strociny	} else {
199231017Strociny		if (proto_recv(conn, data, dsize) == -1)
200204076Spjd			goto end;
201204076Spjd		for (ii = sizeof(pipeline) / sizeof(pipeline[0]); ii > 0;
202204076Spjd		    ii--) {
203204076Spjd			ret = pipeline[ii - 1].hps_recv(res, nv, &dptr,
204204076Spjd			    &dsize, &freedata);
205204076Spjd			if (ret == -1)
206204076Spjd				goto end;
207204076Spjd		}
208204076Spjd		ret = -1;
209219351Spjd		if (dsize > size) {
210219351Spjd			errno = EINVAL;
211204076Spjd			goto end;
212219351Spjd		}
213204076Spjd		if (dptr != data)
214204076Spjd			bcopy(dptr, data, dsize);
215204076Spjd	}
216204076Spjd
217204076Spjd	ret = 0;
218204076Spjdend:
219204076Spjd	if (freedata)
220204076Spjd		free(dptr);
221204076Spjd	return (ret);
222204076Spjd}
223