1/*	$OpenBSD: pdu.c,v 1.13 2021/04/12 10:03:33 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
36size_t	pdu_readbuf_read(struct pdu_readbuf *, void *, size_t);
37size_t	pdu_readbuf_len(struct pdu_readbuf *);
38
39#define PDU_MIN(_x, _y)		((_x) < (_y) ? (_x) : (_y))
40
41void *
42pdu_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
55int
56text_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
90struct kvp *
91pdu_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 */
146u_int64_t
147text_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
191int
192text_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
218void
219pdu_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
229ssize_t
230pdu_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
262ssize_t
263pdu_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
326int
327pdu_pending(struct connection *c)
328{
329	if (TAILQ_EMPTY(&c->pdu_w))
330		return 0;
331	else
332		return 1;
333}
334
335void
336pdu_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);
400fail:
401	fatalx("pdu_parse hit a space oddity");
402}
403
404size_t
405pdu_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
429size_t
430pdu_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
438int
439pdu_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
455void
456pdu_readbuf_free(struct pdu_readbuf *rb)
457{
458	free(rb->buf);
459}
460