1/* 2 * Copyright (c) 2000, Boris Popov 3 * All rights reserved. 4 * 5 * Portions Copyright (C) 2001 - 2010 Apple Inc. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Boris Popov. 18 * 4. Neither the name of the author nor the names of any co-contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $Id: nbns_rq.c,v 1.13.140.1 2006/04/14 23:49:37 gcolley Exp $ 35 */ 36#include <netsmb/netbios.h> 37#include <sys/smb_byte_order.h> 38#include <netsmb/upi_mbuf.h> 39#include <sys/mchain.h> 40#include <netsmb/smb_lib.h> 41#include <netsmb/nb_lib.h> 42#include "preference.h" 43 44/* 45 * nbns request 46 */ 47struct nbns_rq { 48 int nr_opcode; 49 int nr_nmflags; 50 int nr_rcode; 51 int nr_qdcount; 52 int nr_ancount; 53 int nr_nscount; 54 int nr_arcount; 55 struct nb_name* nr_qdname; 56 uint16_t nr_qdtype; 57 uint16_t nr_qdclass; 58 struct sockaddr_in nr_dest; /* receiver of query */ 59 struct sockaddr_in nr_sender; /* sender of response */ 60 int nr_rpnmflags; 61 int nr_rprcode; 62 uint16_t nr_rpancount; 63 uint16_t nr_rpnscount; 64 uint16_t nr_rparcount; 65 uint16_t nr_trnid; 66 struct nb_ctx * nr_nbd; 67 struct mbchain nr_rq; 68 struct mdchain nr_rp; 69 struct nb_ifdesc *nr_if; 70 int nr_flags; /* endian-ness depends on host */ 71 int nr_fd; 72 int32_t nr_timo; 73}; 74 75static struct nb_ifdesc *nb_iflist; 76 77 78static char * smb_optstrncpy(char *d, char *s, unsigned maxlen) 79{ 80 if (d && s) { 81 strncpy(d, s, maxlen); 82 d[maxlen] = (char)0; 83 } 84 return (d); 85} 86 87static int nbns_rq_create(int opcode, struct smb_prefs *prefs, 88 struct nbns_rq **rqpp) 89{ 90 struct nbns_rq *rqp; 91 static uint16_t trnid; 92 int error; 93 94 rqp = malloc(sizeof(*rqp)); 95 if (rqp == NULL) 96 return ENOMEM; 97 bzero(rqp, sizeof(*rqp)); 98 error = mb_init(&rqp->nr_rq); 99 if (error) { 100 free(rqp); 101 return error; 102 } 103 if (prefs) { 104 rqp->nr_timo = prefs->NetBIOSResolverTimeout; 105 } else { 106 rqp->nr_timo = DefaultNetBIOSResolverTimeout; 107 } 108 109 rqp->nr_opcode = opcode; 110 rqp->nr_trnid = trnid++; 111 *rqpp = rqp; 112 return 0; 113} 114 115static void nbns_rq_done(struct nbns_rq *rqp) 116{ 117 if (rqp == NULL) 118 return; 119 if (rqp->nr_fd >= 0) 120 close(rqp->nr_fd); 121 mb_done(&rqp->nr_rq); 122 md_done(&rqp->nr_rp); 123 free(rqp); 124} 125 126/* 127 * Extract resource record from the packet. Assume that there is only 128 * one mbuf. 129 */ 130static int nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp) 131{ 132 mdchain_t mdp = &rqp->nr_rp; 133 u_char *cp; 134 int error, len; 135 136 bzero(rrp, sizeof(*rrp)); 137 cp = (u_char *)(mdp->md_pos); 138 len = nb_encname_len((char *)cp); 139 if (len < 1) 140 return EINVAL; 141 rrp->rr_name = cp; 142 error = md_get_mem(mdp, NULL, len, MB_MSYSTEM); 143 if (error) 144 return error; 145 md_get_uint16be(mdp, &rrp->rr_type); 146 md_get_uint16be(mdp, &rrp->rr_class); 147 md_get_uint32be(mdp, &rrp->rr_ttl); 148 md_get_uint16be(mdp, &rrp->rr_rdlength); 149 rrp->rr_data = (u_char *)mdp->md_pos; 150 error = md_get_mem(mdp, NULL, rrp->rr_rdlength, MB_MSYSTEM); 151 return error; 152} 153 154static int nbns_rq_recv(struct nbns_rq *rqp) 155{ 156 mdchain_t mdp = &rqp->nr_rp; 157 void *rpdata = mbuf_data(mdp->md_top); 158 fd_set rd, wr, ex; 159 struct timeval tv; 160 struct sockaddr_in sender; 161 int s = rqp->nr_fd; 162 int n; 163 socklen_t len; 164 165 FD_ZERO(&rd); 166 FD_ZERO(&wr); 167 FD_ZERO(&ex); 168 FD_SET(s, &rd); 169 170 tv.tv_sec = 0; 171 tv.tv_usec = 500000; /* We wait half a second for a response */ 172 173 n = select(s + 1, &rd, &wr, &ex, &tv); 174 if (n == -1) 175 return -1; 176 if (n == 0) 177 return ETIMEDOUT; 178 if (FD_ISSET(s, &rd) == 0) 179 return ETIMEDOUT; 180 len = (socklen_t)sizeof(sender); 181 n = (int)recvfrom(s, rpdata, mbuf_maxlen(mdp->md_top), 0, (struct sockaddr*)&sender, &len); 182 if (n < 0) 183 return errno; 184 mbuf_setlen(mdp->md_top, n); 185 rqp->nr_sender = sender; 186 return 0; 187} 188 189static int nbns_rq_opensocket(struct nbns_rq *rqp) 190{ 191 struct sockaddr_in locaddr; 192 int opt, s; 193 194 s = rqp->nr_fd = socket(AF_INET, SOCK_DGRAM, 0); 195 if (s < 0) 196 return errno; 197 if (rqp->nr_flags & NBRQF_BROADCAST) { 198 opt = 1; 199 if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &opt, (socklen_t)sizeof(opt)) < 0) 200 return errno; 201 if (rqp->nr_if == NULL) 202 return ENETDOWN; 203 bzero(&locaddr, sizeof(locaddr)); 204 locaddr.sin_family = AF_INET; 205 locaddr.sin_len = sizeof(locaddr); 206 locaddr.sin_addr = rqp->nr_if->id_addr; 207 rqp->nr_dest.sin_addr.s_addr = rqp->nr_if->id_addr.s_addr | ~rqp->nr_if->id_mask.s_addr; 208 if (bind(s, (struct sockaddr*)&locaddr, (socklen_t)sizeof(locaddr)) < 0) 209 return errno; 210 } 211 return 0; 212} 213 214static int nbns_rq_send(struct nbns_rq *rqp) 215{ 216 mbchain_t mbp = &rqp->nr_rq; 217 int s = rqp->nr_fd; 218 219 if (sendto(s, mbuf_data(mbp->mb_top), mbp->mb_count, 0, 220 (struct sockaddr*)&rqp->nr_dest, (socklen_t)sizeof(rqp->nr_dest)) < 0) 221 return errno; 222 return 0; 223} 224 225static int nbns_rq_run(struct nbns_rq *rqp, uint16_t *cancel) 226{ 227 mdchain_t mdp; 228 uint16_t rpid; 229 uint8_t nmflags; 230 int error, retrycount; 231 232 rqp->nr_if = nb_iflist; 233again: 234 error = nbns_rq_opensocket(rqp); 235 if (error) 236 return error; 237 /* 238 * The configuration file alwows the user to set the total amount of time 239 * we will wait for a NetBIOS name lookup to complete. So will always wait 240 * half a second per attempt. So NetBIOSResolverTimeout is the number of seconds we want to 241 * wait. So NetBIOSResolverTimeout * 2 is the number of retries we will attmept. 242 */ 243 retrycount = rqp->nr_timo * 2; 244 for (;;) { 245 if (cancel && *cancel) 246 return ECANCELED; 247 248 error = nbns_rq_send(rqp); 249 if (error) 250 return error; 251 error = nbns_rq_recv(rqp); 252 if (error) { 253 if (error != ETIMEDOUT || --retrycount == 0) { 254 if ((rqp->nr_nmflags & NBNS_NMFLAG_BCAST) && 255 rqp->nr_if != NULL && 256 rqp->nr_if->id_next != NULL) { 257 rqp->nr_if = rqp->nr_if->id_next; 258 close(rqp->nr_fd); 259 goto again; 260 } else 261 return error; 262 } 263 continue; 264 } 265 mdp = &rqp->nr_rp; 266 if (md_get_uint16be(mdp, &rpid)) 267 return EINVAL; 268 269 if (rpid != rqp->nr_trnid) 270 return EINVAL; 271 break; 272 } 273 if (md_get_uint8(mdp, &nmflags)) 274 return EINVAL; 275 276 rqp->nr_rpnmflags = (nmflags & 7) << 4; 277 if (md_get_uint8(mdp, &nmflags)) 278 return EINVAL; 279 rqp->nr_rpnmflags |= (nmflags & 0xf0) >> 4; 280 rqp->nr_rprcode = nmflags & 0xf; 281 if (rqp->nr_rprcode) 282 return nb_error_to_errno(rqp->nr_rprcode); 283 284 if (md_get_uint16be(mdp, &rpid)) /* QDCOUNT */ 285 return EINVAL; 286 if (md_get_uint16be(mdp, &rqp->nr_rpancount)) 287 return EINVAL; 288 if (md_get_uint16be(mdp, &rqp->nr_rpnscount)) 289 return EINVAL; 290 if (md_get_uint16be(mdp, &rqp->nr_rparcount)) 291 return EINVAL; 292 return 0; 293} 294 295static int nbns_rq_prepare(struct nbns_rq *rqp) 296{ 297 mbchain_t mbp = &rqp->nr_rq; 298 uint8_t nmflags; 299 u_char *cp; 300 int len, error; 301 302 error = md_init_rcvsize(&rqp->nr_rp, NBDG_MAXSIZE); 303 if (error) 304 return error; 305 if (rqp->nr_dest.sin_addr.s_addr == htonl(INADDR_BROADCAST)) { 306 rqp->nr_nmflags |= NBNS_NMFLAG_BCAST; 307 rqp->nr_flags |= NBRQF_BROADCAST; 308 if (nb_iflist == NULL) { 309 error = nb_enum_if(&nb_iflist, 100); 310 if (error) 311 return error; 312 } 313 } 314 mb_put_uint16be(mbp, rqp->nr_trnid); 315 nmflags = ((rqp->nr_opcode & 0x1F) << 3) | ((rqp->nr_nmflags & 0x70) >> 4); 316 mb_put_uint8(mbp, nmflags); 317 mb_put_uint8(mbp, (rqp->nr_nmflags & 0x0f) << 4 /* rcode */); 318 mb_put_uint16be(mbp, rqp->nr_qdcount); 319 mb_put_uint16be(mbp, rqp->nr_ancount); 320 mb_put_uint16be(mbp, rqp->nr_nscount); 321 mb_put_uint16be(mbp, rqp->nr_arcount); 322 if (rqp->nr_qdcount) { 323 if (rqp->nr_qdcount > 1) 324 return EINVAL; 325 len = NB_ENCNAMELEN + 2; 326 cp = (u_char *)mb_reserve(mbp, len); 327 if (cp == NULL) 328 return ENOMEM; 329 330 nb_name_encode(rqp->nr_qdname, cp); 331 mb_put_uint16be(mbp, rqp->nr_qdtype); 332 mb_put_uint16be(mbp, rqp->nr_qdclass); 333 } 334 mb_pullup(mbp); 335 return 0; 336} 337 338/* 339 * Resolve a NetBIOS name to an set of address. 340 */ 341static int nbns_resolvename_internal(struct nb_ctx *ctx, struct smb_prefs *prefs, 342 const char *name, uint8_t nodeType, 343 CFMutableArrayRef *outAddressArray, uint16_t port, 344 int allowLocalConn, int tryBothPorts, uint16_t *cancel) 345{ 346 CFMutableArrayRef addressArray = NULL; 347 CFMutableDataRef addressData; 348 struct connectAddress conn; 349 struct nbns_rq *rqp; 350 struct nb_name nn; 351 struct nbns_rr rr; 352 int error, rdrcount; 353 u_char *current_ip, *end_of_rr; 354 355 /* If we are trying both ports always put port 139 in after port 445 */ 356 if (tryBothPorts && (port == NBSS_TCP_PORT_139)) 357 port = SMB_TCP_PORT_445; 358 359 if (strlen(name) > NB_NAMELEN) 360 return ENAMETOOLONG; 361 362 addressArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); 363 if (!addressArray) { 364 return ENOMEM; 365 } 366 367 error = nbns_rq_create(NBNS_OPCODE_QUERY, prefs, &rqp); 368 if (error) { 369 CFRelease(addressArray); 370 return error; 371 } 372 373 bzero(&nn, sizeof(nn)); 374 strlcpy((char *)nn.nn_name, name, sizeof(nn.nn_name)); 375 nn.nn_type = nodeType; 376 rqp->nr_nmflags = NBNS_NMFLAG_RD; 377 rqp->nr_qdname = &nn; 378 rqp->nr_qdtype = NBNS_QUESTION_TYPE_NB; 379 rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN; 380 rqp->nr_qdcount = 1; 381 memcpy(&rqp->nr_dest, &ctx->nb_ns, sizeof(rqp->nr_dest)); 382 error = nbns_rq_prepare(rqp); 383 if (error) { 384 goto done; 385 } 386 rdrcount = NBNS_MAXREDIRECTS; 387 for (;;) { 388 error = nbns_rq_run(rqp, cancel); 389 if (error) 390 break; 391 if ((rqp->nr_rpnmflags & NBNS_NMFLAG_AA) == 0) { 392 if (rdrcount-- == 0) { 393 error = ETOOMANYREFS; 394 break; 395 } 396 error = nbns_rq_getrr(rqp, &rr); 397 if (! error) 398 error = nbns_rq_getrr(rqp, &rr); 399 if (error) 400 break; 401 bcopy(rr.rr_data, &rqp->nr_dest.sin_addr, 4); 402 rqp->nr_flags &= ~NBRQF_BROADCAST; 403 continue; 404 } 405 if (rqp->nr_rpancount == 0) { 406 error = EHOSTUNREACH; 407 break; 408 } 409 error = nbns_rq_getrr(rqp, &rr); 410 if (error) 411 break; 412 413 /* We have an answer, so store away the address of the server that responded */ 414 ctx->nb_sender = rqp->nr_sender; 415 416 end_of_rr = rr.rr_data + rr.rr_rdlength; 417 for(current_ip = rr.rr_data + 2; current_ip < end_of_rr; current_ip += 6) { 418 bzero(&conn, sizeof(conn)); 419 conn.in4.sin_len = (int)sizeof(struct sockaddr_in); 420 conn.in4.sin_family = AF_INET; 421 conn.in4.sin_port = htons(port); 422 memcpy(&conn.in4.sin_addr, current_ip, 4); 423 /* Check to make sure we are not connecting to ourself */ 424 if (isLocalIPAddress((struct sockaddr *)&conn.addr, port, allowLocalConn)) { 425 smb_log_info("The address for `%s' is a loopback address, not allowed!", 426 ASL_LEVEL_DEBUG, name); 427 error = ELOOP; /* AFP returns ELOOP, so we will do the same */ 428 goto done; 429 } 430 addressData = CFDataCreateMutable(NULL, 0); 431 if (addressData) { 432 /* The name is the netbios name, we need a netbios sockaddr */ 433 if (port == NBSS_TCP_PORT_139) 434 convertToNetBIOSaddr(&conn.storage, name); 435 436 CFDataAppendBytes(addressData, (const UInt8 *)&conn, (CFIndex)sizeof(conn)); 437 CFArrayAppendValue(addressArray, addressData); 438 CFRelease(addressData); 439 } 440 /* We only try both ports with IPv4 */ 441 if (tryBothPorts) { 442 conn.in4.sin_port = htons(NBSS_TCP_PORT_139); 443 /* The name is the netbios name, we need a netbios sockaddr */ 444 convertToNetBIOSaddr(&conn.storage, name); 445 446 addressData = CFDataCreateMutable(NULL, 0); 447 if (addressData) { 448 CFDataAppendBytes(addressData, (const UInt8 *)&conn, (CFIndex)sizeof(conn)); 449 CFArrayAppendValue(addressArray, addressData); 450 CFRelease(addressData); 451 } 452 } 453 } 454 break; 455 } /* end big for loop */ 456 457 458done: 459 if (CFArrayGetCount(addressArray) == 0) { 460 error = EHOSTUNREACH; 461 } 462 nbns_rq_done(rqp); 463 464 if (error) { 465 if (addressArray) 466 CFRelease(addressArray); 467 addressArray = NULL; 468 } 469 *outAddressArray = addressArray; 470 471 return error; 472} 473 474/* 475 * Resolve a NetBIOS name to an set of address, We always try WINS first if a 476 * server is provide. If no WINS server or we fail to find one with the WINS 477 * server then try broadcast. 478 */ 479int nbns_resolvename(struct nb_ctx *ctx, struct smb_prefs *prefs, const char *name, 480 uint8_t nodeType, CFMutableArrayRef *outAddressArray, uint16_t port, 481 int allowLocalConn, int tryBothPorts, uint16_t *cancel) 482{ 483 int error; 484 485 error = nb_ctx_resolve(ctx, prefs->WINSAddresses); 486 if (!error) 487 error = nbns_resolvename_internal(ctx, prefs, name, nodeType, outAddressArray, 488 port, allowLocalConn, tryBothPorts, cancel); 489 /* We tried it with WINS and failed try broadcast now */ 490 if (error && (prefs->WINSAddresses != NULL)) { 491 error = nb_ctx_resolve(ctx, NULL); 492 if (error == 0) { 493 error = nbns_resolvename_internal(ctx, prefs, name, nodeType, outAddressArray, 494 port, allowLocalConn, tryBothPorts, cancel); 495 } 496 } 497 return error; 498} 499 500int nbns_getnodestatus(struct sockaddr *targethost, struct nb_ctx *ctx, 501 struct smb_prefs *prefs, uint16_t *cancel, char *nbt_server, 502 char *workgroup, CFMutableArrayRef nbrrArray) 503{ 504 struct nbns_rq *rqp; 505 struct nbns_rr rr; 506 struct nb_name nn; 507 struct nbns_nr *nrp; 508 char nrtype; 509 char *cp, *retname = NULL; 510 unsigned char nrcount; 511 int error, i, foundserver = 0, foundgroup = 0; 512 513 if (targethost->sa_family != AF_INET) 514 return EINVAL; 515 error = nbns_rq_create(NBNS_OPCODE_QUERY, prefs, &rqp); 516 if (error) 517 return error; 518 bzero(&nn, sizeof(nn)); 519 strlcpy((char *)nn.nn_name, "*", sizeof(nn.nn_name)); 520 nn.nn_type = NBT_WKSTA; 521 rqp->nr_nmflags = 0; 522 rqp->nr_qdname = &nn; 523 rqp->nr_qdtype = NBNS_QUESTION_TYPE_NBSTAT; 524 rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN; 525 rqp->nr_qdcount = 1; 526 rqp->nr_dest = *(struct sockaddr_in *)(void *)targethost; 527 rqp->nr_dest.sin_port = htons(NBNS_UDP_PORT_137); 528 if (rqp->nr_dest.sin_addr.s_addr == INADDR_ANY) 529 rqp->nr_dest.sin_addr.s_addr = htonl(INADDR_BROADCAST); 530 error = nbns_rq_prepare(rqp); 531 if (error) { 532 nbns_rq_done(rqp); 533 return error; 534 } 535 for (;;) { 536 error = nbns_rq_run(rqp, cancel); 537 if (error) 538 break; 539 if (rqp->nr_rpancount == 0) { 540 error = EHOSTUNREACH; 541 break; 542 } 543 error = nbns_rq_getrr(rqp, &rr); 544 if (error) 545 break; 546 nrcount = (unsigned char)(*(rr.rr_data)); 547 rr.rr_data++; 548 for (i = 1, nrp = (struct nbns_nr *)(void *)rr.rr_data; 549 i <= nrcount; ++i, ++nrp) { 550 uint16_t nbFlags = betohs(nrp->nr_beflags); 551 552 /* 553 * They want all the names in the format it came into from the 554 * network. Copy in the NetBIOS name and then the flags. 555 */ 556 if (nbrrArray) { 557 CFMutableDataRef addressData = CFDataCreateMutable(NULL, 0); 558 CFDataAppendBytes(addressData, (const UInt8 *)nrp->nr_name, (CFIndex)NB_NAMELEN); 559 CFDataAppendBytes(addressData, (const UInt8 *)&nbFlags, (CFIndex)sizeof(uint16_t)); 560 CFArrayAppendValue(nbrrArray, addressData); 561 CFRelease(addressData); 562 } 563 564 nrtype = nrp->nr_name[NB_NAMELEN-1]; 565 /* Terminate the string: */ 566 nrp->nr_name[NB_NAMELEN-1] = (char)0; 567 /* Strip off trailing spaces */ 568 for (cp = &nrp->nr_name[NB_NAMELEN-2]; 569 cp >= nrp->nr_name; --cp) { 570 if (*cp != (char)0x20) 571 break; 572 *cp = (char)0; 573 } 574 if (nbFlags & NBNS_GROUPFLG) { 575 if (!foundgroup || 576 (foundgroup != NBT_WKSTA+1 && 577 nrtype == NBT_WKSTA)) { 578 if (workgroup) 579 smb_optstrncpy(workgroup, nrp->nr_name, SMB_MAXNetBIOSNAMELEN); 580 foundgroup = nrtype+1; 581 } 582 } else { 583 /* Track at least ONE name, in case 584 no server name is found */ 585 retname = nrp->nr_name; 586 } 587 if (nrtype == NBT_SERVER) { 588 if (nbt_server) 589 smb_optstrncpy(nbt_server, nrp->nr_name, SMB_MAXNetBIOSNAMELEN); 590 foundserver = 1; 591 } 592 } 593 if (!foundserver && nbt_server) 594 smb_optstrncpy(nbt_server, retname, SMB_MAXNetBIOSNAMELEN); 595 ctx->nb_sender = rqp->nr_sender; 596 break; 597 } 598 nbns_rq_done(rqp); 599 return error; 600} 601 602