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