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