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__, conn->addr.sa_family);
727            *dest = conn_port139;
728        }
729    }
730
731	if (!error && (*dest == NULL))
732		error = ETIMEDOUT;
733
734	/* close all open sockets */
735	for (ii = 0; ii < numAddresses; ii++) {
736		dataRef = (CFMutableDataRef)CFArrayGetValueAtIndex(addressArray, ii);
737		if (!dataRef)
738			continue;
739		conn = (struct connectAddress *)((void *)CFDataGetMutableBytePtr(dataRef));
740		if (!conn)
741			continue;
742
743		if (conn->so != -1)
744			close (conn->so);
745	}
746
747	return error;
748}
749