GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/smtpd/smtpd/../dns.c Lines: 0 334 0.0 %
Date: 2017-11-13 Branches: 0 142 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: dns.c,v 1.84 2017/05/31 04:50:55 deraadt Exp $	*/
2
3
/*
4
 * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
5
 * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
6
 * Copyright (c) 2011-2014 Eric Faurot <eric@faurot.net>
7
 *
8
 * Permission to use, copy, modify, and distribute this software for any
9
 * purpose with or without fee is hereby granted, provided that the above
10
 * copyright notice and this permission notice appear in all copies.
11
 *
12
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
 */
20
21
#include <sys/types.h>
22
#include <sys/socket.h>
23
#include <sys/tree.h>
24
#include <sys/queue.h>
25
#include <sys/uio.h>
26
27
#include <netinet/in.h>
28
#include <arpa/inet.h>
29
#include <arpa/nameser.h>
30
#include <netdb.h>
31
32
#include <asr.h>
33
#include <event.h>
34
#include <imsg.h>
35
#include <stdio.h>
36
#include <stdlib.h>
37
#include <string.h>
38
#include <limits.h>
39
40
#include "smtpd.h"
41
#include "log.h"
42
43
struct dns_lookup {
44
	struct dns_session	*session;
45
	int			 preference;
46
};
47
48
struct dns_session {
49
	struct mproc		*p;
50
	uint64_t		 reqid;
51
	int			 type;
52
	char			 name[HOST_NAME_MAX+1];
53
	size_t			 mxfound;
54
	int			 error;
55
	int			 refcount;
56
};
57
58
static void dns_lookup_host(struct dns_session *, const char *, int);
59
static void dns_dispatch_host(struct asr_result *, void *);
60
static void dns_dispatch_ptr(struct asr_result *, void *);
61
static void dns_dispatch_mx(struct asr_result *, void *);
62
static void dns_dispatch_mx_preference(struct asr_result *, void *);
63
64
struct unpack {
65
	const char	*buf;
66
	size_t		 len;
67
	size_t		 offset;
68
	const char	*err;
69
};
70
71
struct dns_header {
72
	uint16_t	id;
73
	uint16_t	flags;
74
	uint16_t	qdcount;
75
	uint16_t	ancount;
76
	uint16_t	nscount;
77
	uint16_t	arcount;
78
};
79
80
struct dns_query {
81
	char		q_dname[MAXDNAME];
82
	uint16_t	q_type;
83
	uint16_t	q_class;
84
};
85
86
struct dns_rr {
87
	char		rr_dname[MAXDNAME];
88
	uint16_t	rr_type;
89
	uint16_t	rr_class;
90
	uint32_t	rr_ttl;
91
	union {
92
		struct {
93
			char	cname[MAXDNAME];
94
		} cname;
95
		struct {
96
			uint16_t	preference;
97
			char		exchange[MAXDNAME];
98
		} mx;
99
		struct {
100
			char	nsname[MAXDNAME];
101
		} ns;
102
		struct {
103
			char	ptrname[MAXDNAME];
104
		} ptr;
105
		struct {
106
			char		mname[MAXDNAME];
107
			char		rname[MAXDNAME];
108
			uint32_t	serial;
109
			uint32_t	refresh;
110
			uint32_t	retry;
111
			uint32_t	expire;
112
			uint32_t	minimum;
113
		} soa;
114
		struct {
115
			struct in_addr	addr;
116
		} in_a;
117
		struct {
118
			struct in6_addr	addr6;
119
		} in_aaaa;
120
		struct {
121
			uint16_t	 rdlen;
122
			const void	*rdata;
123
		} other;
124
	} rr;
125
};
126
127
static char *print_dname(const char *, char *, size_t);
128
static ssize_t dname_expand(const unsigned char *, size_t, size_t, size_t *,
129
    char *, size_t);
