1 /* $OpenBSD: pdu.c,v 1.15 2025/01/23 12:17:48 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 <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 *
pdu_gethdr(struct pdu * p)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
text_to_pdu(struct kvp * k,struct pdu * p)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 *
pdu_to_text(char * buf,size_t len)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
text_to_num(const char * numstr,u_int64_t minval,u_int64_t maxval,const char ** errstrp)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
text_to_bool(const char * buf,const char ** errstrp)192 text_to_bool(const char *buf, const char **errstrp)
193 {
194 int val;
195
196 if (strcmp(buf, "Yes") == 0)
197 val = 1;
198 else if (strcmp(buf, "No") == 0)
199 val = 0;
200 else {
201 if (errstrp != NULL)
202 *errstrp = "invalid";
203 return 0;
204 }
205 if (errstrp != NULL)
206 *errstrp = NULL;
207 return val;
208 }
209
210 int
text_to_digest(const char * buf,const char ** errstrp)211 text_to_digest(const char *buf, const char **errstrp)
212 {
213 int val = 0;
214 size_t len;
215 const char *p;
216
217 while (buf != NULL) {
218 p = strchr(buf, ',');
219 if (p == NULL)
220 len = strlen(buf);
221 else
222 len = p++ - buf;
223
224 if (strncmp(buf, "None", len) == 0)
225 val |= DIGEST_NONE;
226 else if (strncmp(buf, "CRC32C", len) == 0)
227 val |= DIGEST_CRC32C;
228 else {
229 if (errstrp != NULL)
230 *errstrp = "invalid";
231 return 0;
232 }
233 buf = p;
234 }
235 if (errstrp != NULL)
236 *errstrp = NULL;
237 return val;
238 }
239
240 /*
241 * Internal functions to send/recv pdus.
242 */
243
244 void
pdu_free_queue(struct pduq * channel)245 pdu_free_queue(struct pduq *channel)
246 {
247 struct pdu *p;
248
249 while ((p = TAILQ_FIRST(channel))) {
250 TAILQ_REMOVE(channel, p, entry);
251 pdu_free(p);
252 }
253 }
254
255 ssize_t
pdu_read(struct connection * c)256 pdu_read(struct connection *c)
257 {
258 struct iovec iov[2];
259 unsigned int niov = 1;
260 ssize_t n;
261
262 bzero(&iov, sizeof(iov));
263 iov[0].iov_base = c->prbuf.buf + c->prbuf.wpos;
264 if (c->prbuf.wpos < c->prbuf.rpos)
265 iov[0].iov_len = c->prbuf.rpos - c->prbuf.wpos;
266 else {
267 iov[0].iov_len = c->prbuf.size - c->prbuf.wpos;
268 if (c->prbuf.rpos > 0) {
269 niov++;
270 iov[1].iov_base = c->prbuf.buf;
271 iov[1].iov_len = c->prbuf.rpos - 1;
272 }
273 }
274
275 if ((n = readv(c->fd, iov, niov)) == -1)
276 return -1;
277 if (n == 0)
278 /* XXX what should we do on close with remaining data? */
279 return 0;
280
281 c->prbuf.wpos += n;
282 if (c->prbuf.wpos >= c->prbuf.size)
283 c->prbuf.wpos -= c->prbuf.size;
284
285 return n;
286 }
287
288 ssize_t
pdu_write(struct connection * c)289 pdu_write(struct connection *c)
290 {
291 struct iovec iov[PDU_WRIOV];
292 struct pdu *b, *nb;
293 unsigned int niov = 0, j;
294 size_t off, resid, size;
295 ssize_t n;
296
297 TAILQ_FOREACH(b, &c->pdu_w, entry) {
298 if (niov >= PDU_WRIOV)
299 break;
300 off = b->resid;
301 for (j = 0; j < PDU_MAXIOV && niov < PDU_WRIOV; j++) {
302 if (!b->iov[j].iov_len)
303 continue;
304 if (off >= b->iov[j].iov_len) {
305 off -= b->iov[j].iov_len;
306 continue;
307 }
308 iov[niov].iov_base = (char *)b->iov[j].iov_base + off;
309 iov[niov++].iov_len = b->iov[j].iov_len - off;
310 off = 0;
311 }
312 }
313
314 if ((n = writev(c->fd, iov, niov)) == -1) {
315 if (errno == EAGAIN || errno == ENOBUFS ||
316 errno == EINTR) /* try later */
317 return 0;
318 else {
319 log_warn("pdu_write");
320 return -1;
321 }
322 }
323 if (n == 0)
324 return 0;
325
326 size = n;
327 for (b = TAILQ_FIRST(&c->pdu_w); b != NULL && size > 0; b = nb) {
328 nb = TAILQ_NEXT(b, entry);
329 resid = b->resid;
330 for (j = 0; j < PDU_MAXIOV; j++) {
331 if (resid >= b->iov[j].iov_len)
332 resid -= b->iov[j].iov_len;
333 else if (size >= b->iov[j].iov_len - resid) {
334 size -= b->iov[j].iov_len - resid;
335 b->resid += b->iov[j].iov_len - resid;
336 resid = 0;
337 } else {
338 b->resid += size;
339 size = 0;
340 break;
341 }
342 }
343 if (j == PDU_MAXIOV) {
344 /* all written */
345 TAILQ_REMOVE(&c->pdu_w, b, entry);
346 pdu_free(b);
347 }
348 }
349 return n;
350 }
351
352 int
pdu_pending(struct connection * c)353 pdu_pending(struct connection *c)
354 {
355 if (TAILQ_EMPTY(&c->pdu_w))
356 return 0;
357 else
358 return 1;
359 }
360
361 void
pdu_parse(struct connection * c)362 pdu_parse(struct connection *c)
363 {
364 struct pdu *p;
365 struct iscsi_pdu *ipdu;
366 char *ahb, *db;
367 size_t ahslen, dlen, off;
368 ssize_t n;
369 unsigned int j;
370
371 /* XXX XXX I DON'T LIKE YOU. CAN I REWRITE YOU? */
372
373 do {
374 if (!(p = c->prbuf.wip)) {
375 /* get and parse base header */
376 if (pdu_readbuf_len(&c->prbuf) < sizeof(*ipdu))
377 return;
378 if (!(p = pdu_new()))
379 goto fail;
380 if (!(ipdu = pdu_gethdr(p)))
381 goto fail;
382
383 c->prbuf.wip = p;
384 /*
385 * XXX maybe a pdu_readbuf_peek() would allow a better
386 * error handling.
387 */
388 pdu_readbuf_read(&c->prbuf, ipdu, sizeof(*ipdu));
389
390 ahslen = ipdu->ahslen * sizeof(u_int32_t);
391 if (ahslen != 0) {
392 if (!(ahb = pdu_alloc(ahslen)) ||
393 pdu_addbuf(p, ahb, ahslen, PDU_AHS))
394 goto fail;
395 }
396
397 dlen = ipdu->datalen[0] << 16 | ipdu->datalen[1] << 8 |
398 ipdu->datalen[2];
399 if (dlen != 0) {
400 if (!(db = pdu_alloc(dlen)) ||
401 pdu_addbuf(p, db, dlen, PDU_DATA))
402 goto fail;
403 }
404
405 p->resid = sizeof(*ipdu);
406 } else {
407 off = p->resid;
408 for (j = 0; j < PDU_MAXIOV; j++) {
409 if (off >= p->iov[j].iov_len)
410 off -= p->iov[j].iov_len;
411 else {
412 n = pdu_readbuf_read(&c->prbuf,
413 (char *)p->iov[j].iov_base + off,
414 p->iov[j].iov_len - off);
415 p->resid += n;
416 if (n == 0 || off + n !=
417 p->iov[j].iov_len)
418 return;
419 }
420 }
421 p->resid = 0; /* reset resid so pdu can be reused */
422 c->prbuf.wip = NULL;
423 task_pdu_cb(c, p);
424 }
425 } while (1);
426 fail:
427 fatalx("pdu_parse hit a space oddity");
428 }
429
430 size_t
pdu_readbuf_read(struct pdu_readbuf * rb,void * ptr,size_t len)431 pdu_readbuf_read(struct pdu_readbuf *rb, void *ptr, size_t len)
432 {
433 size_t l;
434
435 if (rb->rpos == rb->wpos) {
436 return 0;
437 } else if (rb->rpos < rb->wpos) {
438 l = PDU_MIN(rb->wpos - rb->rpos, len);
439 memcpy(ptr, rb->buf + rb->rpos, l);
440 rb->rpos += l;
441 return l;
442 } else {
443 l = PDU_MIN(rb->size - rb->rpos, len);
444 memcpy(ptr, rb->buf + rb->rpos, l);
445 rb->rpos += l;
446 if (rb->rpos == rb->size)
447 rb->rpos = 0;
448 if (l < len)
449 return l + pdu_readbuf_read(rb, (char *)ptr + l,
450 len - l);
451 return l;
452 }
453 }
454
455 size_t
pdu_readbuf_len(struct pdu_readbuf * rb)456 pdu_readbuf_len(struct pdu_readbuf *rb)
457 {
458 if (rb->rpos <= rb->wpos)
459 return rb->wpos - rb->rpos;
460 else
461 return rb->size - (rb->rpos - rb->wpos);
462 }
463
464 int
pdu_readbuf_set(struct pdu_readbuf * rb,size_t bsize)465 pdu_readbuf_set(struct pdu_readbuf *rb, size_t bsize)
466 {
467 char *nb;
468
469 if (bsize < rb->size)
470 /* can't shrink */
471 return 0;
472 if ((nb = realloc(rb->buf, bsize)) == NULL) {
473 free(rb->buf);
474 return -1;
475 }
476 rb->buf = nb;
477 rb->size = bsize;
478 return 0;
479 }
480
481 void
pdu_readbuf_free(struct pdu_readbuf * rb)482 pdu_readbuf_free(struct pdu_readbuf *rb)
483 {
484 free(rb->buf);
485 }
486