xref: /openbsd-src/usr.sbin/iscsid/pdu.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
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