xref: /openbsd-src/usr.sbin/iscsid/pdu.c (revision f8e180f08610097e0e382117825432da7473d010)
1*f8e180f0Sclaudio /*	$OpenBSD: pdu.c,v 1.15 2025/01/23 12:17:48 claudio Exp $ */
2bde1ae23Sclaudio 
3bde1ae23Sclaudio /*
4bde1ae23Sclaudio  * Copyright (c) 2009 Claudio Jeker <claudio@openbsd.org>
5bde1ae23Sclaudio  *
6bde1ae23Sclaudio  * Permission to use, copy, modify, and distribute this software for any
7bde1ae23Sclaudio  * purpose with or without fee is hereby granted, provided that the above
8bde1ae23Sclaudio  * copyright notice and this permission notice appear in all copies.
9bde1ae23Sclaudio  *
10bde1ae23Sclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11bde1ae23Sclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12bde1ae23Sclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13bde1ae23Sclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14bde1ae23Sclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15bde1ae23Sclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16bde1ae23Sclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17bde1ae23Sclaudio  */
18bde1ae23Sclaudio #include <sys/types.h>
19bde1ae23Sclaudio #include <sys/queue.h>
20bde1ae23Sclaudio #include <sys/socket.h>
21bde1ae23Sclaudio #include <sys/uio.h>
22bde1ae23Sclaudio 
23bde1ae23Sclaudio #include <scsi/iscsi.h>
24bde1ae23Sclaudio 
25bde1ae23Sclaudio #include <errno.h>
26bde1ae23Sclaudio #include <event.h>
274125a3c4Sclaudio #include <limits.h>
28bde1ae23Sclaudio #include <stdio.h>
29bde1ae23Sclaudio #include <stdlib.h>
302f43ab01Smmcc #include <string.h>
31bde1ae23Sclaudio #include <unistd.h>
32bde1ae23Sclaudio 
33bde1ae23Sclaudio #include "iscsid.h"
34bde1ae23Sclaudio #include "log.h"
35bde1ae23Sclaudio 
36bde1ae23Sclaudio size_t	pdu_readbuf_read(struct pdu_readbuf *, void *, size_t);
37bde1ae23Sclaudio size_t	pdu_readbuf_len(struct pdu_readbuf *);
38bde1ae23Sclaudio 
39bde1ae23Sclaudio #define PDU_MIN(_x, _y)		((_x) < (_y) ? (_x) : (_y))
40bde1ae23Sclaudio 
41bde1ae23Sclaudio void *
42bde1ae23Sclaudio pdu_gethdr(struct pdu *p)
43bde1ae23Sclaudio {
44bde1ae23Sclaudio 	void *hdr;
45bde1ae23Sclaudio 
46bde1ae23Sclaudio 	if (!(hdr = calloc(1, sizeof(struct iscsi_pdu))))
47bde1ae23Sclaudio 		return NULL;
48bde1ae23Sclaudio 	if (pdu_addbuf(p, hdr, sizeof(struct iscsi_pdu), PDU_HEADER)) {
49bde1ae23Sclaudio 		free(hdr);
50bde1ae23Sclaudio 		return NULL;
51bde1ae23Sclaudio 	}
52bde1ae23Sclaudio 	return hdr;
53bde1ae23Sclaudio }
54bde1ae23Sclaudio 
55bde1ae23Sclaudio int
56bde1ae23Sclaudio text_to_pdu(struct kvp *k, struct pdu *p)
57bde1ae23Sclaudio {
58bde1ae23Sclaudio 	char *buf, *s;
59bde1ae23Sclaudio 	size_t	len = 0, rem;
60bde1ae23Sclaudio 	int n, nk;
61bde1ae23Sclaudio 
624125a3c4Sclaudio 	if (k == NULL)
63be16f395Sclaudio 		return 0;
644125a3c4Sclaudio 
65bde1ae23Sclaudio 	nk = 0;
66bde1ae23Sclaudio 	while(k[nk].key) {
67bde1ae23Sclaudio 		len += 2 + strlen(k[nk].key) + strlen(k[nk].value);
68bde1ae23Sclaudio 		nk++;
69bde1ae23Sclaudio 	}
70bde1ae23Sclaudio 
71bde1ae23Sclaudio 	if (!(buf = pdu_alloc(len)))
72bde1ae23Sclaudio 		return -1;
73bde1ae23Sclaudio 	s = buf;
74bde1ae23Sclaudio 	rem = len;
75bde1ae23Sclaudio 	nk = 0;
76bde1ae23Sclaudio 	while(k[nk].key) {
77bde1ae23Sclaudio 		n = snprintf(s, rem, "%s=%s", k[nk].key, k[nk].value);
78515e489cSderaadt 		if (n < 0 || (size_t)n >= rem)
79bde1ae23Sclaudio 			fatalx("text_to_pdu");
80bde1ae23Sclaudio 		rem -= n + 1;
81bde1ae23Sclaudio 		s += n + 1;
82bde1ae23Sclaudio 		nk++;
83bde1ae23Sclaudio 	}
84bde1ae23Sclaudio 
85bde1ae23Sclaudio 	if (pdu_addbuf(p, buf, len, PDU_DATA))
86bde1ae23Sclaudio 		return -1;
87bde1ae23Sclaudio 	return len;
88bde1ae23Sclaudio }
89bde1ae23Sclaudio 
90bde1ae23Sclaudio struct kvp *
91bde1ae23Sclaudio pdu_to_text(char *buf, size_t len)
92bde1ae23Sclaudio {
93bde1ae23Sclaudio 	struct kvp *k;
94bde1ae23Sclaudio 	size_t n;
95bde1ae23Sclaudio 	char *eq;
96bde1ae23Sclaudio 	unsigned int nkvp = 0, i;
97bde1ae23Sclaudio 
988b9a361cSclaudio 	/* remove padding zeros */
99cb408c6cSclaudio 	for (n = len; n > 0 && buf[n - 1] == '\0'; n--)
1008b9a361cSclaudio 		;
1018b9a361cSclaudio 	if (n == len) {
102bde1ae23Sclaudio 		log_debug("pdu_to_text: badly terminated text data");
103bde1ae23Sclaudio 		return NULL;
104bde1ae23Sclaudio 	}
1058b9a361cSclaudio 	len = n + 1;
1068b9a361cSclaudio 
107bde1ae23Sclaudio 	for(n = 0; n < len; n++)
108bde1ae23Sclaudio 		if (buf[n] == '\0')
109bde1ae23Sclaudio 			nkvp++;
110bde1ae23Sclaudio 
111bde1ae23Sclaudio 	if (!(k = calloc(nkvp + 1, sizeof(*k))))
112bde1ae23Sclaudio 		return NULL;
113bde1ae23Sclaudio 
114bde1ae23Sclaudio 	for (i = 0; i < nkvp; i++) {
115bde1ae23Sclaudio 		eq = strchr(buf, '=');
116bde1ae23Sclaudio 		if (!eq) {
117bde1ae23Sclaudio 			log_debug("pdu_to_text: badly encoded text data");
118bde1ae23Sclaudio 			free(k);
119bde1ae23Sclaudio 			return NULL;
120bde1ae23Sclaudio 		}
121bde1ae23Sclaudio 		*eq++ = '\0';
122bde1ae23Sclaudio 		k[i].key = buf;
123bde1ae23Sclaudio 		k[i].value = eq;
124bde1ae23Sclaudio 		buf = eq + strlen(eq) + 1;
125bde1ae23Sclaudio 	}
126bde1ae23Sclaudio 	return k;
127bde1ae23Sclaudio }
128bde1ae23Sclaudio 
1294125a3c4Sclaudio /* Modified version of strtonum() to fit iscsid's need
1304125a3c4Sclaudio  *
1314125a3c4Sclaudio  * Copyright (c) 2004 Ted Unangst and Todd Miller
1324125a3c4Sclaudio  * All rights reserved.
1334125a3c4Sclaudio  *
1344125a3c4Sclaudio  * Permission to use, copy, modify, and distribute this software for any
1354125a3c4Sclaudio  * purpose with or without fee is hereby granted, provided that the above
1364125a3c4Sclaudio  * copyright notice and this permission notice appear in all copies.
1374125a3c4Sclaudio  *
1384125a3c4Sclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1394125a3c4Sclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1404125a3c4Sclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1414125a3c4Sclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1424125a3c4Sclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1434125a3c4Sclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1444125a3c4Sclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1454125a3c4Sclaudio  */
1464125a3c4Sclaudio u_int64_t
1474125a3c4Sclaudio text_to_num(const char *numstr, u_int64_t minval, u_int64_t maxval,
1484125a3c4Sclaudio     const char **errstrp)
1494125a3c4Sclaudio {
1504125a3c4Sclaudio 	unsigned long long ull = 0;
1514125a3c4Sclaudio 	char *ep;
1524125a3c4Sclaudio 	int error = 0;
1534125a3c4Sclaudio 	struct errval {
1544125a3c4Sclaudio 		const char *errstr;
1554125a3c4Sclaudio 		int err;
1564125a3c4Sclaudio 	} ev[4] = {
1574125a3c4Sclaudio 		{ NULL,		0 },
1584125a3c4Sclaudio 		{ "invalid",	EINVAL },
1594125a3c4Sclaudio 		{ "too small",  ERANGE },
1604125a3c4Sclaudio 		{ "too large",	ERANGE }
1614125a3c4Sclaudio 	};
1624125a3c4Sclaudio #define INVALID		1
1634125a3c4Sclaudio #define TOOSMALL	2
1644125a3c4Sclaudio #define TOOLARGE	3
1654125a3c4Sclaudio 
1664125a3c4Sclaudio 	ev[0].err = errno;
1674125a3c4Sclaudio 	errno = 0;
1684125a3c4Sclaudio 	if (minval > maxval)
1694125a3c4Sclaudio 		error = INVALID;
1704125a3c4Sclaudio 	else {
1714125a3c4Sclaudio 		ull = strtoull(numstr, &ep, 0);
1724125a3c4Sclaudio 		if (numstr == ep || *ep != '\0')
1734125a3c4Sclaudio 			error = INVALID;
1744125a3c4Sclaudio 		else if (ull < minval)
1754125a3c4Sclaudio 			error = TOOSMALL;
1764125a3c4Sclaudio 		else if ((ull == ULLONG_MAX && errno == ERANGE) || ull > maxval)
1774125a3c4Sclaudio 			error = TOOLARGE;
1784125a3c4Sclaudio 	}
1794125a3c4Sclaudio 	if (errstrp != NULL)
1804125a3c4Sclaudio 		*errstrp = ev[error].errstr;
1814125a3c4Sclaudio 	errno = ev[error].err;
1824125a3c4Sclaudio 	if (error)
1834125a3c4Sclaudio 		ull = 0;
1844125a3c4Sclaudio 
185be16f395Sclaudio 	return ull;
1864125a3c4Sclaudio #undef INVALID
1874125a3c4Sclaudio #undef TOOSMALL
1884125a3c4Sclaudio #undef TOOLARGE
1894125a3c4Sclaudio }
1904125a3c4Sclaudio 
1914125a3c4Sclaudio int
1924125a3c4Sclaudio text_to_bool(const char *buf, const char **errstrp)
1934125a3c4Sclaudio {
194*f8e180f0Sclaudio 	int val;
1954125a3c4Sclaudio 
196aeec9c16Sclaudio 	if (strcmp(buf, "Yes") == 0)
1974125a3c4Sclaudio 		val = 1;
198*f8e180f0Sclaudio 	else if (strcmp(buf, "No") == 0)
199*f8e180f0Sclaudio 		val = 0;
200*f8e180f0Sclaudio 	else {
201aeec9c16Sclaudio 		if (errstrp != NULL)
2024125a3c4Sclaudio 			*errstrp = "invalid";
203*f8e180f0Sclaudio 		return 0;
2044125a3c4Sclaudio 	}
205*f8e180f0Sclaudio 	if (errstrp != NULL)
206*f8e180f0Sclaudio 		*errstrp = NULL;
207be16f395Sclaudio 	return val;
2084125a3c4Sclaudio }
2094125a3c4Sclaudio 
210aeec9c16Sclaudio int
211aeec9c16Sclaudio text_to_digest(const char *buf, const char **errstrp)
212aeec9c16Sclaudio {
213aeec9c16Sclaudio 	int val = 0;
214aeec9c16Sclaudio 	size_t len;
215aeec9c16Sclaudio 	const char *p;
216aeec9c16Sclaudio 
217aeec9c16Sclaudio 	while (buf != NULL) {
218aeec9c16Sclaudio 		p = strchr(buf, ',');
219aeec9c16Sclaudio 		if (p == NULL)
220aeec9c16Sclaudio 			len = strlen(buf);
221aeec9c16Sclaudio 		else
222aeec9c16Sclaudio 			len = p++ - buf;
223aeec9c16Sclaudio 
224aeec9c16Sclaudio 		if (strncmp(buf, "None", len) == 0)
225aeec9c16Sclaudio 			val |= DIGEST_NONE;
226aeec9c16Sclaudio 		else if (strncmp(buf, "CRC32C", len) == 0)
227aeec9c16Sclaudio 			val |= DIGEST_CRC32C;
228aeec9c16Sclaudio 		else {
229aeec9c16Sclaudio 			if (errstrp != NULL)
230aeec9c16Sclaudio 				*errstrp = "invalid";
231aeec9c16Sclaudio 			return 0;
232aeec9c16Sclaudio 		}
233aeec9c16Sclaudio 		buf = p;
234aeec9c16Sclaudio 	}
235*f8e180f0Sclaudio 	if (errstrp != NULL)
236*f8e180f0Sclaudio 		*errstrp = NULL;
237aeec9c16Sclaudio 	return val;
238aeec9c16Sclaudio }
2394125a3c4Sclaudio 
240bde1ae23Sclaudio /*
241bde1ae23Sclaudio  * Internal functions to send/recv pdus.
242bde1ae23Sclaudio  */
243bde1ae23Sclaudio 
244bde1ae23Sclaudio void
245bde1ae23Sclaudio pdu_free_queue(struct pduq *channel)
246bde1ae23Sclaudio {
247bde1ae23Sclaudio 	struct pdu *p;
248bde1ae23Sclaudio 
249bde1ae23Sclaudio 	while ((p = TAILQ_FIRST(channel))) {
250bde1ae23Sclaudio 		TAILQ_REMOVE(channel, p, entry);
251bde1ae23Sclaudio 		pdu_free(p);
252bde1ae23Sclaudio 	}
253bde1ae23Sclaudio }
254bde1ae23Sclaudio 
255bde1ae23Sclaudio ssize_t
256bde1ae23Sclaudio pdu_read(struct connection *c)
257bde1ae23Sclaudio {
258bde1ae23Sclaudio 	struct iovec iov[2];
259bde1ae23Sclaudio 	unsigned int niov = 1;
260bde1ae23Sclaudio 	ssize_t n;
261bde1ae23Sclaudio 
262bde1ae23Sclaudio 	bzero(&iov, sizeof(iov));
263bde1ae23Sclaudio 	iov[0].iov_base = c->prbuf.buf + c->prbuf.wpos;
264bde1ae23Sclaudio 	if (c->prbuf.wpos < c->prbuf.rpos)
265bde1ae23Sclaudio 		iov[0].iov_len = c->prbuf.rpos - c->prbuf.wpos;
266bde1ae23Sclaudio 	else {
267bde1ae23Sclaudio 		iov[0].iov_len = c->prbuf.size - c->prbuf.wpos;
268bde1ae23Sclaudio 		if (c->prbuf.rpos > 0) {
269bde1ae23Sclaudio 			niov++;
270bde1ae23Sclaudio 			iov[1].iov_base = c->prbuf.buf;
271bde1ae23Sclaudio 			iov[1].iov_len = c->prbuf.rpos - 1;
272bde1ae23Sclaudio 		}
273bde1ae23Sclaudio 	}
274bde1ae23Sclaudio 
275f9cc11ecSclaudio 	if ((n = readv(c->fd, iov, niov)) == -1)
276bde1ae23Sclaudio 		return -1;
277bde1ae23Sclaudio 	if (n == 0)
278bde1ae23Sclaudio 		/* XXX what should we do on close with remaining data? */
279bde1ae23Sclaudio 		return 0;
280bde1ae23Sclaudio 
281bde1ae23Sclaudio 	c->prbuf.wpos += n;
282bde1ae23Sclaudio 	if (c->prbuf.wpos >= c->prbuf.size)
283bde1ae23Sclaudio 		c->prbuf.wpos -= c->prbuf.size;
284bde1ae23Sclaudio 
285be16f395Sclaudio 	return n;
286bde1ae23Sclaudio }
287bde1ae23Sclaudio 
288bde1ae23Sclaudio ssize_t
289bde1ae23Sclaudio pdu_write(struct connection *c)
290bde1ae23Sclaudio {
291bde1ae23Sclaudio 	struct iovec iov[PDU_WRIOV];
292bde1ae23Sclaudio 	struct pdu *b, *nb;
293bde1ae23Sclaudio 	unsigned int niov = 0, j;
294bde1ae23Sclaudio 	size_t off, resid, size;
295bde1ae23Sclaudio 	ssize_t n;
296bde1ae23Sclaudio 
297bde1ae23Sclaudio 	TAILQ_FOREACH(b, &c->pdu_w, entry) {
298bde1ae23Sclaudio 		if (niov >= PDU_WRIOV)
299bde1ae23Sclaudio 			break;
300bde1ae23Sclaudio 		off = b->resid;
301bde1ae23Sclaudio 		for (j = 0; j < PDU_MAXIOV && niov < PDU_WRIOV; j++) {
302bde1ae23Sclaudio 			if (!b->iov[j].iov_len)
303bde1ae23Sclaudio 				continue;
304bde1ae23Sclaudio 			if (off >= b->iov[j].iov_len) {
305bde1ae23Sclaudio 				off -= b->iov[j].iov_len;
306bde1ae23Sclaudio 				continue;
307bde1ae23Sclaudio 			}
308bde1ae23Sclaudio 			iov[niov].iov_base = (char *)b->iov[j].iov_base + off;
309bde1ae23Sclaudio 			iov[niov++].iov_len = b->iov[j].iov_len - off;
310bde1ae23Sclaudio 			off = 0;
311bde1ae23Sclaudio 		}
312bde1ae23Sclaudio 	}
313bde1ae23Sclaudio 
314bde1ae23Sclaudio 	if ((n = writev(c->fd, iov, niov)) == -1) {
315bde1ae23Sclaudio 		if (errno == EAGAIN || errno == ENOBUFS ||
316bde1ae23Sclaudio 		    errno == EINTR)	/* try later */
317bde1ae23Sclaudio 			return 0;
318bde1ae23Sclaudio 		else {
319bde1ae23Sclaudio 			log_warn("pdu_write");
320bde1ae23Sclaudio 			return -1;
321bde1ae23Sclaudio 		}
322bde1ae23Sclaudio 	}
323bde1ae23Sclaudio 	if (n == 0)
324bde1ae23Sclaudio 		return 0;
325bde1ae23Sclaudio 
326bde1ae23Sclaudio 	size = n;
327bde1ae23Sclaudio 	for (b = TAILQ_FIRST(&c->pdu_w); b != NULL && size > 0; b = nb) {
328bde1ae23Sclaudio 		nb = TAILQ_NEXT(b, entry);
329bde1ae23Sclaudio 		resid = b->resid;
330bde1ae23Sclaudio 		for (j = 0; j < PDU_MAXIOV; j++) {
331bde1ae23Sclaudio 			if (resid >= b->iov[j].iov_len)
332bde1ae23Sclaudio 				resid -= b->iov[j].iov_len;
333bde1ae23Sclaudio 			else if (size >= b->iov[j].iov_len - resid) {
334bde1ae23Sclaudio 				size -= b->iov[j].iov_len - resid;
335bde1ae23Sclaudio 				b->resid += b->iov[j].iov_len - resid;
336bde1ae23Sclaudio 				resid = 0;
337bde1ae23Sclaudio 			} else {
338bde1ae23Sclaudio 				b->resid += size;
339bde1ae23Sclaudio 				size = 0;
340bde1ae23Sclaudio 				break;
341bde1ae23Sclaudio 			}
342bde1ae23Sclaudio 		}
343bde1ae23Sclaudio 		if (j == PDU_MAXIOV) {
344bde1ae23Sclaudio 			/* all written */
345bde1ae23Sclaudio 			TAILQ_REMOVE(&c->pdu_w, b, entry);
346bde1ae23Sclaudio 			pdu_free(b);
347bde1ae23Sclaudio 		}
348bde1ae23Sclaudio 	}
349bde1ae23Sclaudio 	return n;
350bde1ae23Sclaudio }
351bde1ae23Sclaudio 
352bde1ae23Sclaudio int
353bde1ae23Sclaudio pdu_pending(struct connection *c)
354bde1ae23Sclaudio {
355bde1ae23Sclaudio 	if (TAILQ_EMPTY(&c->pdu_w))
356bde1ae23Sclaudio 		return 0;
357bde1ae23Sclaudio 	else
358bde1ae23Sclaudio 		return 1;
359bde1ae23Sclaudio }
360bde1ae23Sclaudio 
361bde1ae23Sclaudio void
362bde1ae23Sclaudio pdu_parse(struct connection *c)
363bde1ae23Sclaudio {
364bde1ae23Sclaudio 	struct pdu *p;
365bde1ae23Sclaudio 	struct iscsi_pdu *ipdu;
366bde1ae23Sclaudio 	char *ahb, *db;
367bde1ae23Sclaudio 	size_t ahslen, dlen, off;
368bde1ae23Sclaudio 	ssize_t n;
369bde1ae23Sclaudio 	unsigned int j;
370bde1ae23Sclaudio 
371bde1ae23Sclaudio /* XXX XXX I DON'T LIKE YOU. CAN I REWRITE YOU? */
372bde1ae23Sclaudio 
373bde1ae23Sclaudio 	do {
374bde1ae23Sclaudio 		if (!(p = c->prbuf.wip)) {
375bde1ae23Sclaudio 			/* get and parse base header */
376bde1ae23Sclaudio 			if (pdu_readbuf_len(&c->prbuf) < sizeof(*ipdu))
377bde1ae23Sclaudio 				return;
378bde1ae23Sclaudio 			if (!(p = pdu_new()))
379bde1ae23Sclaudio 				goto fail;
380bde1ae23Sclaudio 			if (!(ipdu = pdu_gethdr(p)))
381bde1ae23Sclaudio 				goto fail;
382bde1ae23Sclaudio 
383bde1ae23Sclaudio 			c->prbuf.wip = p;
384bde1ae23Sclaudio 			/*
385bde1ae23Sclaudio 			 * XXX maybe a pdu_readbuf_peek() would allow a better
386bde1ae23Sclaudio 			 * error handling.
387bde1ae23Sclaudio 			 */
388bde1ae23Sclaudio 			pdu_readbuf_read(&c->prbuf, ipdu, sizeof(*ipdu));
389bde1ae23Sclaudio 
390bde1ae23Sclaudio 			ahslen = ipdu->ahslen * sizeof(u_int32_t);
391bde1ae23Sclaudio 			if (ahslen != 0) {
392bde1ae23Sclaudio 				if (!(ahb = pdu_alloc(ahslen)) ||
393bde1ae23Sclaudio 				    pdu_addbuf(p, ahb, ahslen, PDU_AHS))
394bde1ae23Sclaudio 					goto fail;
395bde1ae23Sclaudio 			}
396bde1ae23Sclaudio 
397bde1ae23Sclaudio 			dlen = ipdu->datalen[0] << 16 | ipdu->datalen[1] << 8 |
398bde1ae23Sclaudio 			    ipdu->datalen[2];
399bde1ae23Sclaudio 			if (dlen != 0) {
400bde1ae23Sclaudio 				if (!(db = pdu_alloc(dlen)) ||
401bde1ae23Sclaudio 				    pdu_addbuf(p, db, dlen, PDU_DATA))
402bde1ae23Sclaudio 					goto fail;
403bde1ae23Sclaudio 			}
404bde1ae23Sclaudio 
405bde1ae23Sclaudio 			p->resid = sizeof(*ipdu);
406bde1ae23Sclaudio 		} else {
407bde1ae23Sclaudio 			off = p->resid;
408bde1ae23Sclaudio 			for (j = 0; j < PDU_MAXIOV; j++) {
409bde1ae23Sclaudio 				if (off >= p->iov[j].iov_len)
410bde1ae23Sclaudio 					off -=  p->iov[j].iov_len;
411bde1ae23Sclaudio 				else {
412bde1ae23Sclaudio 					n = pdu_readbuf_read(&c->prbuf,
413bde1ae23Sclaudio 					    (char *)p->iov[j].iov_base + off,
414bde1ae23Sclaudio 					    p->iov[j].iov_len - off);
415bde1ae23Sclaudio 					p->resid += n;
416bde1ae23Sclaudio 					if (n == 0 || off + n !=
417bde1ae23Sclaudio 					    p->iov[j].iov_len)
418bde1ae23Sclaudio 						return;
419bde1ae23Sclaudio 				}
420bde1ae23Sclaudio 			}
4219decbecbSclaudio 			p->resid = 0; /* reset resid so pdu can be reused */
422bde1ae23Sclaudio 			c->prbuf.wip = NULL;
4232337c7c9Sclaudio 			task_pdu_cb(c, p);
424bde1ae23Sclaudio 		}
425bde1ae23Sclaudio 	} while (1);
426bde1ae23Sclaudio fail:
427bde1ae23Sclaudio 	fatalx("pdu_parse hit a space oddity");
428bde1ae23Sclaudio }
429bde1ae23Sclaudio 
430bde1ae23Sclaudio size_t
431bde1ae23Sclaudio pdu_readbuf_read(struct pdu_readbuf *rb, void *ptr, size_t len)
432bde1ae23Sclaudio {
433bde1ae23Sclaudio 	size_t l;
434bde1ae23Sclaudio 
435bde1ae23Sclaudio 	if (rb->rpos == rb->wpos) {
436be16f395Sclaudio 		return 0;
437bde1ae23Sclaudio 	} else if (rb->rpos < rb->wpos) {
438bde1ae23Sclaudio 		l = PDU_MIN(rb->wpos - rb->rpos, len);
439a86db512Sclaudio 		memcpy(ptr, rb->buf + rb->rpos, l);
440bde1ae23Sclaudio 		rb->rpos += l;
441bde1ae23Sclaudio 		return l;
442bde1ae23Sclaudio 	} else {
443bde1ae23Sclaudio 		l = PDU_MIN(rb->size - rb->rpos, len);
444a86db512Sclaudio 		memcpy(ptr, rb->buf + rb->rpos, l);
445bde1ae23Sclaudio 		rb->rpos += l;
446bde1ae23Sclaudio 		if (rb->rpos == rb->size)
447bde1ae23Sclaudio 			rb->rpos = 0;
448bde1ae23Sclaudio 		if (l < len)
449bde1ae23Sclaudio 			return l + pdu_readbuf_read(rb, (char *)ptr + l,
450bde1ae23Sclaudio 			    len - l);
451bde1ae23Sclaudio 		return l;
452bde1ae23Sclaudio 	}
453bde1ae23Sclaudio }
454bde1ae23Sclaudio 
455bde1ae23Sclaudio size_t
456bde1ae23Sclaudio pdu_readbuf_len(struct pdu_readbuf *rb)
457bde1ae23Sclaudio {
458bde1ae23Sclaudio 	if (rb->rpos <= rb->wpos)
459bde1ae23Sclaudio 		return rb->wpos - rb->rpos;
460bde1ae23Sclaudio 	else
461bde1ae23Sclaudio 		return rb->size - (rb->rpos - rb->wpos);
462bde1ae23Sclaudio }
463bde1ae23Sclaudio 
464bde1ae23Sclaudio int
465bde1ae23Sclaudio pdu_readbuf_set(struct pdu_readbuf *rb, size_t bsize)
466bde1ae23Sclaudio {
467bde1ae23Sclaudio 	char *nb;
468bde1ae23Sclaudio 
469bde1ae23Sclaudio 	if (bsize < rb->size)
470bde1ae23Sclaudio 		/* can't shrink */
471bde1ae23Sclaudio 		return 0;
472bde1ae23Sclaudio 	if ((nb = realloc(rb->buf, bsize)) == NULL) {
473bde1ae23Sclaudio 		free(rb->buf);
474bde1ae23Sclaudio 		return -1;
475bde1ae23Sclaudio 	}
476bde1ae23Sclaudio 	rb->buf = nb;
477bde1ae23Sclaudio 	rb->size = bsize;
478bde1ae23Sclaudio 	return 0;
479bde1ae23Sclaudio }
480bde1ae23Sclaudio 
481bde1ae23Sclaudio void
482bde1ae23Sclaudio pdu_readbuf_free(struct pdu_readbuf *rb)
483bde1ae23Sclaudio {
484bde1ae23Sclaudio 	free(rb->buf);
485bde1ae23Sclaudio }
486