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 */
68 struct 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 };
95 typedef struct nbns_rq nbns_rq_t;
96
97 static int nbns_rq_create(int opcode, struct nb_ctx *ctx,
98 struct nbns_rq **rqpp);
99 static void nbns_rq_done(struct nbns_rq *rqp);
100 static int nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp);
101 static int nbns_rq_prepare(struct nbns_rq *rqp);
102 static 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 */
109 int
nbns_getaddrinfo(const char * name,struct nb_ctx * nbc,struct addrinfo ** res)110 nbns_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
165 nomem:
166 err = EAI_MEMORY;
167 out:
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
179 int
nbns_resolvename(const char * name,struct nb_ctx * ctx,struct sockaddr ** adpp)180 nbns_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 */
266 int
nbns_getnodestatus(struct nb_ctx * ctx,struct in_addr * targethost,char * system,char * workgroup)267 nbns_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
362 out:
363 nbns_rq_done(rqp);
364 return (error);
365 }
366
367 int
nbns_rq_create(int opcode,struct nb_ctx * ctx,struct nbns_rq ** rqpp)368 nbns_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
392 void
nbns_rq_done(struct nbns_rq * rqp)393 nbns_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 */
410 int
nbns_rq_getrr(struct nbns_rq * rqp,struct nbns_rr * rrp)411 nbns_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
435 int
nbns_rq_prepare(struct nbns_rq * rqp)436 nbns_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
476 static int
nbns_rq_recv(struct nbns_rq * rqp)477 nbns_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
512 static int
nbns_rq_opensocket(struct nbns_rq * rqp)513 nbns_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
538 static int
nbns_rq_send(struct nbns_rq * rqp,in_addr_t ina)539 nbns_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
580 int
nbns_rq(struct nbns_rq * rqp)581 nbns_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 */
645 do_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