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