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