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