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