130
static int unpack_data(struct unpack *, void *, size_t);
131
static int unpack_u16(struct unpack *, uint16_t *);
132
static int unpack_u32(struct unpack *, uint32_t *);
133
static int unpack_inaddr(struct unpack *, struct in_addr *);
134
static int unpack_in6addr(struct unpack *, struct in6_addr *);
135
static int unpack_dname(struct unpack *, char *, size_t);
136
static void unpack_init(struct unpack *, const char *, size_t);
137
static int unpack_header(struct unpack *, struct dns_header *);
138
static int unpack_query(struct unpack *, struct dns_query *);
139
static int unpack_rr(struct unpack *, struct dns_rr *);
140
141
142
static int
143
domainname_is_addr(const char *s, struct sockaddr *sa, socklen_t *sl)
144
{
145
	struct addrinfo	hints, *res;
146
	socklen_t	sl2;
147
	size_t		l;
148
	char		buf[SMTPD_MAXDOMAINPARTSIZE];
149
	int		i6, error;
150
151
	if (*s != '[')
152
		return (0);
153
154
	i6 = (strncasecmp("[IPv6:", s, 6) == 0);
155
	s += i6 ? 6 : 1;
156
157
	l = strlcpy(buf, s, sizeof(buf));
158
	if (l >= sizeof(buf) || l == 0 || buf[l - 1] != ']')
159
		return (0);
160
161
	buf[l - 1] = '\0';
162
	memset(&hints, 0, sizeof(hints));
163
	hints.ai_flags = AI_NUMERICHOST;
164
	hints.ai_socktype = SOCK_STREAM;
165
	if (i6)
166
		hints.ai_family = AF_INET6;
167
168
	res = NULL;
169
	if ((error = getaddrinfo(buf, NULL, &hints, &res))) {
170
		log_warnx("getaddrinfo: %s", gai_strerror(error));
171
	}
172
173
	if (!res)
174
		return (0);
175
176
	if (sa && sl) {
177
		sl2 = *sl;
178
		if (sl2 > res->ai_addrlen)
179
			sl2 = res->ai_addrlen;
180
		memmove(sa, res->ai_addr, sl2);
181
		*sl = res->ai_addrlen;
182
	}
183
184
	freeaddrinfo(res);
185
	return (1);
186
}
187
188
void
189
dns_imsg(struct mproc *p, struct imsg *imsg)
190
{
191
	struct sockaddr_storage	 ss;
192
	struct dns_session	*s;
193
	struct sockaddr		*sa;
194
	struct asr_query	*as;
195
	struct msg		 m;
196
	const char		*domain, *mx, *host;
197
	socklen_t		 sl;
198
199
	s = xcalloc(1, sizeof *s, "dns_imsg");
200
	s->type = imsg->hdr.type;
201
	s->p = p;
202
203
	m_msg(&m, imsg);
204
	m_get_id(&m, &s->reqid);
205
206
	switch (s->type) {
207
208
	case IMSG_MTA_DNS_HOST:
209
		m_get_string(&m, &host);
210
		m_end(&m);
211
		dns_lookup_host(s, host, -1);
212
		return;
213
214
	case IMSG_MTA_DNS_PTR:
215
	case IMSG_SMTP_DNS_PTR:
216
		sa = (struct sockaddr *)&ss;
217
		m_get_sockaddr(&m, sa);
218
		m_end(&m);
219
		as = getnameinfo_async(sa, sa->sa_len, s->name, sizeof(s->name),
220
		    NULL, 0, 0, NULL);
221
		event_asr_run(as, dns_dispatch_ptr, s);
222
		return;
223
224
	case IMSG_MTA_DNS_MX:
225
		m_get_string(&m, &domain);
226
		m_end(&m);
227
		(void)strlcpy(s->name, domain, sizeof(s->name));
228
229
		sa = (struct sockaddr *)&ss;
230
		sl = sizeof(ss);
231
232
		if (domainname_is_addr(domain, sa, &sl)) {
233
			m_create(s->p, IMSG_MTA_DNS_HOST, 0, 0, -1);
234
			m_add_id(s->p, s->reqid);
235
			m_add_sockaddr(s->p, sa);
236
			m_add_int(s->p, -1);
237
			m_close(s->p);
238
239
			m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1);
240
			m_add_id(s->p, s->reqid);
241
			m_add_int(s->p, DNS_OK);
242
			m_close(s->p);
243
			free(s);
244
			return;
245
		}
246
247
		as = res_query_async(s->name, C_IN, T_MX, NULL);
248
		if (as == NULL) {
249
			log_warn("warn: res_query_async: %s", s->name);
250
			m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1);
251
			m_add_id(s->p, s->reqid);
252
			m_add_int(s->p, DNS_EINVAL);
253
			m_close(s->p);
254
			free(s);
255
			return;
256
		}
