1/*
2 * Copyright (c) 2000, Boris Popov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *    This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $Id: nbns_rq.c,v 1.9 2005/02/24 02:04:38 lindak Exp $
33 */
34
35/*
36 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
37 */
38
39#include <sys/param.h>
40#include <sys/socket.h>
41#include <sys/time.h>
42#include <ctype.h>
43#include <netdb.h>
44#include <errno.h>
45#include <stdlib.h>
46#include <string.h>
47#include <strings.h>
48#include <stdio.h>
49#include <unistd.h>
50#include <libintl.h>
51
52#include <netinet/in.h>
53#include <arpa/inet.h>
54#include <tsol/label.h>
55
56#define	NB_NEEDRESOLVER
57#include <netsmb/netbios.h>
58#include <netsmb/smb_lib.h>
59#include <netsmb/nb_lib.h>
60#include <netsmb/mchain.h>
61
62#include "charsets.h"
63#include "private.h"
64
65/*
66 * nbns request
67 */
68struct nbns_rq {
69	int		nr_opcode;
70	int		nr_nmflags;
71	int		nr_rcode;
72	int		nr_qdcount;
73	int		nr_ancount;
74	int		nr_nscount;
75	int		nr_arcount;
76	struct nb_name	*nr_qdname;
77	uint16_t	nr_qdtype;
78	uint16_t	nr_qdclass;
79	struct in_addr	nr_dest;	/* receiver of query */
80	struct sockaddr_in nr_sender;	/* sender of response */
81	int		nr_rpnmflags;
82	int		nr_rprcode;
83	uint16_t	nr_rpancount;
84	uint16_t	nr_rpnscount;
85	uint16_t	nr_rparcount;
86	uint16_t	nr_trnid;
87	struct nb_ctx	*nr_nbd;
88	struct mbdata	nr_rq;
89	struct mbdata	nr_rp;
90	struct nb_ifdesc *nr_if;
91	int		nr_flags;
92	int		nr_fd;
93	int		nr_maxretry;
94};
95typedef struct nbns_rq nbns_rq_t;
96
97static int  nbns_rq_create(int opcode, struct nb_ctx *ctx,
98    struct nbns_rq **rqpp);
99static void nbns_rq_done(struct nbns_rq *rqp);
100static int  nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp);
101static int  nbns_rq_prepare(struct nbns_rq *rqp);
102static int  nbns_rq(struct nbns_rq *rqp);
103
104/*
105 * Call NetBIOS name lookup and return a result in the
106 * same form as getaddrinfo(3) returns.  Return code is
107 * zero or one of the EAI_xxx codes like getaddrinfo.
108 */
109int
110nbns_getaddrinfo(const char *name, struct nb_ctx *nbc, struct addrinfo **res)
111{
112	struct addrinfo *nai = NULL;
113	struct sockaddr *sap = NULL;
114	char *ucname = NULL;
115	int err;
116
117	/*
118	 * Try NetBIOS name lookup.
119	 */
120	if (strlen(name) >= NB_NAMELEN) {
121		err = EAI_OVERFLOW;
122		goto out;
123	}
124	ucname = utf8_str_toupper(name);
125	if (ucname == NULL)
126		goto nomem;
127
128	/* Note: this returns an NBERROR value. */
129	err = nbns_resolvename(ucname, nbc, &sap);
130	if (err) {
131		if (smb_verbose)
132			smb_error(dgettext(TEXT_DOMAIN,
133			    "nbns_resolvename: %s"),
134			    err, name);
135		err = EAI_NODATA;
136		goto out;
137	}
138	/* Note: sap allocated */
139
140	/*
141	 * Build the addrinfo struct to return.
142	 */
143	nai = malloc(sizeof (*nai));
144	if (nai == NULL)
145		goto nomem;
146	bzero(nai, sizeof (*nai));
147
148	nai->ai_flags = AI_CANONNAME;
149	nai->ai_family = sap->sa_family;
150	nai->ai_socktype = SOCK_STREAM;
151	nai->ai_canonname = ucname;
152	ucname = NULL;
153
154	/*
155	 * The type of this is really sockaddr_in,
156	 * but is returned in the generic form.
157	 * See nbns_resolvename.
158	 */
159	nai->ai_addrlen = sizeof (struct sockaddr_in);
160	nai->ai_addr = sap;
161
162	*res = nai;
163	return (0);
164
165nomem:
166	err = EAI_MEMORY;
167out:
168	if (nai != NULL)
169		free(nai);
170	if (sap)
171		free(sap);
172	if (ucname)
173		free(ucname);
174	*res = NULL;
175
176	return (err);
177}
178
179int
180nbns_resolvename(const char *name, struct nb_ctx *ctx, struct sockaddr **adpp)
181{
182	struct nbns_rq *rqp;
183	struct nb_name nn;
184	struct nbns_rr rr;
185	struct sockaddr_in *dest;
186	int error, rdrcount, len;
187
188	if (strlen(name) >= NB_NAMELEN)
189		return (NBERROR(NBERR_NAMETOOLONG));
190	error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
191	if (error)
192		return (error);
193	/*
194	 * Pad the name with blanks, but
195	 * leave the "type" byte NULL.
196	 * nb_name_encode adds the type.
197	 */
198	bzero(&nn, sizeof (nn));
199	snprintf(nn.nn_name, NB_NAMELEN, "%-15.15s", name);
200	nn.nn_type = NBT_SERVER;
201	nn.nn_scope = ctx->nb_scope;
202	rqp->nr_nmflags = NBNS_NMFLAG_RD;
203	rqp->nr_qdname = &nn;
204	rqp->nr_qdtype = NBNS_QUESTION_TYPE_NB;
205	rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
206	rqp->nr_qdcount = 1;
207	rqp->nr_maxretry = 5;
208
209	error = nbns_rq_prepare(rqp);
210	if (error) {
211		nbns_rq_done(rqp);
212		return (error);
213	}
214	rdrcount = NBNS_MAXREDIRECTS;
215	for (;;) {
216		error = nbns_rq(rqp);
217		if (error)
218			break;
219		if ((rqp->nr_rpnmflags & NBNS_NMFLAG_AA) == 0) {
220			/*
221			 * Not an authoritative answer.  Query again
222			 * using the NS address in the 2nd record.
223			 */
224			if (rdrcount-- == 0) {
225				error = NBERROR(NBERR_TOOMANYREDIRECTS);
226				break;
227			}
228			error = nbns_rq_getrr(rqp, &rr);
229			if (error)
230				break;
231			error = nbns_rq_getrr(rqp, &rr);
232			if (error)
233				break;
234			bcopy(rr.rr_data, &rqp->nr_dest, 4);
235			continue;
236		}
237		if (rqp->nr_rpancount == 0) {
238			error = NBERROR(NBERR_HOSTNOTFOUND);
239			break;
240		}
241		error = nbns_rq_getrr(rqp, &rr);
242		if (error)
243			break;
244		len = sizeof (struct sockaddr_in);
245		dest = malloc(len);
246		if (dest == NULL)
247			return (ENOMEM);
248		bzero(dest, len);
249		/*
250		 * Solaris sockaddr_in doesn't a sin_len field.
251		 * dest->sin_len = len;
252		 */
253		dest->sin_family = AF_NETBIOS;	/* nb_lib.h */
254		bcopy(rr.rr_data + 2, &dest->sin_addr.s_addr, 4);
255		*adpp = (struct sockaddr *)dest;
256		ctx->nb_lastns = rqp->nr_sender;
257		break;
258	}
259	nbns_rq_done(rqp);
260	return (error);
261}
262
263/*
264 * NB: system, workgroup are both NB_NAMELEN
265 */
266int
267nbns_getnodestatus(struct nb_ctx *ctx,
268    struct in_addr *targethost, char *system, char *workgroup)
269{
270	struct nbns_rq *rqp;
271	struct nbns_rr rr;
272	struct nb_name nn;
273	struct nbns_nr *nrp;
274	char nrtype;
275	char *cp, *retname = NULL;
276	unsigned char nrcount;
277	int error, i, foundserver = 0, foundgroup = 0;
278
279	error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
280	if (error)
281		return (error);
282	bzero(&nn, sizeof (nn));
283	strcpy((char *)nn.nn_name, "*");
284	nn.nn_scope = ctx->nb_scope;
285	nn.nn_type = NBT_WKSTA;
286	rqp->nr_nmflags = 0;
287	rqp->nr_qdname = &nn;
288	rqp->nr_qdtype = NBNS_QUESTION_TYPE_NBSTAT;
289	rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
290	rqp->nr_qdcount = 1;
291	rqp->nr_maxretry = 2;
292
293	rqp->nr_dest = *targethost;
294	error = nbns_rq_prepare(rqp);
295	if (error) {
296		nbns_rq_done(rqp);
297		return (error);
298	}
299
300	/*
301	 * Darwin had a loop here, allowing redirect, etc.
302	 * but we only handle point-to-point for node status.
303	 */
304	error = nbns_rq(rqp);
305	if (error)
306		goto out;
307	if (rqp->nr_rpancount == 0) {
308		error = NBERROR(NBERR_HOSTNOTFOUND);
309		goto out;
310	}
311	error = nbns_rq_getrr(rqp, &rr);
312	if (error)
313		goto out;
314
315	/* Compiler didn't like cast on lvalue++ */
316	nrcount = *((unsigned char *)rr.rr_data);
317	rr.rr_data++;
318	/* LINTED */
319	for (i = 1, nrp = (struct nbns_nr *)rr.rr_data;
320	    i <= nrcount; ++i, ++nrp) {
321		nrtype = nrp->ns_name[NB_NAMELEN-1];
322		/* Terminate the string: */
323		nrp->ns_name[NB_NAMELEN-1] = (char)0;
324		/* Strip off trailing spaces */
325		for (cp = &nrp->ns_name[NB_NAMELEN-2];
326		    cp >= nrp->ns_name; --cp) {
327			if (*cp != (char)0x20)
328				break;
329			*cp = (char)0;
330		}
331		nrp->ns_flags = ntohs(nrp->ns_flags);
332		DPRINT(" %s[%02x] Flags 0x%x",
333		    nrp->ns_name, nrtype, nrp->ns_flags);
334		if (nrp->ns_flags & NBNS_GROUPFLG) {
335			if (!foundgroup ||
336			    (foundgroup != NBT_WKSTA+1 &&
337			    nrtype == NBT_WKSTA)) {
338				strlcpy(workgroup, nrp->ns_name,
339				    NB_NAMELEN);
340				foundgroup = nrtype+1;
341			}
342		} else {
343			/*
344			 * Track at least ONE name, in case
345			 * no server name is found
346			 */
347			retname = nrp->ns_name;
348		}
349		/*
350		 * Keep the first NBT_SERVER name.
351		 */
352		if (nrtype == NBT_SERVER && foundserver == 0) {
353			strlcpy(system, nrp->ns_name,
354			    NB_NAMELEN);
355			foundserver = 1;
356		}
357	}
358	if (foundserver == 0 && retname != NULL)
359		strlcpy(system, retname, NB_NAMELEN);
360	ctx->nb_lastns = rqp->nr_sender;
361
362out:
363	nbns_rq_done(rqp);
364	return (error);
365}
366
367int
368nbns_rq_create(int opcode, struct nb_ctx *ctx, struct nbns_rq **rqpp)
369{
370	struct nbns_rq *rqp;
371	static uint16_t trnid;
372	int error;
373
374	if (trnid == 0)
375		trnid = getpid();
376	rqp = malloc(sizeof (*rqp));
377	if (rqp == NULL)
378		return (ENOMEM);
379	bzero(rqp, sizeof (*rqp));
380	error = mb_init_sz(&rqp->nr_rq, NBDG_MAXSIZE);
381	if (error) {
382		free(rqp);
383		return (error);
384	}
385	rqp->nr_opcode = opcode;
386	rqp->nr_nbd = ctx;
387	rqp->nr_trnid = trnid++;
388	*rqpp = rqp;
389	return (0);
390}
391
392void
393nbns_rq_done(struct nbns_rq *rqp)
394{
395	if (rqp == NULL)
396		return;
397	if (rqp->nr_fd >= 0)
398		close(rqp->nr_fd);
399	mb_done(&rqp->nr_rq);
400	mb_done(&rqp->nr_rp);
401	if (rqp->nr_if)
402		free(rqp->nr_if);
403	free(rqp);
404}
405
406/*
407 * Extract resource record from the packet. Assume that there is only
408 * one mbuf.
409 */
410int
411nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp)
412{
413	struct mbdata *mbp = &rqp->nr_rp;
414	uchar_t *cp;
415	int error, len;
416
417	bzero(rrp, sizeof (*rrp));
418	cp = (uchar_t *)mbp->mb_pos;
419	len = nb_encname_len(cp);
420	if (len < 1)
421		return (NBERROR(NBERR_INVALIDRESPONSE));
422	rrp->rr_name = cp;
423	error = md_get_mem(mbp, NULL, len, MB_MSYSTEM);
424	if (error)
425		return (error);
426	md_get_uint16be(mbp, &rrp->rr_type);
427	md_get_uint16be(mbp, &rrp->rr_class);
428	md_get_uint32be(mbp, &rrp->rr_ttl);
429	md_get_uint16be(mbp, &rrp->rr_rdlength);
430	rrp->rr_data = (uchar_t *)mbp->mb_pos;
431	error = md_get_mem(mbp, NULL, rrp->rr_rdlength, MB_MSYSTEM);
432	return (error);
433}
434
435int
436nbns_rq_prepare(struct nbns_rq *rqp)
437{
438	struct nb_ctx *ctx = rqp->nr_nbd;
439	struct mbdata *mbp = &rqp->nr_rq;
440	uint16_t ofr; /* opcode, flags, rcode */
441	int error;
442
443	error = mb_init_sz(&rqp->nr_rp, NBDG_MAXSIZE);
444	if (error)
445		return (error);
446
447	/*
448	 * When looked into the ethereal trace, 'nmblookup' command sets this
449	 * flag. We will also set.
450	 */
451	mb_put_uint16be(mbp, rqp->nr_trnid);
452	ofr = ((rqp->nr_opcode & 0x1F) << 11) |
453	    ((rqp->nr_nmflags & 0x7F) << 4); /* rcode=0 */
454	mb_put_uint16be(mbp, ofr);
455	mb_put_uint16be(mbp, rqp->nr_qdcount);
456	mb_put_uint16be(mbp, rqp->nr_ancount);
457	mb_put_uint16be(mbp, rqp->nr_nscount);
458	error = mb_put_uint16be(mbp, rqp->nr_arcount);
459	if (rqp->nr_qdcount) {
460		if (rqp->nr_qdcount > 1)
461			return (EINVAL);
462		(void) nb_name_encode(mbp, rqp->nr_qdname);
463		mb_put_uint16be(mbp, rqp->nr_qdtype);
464		error = mb_put_uint16be(mbp, rqp->nr_qdclass);
465	}
466	if (error)
467		return (error);
468	error = m_lineup(mbp->mb_top, &mbp->mb_top);
469	if (error)
470		return (error);
471	if (ctx->nb_timo == 0)
472		ctx->nb_timo = 1;	/* by default 1 second */
473	return (0);
474}
475
476static int
477nbns_rq_recv(struct nbns_rq *rqp)
478{
479	struct mbdata *mbp = &rqp->nr_rp;
480	void *rpdata = mtod(mbp->mb_top, void *);
481	fd_set rd, wr, ex;
482	struct timeval tv;
483	struct sockaddr_in sender;
484	int s = rqp->nr_fd;
485	int n, len;
486
487	FD_ZERO(&rd);
488	FD_ZERO(&wr);
489	FD_ZERO(&ex);
490	FD_SET(s, &rd);
491
492	tv.tv_sec = rqp->nr_nbd->nb_timo;
493	tv.tv_usec = 0;
494
495	n = select(s + 1, &rd, &wr, &ex, &tv);
496	if (n == -1)
497		return (-1);
498	if (n == 0)
499		return (ETIMEDOUT);
500	if (FD_ISSET(s, &rd) == 0)
501		return (ETIMEDOUT);
502	len = sizeof (sender);
503	n = recvfrom(s, rpdata, mbp->mb_top->m_maxlen, 0,
504	    (struct sockaddr *)&sender, &len);
505	if (n < 0)
506		return (errno);
507	mbp->mb_top->m_len = mbp->mb_count = n;
508	rqp->nr_sender = sender;
509	return (0);
510}
511
512static int
513nbns_rq_opensocket(struct nbns_rq *rqp)
514{
515	struct sockaddr_in locaddr;
516	int opt = 1, s;
517	struct nb_ctx *ctx = rqp->nr_nbd;
518
519	s = rqp->nr_fd = socket(AF_INET, SOCK_DGRAM, 0);
520	if (s < 0)
521		return (errno);
522	if (ctx->nb_flags & NBCF_BC_ENABLE) {
523		if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &opt,
524		    sizeof (opt)) < 0)
525			return (errno);
526	}
527	if (is_system_labeled())
528		(void) setsockopt(s, SOL_SOCKET, SO_MAC_EXEMPT, &opt,
529		    sizeof (opt));
530	bzero(&locaddr, sizeof (locaddr));
531	locaddr.sin_family = AF_INET;
532	/* locaddr.sin_len = sizeof (locaddr); */
533	if (bind(s, (struct sockaddr *)&locaddr, sizeof (locaddr)) < 0)
534		return (errno);
535	return (0);
536}
537
538static int
539nbns_rq_send(struct nbns_rq *rqp, in_addr_t ina)
540{
541	struct sockaddr_in dest;
542	struct mbdata *mbp = &rqp->nr_rq;
543	int s = rqp->nr_fd;
544	uint16_t ofr, ofr_save; /* opcode, nmflags, rcode */
545	uint16_t *datap;
546	uint8_t nmflags;
547	int rc;
548
549	bzero(&dest, sizeof (dest));
550	dest.sin_family = AF_INET;
551	dest.sin_port = htons(IPPORT_NETBIOS_NS);
552	dest.sin_addr.s_addr = ina;
553
554	if (ina == INADDR_BROADCAST) {
555		/* Turn on the broadcast bit. */
556		nmflags = rqp->nr_nmflags | NBNS_NMFLAG_BCAST;
557		/*LINTED*/
558		datap = mtod(mbp->mb_top, uint16_t *);
559		ofr = ((rqp->nr_opcode & 0x1F) << 11) |
560		    ((nmflags & 0x7F) << 4); /* rcode=0 */
561		ofr_save = datap[1];
562		datap[1] = htons(ofr);
563	}
564
565	rc = sendto(s, mtod(mbp->mb_top, char *), mbp->mb_count, 0,
566	    (struct sockaddr *)&dest, sizeof (dest));
567
568	if (ina == INADDR_BROADCAST) {
569		/* Turn the broadcast bit back off. */
570		datap[1] = ofr_save;
571	}
572
573
574	if (rc < 0)
575		return (errno);
576
577	return (0);
578}
579
580int
581nbns_rq(struct nbns_rq *rqp)
582{
583	struct nb_ctx *ctx = rqp->nr_nbd;
584	struct mbdata *mbp = &rqp->nr_rq;
585	uint16_t ofr, rpid;
586	int error, tries, maxretry;
587
588	error = nbns_rq_opensocket(rqp);
589	if (error)
590		return (error);
591
592	maxretry = rqp->nr_maxretry;
593	for (tries = 0; tries < maxretry; tries++) {
594
595		/*
596		 * Minor hack: If nr_dest is set, send there only.
597		 * Used by _getnodestatus, _resolvname redirects.
598		 */
599		if (rqp->nr_dest.s_addr) {
600			error = nbns_rq_send(rqp, rqp->nr_dest.s_addr);
601			if (error) {
602				smb_error(dgettext(TEXT_DOMAIN,
603				    "nbns error %d sending to %s"),
604				    0, error, inet_ntoa(rqp->nr_dest));
605			}
606			goto do_recv;
607		}
608
609		if (ctx->nb_wins1) {
610			error = nbns_rq_send(rqp, ctx->nb_wins1);
611			if (error) {
612				smb_error(dgettext(TEXT_DOMAIN,
613				    "nbns error %d sending to wins1"),
614				    0, error);
615			}
616		}
617
618		if (ctx->nb_wins2 && (tries > 0)) {
619			error = nbns_rq_send(rqp, ctx->nb_wins2);
620			if (error) {
621				smb_error(dgettext(TEXT_DOMAIN,
622				    "nbns error %d sending to wins2"),
623				    0, error);
624			}
625		}
626
627		/*
628		 * If broadcast is enabled, start broadcasting
629		 * only after wins servers fail to respond, or
630		 * immediately if no WINS servers configured.
631		 */
632		if ((ctx->nb_flags & NBCF_BC_ENABLE) &&
633		    ((tries > 1) || (ctx->nb_wins1 == 0))) {
634			error = nbns_rq_send(rqp, INADDR_BROADCAST);
635			if (error) {
636				smb_error(dgettext(TEXT_DOMAIN,
637				    "nbns error %d sending broadcast"),
638				    0, error);
639			}
640		}
641
642		/*
643		 * Wait for responses from ANY of the above.
644		 */
645do_recv:
646		error = nbns_rq_recv(rqp);
647		if (error == ETIMEDOUT)
648			continue;
649		if (error) {
650			smb_error(dgettext(TEXT_DOMAIN,
651			    "nbns recv error %d"),
652			    0, error);
653			return (error);
654		}
655
656		mbp = &rqp->nr_rp;
657		if (mbp->mb_count < 12)
658			return (NBERROR(NBERR_INVALIDRESPONSE));
659		md_get_uint16be(mbp, &rpid);
660		if (rpid != rqp->nr_trnid)
661			return (NBERROR(NBERR_INVALIDRESPONSE));
662		break;
663	}
664	if (tries == maxretry)
665		return (NBERROR(NBERR_HOSTNOTFOUND));
666
667	md_get_uint16be(mbp, &ofr);
668	rqp->nr_rpnmflags = (ofr >> 4) & 0x7F;
669	rqp->nr_rprcode = ofr & 0xf;
670	if (rqp->nr_rprcode)
671		return (NBERROR(rqp->nr_rprcode));
672	md_get_uint16be(mbp, &rpid);	/* QDCOUNT */
673	md_get_uint16be(mbp, &rqp->nr_rpancount);
674	md_get_uint16be(mbp, &rqp->nr_rpnscount);
675	md_get_uint16be(mbp, &rqp->nr_rparcount);
676	return (0);
677}
678