1 /* $OpenBSD: pdu.c,v 1.6 2011/05/04 21:00:04 claudio 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 <strings.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 == -1 || (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 if (errno == EAGAIN || errno == ENOBUFS || 251 errno == EINTR) /* try later */ 252 return 0; 253 else { 254 log_warn("pdu_read"); 255 return -1; 256 } 257 } 258 if (n == 0) 259 /* XXX what should we do on close with remaining data? */ 260 return 0; 261 262 c->prbuf.wpos += n; 263 if (c->prbuf.wpos >= c->prbuf.size) 264 c->prbuf.wpos -= c->prbuf.size; 265 266 return (n); 267 } 268 269 ssize_t 270 pdu_write(struct connection *c) 271 { 272 struct iovec iov[PDU_WRIOV]; 273 struct pdu *b, *nb; 274 unsigned int niov = 0, j; 275 size_t off, resid, size; 276 ssize_t n; 277 278 TAILQ_FOREACH(b, &c->pdu_w, entry) { 279 if (niov >= PDU_WRIOV) 280 break; 281 off = b->resid; 282 for (j = 0; j < PDU_MAXIOV && niov < PDU_WRIOV; j++) { 283 if (!b->iov[j].iov_len) 284 continue; 285 if (off >= b->iov[j].iov_len) { 286 off -= b->iov[j].iov_len; 287 continue; 288 } 289 iov[niov].iov_base = (char *)b->iov[j].iov_base + off; 290 iov[niov++].iov_len = b->iov[j].iov_len - off; 291 off = 0; 292 } 293 } 294 295 if ((n = writev(c->fd, iov, niov)) == -1) { 296 if (errno == EAGAIN || errno == ENOBUFS || 297 errno == EINTR) /* try later */ 298 return 0; 299 else { 300 log_warn("pdu_write"); 301 return -1; 302 } 303 } 304 if (n == 0) 305 return 0; 306 307 size = n; 308 for (b = TAILQ_FIRST(&c->pdu_w); b != NULL && size > 0; b = nb) { 309 nb = TAILQ_NEXT(b, entry); 310 resid = b->resid; 311 for (j = 0; j < PDU_MAXIOV; j++) { 312 if (resid >= b->iov[j].iov_len) 313 resid -= b->iov[j].iov_len; 314 else if (size >= b->iov[j].iov_len - resid) { 315 size -= b->iov[j].iov_len - resid; 316 b->resid += b->iov[j].iov_len - resid; 317 resid = 0; 318 } else { 319 b->resid += size; 320 size = 0; 321 break; 322 } 323 } 324 if (j == PDU_MAXIOV) { 325 /* all written */ 326 TAILQ_REMOVE(&c->pdu_w, b, entry); 327 pdu_free(b); 328 } 329 } 330 return n; 331 } 332 333 int 334 pdu_pending(struct connection *c) 335 { 336 if (TAILQ_EMPTY(&c->pdu_w)) 337 return 0; 338 else 339 return 1; 340 } 341 342 void 343 pdu_parse(struct connection *c) 344 { 345 struct pdu *p; 346 struct iscsi_pdu *ipdu; 347 char *ahb, *db; 348 size_t ahslen, dlen, off; 349 ssize_t n; 350 unsigned int j; 351 352 /* XXX XXX I DON'T LIKE YOU. CAN I REWRITE YOU? */ 353 354 do { 355 if (!(p = c->prbuf.wip)) { 356 /* get and parse base header */ 357 if (pdu_readbuf_len(&c->prbuf) < sizeof(*ipdu)) 358 return; 359 if (!(p = pdu_new())) 360 goto fail; 361 if (!(ipdu = pdu_gethdr(p))) 362 goto fail; 363 364 c->prbuf.wip = p; 365 /* 366 * XXX maybe a pdu_readbuf_peek() would allow a better 367 * error handling. 368 */ 369 pdu_readbuf_read(&c->prbuf, ipdu, sizeof(*ipdu)); 370 371 ahslen = ipdu->ahslen * sizeof(u_int32_t); 372 if (ahslen != 0) { 373 if (!(ahb = pdu_alloc(ahslen)) || 374 pdu_addbuf(p, ahb, ahslen, PDU_AHS)) 375 goto fail; 376 } 377 378 dlen = ipdu->datalen[0] << 16 | ipdu->datalen[1] << 8 | 379 ipdu->datalen[2]; 380 if (dlen != 0) { 381 if (!(db = pdu_alloc(dlen)) || 382 pdu_addbuf(p, db, dlen, PDU_DATA)) 383 goto fail; 384 } 385 386 p->resid = sizeof(*ipdu); 387 } else { 388 off = p->resid; 389 for (j = 0; j < PDU_MAXIOV; j++) { 390 if (off >= p->iov[j].iov_len) 391 off -= p->iov[j].iov_len; 392 else { 393 n = pdu_readbuf_read(&c->prbuf, 394 (char *)p->iov[j].iov_base + off, 395 p->iov[j].iov_len - off); 396 p->resid += n; 397 if (n == 0 || off + n != 398 p->iov[j].iov_len) 399 return; 400 } 401 } 402 p->resid = 0; /* reset resid so pdu can be reused */ 403 task_pdu_cb(c, p); 404 c->prbuf.wip = NULL; 405 } 406 } while (1); 407 fail: 408 fatalx("pdu_parse hit a space oddity"); 409 } 410 411 size_t 412 pdu_readbuf_read(struct pdu_readbuf *rb, void *ptr, size_t len) 413 { 414 size_t l; 415 416 if (rb->rpos == rb->wpos) { 417 return (0); 418 } else if (rb->rpos < rb->wpos) { 419 l = PDU_MIN(rb->wpos - rb->rpos, len); 420 bcopy(rb->buf + rb->rpos, ptr, l); 421 rb->rpos += l; 422 return l; 423 } else { 424 l = PDU_MIN(rb->size - rb->rpos, len); 425 bcopy(rb->buf + rb->rpos, ptr, l); 426 rb->rpos += l; 427 if (rb->rpos == rb->size) 428 rb->rpos = 0; 429 if (l < len) 430 return l + pdu_readbuf_read(rb, (char *)ptr + l, 431 len - l); 432 return l; 433 } 434 } 435 436 size_t 437 pdu_readbuf_len(struct pdu_readbuf *rb) 438 { 439 if (rb->rpos <= rb->wpos) 440 return rb->wpos - rb->rpos; 441 else 442 return rb->size - (rb->rpos - rb->wpos); 443 } 444 445 int 446 pdu_readbuf_set(struct pdu_readbuf *rb, size_t bsize) 447 { 448 char *nb; 449 450 if (bsize < rb->size) 451 /* can't shrink */ 452 return 0; 453 if ((nb = realloc(rb->buf, bsize)) == NULL) { 454 free(rb->buf); 455 return -1; 456 } 457 rb->buf = nb; 458 rb->size = bsize; 459 return 0; 460 } 461 462 void 463 pdu_readbuf_free(struct pdu_readbuf *rb) 464 { 465 free(rb->buf); 466 } 467