257
258
		event_asr_run(as, dns_dispatch_mx, s);
259
		return;
260
261
	case IMSG_MTA_DNS_MX_PREFERENCE:
262
		m_get_string(&m, &domain);
263
		m_get_string(&m, &mx);
264
		m_end(&m);
265
		(void)strlcpy(s->name, mx, sizeof(s->name));
266
267
		as = res_query_async(domain, C_IN, T_MX, NULL);
268
		if (as == NULL) {
269
			m_create(s->p, IMSG_MTA_DNS_MX_PREFERENCE, 0, 0, -1);
270
			m_add_id(s->p, s->reqid);
271
			m_add_int(s->p, DNS_ENOTFOUND);
272
			m_close(s->p);
273
			free(s);
274
			return;
275
		}
276
277
		event_asr_run(as, dns_dispatch_mx_preference, s);
278
		return;
279
280
	default:
281
		log_warnx("warn: bad dns request %d", s->type);
282
		fatal(NULL);
283
	}
284
}
285
286
static void
287
dns_dispatch_host(struct asr_result *ar, void *arg)
288
{
289
	struct dns_session	*s;
290
	struct dns_lookup	*lookup = arg;
291
	struct addrinfo		*ai;
292
293
	s = lookup->session;
294
295
	for (ai = ar->ar_addrinfo; ai; ai = ai->ai_next) {
296
		s->mxfound++;
297
		m_create(s->p, IMSG_MTA_DNS_HOST, 0, 0, -1);
298
		m_add_id(s->p, s->reqid);
299
		m_add_sockaddr(s->p, ai->ai_addr);
300
		m_add_int(s->p, lookup->preference);
301
		m_close(s->p);
302
	}
303
	free(lookup);
304
	if (ar->ar_addrinfo)
305
		freeaddrinfo(ar->ar_addrinfo);
306
307
	if (ar->ar_gai_errno)
308
		s->error = ar->ar_gai_errno;
309
310
	if (--s->refcount)
311
		return;
312
313
	m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1);
314
	m_add_id(s->p, s->reqid);
315
	m_add_int(s->p, s->mxfound ? DNS_OK : DNS_ENOTFOUND);
316
	m_close(s->p);
317
	free(s);
