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 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 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 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 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 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 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 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 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 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 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 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