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*/ 35#include <string.h> 36 37#include <sys/socket.h> 38#include <sys/ioctl.h> 39#include <fcntl.h> 40#include <netdb.h> 41#include <net/if.h> 42#include <ifaddrs.h> 43#include <SystemConfiguration/SCNetworkConnectionPrivate.h> 44 45#include <netsmb/netbios.h> 46#include <netsmb/smb_lib.h> 47#include <netsmb/nb_lib.h> 48#include "charsets.h" 49 50int getaddrinfo_ipv6(const char *hostname, const char *servname, 51 const struct addrinfo *hints, 52 struct addrinfo **res); 53 54static int SocketUtilsIncrementIfReqIter(UInt8** inIfReqIter, struct ifreq* ifr) 55{ 56 *inIfReqIter += sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len; 57 58 /* If the length of the addr is 0, use the family to determine the addr size */ 59 if (ifr->ifr_addr.sa_len == 0) { 60 switch (ifr->ifr_addr.sa_family) { 61 case AF_INET: 62 *inIfReqIter += sizeof(struct sockaddr_in); 63 break; 64 default: 65 *inIfReqIter += sizeof(struct sockaddr); 66 return FALSE; 67 } 68 } 69 return TRUE; 70} 71 72/* 73 * Check to see if the AF_INET address is a local address. 74 */ 75static int IsLocalIPv4Address(uint32_t addr) 76{ 77 UInt32 kMaxAddrBufferSize = 2048; 78 UInt8 buffer[kMaxAddrBufferSize]; 79 int so; 80 UInt8* ifReqIter = NULL; 81 struct ifconf ifc; 82 struct ifreq ifreq, *ifr; 83 int foundit = FALSE; 84 85 if (addr == htonl(INADDR_LOOPBACK)) { 86 return TRUE; 87 } 88 89 if ((so = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 90 smb_log_info("%s: socket failed, syserr = %s", 91 ASL_LEVEL_ERR, __FUNCTION__, strerror(errno)); 92 return foundit; 93 } 94 ifc.ifc_len = (int)sizeof (buffer); 95 ifc.ifc_buf = (char*) buffer; 96 if (ioctl(so, SIOCGIFCONF, (char *)&ifc) < 0) { 97 smb_log_info("%s: ioctl (get interface configuration), syserr = %s", 98 ASL_LEVEL_ERR, __FUNCTION__, strerror(errno)); 99 goto WeAreDone; 100 } 101 for (ifReqIter = buffer; ifReqIter < (buffer + ifc.ifc_len);) { 102 ifr = (struct ifreq*)((void *)ifReqIter); 103 if (!SocketUtilsIncrementIfReqIter(&ifReqIter, ifr)) { 104 smb_log_info("%s: SocketUtilsIncrementIfReqIter failed!", 105 ASL_LEVEL_ERR, __FUNCTION__); 106 break; 107 } 108 ifreq = *ifr; 109 if ((ifr->ifr_addr.sa_family != AF_INET) || (strncmp(ifr->ifr_name, "lo", 2) == 0)) 110 continue; 111 112 if (ioctl(so, SIOCGIFFLAGS, (char *)&ifreq) < 0) { 113 smb_log_info("%s: SIOCGIFFLAGS ioctl failed, syserr = %s", 114 ASL_LEVEL_ERR, __FUNCTION__, strerror(errno)); 115 continue; 116 } 117 if (ifreq.ifr_flags & IFF_UP) { 118 struct sockaddr_in *laddr = (struct sockaddr_in *)((void *)&(ifreq.ifr_addr)); 119 if ((uint32_t)laddr->sin_addr.s_addr == addr) { 120 foundit = TRUE; 121 break; 122 } 123 } 124 } 125WeAreDone: 126 (void) close(so); 127 return foundit; 128} 129 130/* 131 * Check to see if the AF_INET6 address is a local address. 132 */ 133static int IsLocalIPv6Address ( struct sockaddr_in6 *in6) 134{ 135 struct ifaddrs* addr_list, *ifa; 136 struct sockaddr_in6 *currAddress; 137 138 if (IN6_IS_ADDR_LOOPBACK(&in6->sin6_addr)) { 139 return TRUE; 140 } 141 142 /* Ignore any getifaddrs errors */ 143 if (getifaddrs(&addr_list)) { 144 smb_log_info("%s: getifaddrs failed, syserr = %s", 145 ASL_LEVEL_DEBUG, __FUNCTION__, strerror(errno)); 146 return FALSE; 147 } 148 149 for (ifa = addr_list; ifa != NULL; ifa = ifa->ifa_next) { 150 if (ifa->ifa_addr->sa_family != AF_INET6) 151 continue; 152 currAddress = (struct sockaddr_in6 *)((void *)ifa->ifa_addr); 153 if (IN6_ARE_ADDR_EQUAL (&currAddress->sin6_addr, &in6->sin6_addr)) 154 return TRUE; 155 } 156 freeifaddrs(addr_list); /* release memory */; 157 158 return FALSE; 159} 160 161 162/* 163 * Check to see if this is a local address. We allow command line utilities to 164 * do a loopback connect. Also if the user supplied the port then assume they 165 * know what they are doing and allow the connection. 166 */ 167int isLocalIPAddress(struct sockaddr *addr, uint16_t port, int allowLocalConn) 168{ 169 /* Must be coming from a command line utility let them connect */ 170 if (allowLocalConn) 171 return FALSE; 172 /* Always allow loop back connection if the user supplied the port */ 173 if ((port != NBSS_TCP_PORT_139) && (port != SMB_TCP_PORT_445)) 174 return FALSE; 175 176 if (addr->sa_family == AF_INET) { 177 struct sockaddr_in *in = (struct sockaddr_in *)((void *)addr); 178 179 if (IsLocalIPv4Address(in->sin_addr.s_addr)) { 180 return TRUE; 181 } 182 } else if (addr->sa_family == AF_INET6) { 183 if (IsLocalIPv6Address((struct sockaddr_in6 *)((void *)addr))) { 184 return TRUE; 185 } 186 } else { 187 smb_log_info("%s: Unknown address falmily %d?", 188 ASL_LEVEL_DEBUG, __FUNCTION__, addr->sa_family); 189 } 190 191 return FALSE; 192} 193 194int getaddrinfo_ipv6(const char *hostname, const char *servname, 195 const struct addrinfo *hints, 196 struct addrinfo **res) 197{ 198 int error; 199 size_t len; 200 char *temp_name = NULL; 201 202 /* 203 * Note: getaddrinfo() and inet_pton() both will give errors if its 204 * an IPv6 address enclosed by brackets. I cant find a way to detect 205 * if the address is IPv6 or not if the brackets are present. Thus, the 206 * check for '[' at the start and ']' at the end of the string. 207 */ 208 209 len = strnlen(hostname, 1024); /* assume hostname < 1024 */ 210 if ((len > 3) && (hostname[0] == '[') && (hostname[len - 1] == ']')) { 211 /* Seems to be IPv6 with brackets */ 212 temp_name = malloc(len); 213 214 if (temp_name != NULL) { 215 /* 216 * Copy string and skip beginning '[' (&hostname[1]) and 217 * ending ']' (len - 1) 218 */ 219 strlcpy(temp_name, &hostname[1], len - 1); 220 221 /* Try without the [] and return that error */ 222 error = getaddrinfo (temp_name, servname, hints, res); 223 224 free(temp_name); 225 } 226 else { 227 error = ENOMEM; 228 } 229 } 230 else { 231 /* Not IPv6, just do getaddrinfo */ 232 error = getaddrinfo (hostname, servname, hints, res); 233 } 234 235 return (error); 236} 237 238/* 239 * Resolve the name and retrieve all address associated with that name. 240 */ 241int resolvehost(const char *name, CFMutableArrayRef *outAddressArray, char *netbios_name, 242 uint16_t port, int allowLocalConn, int tryBothPorts) 243{ 244 int error; 245 struct addrinfo hints, *res0, *res; 246 CFMutableArrayRef addressArray = NULL; 247 CFMutableDataRef addressData; 248 CFStringRef hostName = NULL; 249 250 /* If we are trying both ports always put port 139 in after port 445 */ 251 if (tryBothPorts && (port == NBSS_TCP_PORT_139)) 252 port = SMB_TCP_PORT_445; 253 254 memset (&hints, 0, sizeof (hints)); 255 hints.ai_family = PF_UNSPEC; 256 hints.ai_socktype = SOCK_STREAM; 257 hints.ai_protocol = IPPROTO_TCP; 258 error = getaddrinfo_ipv6 (name, NULL, &hints, &res0); 259 if (error != noErr) { 260 hostName = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingUTF8); 261 if(hostName != NULL) { 262 if (SCNetworkConnectionTriggerOnDemandIfNeeded(hostName, TRUE, 60, 0)) { 263 error = getaddrinfo_ipv6 (name, NULL, &hints, &res0); 264 } 265 } 266 } 267 if (error) { 268 if(hostName != NULL) { 269 CFRelease(hostName); 270 } 271 return (error == EAI_SYSTEM) ? errno : EHOSTUNREACH; 272 } 273 addressArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); 274 if (!addressArray) { 275 error = ENOMEM; 276 goto done; 277 } 278 for (res = res0; res; res = res->ai_next) { 279 struct connectAddress conn; 280 281 /* We only support IPv4 or IPv6 */ 282 if ((res->ai_family != PF_INET6) && (res->ai_family != PF_INET)) { 283 smb_log_info("Skipping address for `%s', unknown address family %d", 284 ASL_LEVEL_DEBUG, name, res->ai_family); 285 continue; 286 } 287 /* Check to make sure we are not connecting to ourself */ 288 if (isLocalIPAddress((struct sockaddr *)res->ai_addr, port, allowLocalConn)) { 289 smb_log_info("The address for `%s' is a loopback address, not allowed!", 290 ASL_LEVEL_DEBUG, name); 291 error = ELOOP; /* AFP returns ELOOP, so we will do the same */ 292 goto done; 293 } 294 295 /* We don't support port 137 on IPv6 addresses currently? */ 296 if ((res->ai_family == PF_INET6) && (port == NBNS_UDP_PORT_137)) { 297 smb_log_info("Skipping address of `%s', we don't support port 137 on IPV6 addresses", 298 ASL_LEVEL_DEBUG, name); 299 continue; 300 } 301 /* We don't support port 139 on IPv6 addresses */ 302 if ((res->ai_family == PF_INET6) && (port == NBSS_TCP_PORT_139)) { 303 smb_log_info("Skipping address of `%s', we don't support port 139 on IPV6 addresses", 304 ASL_LEVEL_DEBUG, name); 305 continue; 306 } 307 memset(&conn, 0, sizeof(conn)); 308 conn.so = -1; /* Default to socket create failed */ 309 memcpy(&conn.addr, res->ai_addr, res->ai_addrlen); 310 if (res->ai_family == PF_INET6) { 311 conn.in6.sin6_port = htons(port); 312 } else { 313 conn.in4.sin_port = htons(port); 314 } 315 addressData = CFDataCreateMutable(NULL, 0); 316 if (addressData) { 317 /* We have a netbios name, we need a netbios sockaddr */ 318 if ((port == NBSS_TCP_PORT_139) && (netbios_name)) 319 convertToNetBIOSaddr(&conn.storage, netbios_name); 320 321 CFDataAppendBytes(addressData, (const UInt8 *)&conn, (CFIndex)sizeof(conn)); 322 CFArrayAppendValue(addressArray, addressData); 323 CFRelease(addressData); 324 } 325 /* We only try both ports with IPv4 */ 326 if (tryBothPorts && (res->ai_family == PF_INET)) { 327 conn.in4.sin_port = htons(NBSS_TCP_PORT_139); 328 /* We have a netbios name, we need a netbios sockaddr */ 329 if (netbios_name) 330 convertToNetBIOSaddr(&conn.storage, netbios_name); 331 332 addressData = CFDataCreateMutable(NULL, 0); 333 if (addressData) { 334 CFDataAppendBytes(addressData, (const UInt8 *)&conn, (CFIndex)sizeof(conn)); 335 CFArrayAppendValue(addressArray, addressData); 336 CFRelease(addressData); 337 } 338 } 339 } 340 if (CFArrayGetCount(addressArray) == 0) { 341 error = EHOSTUNREACH; 342 goto done; 343 } 344 345done: 346 freeaddrinfo(res0); 347 if (error) { 348 if (addressArray) 349 CFRelease(addressArray); 350 addressArray = NULL; 351 } 352 if(hostName) { 353 CFRelease(hostName); 354 } 355 *outAddressArray = addressArray; 356 return error; 357} 358 359/* 360 * Is this a IPv6 Dot name. 361 */ 362int isIPv6NumericName(const char *name) 363{ 364 int error; 365 struct addrinfo hints, *res0, *res; 366 367 memset (&hints, 0, sizeof (hints)); 368 hints.ai_flags = AI_NUMERICHOST; 369 hints.ai_family = PF_INET6; 370 hints.ai_socktype = SOCK_STREAM; 371 hints.ai_protocol = IPPROTO_TCP; 372 error = getaddrinfo_ipv6(name, NULL, &hints, &res0); 373 if (error) { 374 return FALSE; 375 } 376 377 for (res = res0; res; res = res->ai_next) { 378 if (res->ai_family == PF_INET6) { 379 freeaddrinfo(res0); 380 return TRUE; 381 } 382 } 383 384 freeaddrinfo(res0); 385 return FALSE; 386} 387 388int 389nb_enum_if(struct nb_ifdesc **iflist, int maxif) 390{ 391 struct ifconf ifc; 392 struct ifreq *ifrqp; 393 struct nb_ifdesc *ifd; 394 struct in_addr iaddr, imask; 395 char *ifrdata, *iname; 396 int s, rdlen, error, iflags, i; 397 unsigned len; 398 399 *iflist = NULL; 400 s = socket(AF_INET, SOCK_DGRAM, 0); 401 if (s == -1) 402 return errno; 403 404 rdlen = (int)(maxif * sizeof(struct ifreq)); 405 ifrdata = malloc(rdlen); 406 if (ifrdata == NULL) { 407 error = ENOMEM; 408 goto bad; 409 } 410 ifc.ifc_len = rdlen; 411 ifc.ifc_buf = ifrdata; 412 if (ioctl(s, SIOCGIFCONF, &ifc) != 0) { 413 error = errno; 414 goto bad; 415 } 416 ifrqp = ifc.ifc_req; 417 error = 0; 418 /* freebsd bug: ifreq size is variable - must use _SIZEOF_ADDR_IFREQ */ 419 for (i = 0; i < ifc.ifc_len; 420 i += len, ifrqp = (struct ifreq *)(void *)((uint8_t *)ifrqp + len)) { 421 len = (int)_SIZEOF_ADDR_IFREQ(*ifrqp); 422 /* XXX for now, avoid IP6 broadcast performance costs */ 423 if (ifrqp->ifr_addr.sa_family != AF_INET) 424 continue; 425 if (ioctl(s, SIOCGIFFLAGS, ifrqp) != 0) 426 continue; 427 iflags = ifrqp->ifr_flags; 428 if ((iflags & IFF_UP) == 0 || (iflags & IFF_BROADCAST) == 0) 429 continue; 430 431 if (ioctl(s, SIOCGIFADDR, ifrqp) != 0 || 432 ifrqp->ifr_addr.sa_family != AF_INET) 433 continue; 434 iname = ifrqp->ifr_name; 435 if (strlen(iname) >= sizeof(ifd->id_name)) 436 continue; 437 iaddr = (*(struct sockaddr_in *)(void *)&ifrqp->ifr_addr).sin_addr; 438 439 if (ioctl(s, SIOCGIFNETMASK, ifrqp) != 0) 440 continue; 441 imask = ((struct sockaddr_in *)(void *)&ifrqp->ifr_addr)->sin_addr; 442 443 ifd = malloc(sizeof(struct nb_ifdesc)); 444 if (ifd == NULL) 445 return ENOMEM; 446 bzero(ifd, sizeof(struct nb_ifdesc)); 447 strlcpy(ifd->id_name, iname, sizeof(ifd->id_name)); 448 ifd->id_flags = iflags; 449 ifd->id_addr = iaddr; 450 ifd->id_mask = imask; 451 ifd->id_next = *iflist; 452 *iflist = ifd; 453 } 454bad: 455 if (ifrdata) 456 free(ifrdata); 457 close(s); 458 return error; 459} 460 461 462#define kPollSeconds 5 463#define kMaxTimeToWait 60 464 465/* 466 * Get a non blocking socket to be used for the connect. Since a connection 467 * failure always returns the same error, we don't worry about errno getting 468 * overwritten by the close call. We just use errno for debug purposes here. 469 */ 470static int nonBlockingSocket(int family) 471{ 472 int so, flags; 473 474 so = socket(family, SOCK_STREAM, 0); 475 if (so < 0) { 476 smb_log_info("%s: socket call failed for family %d, syserr = %s", 477 ASL_LEVEL_DEBUG, __FUNCTION__, family, strerror(errno)); 478 return -1; 479 } 480 if ( (flags = fcntl(so, F_GETFL, NULL)) < 0 ) { 481 smb_log_info("%s: F_GETFL call failed for family %d, syserr = %s", 482 ASL_LEVEL_DEBUG, __FUNCTION__, family, strerror(errno)); 483 close(so); 484 return -1; 485 } 486 flags |= O_NONBLOCK; 487 if ( fcntl(so, F_SETFL, flags) < 0 ) { 488 smb_log_info("%s: F_SETFL call failed for sa_family %d, syserr = %s", 489 ASL_LEVEL_DEBUG, __FUNCTION__, family, strerror(errno)); 490 close(so); 491 return -1; 492 } 493 return so; 494} 495 496int findReachableAddress(CFMutableArrayRef addressArray, uint16_t *cancel, struct connectAddress **dest) 497{ 498 struct timeval tv; 499 int error = 0; 500 fd_set writefds; 501 int nfds = 0; 502 CFIndex ii, numAddresses = CFArrayGetCount(addressArray); 503 int32_t totalWaitTime = 0; 504 CFMutableDataRef dataRef; 505 struct connectAddress *conn; 506 struct connectAddress *conn_port139 = NULL; 507 struct connectAddress *conn_port445 = NULL; 508 in_port_t port; 509 510 *dest = NULL; 511 FD_ZERO(&writefds); 512 513 /* Attempt to connect to all addresses non blocking */ 514 for (ii = 0; ii < numAddresses; ii++) { 515 dataRef = (CFMutableDataRef)CFArrayGetValueAtIndex(addressArray, ii); 516 if (!dataRef) 517 continue; 518 519 conn = (struct connectAddress *)((void *)CFDataGetMutableBytePtr(dataRef)); 520 if (!conn) 521 continue; 522 523 if ( (cancel) && (*cancel == TRUE) ) { 524 smb_log_info("%s: Connection cancelled", ASL_LEVEL_DEBUG, __FUNCTION__); 525 error = ECANCELED; 526 goto done; 527 } 528 529 if (conn->addr.sa_family == AF_NETBIOS) 530 conn->so = nonBlockingSocket(AF_INET); 531 else 532 conn->so = nonBlockingSocket(conn->addr.sa_family); 533 534 if (conn->so < 0) { 535 /* Socket called failed, so skip this address */ 536 continue; 537 } 538 539 /* Connect to the address */ 540 if (conn->addr.sa_family == AF_NETBIOS) { 541 error = connect(conn->so, (struct sockaddr *)&conn->nb.snb_addrin, conn->nb.snb_addrin.sin_len); 542 } 543 else { 544 error = connect(conn->so, &conn->addr, conn->addr.sa_len); 545 } 546 547 if (error < 0) { 548 /* This is a non blocking, so we expect EINPROGRESS */ 549 if (errno == EINPROGRESS) { 550 FD_SET(conn->so, &writefds); /* add socket into set for the select call */ 551 if (conn->so > nfds) /* save max fd for select call */ 552 nfds = conn->so; 553 } else { 554 /* Connection failed skip this address */ 555 smb_log_info("%s: Connection %ld failed, family = %d, syserr = %s", 556 ASL_LEVEL_DEBUG, __FUNCTION__, ii, 557 conn->addr.sa_family, strerror(errno)); 558 close (conn->so); 559 conn->so = -1; 560 } 561 continue; 562 } 563 } 564 565 /* Wait for one or more connects to complete */ 566 while (nfds && (totalWaitTime < kMaxTimeToWait)) { 567 tv.tv_sec = kPollSeconds; 568 tv.tv_usec = 0; 569 error = select(nfds + 1, NULL, &writefds, NULL, &tv); 570 571 if (error < 0) { 572 /* We treat EAGAIN or EINTR the same as a timeout */ 573 if ((errno == EAGAIN) || (errno == EINTR)) { 574 error = 0; 575 } else { 576 /* Not sure what went wrong here just get out */ 577 error = errno; 578 smb_log_info("%s: Select call failed, syserr = %s", 579 ASL_LEVEL_DEBUG, __FUNCTION__, strerror(error)); 580 goto done; 581 } 582 } 583 584 if (error > 0) { 585 /* One or more sockets finished */ 586 nfds = 0; 587 error = 0; 588 for (ii = 0; ii < numAddresses; ii++) { 589 socklen_t dummy; 590 591 dataRef = (CFMutableDataRef)CFArrayGetValueAtIndex(addressArray, ii); 592 if (!dataRef) 593 continue; 594 595 conn = (struct connectAddress *)((void *)CFDataGetMutableBytePtr(dataRef)); 596 if (!conn) 597 continue; 598 599 /* This socket already failed, so skip this connection */ 600 if (conn->so < 0) 601 continue; 602 603 if (FD_ISSET(conn->so, &writefds) == 0) { 604 /* Connection hasn't completed, so skip it */ 605 FD_SET (conn->so, &writefds); 606 if (conn->so > nfds) 607 nfds = conn->so; 608 continue; 609 } 610 611 /* 612 * See what error came back. SO_ERROR gives us an exact error 613 * for why the connect failed 614 */ 615 dummy = sizeof(int); 616 if (getsockopt(conn->so, SOL_SOCKET, SO_ERROR, (void*)(&error), &dummy) < 0) { 617 error = errno; /* Handle this below */ 618 smb_log_info("%s: getsockopt failed, syserr = %s", 619 ASL_LEVEL_DEBUG, __FUNCTION__, strerror(errno)); 620 } 621 622 if (error) { 623 if (error != EINPROGRESS) { 624 smb_log_info("%s: Connection failed, syserr = %s", 625 ASL_LEVEL_DEBUG, __FUNCTION__, strerror(error)); 626 /* We treat all other errors as a connection failure */ 627 FD_CLR (conn->so, &writefds); 628 close (conn->so); 629 conn->so = -1; 630 } else { 631 FD_SET (conn->so, &writefds); 632 if (conn->so > nfds) /* save max fd for select call */ 633 nfds = conn->so; 634 } 635 error = 0; 636 continue; 637 } 638 639 /* 640 * A connection completed. If its port 445, then we are done. 641 * If its port 139, check the others and see if we have a port 642 * 445 completed yet. We prefer port 445 over 139. 643 */ 644 switch (conn->addr.sa_family) { 645 case AF_NETBIOS: 646 port = ntohs(conn->nb.snb_addrin.sin_port); 647 break; 648 case PF_INET6: 649 port = ntohs(conn->in6.sin6_port); 650 break; 651 652 default: 653 /* Must be IPv4 */ 654 port = ntohs(conn->in4.sin_port); 655 break; 656 } 657 658 if (port == SMB_TCP_PORT_445) { 659 if (conn_port445 == NULL) { 660 /* save the first one that connected */ 661 conn_port445 = conn; 662 } 663 } 664 else { 665 if (conn_port139 == NULL) { 666 /* save the first one that connected */ 667 conn_port139 = conn; 668 } 669 } 670 671 if (conn_port445 != NULL) { 672 /* Found a port 445, so we are done */ 673 goto done; 674 } 675 } 676 } else { 677 /* time limit expired */ 678 totalWaitTime += kPollSeconds; 679 680 if ( (cancel) && (*cancel == TRUE) ) { 681 smb_log_info("%s: Connection cancelled", ASL_LEVEL_DEBUG, __FUNCTION__); 682 error = ECANCELED; 683 goto done; 684 } 685 686 if ((conn_port139 != NULL) && (totalWaitTime > kPollSeconds)) { 687 /* 688 * Found a port 139 connection, we waited one more time to see 689 * if port 445 connection will be found and it was not found 690 * so just use the 139 connection. I dont think this can ever 691 * happen as port 445 always seems to be supported and found. 692 */ 693 goto done; 694 } 695 696 697 /* we are going to do the select call again, so setup the FD list */ 698 nfds = 0; 699 for (ii = 0; ii < numAddresses; ii++) { 700 dataRef = (CFMutableDataRef)CFArrayGetValueAtIndex(addressArray, ii); 701 if (!dataRef) 702 continue; 703 conn = (struct connectAddress *)((void *)CFDataGetMutableBytePtr(dataRef)); 704 if (!conn) 705 continue; 706 707 if (conn->so < 0) 708 continue; 709 710 if (FD_ISSET(conn->so, &writefds) == 0) { 711 FD_SET (conn->so, &writefds); 712 if (conn->so > nfds) /* save max fd for select call */ 713 nfds = conn->so; 714 } 715 } 716 } 717 } 718 719done: 720 if (conn_port445 != NULL) { 721 *dest = conn_port445; 722 } 723 else { 724 if (conn_port139 != NULL) { 725 smb_log_info("%s: Using port 139 family = %d", 726 ASL_LEVEL_ERR, __FUNCTION__, 727 (conn != NULL) ? conn->addr.sa_family : 0); 728 *dest = conn_port139; 729 } 730 } 731 732 if (!error && (*dest == NULL)) 733 error = ETIMEDOUT; 734 735 /* close all open sockets */ 736 for (ii = 0; ii < numAddresses; ii++) { 737 dataRef = (CFMutableDataRef)CFArrayGetValueAtIndex(addressArray, ii); 738 if (!dataRef) 739 continue; 740 conn = (struct connectAddress *)((void *)CFDataGetMutableBytePtr(dataRef)); 741 if (!conn) 742 continue; 743 744 if (conn->so != -1) 745 close (conn->so); 746 } 747 748 return error; 749} 750