318
}
319
320
static void
321
dns_dispatch_ptr(struct asr_result *ar, void *arg)
322
{
323
	struct dns_session	*s = arg;
324
325
	/* The error code could be more precise, but we don't currently care */
326
	m_create(s->p,  s->type, 0, 0, -1);
327
	m_add_id(s->p, s->reqid);
328
	m_add_int(s->p, ar->ar_gai_errno ? DNS_ENOTFOUND : DNS_OK);
329
	if (ar->ar_gai_errno == 0)
330
		m_add_string(s->p, s->name);
331
	m_close(s->p);
332
	free(s);
333
}
334
335
static void
336
dns_dispatch_mx(struct asr_result *ar, void *arg)
337
{
338
	struct dns_session	*s = arg;
339
	struct unpack		 pack;
340
	struct dns_header	 h;
341
	struct dns_query	 q;
342
	struct dns_rr		 rr;
343
	char			 buf[512];
344
	size_t			 found;
345
346
	if (ar->ar_h_errno && ar->ar_h_errno != NO_DATA) {
347
348
		m_create(s->p,  IMSG_MTA_DNS_HOST_END, 0, 0, -1);
349
		m_add_id(s->p, s->reqid);
350
		if (ar->ar_rcode == NXDOMAIN)
351
			m_add_int(s->p, DNS_ENONAME);
352
		else if (ar->ar_h_errno == NO_RECOVERY)
353
			m_add_int(s->p, DNS_EINVAL);
354
		else
355
			m_add_int(s->p, DNS_RETRY);
356
		m_close(s->p);
357
		free(s);
358
		free(ar->ar_data);
359
		return;
360
	}
361
362
	unpack_init(&pack, ar->ar_data, ar->ar_datalen);
363
	unpack_header(&pack, &h);
364
	unpack_query(&pack, &q);
365
366
	found = 0;
367
	for (; h.ancount; h.ancount--) {
368
		unpack_rr(&pack, &rr);
369
		if (rr.rr_type != T_MX)
370
			continue;
371
		print_dname(rr.rr.mx.exchange, buf, sizeof(buf));
372
		buf[strlen(buf) - 1] = '\0';
373
		dns_lookup_host(s, buf, rr.rr.mx.preference);
374
		found++;
375
	}
376
	free(ar->ar_data);
377
378
	/* fallback to host if no MX is found. */
379
	if (found == 0)
380
		dns_lookup_host(s, s->name, 0);
381
}
382
383
static void
384
dns_dispatch_mx_preference(struct asr_result *ar, void *arg)
385
{
386
	struct dns_session	*s = arg;
387
	struct unpack		 pack;
388
	struct dns_header	 h;
389
	struct dns_query	 q;
390
	struct dns_rr		 rr;
391
	char			 buf[512];
392
	int			 error;
393
394
	if (ar->ar_h_errno) {
395
		if (ar->ar_rcode == NXDOMAIN)
396
			error = DNS_ENONAME;
397
		else if (ar->ar_h_errno == NO_RECOVERY
398
		    || ar->ar_h_errno == NO_DATA)
399
			error = DNS_EINVAL;
400
		else
401
			error = DNS_RETRY;
402
	}
403
	else {
404
		error = DNS_ENOTFOUND;
405
		unpack_init(&pack, ar->ar_data, ar->ar_datalen);
406
		unpack_header(&pack, &h);
407
		unpack_query(&pack, &q);
408
		for (; h.ancount; h.ancount--) {
409
			unpack_rr(&pack, &rr);
410
			if (rr.rr_type != T_MX)
411
				continue;
412
			print_dname(rr.rr.mx.exchange, buf, sizeof(buf));
413
			buf[strlen(buf) - 1] = '\0';
414
			if (!strcasecmp(s->name, buf)) {
415
				error = DNS_OK;
416
				break;
417
			}
418
		}
419
	}
420
421
	free(ar->ar_data);
422
423
	m_create(s->p, IMSG_MTA_DNS_MX_PREFERENCE, 0, 0, -1);
424
	m_add_id(s->p, s->reqid);
425
	m_add_int(s->p, error);
426
	if (error == DNS_OK)
427
		m_add_int(s->p, rr.rr.mx.preference);
428
	m_close(s->p);
429
	free(s);
430
}
431
432
static void
433
dns_lookup_host(struct dns_session *s, const char *host, int preference)
434
{
435
	struct dns_lookup	*lookup;
436
	struct addrinfo		 hints;
437
	char			 hostcopy[HOST_NAME_MAX+1];
438
	char			*p;
439
	void			*as;
440
441
	lookup = xcalloc(1, sizeof *lookup, "dns_lookup_host");
442
	lookup->preference = preference;
443
	lookup->session = s;
444
	s->refcount++;
445
446
	if (*host == '[') {
447
		if (strncasecmp("[IPv6:", host, 6) == 0)
448
			host += 6;
449
		else
450
			host += 1;
451
		(void)strlcpy(hostcopy, host, sizeof hostcopy);
452
		p = strchr(hostcopy, ']');
453
		if (p)
454
			*p = 0;
455
		host = hostcopy;
456
	}
457
458
	memset(&hints, 0, sizeof(hints));
459
	hints.ai_flags = AI_ADDRCONFIG;
460
	hints.ai_family = PF_UNSPEC;
461
	hints.ai_socktype = SOCK_STREAM;
462
	as = getaddrinfo_async(host, NULL, &hints, NULL);
463
	event_asr_run(as, dns_dispatch_host, lookup);
464
}
465
466
static char *
467
print_dname(const char *_dname, char *buf, size_t max)
468
{
469
	const unsigned char *dname = _dname;
470
	char    *res;
471
	size_t   left, n, count;
472
473
	if (_dname[0] == 0) {
474
		(void)strlcpy(buf, ".", max);
475
		return buf;
476
	}
477
478
	res = buf;
479
	left = max - 1;
480
	for (n = 0; dname[0] && left; n += dname[0]) {
481
		count = (dname[0] < (left - 1)) ? dname[0] : (left - 1);
482
		memmove(buf, dname + 1, count);
483
		dname += dname[0] + 1;
484
		left -= count;
485
		buf += count;
486
		if (left) {
487
			left -= 1;
488
			*buf++ = '.';
489
		}
490
	}
491
	buf[0] = 0;
492
493
	return (res);
494
}
495
496
static ssize_t
497
dname_expand(const unsigned char *data, size_t len, size_t offset,
498
    size_t *newoffset, char *dst, size_t max)
