1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2012 The FreeBSD Foundation 5 * 6 * This software was developed by Edward Tomasz Napierala under sponsorship 7 * from 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 AUTHOR 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 AUTHOR 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 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 #include <sys/types.h> 36 #include <sys/uio.h> 37 #include <assert.h> 38 #include <errno.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 43 #include <iscsi_proto.h> 44 #include "libiscsiutil.h" 45 46 int 47 pdu_ahs_length(const struct pdu *pdu) 48 { 49 50 return (pdu->pdu_bhs->bhs_total_ahs_len * 4); 51 } 52 53 int 54 pdu_data_segment_length(const struct pdu *pdu) 55 { 56 uint32_t len = 0; 57 58 len += pdu->pdu_bhs->bhs_data_segment_len[0]; 59 len <<= 8; 60 len += pdu->pdu_bhs->bhs_data_segment_len[1]; 61 len <<= 8; 62 len += pdu->pdu_bhs->bhs_data_segment_len[2]; 63 64 return (len); 65 } 66 67 void 68 pdu_set_data_segment_length(struct pdu *pdu, uint32_t len) 69 { 70 71 pdu->pdu_bhs->bhs_data_segment_len[2] = len; 72 pdu->pdu_bhs->bhs_data_segment_len[1] = len >> 8; 73 pdu->pdu_bhs->bhs_data_segment_len[0] = len >> 16; 74 } 75 76 struct pdu * 77 pdu_new(struct connection *conn) 78 { 79 struct pdu *pdu; 80 81 pdu = calloc(1, sizeof(*pdu)); 82 if (pdu == NULL) 83 log_err(1, "calloc"); 84 85 pdu->pdu_bhs = calloc(1, sizeof(*pdu->pdu_bhs)); 86 if (pdu->pdu_bhs == NULL) 87 log_err(1, "calloc"); 88 89 pdu->pdu_connection = conn; 90 91 return (pdu); 92 } 93 94 struct pdu * 95 pdu_new_response(struct pdu *request) 96 { 97 98 return (pdu_new(request->pdu_connection)); 99 } 100 101 static size_t 102 pdu_padding(const struct pdu *pdu) 103 { 104 105 if ((pdu->pdu_data_len % 4) != 0) 106 return (4 - (pdu->pdu_data_len % 4)); 107 108 return (0); 109 } 110 111 static void 112 pdu_read(const struct connection *conn, char *data, size_t len) 113 { 114 ssize_t ret; 115 116 while (len > 0) { 117 ret = read(conn->conn_socket, data, len); 118 if (ret < 0) { 119 if (conn->conn_ops->timed_out()) { 120 conn->conn_ops->fail(conn, 121 "Login Phase timeout"); 122 log_errx(1, "exiting due to timeout"); 123 } 124 conn->conn_ops->fail(conn, strerror(errno)); 125 log_err(1, "read"); 126 } else if (ret == 0) { 127 conn->conn_ops->fail(conn, "connection lost"); 128 log_errx(1, "read: connection lost"); 129 } 130 len -= ret; 131 data += ret; 132 } 133 } 134 135 void 136 pdu_receive(struct pdu *pdu) 137 { 138 struct connection *conn; 139 size_t len, padding; 140 char dummy[4]; 141 142 conn = pdu->pdu_connection; 143 if (conn->conn_use_proxy) 144 return (conn->conn_ops->pdu_receive_proxy(pdu)); 145 146 pdu_read(conn, (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs)); 147 148 len = pdu_ahs_length(pdu); 149 if (len > 0) 150 log_errx(1, "protocol error: non-empty AHS"); 151 152 len = pdu_data_segment_length(pdu); 153 if (len > 0) { 154 if (len > (size_t)conn->conn_max_recv_data_segment_length) { 155 log_errx(1, "protocol error: received PDU " 156 "with DataSegmentLength exceeding %d", 157 conn->conn_max_recv_data_segment_length); 158 } 159 160 pdu->pdu_data_len = len; 161 pdu->pdu_data = malloc(len); 162 if (pdu->pdu_data == NULL) 163 log_err(1, "malloc"); 164 165 pdu_read(conn, (char *)pdu->pdu_data, pdu->pdu_data_len); 166 167 padding = pdu_padding(pdu); 168 if (padding != 0) { 169 assert(padding < sizeof(dummy)); 170 pdu_read(conn, (char *)dummy, padding); 171 } 172 } 173 } 174 175 void 176 pdu_send(struct pdu *pdu) 177 { 178 struct connection *conn; 179 ssize_t ret, total_len; 180 size_t padding; 181 uint32_t zero = 0; 182 struct iovec iov[3]; 183 int iovcnt; 184 185 conn = pdu->pdu_connection; 186 if (conn->conn_use_proxy) 187 return (conn->conn_ops->pdu_send_proxy(pdu)); 188 189 pdu_set_data_segment_length(pdu, pdu->pdu_data_len); 190 iov[0].iov_base = pdu->pdu_bhs; 191 iov[0].iov_len = sizeof(*pdu->pdu_bhs); 192 total_len = iov[0].iov_len; 193 iovcnt = 1; 194 195 if (pdu->pdu_data_len > 0) { 196 iov[1].iov_base = pdu->pdu_data; 197 iov[1].iov_len = pdu->pdu_data_len; 198 total_len += iov[1].iov_len; 199 iovcnt = 2; 200 201 padding = pdu_padding(pdu); 202 if (padding > 0) { 203 assert(padding < sizeof(zero)); 204 iov[2].iov_base = &zero; 205 iov[2].iov_len = padding; 206 total_len += iov[2].iov_len; 207 iovcnt = 3; 208 } 209 } 210 211 ret = writev(conn->conn_socket, iov, iovcnt); 212 if (ret < 0) { 213 if (conn->conn_ops->timed_out()) 214 log_errx(1, "exiting due to timeout"); 215 log_err(1, "writev"); 216 } 217 if (ret != total_len) 218 log_errx(1, "short write"); 219 } 220 221 void 222 pdu_delete(struct pdu *pdu) 223 { 224 225 free(pdu->pdu_data); 226 free(pdu->pdu_bhs); 227 free(pdu); 228 } 229