GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/iscsid/pdu.c Lines: 0 190 0.0 %
Date: 2017-11-07 Branches: 0 148 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: pdu.c,v 1.11 2015/12/05 06:38:18 mmcc 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 *
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
		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
262
ssize_t
263
pdu_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
326
int
327
pdu_pending(struct connection *c)
328
{
329
	if (TAILQ_EMPTY(&c->pdu_w))
330
		return 0;
331
	else
332
		return 1;
333
}
334
335
void
336
pdu_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);
400
fail:
401
	fatalx("pdu_parse hit a space oddity");
402
}
403
404
size_t
405
pdu_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
429
size_t
430
pdu_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
438
int
439
pdu_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
455
void
456
pdu_readbuf_free(struct pdu_readbuf *rb)
457
{
458
	free(rb->buf);
459
}