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