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