499
{
500
	size_t		 n, count, end, ptr, start;
501
	ssize_t		 res;
502
503
	if (offset >= len)
504
		return (-1);
505
506
	res = 0;
507
	end = start = offset;
508
509
	for (; (n = data[offset]); ) {
510
		if ((n & 0xc0) == 0xc0) {
511
			if (offset + 2 > len)
512
				return (-1);
513
			ptr = 256 * (n & ~0xc0) + data[offset + 1];
514
			if (ptr >= start)
515
				return (-1);
516
			if (end < offset + 2)
517
				end = offset + 2;
518
			offset = start = ptr;
519
			continue;
520
		}
521
		if (offset + n + 1 > len)
522
			return (-1);
523
524
		/* copy n + at offset+1 */
525
		if (dst != NULL && max != 0) {
526
			count = (max < n + 1) ? (max) : (n + 1);
527
			memmove(dst, data + offset, count);
528
			dst += count;
529
			max -= count;
530
		}
531
		res += n + 1;
532
		offset += n + 1;
533
		if (end < offset)
534
			end = offset;
535
	}
536
	if (end < offset + 1)
537
		end = offset + 1;
538
539
	if (dst != NULL && max != 0)
540
		dst[0] = 0;
541
	if (newoffset)
542
		*newoffset = end;
543
	return (res + 1);
544
}
545
546
void
547
unpack_init(struct unpack *unpack, const char *buf, size_t len)
548
{
549
	unpack->buf = buf;
550
	unpack->len = len;
551
	unpack->offset = 0;
552
	unpack->err = NULL;
553
}
554
555
static int
556
unpack_data(struct unpack *p, void *data, size_t len)
557
{
558
	if (p->err)
559
		return (-1);
560
561
	if (p->len - p->offset < len) {
562
		p->err = "too short";
563
		return (-1);
564
	}
565
566
	memmove(data, p->buf + p->offset, len);
567
	p->offset += len;
568
569
	return (0);
570
}
571
572
static int
573
unpack_u16(struct unpack *p, uint16_t *u16)
574
{
575
	if (unpack_data(p, u16, 2) == -1)
576
		return (-1);
577
578
	*u16 = ntohs(*u16);
579
580
	return (0);
581
}
582
583
static int
584
unpack_u32(struct unpack *p, uint32_t *u32)
585
{
586
	if (unpack_data(p, u32, 4) == -1)
587
		return (-1);
588
589
	*u32 = ntohl(*u32);
590
591
	return (0);
592
}
593
594
static int
595
unpack_inaddr(struct unpack *p, struct in_addr *a)
596
{
597
	return (unpack_data(p, a, 4));
598
}
599
600
static int
601
unpack_in6addr(struct unpack *p, struct in6_addr *a6)
602
{
603
	return (unpack_data(p, a6, 16));
604
}
605
606
static int
607
unpack_dname(struct unpack *p, char *dst, size_t max)
608
{
609
	ssize_t e;
610
611
	if (p->err)
612
		return (-1);
613
614
	e = dname_expand(p->buf, p->len, p->offset, &p->offset, dst, max);
615
	if (e == -1) {
616
		p->err = "bad domain name";
617
		return (-1);
618
	}
619
	if (e < 0 || e > MAXDNAME) {
620
		p->err = "domain name too long";
621
		return (-1);
622
	}
623
624
	return (0);
625
}
626
627
static int
628
unpack_header(struct unpack *p, struct dns_header *h)
629
{
630
	if (unpack_data(p, h, HFIXEDSZ) == -1)
631
		return (-1);
632
633
	h->flags = ntohs(h->flags);
634
	h->qdcount = ntohs(h->qdcount);
635
	h->ancount = ntohs(h->ancount);
636
	h->nscount = ntohs(h->nscount);
637
	h->arcount = ntohs(h->arcount);
638
639
	return (0);
640
}
641
642
static int
643
unpack_query(struct unpack *p, struct dns_query *q)
644
{
645
	unpack_dname(p, q->q_dname, sizeof(q->q_dname));
646
	unpack_u16(p, &q->q_type);
647
	unpack_u16(p, &q->q_class);
648
649
	return (p->err) ? (-1) : (0);
650
}
651
652
static int
653
unpack_rr(struct unpack *p, struct dns_rr *rr)
654
{
655
	uint16_t	rdlen;
656
	size_t		save_offset;
657
658
	unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname));
