1 /* $OpenBSD: pdu.c,v 1.12 2019/07/03 03:24:03 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Claudio Jeker <claudio@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 #include <sys/types.h> 19 #include <sys/queue.h> 20 #include <sys/socket.h> 21 #include <sys/uio.h> 22 23 #include <scsi/iscsi.h> 24 25 #include <errno.h> 26 #include <event.h> 27 #include <limits.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <unistd.h> 32 33 #include "iscsid.h" 34 #include "log.h" 35 36 size_t pdu_readbuf_read(struct pdu_readbuf *, void *, size_t); 37 size_t pdu_readbuf_len(struct pdu_readbuf *); 38 39 #define PDU_MIN(_x, _y) ((_x) < (_y) ? (_x) : (_y)) 40 41 void * 42 pdu_gethdr(struct pdu *p) 43 { 44 void *hdr; 45 46 if (!(hdr = calloc(1, sizeof(struct iscsi_pdu)))) 47 return NULL; 48 if (pdu_addbuf(p, hdr, sizeof(struct iscsi_pdu), PDU_HEADER)) { 49 free(hdr); 50 return NULL; 51 } 52 return hdr; 53 } 54 55 int 56 text_to_pdu(struct kvp *k, struct pdu *p) 57 { 58 char *buf, *s; 59 size_t len = 0, rem; 60 int n, nk; 61 62 if (k == NULL) 63 return 0; 64 65 nk = 0; 66 while(k[nk].key) { 67 len += 2 + strlen(k[nk].key) + strlen(k[nk].value); 68 nk++; 69 } 70 71 if (!(buf = pdu_alloc(len))) 72 return -1; 73 s = buf; 74 rem = len; 75 nk = 0; 76 while(k[nk].key) { 77 n = snprintf(s, rem, "%s=%s", k[nk].key, k[nk].value); 78 if (n < 0 || (size_t)n >= rem) 79 fatalx("text_to_pdu"); 80 rem -= n + 1; 81 s += n + 1; 82 nk++; 83 } 84 85 if (pdu_addbuf(p, buf, len, PDU_DATA)) 86 return -1; 87 return len; 88 } 89 90 struct kvp * 91 pdu_to_text(char *buf, size_t len) 92 { 93 struct kvp *k; 94 size_t n; 95 char *eq; 96 unsigned int nkvp = 0, i; 97 98 /* remove padding zeros */ 99 for (n = len; n > 0 && buf[n - 1] == '\0'; n--) 100 ; 101 if (n == len) { 102 log_debug("pdu_to_text: badly terminated text data"); 103 return NULL; 104 } 105 len = n + 1; 106 107 for(n = 0; n < len; n++) 108 if (buf[n] == '\0') 109 nkvp++; 110 111 if (!(k = calloc(nkvp + 1, sizeof(*k)))) 112 return NULL; 113 114 for (i = 0; i < nkvp; i++) { 115 eq = strchr(buf, '='); 116 if (!eq) { 117 log_debug("pdu_to_text: badly encoded text data"); 118 free(k); 119 return NULL; 120 } 121 *eq++ = '\0'; 122 k[i].key = buf; 123 k[i].value = eq; 124 buf = eq + strlen(eq) + 1; 125 } 126 return k; 127 } 128 129 /* Modified version of strtonum() to fit iscsid's need 130 * 131 * Copyright (c) 2004 Ted Unangst and Todd Miller 132 * All rights reserved. 133 * 134 * Permission to use, copy, modify, and distribute this software for any 135 * purpose with or without fee is hereby granted, provided that the above 136 * copyright notice and this permission notice appear in all copies. 137 * 138 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 139 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 140 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 141 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 142 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 143 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 144 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 145 */ 146 u_int64_t 147 text_to_num(const char *numstr, u_int64_t minval, u_int64_t maxval, 148 const char **errstrp) 149 { 150 unsigned long long ull = 0; 151 char *ep; 152 int error = 0; 153 struct errval { 154 const char *errstr; 155 int err; 156 } ev[4] = { 157 { NULL, 0 }, 158 { "invalid", EINVAL }, 159 { "too small", ERANGE }, 160 { "too large", ERANGE } 161 }; 162 #define INVALID 1 163 #define TOOSMALL 2 164 #define TOOLARGE 3 165 166 ev[0].err = errno; 167 errno = 0; 168 if (minval > maxval) 169 error = INVALID; 170 else { 171 ull = strtoull(numstr, &ep, 0); 172 if (numstr == ep || *ep != '\0') 173 error = INVALID; 174 else if (ull < minval) 175 error = TOOSMALL; 176 else if ((ull == ULLONG_MAX && errno == ERANGE) || ull > maxval) 177 error = TOOLARGE; 178 } 179 if (errstrp != NULL) 180 *errstrp = ev[error].errstr; 181 errno = ev[error].err; 182 if (error) 183 ull = 0; 184 185 return ull; 186 #undef INVALID 187 #undef TOOSMALL 188 #undef TOOLARGE 189 } 190 191 int 192 text_to_bool(const char *buf, const char **errstrp) 193 { 194 int val = 0; 195 196 if (!strcmp(buf, "Yes")) { 197 val = 1; 198 errno = 0; 199 } else if (!strcmp(buf, "No")) 200 errno = 0; 201 else 202 errno = EINVAL; 203 204 if (errstrp != NULL) { 205 if (errno == 0) 206 *errstrp = NULL; 207 else 208 *errstrp = "invalid"; 209 } 210 return val; 211 } 212 213 214 /* 215 * Internal functions to send/recv pdus. 216 */ 217 218 void 219 pdu_free_queue(struct pduq *channel) 220 { 221 struct pdu *p; 222 223 while ((p = TAILQ_FIRST(channel))) { 224 TAILQ_REMOVE(channel, p, entry); 225 pdu_free(p); 226 } 227 } 228 229 ssize_t 230 pdu_read(struct connection *c) 231 { 232 struct iovec iov[2]; 233 unsigned int niov = 1; 234 ssize_t n; 235 236 bzero(&iov, sizeof(iov)); 237 iov[0].iov_base = c->prbuf.buf + c->prbuf.wpos; 238 if (c->prbuf.wpos < c->prbuf.rpos) 239 iov[0].iov_len = c->prbuf.rpos - c->prbuf.wpos; 240 else { 241 iov[0].iov_len = c->prbuf.size - c->prbuf.wpos; 242 if (c->prbuf.rpos > 0) { 243 niov++; 244 iov[1].iov_base = c->prbuf.buf; 245 iov[1].iov_len = c->prbuf.rpos - 1; 246 } 247 } 248 249 if ((n = readv(c->fd, iov, niov)) == -1) 250 return -1; 251 if (n == 0) 252 /* XXX what should we do on close with remaining data? */ 253 return 0; 254 255 c->prbuf.wpos += n; 256 if (c->prbuf.wpos >= c->prbuf.size) 257 c->prbuf.wpos -= c->prbuf.size; 258 259 return n; 260 } 261 262 ssize_t 263 pdu_write(struct connection *c) 264 { 265 struct iovec iov[PDU_WRIOV]; 266 struct pdu *b, *nb; 267 unsigned int niov = 0, j; 268 size_t off, resid, size; 269 ssize_t n; 270 271 TAILQ_FOREACH(b, &c->pdu_w, entry) { 272 if (niov >= PDU_WRIOV) 273 break; 274 off = b->resid; 275 for (j = 0; j < PDU_MAXIOV && niov < PDU_WRIOV; j++) { 276 if (!b->iov[j].iov_len) 277 continue; 278 if (off >= b->iov[j].iov_len) { 279 off -= b->iov[j].iov_len; 280 continue; 281 } 282 iov[niov].iov_base = (char *)b->iov[j].iov_base + off; 283 iov[niov++].iov_len = b->iov[j].iov_len - off; 284 off = 0; 285 } 286 } 287 288 if ((n = writev(c->fd, iov, niov)) == -1) { 289 if (errno == EAGAIN || errno == ENOBUFS || 290 errno == EINTR) /* try later */ 291 return 0; 292 else { 293 log_warn("pdu_write"); 294 return -1; 295 } 296 } 297 if (n == 0) 298 return 0; 299 300 size = n; 301 for (b = TAILQ_FIRST(&c->pdu_w); b != NULL && size > 0; b = nb) { 302 nb = TAILQ_NEXT(b, entry); 303 resid = b->resid; 304 for (j = 0; j < PDU_MAXIOV; j++) { 305 if (resid >= b->iov[j].iov_len) 306 resid -= b->iov[j].iov_len; 307 else if (size >= b->iov[j].iov_len - resid) { 308 size -= b->iov[j].iov_len - resid; 309 b->resid += b->iov[j].iov_len - resid; 310 resid = 0; 311 } else { 312 b->resid += size; 313 size = 0; 314 break; 315 } 316 } 317 if (j == PDU_MAXIOV) { 318 /* all written */ 319 TAILQ_REMOVE(&c->pdu_w, b, entry); 320 pdu_free(b); 321 } 322 } 323 return n; 324 } 325 326 int 327 pdu_pending(struct connection *c) 328 { 329 if (TAILQ_EMPTY(&c->pdu_w)) 330 return 0; 331 else 332 return 1; 333 } 334 335 void 336 pdu_parse(struct connection *c) 337 { 338 struct pdu *p; 339 struct iscsi_pdu *ipdu; 340 char *ahb, *db; 341 size_t ahslen, dlen, off; 342 ssize_t n; 343 unsigned int j; 344 345 /* XXX XXX I DON'T LIKE YOU. CAN I REWRITE YOU? */ 346 347 do { 348 if (!(p = c->prbuf.wip)) { 349 /* get and parse base header */ 350 if (pdu_readbuf_len(&c->prbuf) < sizeof(*ipdu)) 351 return; 352 if (!(p = pdu_new())) 353 goto fail; 354 if (!(ipdu = pdu_gethdr(p))) 355 goto fail; 356 357 c->prbuf.wip = p; 358 /* 359 * XXX maybe a pdu_readbuf_peek() would allow a better 360 * error handling. 361 */ 362 pdu_readbuf_read(&c->prbuf, ipdu, sizeof(*ipdu)); 363 364 ahslen = ipdu->ahslen * sizeof(u_int32_t); 365 if (ahslen != 0) { 366 if (!(ahb = pdu_alloc(ahslen)) || 367 pdu_addbuf(p, ahb, ahslen, PDU_AHS)) 368 goto fail; 369 } 370 371 dlen = ipdu->datalen[0] << 16 | ipdu->datalen[1] << 8 | 372 ipdu->datalen[2]; 373 if (dlen != 0) { 374 if (!(db = pdu_alloc(dlen)) || 375 pdu_addbuf(p, db, dlen, PDU_DATA)) 376 goto fail; 377 } 378 379 p->resid = sizeof(*ipdu); 380 } else { 381 off = p->resid; 382 for (j = 0; j < PDU_MAXIOV; j++) { 383 if (off >= p->iov[j].iov_len) 384 off -= p->iov[j].iov_len; 385 else { 386 n = pdu_readbuf_read(&c->prbuf, 387 (char *)p->iov[j].iov_base + off, 388 p->iov[j].iov_len - off); 389 p->resid += n; 390 if (n == 0 || off + n != 391 p->iov[j].iov_len) 392 return; 393 } 394 } 395 p->resid = 0; /* reset resid so pdu can be reused */ 396 c->prbuf.wip = NULL; 397 task_pdu_cb(c, p); 398 } 399 } while (1); 400 fail: 401 fatalx("pdu_parse hit a space oddity"); 402 } 403 404 size_t 405 pdu_readbuf_read(struct pdu_readbuf *rb, void *ptr, size_t len) 406 { 407 size_t l; 408 409 if (rb->rpos == rb->wpos) { 410 return 0; 411 } else if (rb->rpos < rb->wpos) { 412 l = PDU_MIN(rb->wpos - rb->rpos, len); 413 memcpy(ptr, rb->buf + rb->rpos, l); 414 rb->rpos += l; 415 return l; 416 } else { 417 l = PDU_MIN(rb->size - rb->rpos, len); 418 memcpy(ptr, rb->buf + rb->rpos, l); 419 rb->rpos += l; 420 if (rb->rpos == rb->size) 421 rb->rpos = 0; 422 if (l < len) 423 return l + pdu_readbuf_read(rb, (char *)ptr + l, 424 len - l); 425 return l; 426 } 427 } 428 429 size_t 430 pdu_readbuf_len(struct pdu_readbuf *rb) 431 { 432 if (rb->rpos <= rb->wpos) 433 return rb->wpos - rb->rpos; 434 else 435 return rb->size - (rb->rpos - rb->wpos); 436 } 437 438 int 439 pdu_readbuf_set(struct pdu_readbuf *rb, size_t bsize) 440 { 441 char *nb; 442 443 if (bsize < rb->size) 444 /* can't shrink */ 445 return 0; 446 if ((nb = realloc(rb->buf, bsize)) == NULL) { 447 free(rb->buf); 448 return -1; 449 } 450 rb->buf = nb; 451 rb->size = bsize; 452 return 0; 453 } 454 455 void 456 pdu_readbuf_free(struct pdu_readbuf *rb) 457 { 458 free(rb->buf); 459 } 460