hast_proto.c revision 204076
1204076Spjd/*-
2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation
3204076Spjd * All rights reserved.
4204076Spjd *
5204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from
6204076Spjd * the FreeBSD Foundation.
7204076Spjd *
8204076Spjd * Redistribution and use in source and binary forms, with or without
9204076Spjd * modification, are permitted provided that the following conditions
10204076Spjd * are met:
11204076Spjd * 1. Redistributions of source code must retain the above copyright
12204076Spjd *    notice, this list of conditions and the following disclaimer.
13204076Spjd * 2. Redistributions in binary form must reproduce the above copyright
14204076Spjd *    notice, this list of conditions and the following disclaimer in the
15204076Spjd *    documentation and/or other materials provided with the distribution.
16204076Spjd *
17204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20204076Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27204076Spjd * SUCH DAMAGE.
28204076Spjd */
29204076Spjd
30204076Spjd#include <sys/cdefs.h>
31204076Spjd__FBSDID("$FreeBSD: head/sbin/hastd/hast_proto.c 204076 2010-02-18 23:16:19Z pjd $");
32204076Spjd
33204076Spjd#include <sys/endian.h>
34204076Spjd
35204076Spjd#include <assert.h>
36204076Spjd#include <errno.h>
37204076Spjd#include <string.h>
38204076Spjd#include <strings.h>
39204076Spjd
40204076Spjd#include <openssl/sha.h>
41204076Spjd
42204076Spjd#include <hast.h>
43204076Spjd#include <ebuf.h>
44204076Spjd#include <nv.h>
45204076Spjd#include <pjdlog.h>
46204076Spjd#include <proto.h>
47204076Spjd
48204076Spjd#include "hast_proto.h"
49204076Spjd
50204076Spjdstruct hast_main_header {
51204076Spjd	/* Protocol version. */
52204076Spjd	uint8_t		version;
53204076Spjd	/* Size of nv headers. */
54204076Spjd	uint32_t	size;
55204076Spjd} __packed;
56204076Spjd
57204076Spjdtypedef int hps_send_t(struct hast_resource *, struct nv *nv, void **, size_t *, bool *);
58204076Spjdtypedef int hps_recv_t(struct hast_resource *, struct nv *nv, void **, size_t *, bool *);
59204076Spjd
60204076Spjdstruct hast_pipe_stage {
61204076Spjd	const char	*hps_name;
62204076Spjd	hps_send_t	*hps_send;
63204076Spjd	hps_recv_t	*hps_recv;
64204076Spjd};
65204076Spjd
66204076Spjdstatic int compression_send(struct hast_resource *res, struct nv *nv,
67204076Spjd    void **datap, size_t *sizep, bool *freedatap);
68204076Spjdstatic int compression_recv(struct hast_resource *res, struct nv *nv,
69204076Spjd    void **datap, size_t *sizep, bool *freedatap);
70204076Spjdstatic int checksum_send(struct hast_resource *res, struct nv *nv,
71204076Spjd    void **datap, size_t *sizep, bool *freedatap);
72204076Spjdstatic int checksum_recv(struct hast_resource *res, struct nv *nv,
73204076Spjd    void **datap, size_t *sizep, bool *freedatap);
74204076Spjd
75204076Spjdstatic struct hast_pipe_stage pipeline[] = {
76204076Spjd	{ "compression", compression_send, compression_recv },
77204076Spjd	{ "checksum", checksum_send, checksum_recv }
78204076Spjd};
79204076Spjd
80204076Spjdstatic int
81204076Spjdcompression_send(struct hast_resource *res, struct nv *nv, void **datap,
82204076Spjd    size_t *sizep, bool *freedatap)
83204076Spjd{
84204076Spjd	unsigned char *newbuf;
85204076Spjd
86204076Spjd	res = res;	/* TODO */
87204076Spjd
88204076Spjd	/*
89204076Spjd	 * TODO: For now we emulate compression.
90204076Spjd	 * At 80% probability we succeed to compress data, which means we
91204076Spjd	 * allocate new buffer, copy the data over set *freedatap to true.
92204076Spjd	 */
93204076Spjd
94204076Spjd	if (arc4random_uniform(100) < 80) {
95204076Spjd		uint32_t *origsize;
96204076Spjd
97204076Spjd		/*
98204076Spjd		 * Compression succeeded (but we will grow by 4 bytes, not
99204076Spjd		 * shrink for now).
100204076Spjd		 */
101204076Spjd		newbuf = malloc(sizeof(uint32_t) + *sizep);
102204076Spjd		if (newbuf == NULL)
103204076Spjd			return (-1);
104204076Spjd		origsize = (void *)newbuf;
105204076Spjd		*origsize = htole32((uint32_t)*sizep);
106204076Spjd		nv_add_string(nv, "null", "compression");
107204076Spjd		if (nv_error(nv) != 0) {
108204076Spjd			free(newbuf);
109204076Spjd			errno = nv_error(nv);
110204076Spjd			return (-1);
111204076Spjd		}
112204076Spjd		bcopy(*datap, newbuf + sizeof(uint32_t), *sizep);
113204076Spjd		if (*freedatap)
114204076Spjd			free(*datap);
115204076Spjd		*freedatap = true;
116204076Spjd		*datap = newbuf;
117204076Spjd		*sizep = sizeof(uint32_t) + *sizep;
118204076Spjd	} else {
119204076Spjd		/*
120204076Spjd		 * Compression failed, so we leave everything as it was.
121204076Spjd		 * It is not critical for compression to succeed.
122204076Spjd		 */
123204076Spjd	}
124204076Spjd
125204076Spjd	return (0);
126204076Spjd}
127204076Spjd
128204076Spjdstatic int
129204076Spjdcompression_recv(struct hast_resource *res, struct nv *nv, void **datap,
130204076Spjd    size_t *sizep, bool *freedatap)
131204076Spjd{
132204076Spjd	unsigned char *newbuf;
133204076Spjd	const char *algo;
134204076Spjd	size_t origsize;
135204076Spjd
136204076Spjd	res = res;	/* TODO */
137204076Spjd
138204076Spjd	/*
139204076Spjd	 * TODO: For now we emulate compression.
140204076Spjd	 */
141204076Spjd
142204076Spjd	algo = nv_get_string(nv, "compression");
143204076Spjd	if (algo == NULL)
144204076Spjd		return (0);	/* No compression. */
145204076Spjd	if (strcmp(algo, "null") != 0) {
146204076Spjd		pjdlog_error("Unknown compression algorithm '%s'.", algo);
147204076Spjd		return (-1);	/* Unknown compression algorithm. */
148204076Spjd	}
149204076Spjd
150204076Spjd	origsize = le32toh(*(uint32_t *)*datap);
151204076Spjd	newbuf = malloc(origsize);
152204076Spjd	if (newbuf == NULL)
153204076Spjd		return (-1);
154204076Spjd	bcopy((unsigned char *)*datap + sizeof(uint32_t), newbuf, origsize);
155204076Spjd	if (*freedatap)
156204076Spjd		free(*datap);
157204076Spjd	*freedatap = true;
158204076Spjd	*datap = newbuf;
159204076Spjd	*sizep = origsize;
160204076Spjd
161204076Spjd	return (0);
162204076Spjd}
163204076Spjd
164204076Spjdstatic int
165204076Spjdchecksum_send(struct hast_resource *res, struct nv *nv, void **datap,
166204076Spjd    size_t *sizep, bool *freedatap __unused)
167204076Spjd{
168204076Spjd	unsigned char hash[SHA256_DIGEST_LENGTH];
169204076Spjd	SHA256_CTX ctx;
170204076Spjd
171204076Spjd	res = res;	/* TODO */
172204076Spjd
173204076Spjd	SHA256_Init(&ctx);
174204076Spjd	SHA256_Update(&ctx, *datap, *sizep);
175204076Spjd	SHA256_Final(hash, &ctx);
176204076Spjd
177204076Spjd	nv_add_string(nv, "sha256", "checksum");
178204076Spjd	nv_add_uint8_array(nv, hash, sizeof(hash), "hash");
179204076Spjd
180204076Spjd	return (0);
181204076Spjd}
182204076Spjd
183204076Spjdstatic int
184204076Spjdchecksum_recv(struct hast_resource *res, struct nv *nv, void **datap,
185204076Spjd    size_t *sizep, bool *freedatap __unused)
186204076Spjd{
187204076Spjd	unsigned char chash[SHA256_DIGEST_LENGTH];
188204076Spjd	const unsigned char *rhash;
189204076Spjd	SHA256_CTX ctx;
190204076Spjd	const char *algo;
191204076Spjd	size_t size;
192204076Spjd
193204076Spjd	res = res;	/* TODO */
194204076Spjd
195204076Spjd	algo = nv_get_string(nv, "checksum");
196204076Spjd	if (algo == NULL)
197204076Spjd		return (0);	/* No checksum. */
198204076Spjd	if (strcmp(algo, "sha256") != 0) {
199204076Spjd		pjdlog_error("Unknown checksum algorithm '%s'.", algo);
200204076Spjd		return (-1);	/* Unknown checksum algorithm. */
201204076Spjd	}
202204076Spjd	rhash = nv_get_uint8_array(nv, &size, "hash");
203204076Spjd	if (rhash == NULL) {
204204076Spjd		pjdlog_error("Checksum algorithm is present, but hash is missing.");
205204076Spjd		return (-1);	/* Hash not found. */
206204076Spjd	}
207204076Spjd	if (size != sizeof(chash)) {
208204076Spjd		pjdlog_error("Invalid hash size (%zu) for %s, should be %zu.",
209204076Spjd		    size, algo, sizeof(chash));
210204076Spjd		return (-1);	/* Different hash size. */
211204076Spjd	}
212204076Spjd
213204076Spjd	SHA256_Init(&ctx);
214204076Spjd	SHA256_Update(&ctx, *datap, *sizep);
215204076Spjd	SHA256_Final(chash, &ctx);
216204076Spjd
217204076Spjd	if (bcmp(rhash, chash, sizeof(chash)) != 0) {
218204076Spjd		pjdlog_error("Hash mismatch.");
219204076Spjd		return (-1);	/* Hash mismatch. */
220204076Spjd	}
221204076Spjd
222204076Spjd	return (0);
223204076Spjd}
224204076Spjd
225204076Spjd/*
226204076Spjd * Send the given nv structure via conn.
227204076Spjd * We keep headers in nv structure and pass data in separate argument.
228204076Spjd * There can be no data at all (data is NULL then).
229204076Spjd */
230204076Spjdint
231204076Spjdhast_proto_send(struct hast_resource *res, struct proto_conn *conn,
232204076Spjd    struct nv *nv, const void *data, size_t size)
233204076Spjd{
234204076Spjd	struct hast_main_header hdr;
235204076Spjd	struct ebuf *eb;
236204076Spjd	bool freedata;
237204076Spjd	void *dptr, *hptr;
238204076Spjd	size_t hsize;
239204076Spjd	int ret;
240204076Spjd
241204076Spjd	dptr = (void *)(uintptr_t)data;
242204076Spjd	freedata = false;
243204076Spjd	ret = -1;
244204076Spjd
245204076Spjd	if (data != NULL) {
246204076Spjdif (false) {
247204076Spjd		unsigned int ii;
248204076Spjd
249204076Spjd		for (ii = 0; ii < sizeof(pipeline) / sizeof(pipeline[0]);
250204076Spjd		    ii++) {
251204076Spjd			ret = pipeline[ii].hps_send(res, nv, &dptr, &size,
252204076Spjd			    &freedata);
253204076Spjd			if (ret == -1)
254204076Spjd				goto end;
255204076Spjd		}
256204076Spjd		ret = -1;
257204076Spjd}
258204076Spjd		nv_add_uint32(nv, size, "size");
259204076Spjd		if (nv_error(nv) != 0) {
260204076Spjd			errno = nv_error(nv);
261204076Spjd			goto end;
262204076Spjd		}
263204076Spjd	}
264204076Spjd
265204076Spjd	eb = nv_hton(nv);
266204076Spjd	if (eb == NULL)
267204076Spjd		goto end;
268204076Spjd
269204076Spjd	hdr.version = HAST_PROTO_VERSION;
270204076Spjd	hdr.size = htole32((uint32_t)ebuf_size(eb));
271204076Spjd	if (ebuf_add_head(eb, &hdr, sizeof(hdr)) < 0)
272204076Spjd		goto end;
273204076Spjd
274204076Spjd	hptr = ebuf_data(eb, &hsize);
275204076Spjd	if (proto_send(conn, hptr, hsize) < 0)
276204076Spjd		goto end;
277204076Spjd	if (data != NULL && proto_send(conn, dptr, size) < 0)
278204076Spjd		goto end;
279204076Spjd
280204076Spjd	ret = 0;
281204076Spjdend:
282204076Spjd	if (freedata)
283204076Spjd		free(dptr);
284204076Spjd	return (ret);
285204076Spjd}
286204076Spjd
287204076Spjdint
288204076Spjdhast_proto_recv_hdr(struct proto_conn *conn, struct nv **nvp)
289204076Spjd{
290204076Spjd	struct hast_main_header hdr;
291204076Spjd	struct nv *nv;
292204076Spjd	struct ebuf *eb;
293204076Spjd	void *hptr;
294204076Spjd
295204076Spjd	eb = NULL;
296204076Spjd	nv = NULL;
297204076Spjd
298204076Spjd	if (proto_recv(conn, &hdr, sizeof(hdr)) < 0)
299204076Spjd		goto fail;
300204076Spjd
301204076Spjd	if (hdr.version != HAST_PROTO_VERSION) {
302204076Spjd		errno = ERPCMISMATCH;
303204076Spjd		goto fail;
304204076Spjd	}
305204076Spjd
306204076Spjd	hdr.size = le32toh(hdr.size);
307204076Spjd
308204076Spjd	eb = ebuf_alloc(hdr.size);
309204076Spjd	if (eb == NULL)
310204076Spjd		goto fail;
311204076Spjd	if (ebuf_add_tail(eb, NULL, hdr.size) < 0)
312204076Spjd		goto fail;
313204076Spjd	hptr = ebuf_data(eb, NULL);
314204076Spjd	assert(hptr != NULL);
315204076Spjd	if (proto_recv(conn, hptr, hdr.size) < 0)
316204076Spjd		goto fail;
317204076Spjd	nv = nv_ntoh(eb);
318204076Spjd	if (nv == NULL)
319204076Spjd		goto fail;
320204076Spjd
321204076Spjd	*nvp = nv;
322204076Spjd	return (0);
323204076Spjdfail:
324204076Spjd	if (nv != NULL)
325204076Spjd		nv_free(nv);
326204076Spjd	else if (eb != NULL)
327204076Spjd		ebuf_free(eb);
328204076Spjd	return (-1);
329204076Spjd}
330204076Spjd
331204076Spjdint
332204076Spjdhast_proto_recv_data(struct hast_resource *res, struct proto_conn *conn,
333204076Spjd    struct nv *nv, void *data, size_t size)
334204076Spjd{
335204076Spjd	unsigned int ii;
336204076Spjd	bool freedata;
337204076Spjd	size_t dsize;
338204076Spjd	void *dptr;
339204076Spjd	int ret;
340204076Spjd
341204076Spjd	assert(data != NULL);
342204076Spjd	assert(size > 0);
343204076Spjd
344204076Spjd	ret = -1;
345204076Spjd	freedata = false;
346204076Spjd	dptr = data;
347204076Spjd
348204076Spjd	dsize = nv_get_uint32(nv, "size");
349204076Spjd	if (dsize == 0)
350204076Spjd		(void)nv_set_error(nv, 0);
351204076Spjd	else {
352204076Spjd		if (proto_recv(conn, data, dsize) < 0)
353204076Spjd			goto end;
354204076Spjdif (false) {
355204076Spjd		for (ii = sizeof(pipeline) / sizeof(pipeline[0]); ii > 0;
356204076Spjd		    ii--) {
357204076Spjd			assert(!"to be verified");
358204076Spjd			ret = pipeline[ii - 1].hps_recv(res, nv, &dptr,
359204076Spjd			    &dsize, &freedata);
360204076Spjd			if (ret == -1)
361204076Spjd				goto end;
362204076Spjd		}
363204076Spjd		ret = -1;
364204076Spjd		if (dsize < size)
365204076Spjd			goto end;
366204076Spjd		/* TODO: 'size' doesn't seem right here. It is maximum data size. */
367204076Spjd		if (dptr != data)
368204076Spjd			bcopy(dptr, data, dsize);
369204076Spjd}
370204076Spjd	}
371204076Spjd
372204076Spjd	ret = 0;
373204076Spjdend:
374204076Spjdif (ret < 0) printf("%s:%u %s\n", __func__, __LINE__, strerror(errno));
375204076Spjd	if (freedata)
376204076Spjd		free(dptr);
377204076Spjd	return (ret);
378204076Spjd}
379204076Spjd
380204076Spjdint
381204076Spjdhast_proto_recv(struct hast_resource *res, struct proto_conn *conn,
382204076Spjd    struct nv **nvp, void *data, size_t size)
383204076Spjd{
384204076Spjd	struct nv *nv;
385204076Spjd	size_t dsize;
386204076Spjd	int ret;
387204076Spjd
388204076Spjd	ret = hast_proto_recv_hdr(conn, &nv);
389204076Spjd	if (ret < 0)
390204076Spjd		return (ret);
391204076Spjd	dsize = nv_get_uint32(nv, "size");
392204076Spjd	if (dsize == 0)
393204076Spjd		(void)nv_set_error(nv, 0);
394204076Spjd	else
395204076Spjd		ret = hast_proto_recv_data(res, conn, nv, data, size);
396204076Spjd	if (ret < 0)
397204076Spjd		nv_free(nv);
398204076Spjd	else
399204076Spjd		*nvp = nv;
400204076Spjd	return (ret);
401204076Spjd}
402