659
	unpack_u16(p, &rr->rr_type);
660
	unpack_u16(p, &rr->rr_class);
661
	unpack_u32(p, &rr->rr_ttl);
662
	unpack_u16(p, &rdlen);
663
664
	if (p->err)
665
		return (-1);
666
667
	if (p->len - p->offset < rdlen) {
668
		p->err = "too short";
669
		return (-1);
670
	}
671
672
	save_offset = p->offset;
673
674
	switch (rr->rr_type) {
675
676
	case T_CNAME:
677
		unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname));
678
		break;
679
680
	case T_MX:
681
		unpack_u16(p, &rr->rr.mx.preference);
682
		unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange));
683
		break;
684
685
	case T_NS:
686
		unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname));
687
		break;
688
689
	case T_PTR:
690
		unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname));
691
		break;
692
693
	case T_SOA:
694
		unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname));
695
		unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname));
696
		unpack_u32(p, &rr->rr.soa.serial);
697
		unpack_u32(p, &rr->rr.soa.refresh);
698
		unpack_u32(p, &rr->rr.soa.retry);
699
		unpack_u32(p, &rr->rr.soa.expire);
700
		unpack_u32(p, &rr->rr.soa.minimum);
701
		break;
702
703
	case T_A:
704
		if (rr->rr_class != C_IN)
705
			goto other;
706
		unpack_inaddr(p, &rr->rr.in_a.addr);
707
		break;
708
709
	case T_AAAA:
710
		if (rr->rr_class != C_IN)
711
			goto other;
712
		unpack_in6addr(p, &rr->rr.in_aaaa.addr6);
713
		break;
714
	default:
715
	other:
716
		rr->rr.other.rdata = p->buf + p->offset;
717
		rr->rr.other.rdlen = rdlen;
718
		p->offset += rdlen;
719
	}
720
721
	if (p->err)
722
		return (-1);
723
724
	/* make sure that the advertised rdlen is really ok */
725
	if (p->offset - save_offset != rdlen)
726
		p->err = "bad dlen";
727
728
	return (p->err) ? (-1) : (0);
729
}