| /* |
| * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. All rights reserved. |
| * |
| * This source code is a modified version of the CoreFoundation sources released by Apple Inc. under |
| * the terms of the APSL version 2.0 (see below). |
| * |
| * For information about changes from the original Apple source release can be found by reviewing the |
| * source control system for the project at https://sourceforge.net/svn/?group_id=246198. |
| * |
| * The original license information is as follows: |
| * |
| * Copyright (c) 2008 Apple Inc. All rights reserved. |
| * |
| * @APPLE_LICENSE_HEADER_START@ |
| * |
| * This file contains Original Code and/or Modifications of Original Code |
| * as defined in and that are subject to the Apple Public Source License |
| * Version 2.0 (the 'License'). You may not use this file except in |
| * compliance with the License. Please obtain a copy of the License at |
| * http://www.opensource.apple.com/apsl/ and read it before using this |
| * file. |
| * |
| * The Original Code and all software distributed under the License are |
| * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
| * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
| * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
| * Please see the License for the specific language governing rights and |
| * limitations under the License. |
| * |
| * @APPLE_LICENSE_HEADER_END@ |
| */ |
| /* CFSocket.c |
| Copyright (c) 1999-2007 Apple Inc. All rights reserved. |
| Responsibility: Christopher Kane |
| */ |
| |
| #define _DARWIN_UNLIMITED_SELECT 1 |
| |
| #include <CoreFoundation/CFSocket.h> |
| #include <sys/types.h> |
| #include <math.h> |
| #include <limits.h> |
| #include <CoreFoundation/CFArray.h> |
| #include <CoreFoundation/CFData.h> |
| #include <CoreFoundation/CFDictionary.h> |
| #include <CoreFoundation/CFRunLoop.h> |
| #include <CoreFoundation/CFString.h> |
| #include <CoreFoundation/CFPropertyList.h> |
| #include "CFInternal.h" |
| #if DEPLOYMENT_TARGET_MACOSX |
| #include <libc.h> |
| #include <dlfcn.h> |
| #define SOCK_DATA_TYPE uint8_t* |
| #elif DEPLOYMENT_TARGET_WINDOWS |
| #include <winsock2.h> |
| #include <ws2tcpip.h> |
| #include <fcntl.h> |
| #include <io.h> |
| #include <stdio.h> |
| // Careful with remapping these - different WinSock routines return different errors than |
| // on BSD, so if these are used many places they won't work. |
| #define EINPROGRESS WSAEINPROGRESS |
| #ifdef EBADF |
| #undef EBADF |
| #endif |
| #define EBADF WSAENOTSOCK |
| extern void gettimeofday(struct timeval *tv, void *dummy); |
| #elif DEPLOYMENT_TARGET_LINUX |
| #include <dlfcn.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <netinet/in.h> |
| #include <sys/ioctl.h> |
| #include <sys/param.h> |
| #include <sys/socket.h> |
| #endif /* DEPLOYMENT_TARGET_MACOSX */ |
| #include "auto_stubs.h" |
| |
| #if defined(DEBUG) |
| #include <stdio.h> |
| #endif |
| |
| #if DEPLOYMENT_TARGET_ANDROID |
| #define NBBY 8 |
| typedef long int fd_mask; |
| #endif |
| |
| #if DEPLOYMENT_TARGET_WINDOWS |
| /* Use crazy Microsoft signatures */ |
| #define SOCK_DATA char* |
| #define SOCK_CONST_DATA const char* |
| #define read _read |
| |
| /* The following macro is from: |
| * The olsr.org Optimized Link-State Routing daemon (olsrd) |
| * Copyright (c) 2004, Thomas Lopatic (thomas@lopatic.de) |
| * All rights reserved. |
| * |
| * BSD license |
| */ |
| #define timersub(x, y, z) \ |
| do \ |
| { \ |
| (z)->tv_sec = (x)->tv_sec - (y)->tv_sec; \ |
| \ |
| (z)->tv_usec = (x)->tv_usec - (y)->tv_usec; \ |
| \ |
| if ((z)->tv_usec < 0) \ |
| { \ |
| (z)->tv_sec--; \ |
| (z)->tv_usec += 1000000; \ |
| } \ |
| } \ |
| while (0) |
| |
| #else |
| #define SOCK_DATA void* |
| #define SOCK_CONST_DATA const void* |
| #endif |
| |
| // On Mach we use a v0 RunLoopSource to make client callbacks. That source is signalled by a |
| // separate SocketManager thread who uses select() to watch the sockets' fds. |
| |
| //#define LOG_CFSOCKET |
| |
| #if !DEPLOYMENT_TARGET_WINDOWS |
| #define INVALID_SOCKET (CFSocketNativeHandle)(-1) |
| #endif |
| |
| enum { |
| kCFSocketLeaveErrors = 64 // candidate for publicization in future |
| }; |
| |
| static uint16_t __CFSocketDefaultNameRegistryPortNumber = 2454; |
| |
| CONST_STRING_DECL(kCFSocketCommandKey, "Command") |
| CONST_STRING_DECL(kCFSocketNameKey, "Name") |
| CONST_STRING_DECL(kCFSocketValueKey, "Value") |
| CONST_STRING_DECL(kCFSocketResultKey, "Result") |
| CONST_STRING_DECL(kCFSocketErrorKey, "Error") |
| CONST_STRING_DECL(kCFSocketRegisterCommand, "Register") |
| CONST_STRING_DECL(kCFSocketRetrieveCommand, "Retrieve") |
| CONST_STRING_DECL(__kCFSocketRegistryRequestRunLoopMode, "CFSocketRegistryRequest") |
| |
| #if DEPLOYMENT_TARGET_WINDOWS |
| static Boolean __CFSocketWinSockInitialized = false; |
| #else |
| #define closesocket(a) close((a)) |
| #define ioctlsocket(a,b,c) ioctl((a),(b),(c)) |
| #endif |
| |
| |
| CF_INLINE int __CFSocketLastError(void) { |
| #if DEPLOYMENT_TARGET_WINDOWS |
| return WSAGetLastError(); |
| #else |
| return thread_errno(); |
| #endif |
| } |
| |
| CF_INLINE CFIndex __CFSocketFdGetSize(CFDataRef fdSet) { |
| #if DEPLOYMENT_TARGET_WINDOWS |
| fd_set* set = (fd_set*)CFDataGetBytePtr(fdSet); |
| return set ? set->fd_count : 0; |
| #else |
| return NBBY * CFDataGetLength(fdSet); |
| #endif |
| } |
| |
| CF_INLINE Boolean __CFSocketFdSet(CFSocketNativeHandle sock, CFMutableDataRef fdSet) { |
| /* returns true if a change occurred, false otherwise */ |
| Boolean retval = false; |
| if (INVALID_SOCKET != sock && 0 <= sock) { |
| #if DEPLOYMENT_TARGET_WINDOWS |
| fd_set* set = (fd_set*)CFDataGetMutableBytePtr(fdSet); |
| if ((set->fd_count * sizeof(SOCKET) + sizeof(u_int)) >= CFDataGetLength(fdSet)) { |
| CFDataIncreaseLength(fdSet, sizeof(SOCKET)); |
| set = (fd_set*)CFDataGetMutableBytePtr(fdSet); |
| } |
| if (!FD_ISSET(sock, set)) { |
| retval = true; |
| FD_SET(sock, set); |
| } |
| #else |
| CFIndex numFds = NBBY * CFDataGetLength(fdSet); |
| fd_mask *fds_bits; |
| if (sock >= numFds) { |
| CFIndex oldSize = numFds / NFDBITS, newSize = (sock + NFDBITS) / NFDBITS, changeInBytes = (newSize - oldSize) * sizeof(fd_mask); |
| CFDataIncreaseLength(fdSet, changeInBytes); |
| fds_bits = (fd_mask *)CFDataGetMutableBytePtr(fdSet); |
| memset(fds_bits + oldSize, 0, changeInBytes); |
| } else { |
| fds_bits = (fd_mask *)CFDataGetMutableBytePtr(fdSet); |
| } |
| if (!FD_ISSET(sock, (fd_set *)fds_bits)) { |
| retval = true; |
| FD_SET(sock, (fd_set *)fds_bits); |
| } |
| #endif |
| } |
| return retval; |
| } |
| |
| |
| #define NEW_SOCKET 0 |
| #if NEW_SOCKET |
| |
| __private_extern__ void __CFSocketInitialize(void) {} |
| |
| #else |
| |
| #define MAX_SOCKADDR_LEN 256 |
| #define MAX_DATA_SIZE 65535 |
| #define MAX_CONNECTION_ORIENTED_DATA_SIZE 32768 |
| |
| /* locks are to be acquired in the following order: |
| (1) __CFAllSocketsLock |
| (2) an individual CFSocket's lock |
| (3) __CFActiveSocketsLock |
| */ |
| static CFSpinLock_t __CFAllSocketsLock = CFSpinLockInit; /* controls __CFAllSockets */ |
| static CFMutableDictionaryRef __CFAllSockets = NULL; |
| static CFSpinLock_t __CFActiveSocketsLock = CFSpinLockInit; /* controls __CFRead/WriteSockets, __CFRead/WriteSocketsFds, __CFSocketManagerThread, and __CFSocketManagerIteration */ |
| static volatile UInt32 __CFSocketManagerIteration = 0; |
| static CFMutableArrayRef __CFWriteSockets = NULL; |
| static CFMutableArrayRef __CFReadSockets = NULL; |
| static CFMutableDataRef __CFWriteSocketsFds = NULL; |
| static CFMutableDataRef __CFReadSocketsFds = NULL; |
| #if DEPLOYMENT_TARGET_WINDOWS |
| // We need to select on exceptFDs on Win32 to hear of connect failures |
| static CFMutableDataRef __CFExceptSocketsFds = NULL; |
| #endif |
| static CFDataRef zeroLengthData = NULL; |
| static Boolean __CFReadSocketsTimeoutInvalid = true; /* rebuild the timeout value before calling select */ |
| |
| static CFSocketNativeHandle __CFWakeupSocketPair[2] = {INVALID_SOCKET, INVALID_SOCKET}; |
| static void *__CFSocketManagerThread = NULL; |
| |
| static CFTypeID __kCFSocketTypeID = _kCFRuntimeNotATypeID; |
| static void __CFSocketDoCallback(CFSocketRef s, CFDataRef data, CFDataRef address, CFSocketNativeHandle sock); |
| |
| struct __CFSocket { |
| CFRuntimeBase _base; |
| struct { |
| unsigned client:8; // flags set by client (reenable, CloseOnInvalidate) |
| unsigned disabled:8; // flags marking disabled callbacks |
| unsigned connected:1; // Are we connected yet? (also true for connectionless sockets) |
| unsigned writableHint:1; // Did the polling the socket show it to be writable? |
| unsigned closeSignaled:1; // Have we seen FD_CLOSE? (only used on Win32) |
| unsigned unused:13; |
| } _f; |
| CFSpinLock_t _lock; |
| CFSpinLock_t _writeLock; |
| CFSocketNativeHandle _socket; /* immutable */ |
| SInt32 _socketType; |
| SInt32 _errorCode; |
| CFDataRef _address; |
| CFDataRef _peerAddress; |
| SInt32 _socketSetCount; |
| CFRunLoopSourceRef _source0; // v0 RLS, messaged from SocketMgr |
| CFMutableArrayRef _runLoops; |
| CFSocketCallBack _callout; /* immutable */ |
| CFSocketContext _context; /* immutable */ |
| CFMutableArrayRef _dataQueue; // queues to pass data from SocketMgr thread |
| CFMutableArrayRef _addressQueue; |
| |
| struct timeval _readBufferTimeout; |
| CFMutableDataRef _readBuffer; |
| CFIndex _bytesToBuffer; /* is length of _readBuffer */ |
| CFIndex _bytesToBufferPos; /* where the next _CFSocketRead starts from */ |
| CFIndex _bytesToBufferReadPos; /* Where the buffer will next be read into (always after _bytesToBufferPos, but less than _bytesToBuffer) */ |
| Boolean _atEOF; |
| int _bufferedReadError; |
| |
| CFMutableDataRef _leftoverBytes; |
| }; |
| |
| /* Bit 6 in the base reserved bits is used for write-signalled state (mutable) */ |
| /* Bit 5 in the base reserved bits is used for read-signalled state (mutable) */ |
| /* Bit 4 in the base reserved bits is used for invalid state (mutable) */ |
| /* Bits 0-3 in the base reserved bits are used for callback types (immutable) */ |
| /* Of this, bits 0-1 are used for the read callback type. */ |
| |
| CF_INLINE Boolean __CFSocketIsWriteSignalled(CFSocketRef s) { |
| return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 6, 6); |
| } |
| |
| CF_INLINE void __CFSocketSetWriteSignalled(CFSocketRef s) { |
| __CFBitfieldSetValue(((CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 6, 6, 1); |
| } |
| |
| CF_INLINE void __CFSocketUnsetWriteSignalled(CFSocketRef s) { |
| __CFBitfieldSetValue(((CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 6, 6, 0); |
| } |
| |
| CF_INLINE Boolean __CFSocketIsReadSignalled(CFSocketRef s) { |
| return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 5, 5); |
| } |
| |
| CF_INLINE void __CFSocketSetReadSignalled(CFSocketRef s) { |
| __CFBitfieldSetValue(((CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 5, 5, 1); |
| } |
| |
| CF_INLINE void __CFSocketUnsetReadSignalled(CFSocketRef s) { |
| __CFBitfieldSetValue(((CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 5, 5, 0); |
| } |
| |
| CF_INLINE Boolean __CFSocketIsValid(CFSocketRef s) { |
| return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 4, 4); |
| } |
| |
| CF_INLINE void __CFSocketSetValid(CFSocketRef s) { |
| __CFBitfieldSetValue(((CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 4, 4, 1); |
| } |
| |
| CF_INLINE void __CFSocketUnsetValid(CFSocketRef s) { |
| __CFBitfieldSetValue(((CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 4, 4, 0); |
| } |
| |
| CF_INLINE uint8_t __CFSocketCallBackTypes(CFSocketRef s) { |
| return (uint8_t)__CFBitfieldGetValue(((const CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 3, 0); |
| } |
| |
| CF_INLINE uint8_t __CFSocketReadCallBackType(CFSocketRef s) { |
| return (uint8_t)__CFBitfieldGetValue(((const CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 1, 0); |
| } |
| |
| CF_INLINE void __CFSocketSetCallBackTypes(CFSocketRef s, uint8_t types) { |
| __CFBitfieldSetValue(((CFRuntimeBase *)s)->_cfinfo[CF_INFO_BITS], 3, 0, types & 0xF); |
| } |
| |
| CF_INLINE void __CFSocketLock(CFSocketRef s) { |
| __CFSpinLock(&(s->_lock)); |
| } |
| |
| CF_INLINE void __CFSocketUnlock(CFSocketRef s) { |
| __CFSpinUnlock(&(s->_lock)); |
| } |
| |
| CF_INLINE Boolean __CFSocketIsConnectionOriented(CFSocketRef s) { |
| return (SOCK_STREAM == s->_socketType || SOCK_SEQPACKET == s->_socketType); |
| } |
| |
| CF_INLINE Boolean __CFSocketIsScheduled(CFSocketRef s) { |
| return (s->_socketSetCount > 0); |
| } |
| |
| CF_INLINE void __CFSocketEstablishAddress(CFSocketRef s) { |
| /* socket should already be locked */ |
| uint8_t name[MAX_SOCKADDR_LEN]; |
| int namelen = sizeof(name); |
| if (__CFSocketIsValid(s) && NULL == s->_address && INVALID_SOCKET != s->_socket && 0 == getsockname(s->_socket, (struct sockaddr *)name, (socklen_t *)&namelen) && NULL != name && 0 < namelen) { |
| s->_address = CFDataCreate(CFGetAllocator(s), name, namelen); |
| } |
| } |
| |
| CF_INLINE void __CFSocketEstablishPeerAddress(CFSocketRef s) { |
| /* socket should already be locked */ |
| uint8_t name[MAX_SOCKADDR_LEN]; |
| int namelen = sizeof(name); |
| if (__CFSocketIsValid(s) && NULL == s->_peerAddress && INVALID_SOCKET != s->_socket && 0 == getpeername(s->_socket, (struct sockaddr *)name, (socklen_t *)&namelen) && NULL != name && 0 < namelen) { |
| s->_peerAddress = CFDataCreate(CFGetAllocator(s), name, namelen); |
| } |
| } |
| |
| static Boolean __CFNativeSocketIsValid(CFSocketNativeHandle sock) { |
| #if DEPLOYMENT_TARGET_WINDOWS |
| SInt32 flags = ioctlsocket (sock, FIONREAD, 0); |
| return (0 == flags); |
| #else |
| SInt32 flags = fcntl(sock, F_GETFL, 0); |
| return !(0 > flags && EBADF == thread_errno()); |
| #endif |
| } |
| |
| CF_INLINE Boolean __CFSocketFdClr(CFSocketNativeHandle sock, CFMutableDataRef fdSet) { |
| /* returns true if a change occurred, false otherwise */ |
| Boolean retval = false; |
| if (INVALID_SOCKET != sock && 0 <= sock) { |
| #if DEPLOYMENT_TARGET_WINDOWS |
| fd_set* set = (fd_set*)CFDataGetMutableBytePtr(fdSet); |
| if (FD_ISSET(sock, set)) { |
| retval = true; |
| FD_CLR(sock, set); |
| } |
| #else |
| CFIndex numFds = NBBY * CFDataGetLength(fdSet); |
| fd_mask *fds_bits; |
| if (sock < numFds) { |
| fds_bits = (fd_mask *)CFDataGetMutableBytePtr(fdSet); |
| if (FD_ISSET(sock, (fd_set *)fds_bits)) { |
| retval = true; |
| FD_CLR(sock, (fd_set *)fds_bits); |
| } |
| } |
| #endif |
| } |
| return retval; |
| } |
| |
| static SInt32 __CFSocketCreateWakeupSocketPair(void) { |
| #if !DEPLOYMENT_TARGET_WINDOWS |
| return socketpair(PF_LOCAL, SOCK_DGRAM, 0, __CFWakeupSocketPair); |
| #else |
| //??? should really use native Win32 facilities |
| UInt32 i; |
| SInt32 error = 0; |
| struct sockaddr_in address[2]; |
| int namelen = sizeof(struct sockaddr_in); |
| for (i = 0; i < 2; i++) { |
| __CFWakeupSocketPair[i] = (CFSocketNativeHandle)socket(PF_INET, SOCK_DGRAM, 0); |
| memset(&(address[i]), 0, sizeof(struct sockaddr_in)); |
| address[i].sin_family = AF_INET; |
| address[i].sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
| if (0 <= error) error = bind(__CFWakeupSocketPair[i], (struct sockaddr *)&(address[i]), sizeof(struct sockaddr_in)); |
| if (0 <= error) error = getsockname(__CFWakeupSocketPair[i], (struct sockaddr *)&(address[i]), &namelen); |
| if (sizeof(struct sockaddr_in) != namelen) error = -1; |
| } |
| if (0 <= error) error = connect(__CFWakeupSocketPair[0], (struct sockaddr *)&(address[1]), sizeof(struct sockaddr_in)); |
| if (0 <= error) error = connect(__CFWakeupSocketPair[1], (struct sockaddr *)&(address[0]), sizeof(struct sockaddr_in)); |
| if (0 > error) { |
| closesocket(__CFWakeupSocketPair[0]); |
| closesocket(__CFWakeupSocketPair[1]); |
| } |
| return error; |
| #endif |
| } |
| |
| |
| // Version 0 RunLoopSources set a mask in an FD set to control what socket activity we hear about. |
| CF_INLINE Boolean __CFSocketSetFDForRead(CFSocketRef s) { |
| __CFReadSocketsTimeoutInvalid = true; |
| return __CFSocketFdSet(s->_socket, __CFReadSocketsFds); |
| } |
| |
| CF_INLINE Boolean __CFSocketClearFDForRead(CFSocketRef s) { |
| __CFReadSocketsTimeoutInvalid = true; |
| return __CFSocketFdClr(s->_socket, __CFReadSocketsFds); |
| } |
| |
| CF_INLINE Boolean __CFSocketSetFDForWrite(CFSocketRef s) { |
| return __CFSocketFdSet(s->_socket, __CFWriteSocketsFds); |
| } |
| |
| CF_INLINE Boolean __CFSocketClearFDForWrite(CFSocketRef s) { |
| return __CFSocketFdClr(s->_socket, __CFWriteSocketsFds); |
| } |
| |
| #if DEPLOYMENT_TARGET_WINDOWS |
| static Boolean WinSockUsed = FALSE; |
| |
| static void __CFSocketInitializeWinSock_Guts(void) { |
| if (!WinSockUsed) { |
| WinSockUsed = TRUE; |
| WORD versionRequested = MAKEWORD(2, 0); |
| WSADATA wsaData; |
| int errorStatus = WSAStartup(versionRequested, &wsaData); |
| if (errorStatus != 0 || LOBYTE(wsaData.wVersion) != LOBYTE(versionRequested) || HIBYTE(wsaData.wVersion) != HIBYTE(versionRequested)) { |
| WSACleanup(); |
| CFLog(0, CFSTR("*** Could not initialize WinSock subsystem!!!")); |
| } |
| } |
| } |
| |
| CF_EXPORT void __CFSocketInitializeWinSock(void) { |
| __CFSpinLock(&__CFActiveSocketsLock); |
| __CFSocketInitializeWinSock_Guts(); |
| __CFSpinUnlock(&__CFActiveSocketsLock); |
| } |
| |
| __private_extern__ void __CFSocketCleanup(void) { |
| __CFSpinLock(&__CFActiveSocketsLock); |
| if (NULL != __CFReadSockets) { |
| CFRelease(__CFWriteSockets); |
| __CFWriteSockets = NULL; |
| CFRelease(__CFReadSockets); |
| __CFReadSockets = NULL; |
| CFRelease(__CFWriteSocketsFds); |
| __CFWriteSocketsFds = NULL; |
| CFRelease(__CFReadSocketsFds); |
| __CFReadSocketsFds = NULL; |
| CFRelease(__CFExceptSocketsFds); |
| __CFExceptSocketsFds = NULL; |
| CFRelease(zeroLengthData); |
| zeroLengthData = NULL; |
| } |
| if (NULL != __CFAllSockets) { |
| CFRelease(__CFAllSockets); |
| __CFAllSockets = NULL; |
| } |
| if (INVALID_SOCKET != __CFWakeupSocketPair[0]) { |
| closesocket(__CFWakeupSocketPair[0]); |
| __CFWakeupSocketPair[0] = INVALID_SOCKET; |
| } |
| if (INVALID_SOCKET != __CFWakeupSocketPair[1]) { |
| closesocket(__CFWakeupSocketPair[1]); |
| __CFWakeupSocketPair[1] = INVALID_SOCKET; |
| } |
| if (WinSockUsed) { |
| WSACleanup(); |
| } |
| __CFSpinUnlock(&__CFActiveSocketsLock); |
| } |
| #endif |
| |
| |
| // CFNetwork needs to call this, especially for Win32 to get WSAStartup |
| static void __CFSocketInitializeSockets(void) { |
| __CFWriteSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL); |
| __CFReadSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL); |
| __CFWriteSocketsFds = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); |
| __CFReadSocketsFds = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); |
| zeroLengthData = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); |
| #if DEPLOYMENT_TARGET_WINDOWS |
| __CFSocketInitializeWinSock_Guts(); |
| // make sure we have space for the count field and the first socket |
| CFDataIncreaseLength(__CFWriteSocketsFds, sizeof(u_int) + sizeof(SOCKET)); |
| CFDataIncreaseLength(__CFReadSocketsFds, sizeof(u_int) + sizeof(SOCKET)); |
| __CFExceptSocketsFds = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); |
| CFDataIncreaseLength(__CFExceptSocketsFds, sizeof(u_int) + sizeof(SOCKET)); |
| #endif |
| if (0 > __CFSocketCreateWakeupSocketPair()) { |
| CFLog(kCFLogLevelWarning, CFSTR("*** Could not create wakeup socket pair for CFSocket!!!")); |
| } else { |
| #if DEPLOYMENT_TARGET_WINDOWS |
| u_long yes = 1; |
| #else |
| UInt32 yes = 1; |
| #endif |
| /* wakeup sockets must be non-blocking */ |
| ioctlsocket(__CFWakeupSocketPair[0], FIONBIO, &yes); |
| ioctlsocket(__CFWakeupSocketPair[1], FIONBIO, &yes); |
| __CFSocketFdSet(__CFWakeupSocketPair[1], __CFReadSocketsFds); |
| } |
| } |
| |
| static CFRunLoopRef __CFSocketCopyRunLoopToWakeUp(CFSocketRef s) { |
| CFRunLoopRef rl = NULL; |
| SInt32 idx, cnt = CFArrayGetCount(s->_runLoops); |
| if (0 < cnt) { |
| rl = (CFRunLoopRef)CFArrayGetValueAtIndex(s->_runLoops, 0); |
| for (idx = 1; NULL != rl && idx < cnt; idx++) { |
| CFRunLoopRef value = (CFRunLoopRef)CFArrayGetValueAtIndex(s->_runLoops, idx); |
| if (value != rl) rl = NULL; |
| } |
| if (NULL == rl) { /* more than one different rl, so we must pick one */ |
| /* ideally, this would be a run loop which isn't also in a |
| * signaled state for this or another source, but that's tricky; |
| * we pick one that is running in an appropriate mode for this |
| * source, and from those if possible one that is waiting; then |
| * we move this run loop to the end of the list to scramble them |
| * a bit, and always search from the front */ |
| Boolean foundIt = false, foundBackup = false; |
| SInt32 foundIdx = 0; |
| for (idx = 0; !foundIt && idx < cnt; idx++) { |
| CFRunLoopRef value = (CFRunLoopRef)CFArrayGetValueAtIndex(s->_runLoops, idx); |
| CFStringRef currentMode = CFRunLoopCopyCurrentMode(value); |
| if (NULL != currentMode) { |
| if (CFRunLoopContainsSource(value, s->_source0, currentMode)) { |
| if (CFRunLoopIsWaiting(value)) { |
| foundIdx = idx; |
| foundIt = true; |
| } else if (!foundBackup) { |
| foundIdx = idx; |
| foundBackup = true; |
| } |
| } |
| CFRelease(currentMode); |
| } |
| } |
| rl = (CFRunLoopRef)CFArrayGetValueAtIndex(s->_runLoops, foundIdx); |
| CFRetain(rl); |
| CFArrayRemoveValueAtIndex(s->_runLoops, foundIdx); |
| CFArrayAppendValue(s->_runLoops, rl); |
| } else { |
| CFRetain(rl); |
| } |
| } |
| return rl; |
| } |
| |
| // If callBackNow, we immediately do client callbacks, else we have to signal a v0 RunLoopSource so the |
| // callbacks can happen in another thread. |
| static void __CFSocketHandleWrite(CFSocketRef s, Boolean callBackNow) { |
| SInt32 errorCode = 0; |
| int errorSize = sizeof(errorCode); |
| CFOptionFlags writeCallBacksAvailable; |
| |
| if (!CFSocketIsValid(s)) return; |
| #if DEPLOYMENT_TARGET_WINDOWS |
| if (0 != (s->_f.client & kCFSocketLeaveErrors) || 0 != getsockopt(s->_socket, SOL_SOCKET, SO_ERROR, (char *)&errorCode, (socklen_t *)&errorSize)) errorCode = 0; // cast for WinSock bad API |
| #else |
| if (0 != (s->_f.client & kCFSocketLeaveErrors) || 0 != getsockopt(s->_socket, SOL_SOCKET, SO_ERROR, (void *)&errorCode, (socklen_t *)&errorSize)) errorCode = 0; // cast for WinSock bad API |
| #endif |
| #if defined(LOG_CFSOCKET) |
| if (errorCode) fprintf(stdout, "error %ld on socket %d\n", errorCode, s->_socket); |
| #endif |
| __CFSocketLock(s); |
| writeCallBacksAvailable = __CFSocketCallBackTypes(s) & (kCFSocketWriteCallBack | kCFSocketConnectCallBack); |
| if ((s->_f.client & kCFSocketConnectCallBack) != 0) writeCallBacksAvailable &= ~kCFSocketConnectCallBack; |
| if (!__CFSocketIsValid(s) || ((s->_f.disabled & writeCallBacksAvailable) == writeCallBacksAvailable)) { |
| __CFSocketUnlock(s); |
| return; |
| } |
| s->_errorCode = errorCode; |
| __CFSocketSetWriteSignalled(s); |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "write signaling source for socket %d\n", s->_socket); |
| #endif |
| if (callBackNow) { |
| __CFSocketDoCallback(s, NULL, NULL, 0); |
| } else { |
| CFRunLoopRef rl; |
| CFRunLoopSourceSignal(s->_source0); |
| rl = __CFSocketCopyRunLoopToWakeUp(s); |
| __CFSocketUnlock(s); |
| if (NULL != rl) { |
| CFRunLoopWakeUp(rl); |
| CFRelease(rl); |
| } |
| } |
| } |
| |
| static void __CFSocketHandleRead(CFSocketRef s, Boolean causedByTimeout) |
| { |
| CFDataRef data = NULL, address = NULL; |
| CFSocketNativeHandle sock = INVALID_SOCKET; |
| if (!CFSocketIsValid(s)) return; |
| if (__CFSocketReadCallBackType(s) == kCFSocketDataCallBack) { |
| uint8_t bufferArray[MAX_CONNECTION_ORIENTED_DATA_SIZE], *buffer; |
| uint8_t name[MAX_SOCKADDR_LEN]; |
| #if !DEPLOYMENT_TARGET_WINDOWS |
| int namelen = sizeof(name); |
| #else |
| int namelen = 0; |
| #endif |
| SInt32 recvlen = 0; |
| if (__CFSocketIsConnectionOriented(s)) { |
| buffer = bufferArray; |
| recvlen = recvfrom(s->_socket, (SOCK_DATA)buffer, MAX_CONNECTION_ORIENTED_DATA_SIZE, 0, (struct sockaddr *)name, (socklen_t *)&namelen); |
| } else { |
| buffer = (uint8_t*)malloc(MAX_DATA_SIZE); |
| if (buffer) recvlen = recvfrom(s->_socket, (SOCK_DATA)buffer, MAX_DATA_SIZE, 0, (struct sockaddr *)name, (socklen_t *)&namelen); |
| } |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "read %ld bytes on socket %d\n", recvlen, s->_socket); |
| #endif |
| if (0 >= recvlen) { |
| //??? should return error if <0 |
| /* zero-length data is the signal for perform to invalidate */ |
| data = (CFDataRef)CFRetain(zeroLengthData); |
| } else { |
| data = CFDataCreate(CFGetAllocator(s), buffer, recvlen); |
| } |
| if (buffer && buffer != bufferArray) free(buffer); |
| __CFSocketLock(s); |
| if (!__CFSocketIsValid(s)) { |
| CFRelease(data); |
| __CFSocketUnlock(s); |
| return; |
| } |
| __CFSocketSetReadSignalled(s); |
| if (NULL != name && 0 < namelen) { |
| //??? possible optimizations: uniquing; storing last value |
| address = CFDataCreate(CFGetAllocator(s), name, namelen); |
| } else if (__CFSocketIsConnectionOriented(s)) { |
| if (NULL == s->_peerAddress) __CFSocketEstablishPeerAddress(s); |
| if (NULL != s->_peerAddress) address = (CFDataRef)CFRetain(s->_peerAddress); |
| } |
| if (NULL == address) { |
| address = (CFDataRef)CFRetain(zeroLengthData); |
| } |
| if (NULL == s->_dataQueue) { |
| s->_dataQueue = CFArrayCreateMutable(CFGetAllocator(s), 0, &kCFTypeArrayCallBacks); |
| } |
| if (NULL == s->_addressQueue) { |
| s->_addressQueue = CFArrayCreateMutable(CFGetAllocator(s), 0, &kCFTypeArrayCallBacks); |
| } |
| CFArrayAppendValue(s->_dataQueue, data); |
| CFRelease(data); |
| CFArrayAppendValue(s->_addressQueue, address); |
| CFRelease(address); |
| if (0 < recvlen |
| && (s->_f.client & kCFSocketDataCallBack) != 0 && (s->_f.disabled & kCFSocketDataCallBack) == 0 |
| && __CFSocketIsScheduled(s) |
| ) { |
| __CFSpinLock(&__CFActiveSocketsLock); |
| /* restore socket to fds */ |
| __CFSocketSetFDForRead(s); |
| __CFSpinUnlock(&__CFActiveSocketsLock); |
| } |
| } else if (__CFSocketReadCallBackType(s) == kCFSocketAcceptCallBack) { |
| uint8_t name[MAX_SOCKADDR_LEN]; |
| int namelen = sizeof(name); |
| sock = (CFSocketNativeHandle)accept(s->_socket, (struct sockaddr *)name, (socklen_t *)&namelen); |
| if (INVALID_SOCKET == sock) { |
| //??? should return error |
| return; |
| } |
| if (NULL != name && 0 < namelen) { |
| address = CFDataCreate(CFGetAllocator(s), name, namelen); |
| } else { |
| address = (CFDataRef)CFRetain(zeroLengthData); |
| } |
| __CFSocketLock(s); |
| if (!__CFSocketIsValid(s)) { |
| closesocket(sock); |
| CFRelease(address); |
| __CFSocketUnlock(s); |
| return; |
| } |
| __CFSocketSetReadSignalled(s); |
| if (NULL == s->_dataQueue) { |
| s->_dataQueue = CFArrayCreateMutable(CFGetAllocator(s), 0, NULL); |
| } |
| if (NULL == s->_addressQueue) { |
| s->_addressQueue = CFArrayCreateMutable(CFGetAllocator(s), 0, &kCFTypeArrayCallBacks); |
| } |
| CFArrayAppendValue(s->_dataQueue, (void *)(uintptr_t)sock); |
| CFArrayAppendValue(s->_addressQueue, address); |
| CFRelease(address); |
| if ((s->_f.client & kCFSocketAcceptCallBack) != 0 && (s->_f.disabled & kCFSocketAcceptCallBack) == 0 |
| && __CFSocketIsScheduled(s) |
| ) { |
| __CFSpinLock(&__CFActiveSocketsLock); |
| /* restore socket to fds */ |
| __CFSocketSetFDForRead(s); |
| __CFSpinUnlock(&__CFActiveSocketsLock); |
| } |
| } else { |
| __CFSocketLock(s); |
| if (!__CFSocketIsValid(s) || (s->_f.disabled & kCFSocketReadCallBack) != 0) { |
| __CFSocketUnlock(s); |
| return; |
| } |
| |
| if (causedByTimeout) { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "TIMEOUT RECEIVED - WILL SIGNAL IMMEDIATELY TO FLUSH (%d buffered)\n", s->_bytesToBufferPos); |
| #endif |
| /* we've got a timeout, but no bytes read. Ignore the timeout. */ |
| if (s->_bytesToBufferPos == 0) { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "TIMEOUT - but no bytes, restoring to active set\n"); |
| fflush(stdout); |
| #endif |
| |
| __CFSpinLock(&__CFActiveSocketsLock); |
| /* restore socket to fds */ |
| __CFSocketSetFDForRead(s); |
| __CFSpinUnlock(&__CFActiveSocketsLock); |
| __CFSocketUnlock(s); |
| return; |
| } |
| } else if (s->_bytesToBuffer != 0 && ! s->_atEOF) { |
| UInt8* base; |
| CFIndex ctRead; |
| CFIndex ctRemaining = s->_bytesToBuffer - s->_bytesToBufferPos; |
| |
| /* if our buffer has room, we go ahead and buffer */ |
| if (ctRemaining > 0) { |
| base = CFDataGetMutableBytePtr(s->_readBuffer); |
| |
| do { |
| ctRead = read(CFSocketGetNative(s), &base[s->_bytesToBufferPos], ctRemaining); |
| } while (ctRead == -1 && errno == EAGAIN); |
| |
| switch (ctRead) { |
| case -1: |
| s->_bufferedReadError = errno; |
| s->_atEOF = true; |
| #if defined(LOG_CFSOCKET) |
| fprintf(stderr, "BUFFERED READ GOT ERROR %d\n", errno); |
| #endif |
| break; |
| |
| case 0: |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "DONE READING (EOF) - GOING TO SIGNAL\n"); |
| #endif |
| s->_atEOF = true; |
| break; |
| |
| default: |
| s->_bytesToBufferPos += ctRead; |
| if (s->_bytesToBuffer != s->_bytesToBufferPos) { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "READ %d - need %d MORE - GOING BACK FOR MORE\n", ctRead, s->_bytesToBuffer - s->_bytesToBufferPos); |
| #endif |
| __CFSpinLock(&__CFActiveSocketsLock); |
| /* restore socket to fds */ |
| __CFSocketSetFDForRead(s); |
| __CFSpinUnlock(&__CFActiveSocketsLock); |
| __CFSocketUnlock(s); |
| return; |
| } else { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "DONE READING (read %d bytes) - GOING TO SIGNAL\n", ctRead); |
| #endif |
| } |
| } |
| } |
| } |
| |
| __CFSocketSetReadSignalled(s); |
| } |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "read signaling source for socket %d\n", s->_socket); |
| #endif |
| CFRunLoopSourceSignal(s->_source0); |
| CFRunLoopRef rl = __CFSocketCopyRunLoopToWakeUp(s); |
| __CFSocketUnlock(s); |
| if (NULL != rl) { |
| CFRunLoopWakeUp(rl); |
| CFRelease(rl); |
| } |
| } |
| |
| static struct timeval* intervalToTimeval(CFTimeInterval timeout, struct timeval* tv) |
| { |
| if (timeout == 0.0) |
| timerclear(tv); |
| else { |
| tv->tv_sec = (0 >= timeout || INT_MAX <= timeout) ? INT_MAX : (int)(float)floor(timeout); |
| tv->tv_usec = (int)((timeout - floor(timeout)) * 1.0E6); |
| } |
| return tv; |
| } |
| |
| /* note that this returns a pointer to the min value, which won't have changed during |
| the dictionary apply, since we've got the active sockets lock held */ |
| static void _calcMinTimeout_locked(const void* val, void* ctxt) |
| { |
| CFSocketRef s = (CFSocketRef) val; |
| struct timeval** minTime = (struct timeval**) ctxt; |
| if (timerisset(&s->_readBufferTimeout) && (*minTime == NULL || timercmp(&s->_readBufferTimeout, *minTime, <))) |
| *minTime = &s->_readBufferTimeout; |
| else if (s->_leftoverBytes) { |
| /* If there's anyone with leftover bytes, they'll need to be awoken immediately */ |
| static struct timeval sKickerTime = { 0, 0 }; |
| *minTime = &sKickerTime; |
| } |
| } |
| |
| void __CFSocketSetSocketReadBufferAttrs(CFSocketRef s, CFTimeInterval timeout, CFIndex length) |
| { |
| struct timeval timeoutVal; |
| |
| intervalToTimeval(timeout, &timeoutVal); |
| |
| /* lock ordering is socket lock, activesocketslock */ |
| /* activesocketslock protects our timeout calculation */ |
| __CFSocketLock(s); |
| __CFSpinLock(&__CFActiveSocketsLock); |
| |
| #if defined(LOG_CFSOCKET) |
| s->didLogSomething = false; |
| #endif |
| |
| if (s->_bytesToBuffer != length) { |
| CFIndex ctBuffer = s->_bytesToBufferPos - s->_bytesToBufferReadPos; |
| |
| if (ctBuffer) { |
| /* As originally envisaged, you were supposed to be sure to drain the buffer before |
| * issuing another request on the socket. In practice, there seem to be times when we want to re-use |
| * the stream (or perhaps, are on our way to closing it out) and this policy doesn't work so well. |
| * So, if someone changes the buffer size while we have bytes already buffered, we put them |
| * aside and use them to satisfy any subsequent reads. |
| */ |
| #if defined(LOG_CFSOCKET) |
| __socketLog("%s(%d): WARNING: shouldn't set read buffer length while data (%d bytes) is still in the read buffer (leftover total %d)", __FUNCTION__, __LINE__, ctBuffer, s->_leftoverBytes? CFDataGetLength(s->_leftoverBytes) : 0); |
| #endif |
| |
| if (s->_leftoverBytes == NULL) |
| s->_leftoverBytes = CFDataCreateMutable(CFGetAllocator(s), 0); |
| |
| /* append the current buffered bytes over. We'll keep draining _leftoverBytes while we have them... */ |
| CFDataAppendBytes(s->_leftoverBytes, CFDataGetBytePtr(s->_readBuffer) + s->_bytesToBufferReadPos, ctBuffer); |
| CFRelease(s->_readBuffer); |
| s->_readBuffer = NULL; |
| |
| s->_bytesToBuffer = 0; |
| s->_bytesToBufferPos = 0; |
| s->_bytesToBufferReadPos = 0; |
| } |
| if (length == 0) { |
| s->_bytesToBuffer = 0; |
| s->_bytesToBufferPos = 0; |
| s->_bytesToBufferReadPos = 0; |
| if (s->_readBuffer) { |
| CFRelease(s->_readBuffer); |
| s->_readBuffer = NULL; |
| } |
| // Zero length buffer, smash the timeout |
| static struct timeval sReset = { 0, 0 }; |
| timeoutVal = sReset; |
| } else { |
| /* if the buffer shrank, we can re-use the old one */ |
| if (length > s->_bytesToBuffer) { |
| if (s->_readBuffer) { |
| CFRelease(s->_readBuffer); |
| s->_readBuffer = NULL; |
| } |
| } |
| |
| s->_bytesToBuffer = length; |
| s->_bytesToBufferPos = 0; |
| s->_bytesToBufferReadPos = 0; |
| if (s->_readBuffer == NULL) { |
| s->_readBuffer = CFDataCreateMutable(kCFAllocatorSystemDefault, length); |
| CFDataSetLength(s->_readBuffer, length); |
| } |
| } |
| } |
| |
| if (timercmp(&s->_readBufferTimeout, &timeoutVal, !=)) { |
| s->_readBufferTimeout = timeoutVal; |
| __CFReadSocketsTimeoutInvalid = true; |
| } |
| |
| __CFSpinUnlock(&__CFActiveSocketsLock); |
| __CFSocketUnlock(s); |
| } |
| |
| void __CFSocketSetReadBufferTimeout(CFSocketRef s, CFTimeInterval timeout) |
| { |
| #if defined(LOG_CFSOCKET) |
| __socketLog("### (%s DEPRECATED) SET READ BUFFER TIMEOUT for %p (bufferSize %d, timeout %d.%d)\n", __FUNCTION__, s, s->_bytesToBuffer, s->_readBufferTimeout.tv_sec, s->_readBufferTimeout.tv_usec); |
| #endif |
| |
| __CFSocketSetSocketReadBufferAttrs(s, timeout, s->_bytesToBuffer); |
| } |
| |
| void __CFSocketSetReadBufferLength(CFSocketRef s, CFIndex length) |
| { |
| #if defined(LOG_CFSOCKET) |
| __socketLog("### (%s DEPRECATED) SET READ BUFFER LENGTH for %p (bufferSize %d, timeout %d.%d)\n", __FUNCTION__, s, s->_bytesToBuffer, s->_readBufferTimeout.tv_sec, s->_readBufferTimeout.tv_usec); |
| #endif |
| |
| CFTimeInterval timeout; |
| |
| if (! timerisset(&s->_readBufferTimeout)) |
| timeout = 0; |
| else |
| timeout = 0.1; |
| |
| __CFSocketSetSocketReadBufferAttrs(s, timeout, length); |
| } |
| |
| CFIndex __CFSocketRead(CFSocketRef s, UInt8* buffer, CFIndex length, int* error) |
| { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "READING BYTES FOR SOCKET %d (%d buffered, out of %d desired, eof = %d, err = %d)\n", s->_socket, s->_bytesToBufferPos, s->_bytesToBuffer, s->_atEOF, s->_bufferedReadError); |
| #endif |
| |
| CFIndex result = -1; |
| |
| __CFSocketLock(s); |
| |
| *error = 0; |
| |
| /* Any leftover buffered bytes? */ |
| if (s->_leftoverBytes) { |
| CFIndex ctBuffer = CFDataGetLength(s->_leftoverBytes); |
| #if defined(DEBUG) |
| fprintf(stderr, "%s(%d): WARNING: Draining %lu leftover bytes first\n\n", __FUNCTION__, __LINE__, ctBuffer); |
| #endif |
| if (ctBuffer > length) |
| ctBuffer = length; |
| memcpy(buffer, CFDataGetBytePtr(s->_leftoverBytes), ctBuffer); |
| if (ctBuffer < CFDataGetLength(s->_leftoverBytes)) |
| CFDataReplaceBytes(s->_leftoverBytes, CFRangeMake(0, ctBuffer), NULL, 0); |
| else { |
| CFRelease(s->_leftoverBytes); |
| s->_leftoverBytes = NULL; |
| } |
| result = ctBuffer; |
| goto unlock; |
| } |
| |
| /* return whatever we've buffered */ |
| if (s->_bytesToBuffer != 0) { |
| CFIndex ctBuffer = s->_bytesToBufferPos - s->_bytesToBufferReadPos; |
| if (ctBuffer > 0) { |
| /* drain our buffer first */ |
| if (ctBuffer > length) |
| ctBuffer = length; |
| memcpy(buffer, CFDataGetBytePtr(s->_readBuffer) + s->_bytesToBufferReadPos, ctBuffer); |
| s->_bytesToBufferReadPos += ctBuffer; |
| if (s->_bytesToBufferReadPos == s->_bytesToBufferPos) { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "DRAINED BUFFER - SHOULD START BUFFERING AGAIN!\n"); |
| #endif |
| s->_bytesToBufferPos = 0; |
| s->_bytesToBufferReadPos = 0; |
| } |
| |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "SLURPED %d BYTES FROM BUFFER %d LEFT TO READ!\n", ctBuffer, length); |
| #endif |
| |
| result = ctBuffer; |
| goto unlock; |
| } |
| } |
| /* nothing buffered, or no buffer selected */ |
| |
| /* Did we get an error on a previous read (or buffered read)? */ |
| if (s->_bufferedReadError != 0) { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "RETURNING ERROR %d\n", s->_bufferedReadError); |
| #endif |
| *error = s->_bufferedReadError; |
| result = -1; |
| goto unlock; |
| } |
| |
| /* nothing buffered, if we've hit eof, don't bother reading any more */ |
| if (s->_atEOF) { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "RETURNING EOF\n"); |
| #endif |
| result = 0; |
| goto unlock; |
| } |
| |
| /* normal read */ |
| result = read(CFSocketGetNative(s), buffer, length); |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "READ %d bytes", result); |
| #endif |
| |
| if (result == 0) { |
| /* note that we hit EOF */ |
| s->_atEOF = true; |
| } else if (result < 0) { |
| *error = errno; |
| |
| /* if it wasn't EAGAIN, record it (although we shouldn't get called again) */ |
| if (*error != EAGAIN) { |
| s->_bufferedReadError = *error; |
| } |
| } |
| |
| unlock: |
| __CFSocketUnlock(s); |
| |
| return result; |
| } |
| |
| Boolean __CFSocketGetBytesAvailable(CFSocketRef s, CFIndex* ctBytesAvailable) |
| { |
| CFIndex ctBuffer = s->_bytesToBufferPos - s->_bytesToBufferReadPos; |
| if (ctBuffer != 0) { |
| *ctBytesAvailable = ctBuffer; |
| return true; |
| } else { |
| int result; |
| #if DEPLOYMENT_TARGET_MACOSX |
| int bytesAvailable, intLen = sizeof(bytesAvailable); |
| result = getsockopt(CFSocketGetNative(s), SOL_SOCKET, SO_NREAD, &bytesAvailable, (socklen_t *)&intLen); |
| #elif DEPLOYMENT_TARGET_WINDOWS |
| unsigned long bytesAvailable; |
| result = ioctlsocket(CFSocketGetNative(s), FIONREAD, &bytesAvailable); |
| #else |
| CFIndex bytesAvailable = 0; |
| result = -1; |
| #endif |
| if (result < 0) |
| return false; |
| *ctBytesAvailable = (CFIndex) bytesAvailable; |
| return true; |
| } |
| } |
| |
| #if defined(LOG_CFSOCKET) |
| static void __CFSocketWriteSocketList(CFArrayRef sockets, CFDataRef fdSet, Boolean onlyIfSet) { |
| fd_set *tempfds = (fd_set *)CFDataGetBytePtr(fdSet); |
| SInt32 idx, cnt; |
| for (idx = 0, cnt = CFArrayGetCount(sockets); idx < cnt; idx++) { |
| CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(sockets, idx); |
| if (FD_ISSET(s->_socket, tempfds)) { |
| fprintf(stdout, "%d ", s->_socket); |
| } else if (!onlyIfSet) { |
| fprintf(stdout, "(%d) ", s->_socket); |
| } |
| } |
| } |
| #endif |
| |
| #ifdef __GNUC__ |
| __attribute__ ((noreturn)) // mostly interesting for shutting up a warning |
| #endif /* __GNUC__ */ |
| static void __CFSocketManager(void * arg) |
| { |
| #if defined(__OBJC__) |
| if (objc_collecting_enabled()) auto_zone_register_thread(auto_zone()); |
| #endif |
| SInt32 nrfds, maxnrfds, fdentries = 1; |
| SInt32 rfds, wfds; |
| #if !DEPLOYMENT_TARGET_WINDOWS |
| fd_set *exceptfds = NULL; |
| fd_set *writefds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(fd_mask), 0); |
| fd_set *readfds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(fd_mask), 0); |
| #else |
| fd_set *exceptfds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); |
| fd_set *writefds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); |
| fd_set *readfds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); |
| #endif |
| fd_set *tempfds; |
| SInt32 idx, cnt; |
| uint8_t buffer[256]; |
| CFMutableArrayRef selectedWriteSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); |
| CFMutableArrayRef selectedReadSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); |
| CFIndex selectedWriteSocketsIndex = 0, selectedReadSocketsIndex = 0; |
| |
| struct timeval tv; |
| struct timeval* pTimeout = NULL; |
| struct timeval timeBeforeSelect; |
| |
| for (;;) { |
| __CFSpinLock(&__CFActiveSocketsLock); |
| __CFSocketManagerIteration++; |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "socket manager iteration %lu looking at read sockets ", __CFSocketManagerIteration); |
| __CFSocketWriteSocketList(__CFReadSockets, __CFReadSocketsFds, FALSE); |
| if (0 < CFArrayGetCount(__CFWriteSockets)) { |
| fprintf(stdout, " and write sockets "); |
| __CFSocketWriteSocketList(__CFWriteSockets, __CFWriteSocketsFds, FALSE); |
| #if DEPLOYMENT_TARGET_WINDOWS |
| fprintf(stdout, " and except sockets "); |
| __CFSocketWriteSocketList(__CFWriteSockets, __CFExceptSocketsFds, TRUE); |
| #endif |
| } |
| fprintf(stdout, "\n"); |
| #endif |
| rfds = __CFSocketFdGetSize(__CFReadSocketsFds); |
| wfds = __CFSocketFdGetSize(__CFWriteSocketsFds); |
| maxnrfds = __CFMax(rfds, wfds); |
| #if !DEPLOYMENT_TARGET_WINDOWS |
| if (maxnrfds > fdentries * (int)NFDBITS) { |
| fdentries = (maxnrfds + NFDBITS - 1) / NFDBITS; |
| writefds = (fd_set *)CFAllocatorReallocate(kCFAllocatorSystemDefault, writefds, fdentries * sizeof(fd_mask), 0); |
| readfds = (fd_set *)CFAllocatorReallocate(kCFAllocatorSystemDefault, readfds, fdentries * sizeof(fd_mask), 0); |
| } |
| memset(writefds, 0, fdentries * sizeof(fd_mask)); |
| memset(readfds, 0, fdentries * sizeof(fd_mask)); |
| #else |
| if (maxnrfds > fdentries) { |
| fdentries = maxnrfds; |
| exceptfds = (fd_set *)CFAllocatorReallocate(kCFAllocatorSystemDefault, exceptfds, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); |
| writefds = (fd_set *)CFAllocatorReallocate(kCFAllocatorSystemDefault, writefds, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); |
| readfds = (fd_set *)CFAllocatorReallocate(kCFAllocatorSystemDefault, readfds, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); |
| } |
| memset(exceptfds, 0, fdentries * sizeof(SOCKET) + sizeof(u_int)); |
| memset(writefds, 0, fdentries * sizeof(SOCKET) + sizeof(u_int)); |
| memset(readfds, 0, fdentries * sizeof(SOCKET) + sizeof(u_int)); |
| CFDataGetBytes(__CFExceptSocketsFds, CFRangeMake(0, __CFSocketFdGetSize(__CFExceptSocketsFds) * sizeof(SOCKET) + sizeof(u_int)), (UInt8 *)exceptfds); |
| #endif |
| CFDataGetBytes(__CFWriteSocketsFds, CFRangeMake(0, CFDataGetLength(__CFWriteSocketsFds)), (UInt8 *)writefds); |
| CFDataGetBytes(__CFReadSocketsFds, CFRangeMake(0, CFDataGetLength(__CFReadSocketsFds)), (UInt8 *)readfds); |
| |
| if (__CFReadSocketsTimeoutInvalid) { |
| struct timeval* minTimeout = NULL; |
| __CFReadSocketsTimeoutInvalid = false; |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "Figuring out which sockets have timeouts...\n"); |
| #endif |
| CFArrayApplyFunction(__CFReadSockets, CFRangeMake(0, CFArrayGetCount(__CFReadSockets)), _calcMinTimeout_locked, (void*) &minTimeout); |
| |
| if (minTimeout == NULL) { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "No one wants a timeout!\n"); |
| #endif |
| pTimeout = NULL; |
| } else { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "timeout will be %d, %d!\n", minTimeout->tv_sec, minTimeout->tv_usec); |
| #endif |
| tv = *minTimeout; |
| pTimeout = &tv; |
| } |
| } |
| |
| if (pTimeout) { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "select will have a %d, %d timeout\n", pTimeout->tv_sec, pTimeout->tv_usec); |
| #endif |
| gettimeofday(&timeBeforeSelect, NULL); |
| } |
| |
| __CFSpinUnlock(&__CFActiveSocketsLock); |
| |
| nrfds = select(maxnrfds, readfds, writefds, exceptfds, pTimeout); |
| |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "socket manager woke from select, ret=%ld\n", nrfds); |
| #endif |
| |
| /* |
| * select returned a timeout |
| */ |
| if (0 == nrfds) { |
| struct timeval timeAfterSelect; |
| struct timeval deltaTime; |
| gettimeofday(&timeAfterSelect, NULL); |
| /* timeBeforeSelect becomes the delta */ |
| timersub(&timeAfterSelect, &timeBeforeSelect, &deltaTime); |
| |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "Socket manager received timeout - kicking off expired reads (expired delta %d, %d)\n", deltaTime.tv_sec, deltaTime.tv_usec); |
| #endif |
| |
| __CFSpinLock(&__CFActiveSocketsLock); |
| |
| tempfds = NULL; |
| cnt = CFArrayGetCount(__CFReadSockets); |
| for (idx = 0; idx < cnt; idx++) { |
| CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(__CFReadSockets, idx); |
| if (timerisset(&s->_readBufferTimeout) || s->_leftoverBytes) { |
| CFSocketNativeHandle sock = s->_socket; |
| // We might have an new element in __CFReadSockets that we weren't listening to, |
| // in which case we must be sure not to test a bit in the fdset that is |
| // outside our mask size. |
| Boolean sockInBounds = (0 <= sock && sock < maxnrfds); |
| /* if this sockets timeout is less than or equal elapsed time, then signal it */ |
| if (INVALID_SOCKET != sock && sockInBounds) { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "Expiring socket %d (delta %d, %d)\n", sock, s->_readBufferTimeout.tv_sec, s->_readBufferTimeout.tv_usec); |
| #endif |
| CFArraySetValueAtIndex(selectedReadSockets, selectedReadSocketsIndex, s); |
| selectedReadSocketsIndex++; |
| /* socket is removed from fds here, will be restored in read handling or in perform function */ |
| if (!tempfds) tempfds = (fd_set *)CFDataGetMutableBytePtr(__CFReadSocketsFds); |
| FD_CLR(sock, tempfds); |
| } |
| } |
| } |
| |
| __CFSpinUnlock(&__CFActiveSocketsLock); |
| |
| /* and below, we dispatch through the normal read dispatch mechanism */ |
| } |
| |
| if (0 > nrfds) { |
| SInt32 selectError = __CFSocketLastError(); |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "socket manager received error %ld from select\n", selectError); |
| #endif |
| if (EBADF == selectError) { |
| CFMutableArrayRef invalidSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); |
| __CFSpinLock(&__CFActiveSocketsLock); |
| cnt = CFArrayGetCount(__CFWriteSockets); |
| for (idx = 0; idx < cnt; idx++) { |
| CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(__CFWriteSockets, idx); |
| if (!__CFNativeSocketIsValid(s->_socket)) { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "socket manager found write socket %d invalid\n", s->_socket); |
| #endif |
| CFArrayAppendValue(invalidSockets, s); |
| } |
| } |
| cnt = CFArrayGetCount(__CFReadSockets); |
| for (idx = 0; idx < cnt; idx++) { |
| CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(__CFReadSockets, idx); |
| if (!__CFNativeSocketIsValid(s->_socket)) { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "socket manager found read socket %d invalid\n", s->_socket); |
| #endif |
| CFArrayAppendValue(invalidSockets, s); |
| } |
| } |
| __CFSpinUnlock(&__CFActiveSocketsLock); |
| |
| cnt = CFArrayGetCount(invalidSockets); |
| for (idx = 0; idx < cnt; idx++) { |
| CFSocketInvalidate(((CFSocketRef)CFArrayGetValueAtIndex(invalidSockets, idx))); |
| } |
| CFRelease(invalidSockets); |
| } |
| continue; |
| } |
| if (FD_ISSET(__CFWakeupSocketPair[1], readfds)) { |
| recv(__CFWakeupSocketPair[1], (SOCK_DATA)buffer, sizeof(buffer), 0); |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "socket manager received %c on wakeup socket\n", buffer[0]); |
| #endif |
| } |
| __CFSpinLock(&__CFActiveSocketsLock); |
| tempfds = NULL; |
| cnt = CFArrayGetCount(__CFWriteSockets); |
| for (idx = 0; idx < cnt; idx++) { |
| CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(__CFWriteSockets, idx); |
| CFSocketNativeHandle sock = s->_socket; |
| // We might have an new element in __CFWriteSockets that we weren't listening to, |
| // in which case we must be sure not to test a bit in the fdset that is |
| // outside our mask size. |
| #if !DEPLOYMENT_TARGET_WINDOWS |
| Boolean sockInBounds = (0 <= sock && sock < maxnrfds); |
| #else |
| Boolean sockInBounds = true; |
| #endif |
| if (INVALID_SOCKET != sock && sockInBounds) { |
| if (FD_ISSET(sock, writefds)) { |
| CFArraySetValueAtIndex(selectedWriteSockets, selectedWriteSocketsIndex, s); |
| selectedWriteSocketsIndex++; |
| /* socket is removed from fds here, restored by CFSocketReschedule */ |
| if (!tempfds) tempfds = (fd_set *)CFDataGetMutableBytePtr(__CFWriteSocketsFds); |
| FD_CLR(sock, tempfds); |
| #if DEPLOYMENT_TARGET_WINDOWS |
| fd_set *exfds = (fd_set *)CFDataGetMutableBytePtr(__CFExceptSocketsFds); |
| FD_CLR(sock, exfds); |
| #endif |
| |
| } |
| #if DEPLOYMENT_TARGET_WINDOWS |
| else if (FD_ISSET(sock, exceptfds)) { |
| // On Win32 connect errors come in on exceptFDs. We treat these as if |
| // they had come on writeFDs, since the rest of our Unix-based code |
| // expects that. |
| CFArrayAppendValue(selectedWriteSockets, s); |
| fd_set *exfds = (fd_set *)CFDataGetMutableBytePtr(__CFExceptSocketsFds); |
| FD_CLR(sock, exfds); |
| } |
| #endif |
| } |
| } |
| tempfds = NULL; |
| cnt = CFArrayGetCount(__CFReadSockets); |
| for (idx = 0; idx < cnt; idx++) { |
| CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(__CFReadSockets, idx); |
| CFSocketNativeHandle sock = s->_socket; |
| #if !DEPLOYMENT_TARGET_WINDOWS |
| // We might have an new element in __CFReadSockets that we weren't listening to, |
| // in which case we must be sure not to test a bit in the fdset that is |
| // outside our mask size. |
| Boolean sockInBounds = (0 <= sock && sock < maxnrfds); |
| #else |
| // fdset's are arrays, so we don't have that issue above |
| Boolean sockInBounds = true; |
| #endif |
| if (INVALID_SOCKET != sock && sockInBounds && FD_ISSET(sock, readfds)) { |
| CFArraySetValueAtIndex(selectedReadSockets, selectedReadSocketsIndex, s); |
| selectedReadSocketsIndex++; |
| /* socket is removed from fds here, will be restored in read handling or in perform function */ |
| if (!tempfds) tempfds = (fd_set *)CFDataGetMutableBytePtr(__CFReadSocketsFds); |
| FD_CLR(sock, tempfds); |
| } |
| } |
| __CFSpinUnlock(&__CFActiveSocketsLock); |
| |
| for (idx = 0; idx < selectedWriteSocketsIndex; idx++) { |
| CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(selectedWriteSockets, idx); |
| if (kCFNull == (CFNullRef)s) continue; |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "socket manager signaling socket %d for write\n", s->_socket); |
| #endif |
| __CFSocketHandleWrite(s, FALSE); |
| CFArraySetValueAtIndex(selectedWriteSockets, idx, kCFNull); |
| } |
| selectedWriteSocketsIndex = 0; |
| |
| for (idx = 0; idx < selectedReadSocketsIndex; idx++) { |
| CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(selectedReadSockets, idx); |
| if (kCFNull == (CFNullRef)s) continue; |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "socket manager signaling socket %d for read\n", s->_socket); |
| #endif |
| __CFSocketHandleRead(s, nrfds == 0); |
| CFArraySetValueAtIndex(selectedReadSockets, idx, kCFNull); |
| } |
| selectedReadSocketsIndex = 0; |
| } |
| #if defined(__OBJC__) |
| if (objc_collecting_enabled()) auto_zone_unregister_thread(auto_zone()); |
| #endif |
| } |
| |
| static CFStringRef __CFSocketCopyDescription(CFTypeRef cf) { |
| CFSocketRef s = (CFSocketRef)cf; |
| CFMutableStringRef result; |
| CFStringRef contextDesc = NULL; |
| void *contextInfo = NULL; |
| CFStringRef (*contextCopyDescription)(const void *info) = NULL; |
| result = CFStringCreateMutable(CFGetAllocator(s), 0); |
| __CFSocketLock(s); |
| void *addr = (void*)s->_callout; |
| #if DEPLOYMENT_TARGET_WINDOWS |
| // FIXME: Get name using win32 analog of dladdr? |
| const char* name = "???"; |
| #else |
| Dl_info info; |
| const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???"; |
| #endif |
| CFStringAppendFormat(result, NULL, CFSTR("<CFSocket %p [%p]>{valid = %s, type = %d, socket = %d, socket set count = %ld,\n callback types = 0x%x, callout = %s (%p), source = %p,\n run loops = %@,\n context = "), cf, CFGetAllocator(s), (__CFSocketIsValid(s) ? "Yes" : "No"), s->_socketType, s->_socket, s->_socketSetCount, __CFSocketCallBackTypes(s), name, addr, s->_source0, s->_runLoops); |
| contextInfo = s->_context.info; |
| contextCopyDescription = s->_context.copyDescription; |
| __CFSocketUnlock(s); |
| if (NULL != contextInfo && NULL != contextCopyDescription) { |
| contextDesc = (CFStringRef)contextCopyDescription(contextInfo); |
| } |
| if (NULL == contextDesc) { |
| contextDesc = CFStringCreateWithFormat(CFGetAllocator(s), NULL, CFSTR("<CFSocket context %p>"), contextInfo); |
| } |
| CFStringAppend(result, contextDesc); |
| CFStringAppend(result, CFSTR("}")); |
| CFRelease(contextDesc); |
| return result; |
| } |
| |
| static void __CFSocketDeallocate(CFTypeRef cf) { |
| /* Since CFSockets are cached, we can only get here sometime after being invalidated */ |
| CFSocketRef s = (CFSocketRef)cf; |
| if (NULL != s->_address) { |
| CFRelease(s->_address); |
| s->_address = NULL; |
| } |
| if (NULL != s->_readBuffer) { |
| CFRelease(s->_readBuffer); |
| s->_readBuffer = NULL; |
| } |
| if (NULL != s->_leftoverBytes) { |
| CFRelease(s->_leftoverBytes); |
| s->_leftoverBytes = NULL; |
| } |
| timerclear(&s->_readBufferTimeout); |
| s->_bytesToBuffer = 0; |
| s->_bytesToBufferPos = 0; |
| s->_bytesToBufferReadPos = 0; |
| s->_atEOF = true; |
| s->_bufferedReadError = 0; |
| } |
| |
| static const CFRuntimeClass __CFSocketClass = { |
| 0, |
| "CFSocket", |
| NULL, // init |
| NULL, // copy |
| __CFSocketDeallocate, |
| NULL, // equal |
| NULL, // hash |
| NULL, // |
| __CFSocketCopyDescription |
| }; |
| |
| __private_extern__ void __CFSocketInitialize(void) { |
| __kCFSocketTypeID = _CFRuntimeRegisterClass(&__CFSocketClass); |
| } |
| |
| CFTypeID CFSocketGetTypeID(void) { |
| return __kCFSocketTypeID; |
| } |
| static CFSocketRef _CFSocketCreateWithNative(CFAllocatorRef allocator, CFSocketNativeHandle sock, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context, Boolean useExistingInstance) { |
| CHECK_FOR_FORK(); |
| CFSocketRef memory; |
| int typeSize = sizeof(memory->_socketType); |
| __CFSpinLock(&__CFActiveSocketsLock); |
| if (NULL == __CFReadSockets) __CFSocketInitializeSockets(); |
| __CFSpinUnlock(&__CFActiveSocketsLock); |
| __CFSpinLock(&__CFAllSocketsLock); |
| if (NULL == __CFAllSockets) { |
| __CFAllSockets = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); |
| } |
| if (INVALID_SOCKET != sock && CFDictionaryGetValueIfPresent(__CFAllSockets, (void *)(uintptr_t)sock, (const void **)&memory)) { |
| if (useExistingInstance) { |
| __CFSpinUnlock(&__CFAllSocketsLock); |
| CFRetain(memory); |
| return memory; |
| } else { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "useExistingInstance is FALSE, removing existing instance %d from __CFAllSockets\n", (int)memory); |
| #endif |
| __CFSpinUnlock(&__CFAllSocketsLock); |
| CFSocketInvalidate(memory); |
| __CFSpinLock(&__CFAllSocketsLock); |
| } |
| } |
| memory = (CFSocketRef)_CFRuntimeCreateInstance(allocator, __kCFSocketTypeID, sizeof(struct __CFSocket) - sizeof(CFRuntimeBase), NULL); |
| if (NULL == memory) { |
| __CFSpinUnlock(&__CFAllSocketsLock); |
| return NULL; |
| } |
| __CFSocketSetCallBackTypes(memory, callBackTypes); |
| if (INVALID_SOCKET != sock) __CFSocketSetValid(memory); |
| __CFSocketUnsetWriteSignalled(memory); |
| __CFSocketUnsetReadSignalled(memory); |
| memory->_f.client = ((callBackTypes & (~kCFSocketConnectCallBack)) & (~kCFSocketWriteCallBack)) | kCFSocketCloseOnInvalidate; |
| memory->_f.disabled = 0; |
| memory->_f.connected = FALSE; |
| memory->_f.writableHint = FALSE; |
| memory->_f.closeSignaled = FALSE; |
| CF_SPINLOCK_INIT_FOR_STRUCTS(memory->_lock); |
| CF_SPINLOCK_INIT_FOR_STRUCTS(memory->_writeLock); |
| memory->_socket = sock; |
| if (INVALID_SOCKET == sock || 0 != getsockopt(sock, SOL_SOCKET, SO_TYPE, (SOCK_DATA)&(memory->_socketType), (socklen_t *)&typeSize)) memory->_socketType = 0; // cast for WinSock bad API |
| memory->_errorCode = 0; |
| memory->_address = NULL; |
| memory->_peerAddress = NULL; |
| memory->_socketSetCount = 0; |
| memory->_source0 = NULL; |
| if (INVALID_SOCKET != sock) { |
| memory->_runLoops = CFArrayCreateMutable(allocator, 0, NULL); |
| } else { |
| memory->_runLoops = NULL; |
| } |
| memory->_callout = callout; |
| memory->_dataQueue = NULL; |
| memory->_addressQueue = NULL; |
| memory->_context.info = 0; |
| memory->_context.retain = 0; |
| memory->_context.release = 0; |
| memory->_context.copyDescription = 0; |
| timerclear(&memory->_readBufferTimeout); |
| memory->_readBuffer = NULL; |
| memory->_bytesToBuffer = 0; |
| memory->_bytesToBufferPos = 0; |
| memory->_bytesToBufferReadPos = 0; |
| memory->_atEOF = false; |
| memory->_bufferedReadError = 0; |
| memory->_leftoverBytes = NULL; |
| |
| if (INVALID_SOCKET != sock) CFDictionaryAddValue(__CFAllSockets, (void *)(uintptr_t)sock, memory); |
| __CFSpinUnlock(&__CFAllSocketsLock); |
| if (NULL != context) { |
| void *contextInfo = context->retain ? (void *)context->retain(context->info) : context->info; |
| __CFSocketLock(memory); |
| memory->_context.retain = context->retain; |
| memory->_context.release = context->release; |
| memory->_context.copyDescription = context->copyDescription; |
| memory->_context.info = contextInfo; |
| __CFSocketUnlock(memory); |
| } |
| return memory; |
| } |
| |
| CFSocketRef CFSocketCreateWithNative(CFAllocatorRef allocator, CFSocketNativeHandle sock, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context) { |
| return _CFSocketCreateWithNative(allocator, sock, callBackTypes, callout, context, TRUE); |
| } |
| |
| void CFSocketInvalidate(CFSocketRef s) { |
| CHECK_FOR_FORK(); |
| UInt32 previousSocketManagerIteration; |
| __CFGenericValidateType(s, __kCFSocketTypeID); |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "invalidating socket %d with flags 0x%x disabled 0x%x connected 0x%x\n", s->_socket, s->_f.client, s->_f.disabled, s->_f.connected); |
| #endif |
| CFRetain(s); |
| __CFSpinLock(&__CFAllSocketsLock); |
| __CFSocketLock(s); |
| if (__CFSocketIsValid(s)) { |
| SInt32 idx; |
| CFRunLoopSourceRef source0; |
| void *contextInfo = NULL; |
| void (*contextRelease)(const void *info) = NULL; |
| __CFSocketUnsetValid(s); |
| __CFSocketUnsetWriteSignalled(s); |
| __CFSocketUnsetReadSignalled(s); |
| __CFSpinLock(&__CFActiveSocketsLock); |
| idx = CFArrayGetFirstIndexOfValue(__CFWriteSockets, CFRangeMake(0, CFArrayGetCount(__CFWriteSockets)), s); |
| if (0 <= idx) { |
| CFArrayRemoveValueAtIndex(__CFWriteSockets, idx); |
| __CFSocketClearFDForWrite(s); |
| #if DENT_TARGET_WIN32 |
| __CFSocketFdClr(s->_socket, __CFExceptSocketsFds); |
| #endif |
| } |
| // No need to clear FD's for V1 sources, since we'll just throw the whole event away |
| idx = CFArrayGetFirstIndexOfValue(__CFReadSockets, CFRangeMake(0, CFArrayGetCount(__CFReadSockets)), s); |
| if (0 <= idx) { |
| CFArrayRemoveValueAtIndex(__CFReadSockets, idx); |
| __CFSocketClearFDForRead(s); |
| } |
| previousSocketManagerIteration = __CFSocketManagerIteration; |
| __CFSpinUnlock(&__CFActiveSocketsLock); |
| CFDictionaryRemoveValue(__CFAllSockets, (void *)(uintptr_t)(s->_socket)); |
| if ((s->_f.client & kCFSocketCloseOnInvalidate) != 0) closesocket(s->_socket); |
| s->_socket = INVALID_SOCKET; |
| if (NULL != s->_peerAddress) { |
| CFRelease(s->_peerAddress); |
| s->_peerAddress = NULL; |
| } |
| if (NULL != s->_dataQueue) { |
| CFRelease(s->_dataQueue); |
| s->_dataQueue = NULL; |
| } |
| if (NULL != s->_addressQueue) { |
| CFRelease(s->_addressQueue); |
| s->_addressQueue = NULL; |
| } |
| s->_socketSetCount = 0; |
| for (idx = CFArrayGetCount(s->_runLoops); idx--;) { |
| CFRunLoopWakeUp((CFRunLoopRef)CFArrayGetValueAtIndex(s->_runLoops, idx)); |
| } |
| CFRelease(s->_runLoops); |
| s->_runLoops = NULL; |
| source0 = s->_source0; |
| s->_source0 = NULL; |
| contextInfo = s->_context.info; |
| contextRelease = s->_context.release; |
| s->_context.info = 0; |
| s->_context.retain = 0; |
| s->_context.release = 0; |
| s->_context.copyDescription = 0; |
| __CFSocketUnlock(s); |
| if (NULL != contextRelease) { |
| contextRelease(contextInfo); |
| } |
| if (NULL != source0) { |
| CFRunLoopSourceInvalidate(source0); |
| CFRelease(source0); |
| } |
| } else { |
| __CFSocketUnlock(s); |
| } |
| __CFSpinUnlock(&__CFAllSocketsLock); |
| CFRelease(s); |
| } |
| |
| Boolean CFSocketIsValid(CFSocketRef s) { |
| CHECK_FOR_FORK(); |
| __CFGenericValidateType(s, __kCFSocketTypeID); |
| return __CFSocketIsValid(s); |
| } |
| |
| CFSocketNativeHandle CFSocketGetNative(CFSocketRef s) { |
| CHECK_FOR_FORK(); |
| __CFGenericValidateType(s, __kCFSocketTypeID); |
| return s->_socket; |
| } |
| |
| CFDataRef CFSocketCopyAddress(CFSocketRef s) { |
| CHECK_FOR_FORK(); |
| CFDataRef result = NULL; |
| __CFGenericValidateType(s, __kCFSocketTypeID); |
| __CFSocketLock(s); |
| __CFSocketEstablishAddress(s); |
| if (NULL != s->_address) { |
| result = (CFDataRef)CFRetain(s->_address); |
| } |
| __CFSocketUnlock(s); |
| return result; |
| } |
| |
| CFDataRef CFSocketCopyPeerAddress(CFSocketRef s) { |
| CHECK_FOR_FORK(); |
| CFDataRef result = NULL; |
| __CFGenericValidateType(s, __kCFSocketTypeID); |
| __CFSocketLock(s); |
| __CFSocketEstablishPeerAddress(s); |
| if (NULL != s->_peerAddress) { |
| result = (CFDataRef)CFRetain(s->_peerAddress); |
| } |
| __CFSocketUnlock(s); |
| return result; |
| } |
| |
| void CFSocketGetContext(CFSocketRef s, CFSocketContext *context) { |
| CHECK_FOR_FORK(); |
| __CFGenericValidateType(s, __kCFSocketTypeID); |
| CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); |
| *context = s->_context; |
| } |
| |
| CFOptionFlags CFSocketGetSocketFlags(CFSocketRef s) { |
| CHECK_FOR_FORK(); |
| __CFGenericValidateType(s, __kCFSocketTypeID); |
| return s->_f.client; |
| } |
| |
| void CFSocketSetSocketFlags(CFSocketRef s, CFOptionFlags flags) { |
| CHECK_FOR_FORK(); |
| __CFGenericValidateType(s, __kCFSocketTypeID); |
| __CFSocketLock(s); |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "setting flags for socket %d, from 0x%x to 0x%lx\n", s->_socket, s->_f.client, flags); |
| #endif |
| s->_f.client = flags; |
| __CFSocketUnlock(s); |
| } |
| |
| void CFSocketDisableCallBacks(CFSocketRef s, CFOptionFlags callBackTypes) { |
| CHECK_FOR_FORK(); |
| Boolean wakeup = false; |
| uint8_t readCallBackType; |
| __CFGenericValidateType(s, __kCFSocketTypeID); |
| __CFSocketLock(s); |
| if (__CFSocketIsValid(s) && __CFSocketIsScheduled(s)) { |
| callBackTypes &= __CFSocketCallBackTypes(s); |
| readCallBackType = __CFSocketReadCallBackType(s); |
| s->_f.disabled |= callBackTypes; |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "unscheduling socket %d with flags 0x%x disabled 0x%x connected 0x%x for types 0x%lx\n", s->_socket, s->_f.client, s->_f.disabled, s->_f.connected, callBackTypes); |
| #endif |
| __CFSpinLock(&__CFActiveSocketsLock); |
| if ((readCallBackType == kCFSocketAcceptCallBack) || !__CFSocketIsConnectionOriented(s)) s->_f.connected = TRUE; |
| if (((callBackTypes & kCFSocketWriteCallBack) != 0) || (((callBackTypes & kCFSocketConnectCallBack) != 0) && !s->_f.connected)) { |
| if (__CFSocketClearFDForWrite(s)) { |
| // do not wake up the socket manager thread if all relevant write callbacks are disabled |
| CFOptionFlags writeCallBacksAvailable = __CFSocketCallBackTypes(s) & (kCFSocketWriteCallBack | kCFSocketConnectCallBack); |
| if (s->_f.connected) writeCallBacksAvailable &= ~kCFSocketConnectCallBack; |
| if ((s->_f.disabled & writeCallBacksAvailable) != writeCallBacksAvailable) wakeup = true; |
| } |
| } |
| if (readCallBackType != kCFSocketNoCallBack && (callBackTypes & readCallBackType) != 0) { |
| if (__CFSocketClearFDForRead(s)) { |
| // do not wake up the socket manager thread if callback type is read |
| if (readCallBackType != kCFSocketReadCallBack) wakeup = true; |
| } |
| } |
| __CFSpinUnlock(&__CFActiveSocketsLock); |
| } |
| __CFSocketUnlock(s); |
| if (wakeup && __CFSocketManagerThread) { |
| uint8_t c = 'u'; |
| send(__CFWakeupSocketPair[0], (SOCK_CONST_DATA)&c, sizeof(c), 0); |
| } |
| } |
| |
| // "force" means to clear the disabled bits set by DisableCallBacks and always reenable. |
| // if (!force) we respect those bits, meaning they may stop us from enabling. |
| // In addition, if !force we assume that the sockets have already been added to the |
| // __CFReadSockets and __CFWriteSockets arrays. This is true because the callbacks start |
| // enabled when the CFSocket is created (at which time we enable with force). |
| // Called with SocketLock held, returns with it released! |
| void __CFSocketEnableCallBacks(CFSocketRef s, CFOptionFlags callBackTypes, Boolean force, uint8_t wakeupChar) { |
| CHECK_FOR_FORK(); |
| Boolean wakeup = FALSE; |
| if (!callBackTypes) { |
| __CFSocketUnlock(s); |
| return; |
| } |
| if (__CFSocketIsValid(s) && __CFSocketIsScheduled(s)) { |
| Boolean turnOnWrite = FALSE, turnOnConnect = FALSE, turnOnRead = FALSE; |
| uint8_t readCallBackType = __CFSocketReadCallBackType(s); |
| callBackTypes &= __CFSocketCallBackTypes(s); |
| if (force) s->_f.disabled &= ~callBackTypes; |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "rescheduling socket %d with flags 0x%x disabled 0x%x connected 0x%x for types 0x%lx\n", s->_socket, s->_f.client, s->_f.disabled, s->_f.connected, callBackTypes); |
| #endif |
| /* We will wait for connection only for connection-oriented, non-rendezvous sockets that are not already connected. Mark others as already connected. */ |
| if ((readCallBackType == kCFSocketAcceptCallBack) || !__CFSocketIsConnectionOriented(s)) s->_f.connected = TRUE; |
| |
| // First figure out what to turn on |
| if (s->_f.connected || (callBackTypes & kCFSocketConnectCallBack) == 0) { |
| // if we want write callbacks and they're not disabled... |
| if ((callBackTypes & kCFSocketWriteCallBack) != 0 && (s->_f.disabled & kCFSocketWriteCallBack) == 0) turnOnWrite = TRUE; |
| } else { |
| // if we want connect callbacks and they're not disabled... |
| if ((callBackTypes & kCFSocketConnectCallBack) != 0 && (s->_f.disabled & kCFSocketConnectCallBack) == 0) turnOnConnect = TRUE; |
| } |
| // if we want read callbacks and they're not disabled... |
| if (readCallBackType != kCFSocketNoCallBack && (callBackTypes & readCallBackType) != 0 && (s->_f.disabled & kCFSocketReadCallBack) == 0) turnOnRead = TRUE; |
| |
| // Now turn on the callbacks we've determined that we want on |
| if (turnOnRead || turnOnWrite || turnOnConnect) { |
| __CFSpinLock(&__CFActiveSocketsLock); |
| if (turnOnWrite || turnOnConnect) { |
| if (force) { |
| SInt32 idx = CFArrayGetFirstIndexOfValue(__CFWriteSockets, CFRangeMake(0, CFArrayGetCount(__CFWriteSockets)), s); |
| if (kCFNotFound == idx) CFArrayAppendValue(__CFWriteSockets, s); |
| } |
| if (__CFSocketSetFDForWrite(s)) wakeup = true; |
| #if DEPLOYMENT_TARGET_WINDOWS |
| if ((callBackTypes & kCFSocketConnectCallBack) != 0 && !s->_f.connected) __CFSocketFdSet(s->_socket, __CFExceptSocketsFds); |
| #endif |
| } |
| if (turnOnRead) { |
| if (force) { |
| SInt32 idx = CFArrayGetFirstIndexOfValue(__CFReadSockets, CFRangeMake(0, CFArrayGetCount(__CFReadSockets)), s); |
| if (kCFNotFound == idx) CFArrayAppendValue(__CFReadSockets, s); |
| } |
| if (__CFSocketSetFDForRead(s)) wakeup = true; |
| } |
| if (wakeup && NULL == __CFSocketManagerThread) __CFSocketManagerThread = __CFStartSimpleThread((void*)__CFSocketManager, 0); |
| __CFSpinUnlock(&__CFActiveSocketsLock); |
| } |
| } |
| __CFSocketUnlock(s); |
| if (wakeup) send(__CFWakeupSocketPair[0], (SOCK_CONST_DATA)&wakeupChar, sizeof(wakeupChar), 0); |
| } |
| |
| void CFSocketEnableCallBacks(CFSocketRef s, CFOptionFlags callBackTypes) { |
| CHECK_FOR_FORK(); |
| __CFGenericValidateType(s, __kCFSocketTypeID); |
| __CFSocketLock(s); |
| __CFSocketEnableCallBacks(s, callBackTypes, TRUE, 'r'); |
| } |
| |
| static void __CFSocketSchedule(void *info, CFRunLoopRef rl, CFStringRef mode) { |
| CFSocketRef s = (CFSocketRef)info; |
| __CFSocketLock(s); |
| //??? also need to arrange delivery of all pending data |
| if (__CFSocketIsValid(s)) { |
| CFArrayAppendValue(s->_runLoops, rl); |
| s->_socketSetCount++; |
| // Since the v0 source is listened to on the SocketMgr thread, no matter how many modes it |
| // is added to we just need to enable it there once (and _socketSetCount gives us a refCount |
| // to know when we can finally disable it). |
| if (1 == s->_socketSetCount) { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "scheduling socket %d\n", s->_socket); |
| #endif |
| __CFSocketEnableCallBacks(s, __CFSocketCallBackTypes(s), TRUE, 's'); // unlocks s |
| } else |
| __CFSocketUnlock(s); |
| } else |
| __CFSocketUnlock(s); |
| } |
| |
| static void __CFSocketCancel(void *info, CFRunLoopRef rl, CFStringRef mode) { |
| CFSocketRef s = (CFSocketRef)info; |
| SInt32 idx; |
| __CFSocketLock(s); |
| s->_socketSetCount--; |
| if (0 == s->_socketSetCount) { |
| __CFSpinLock(&__CFActiveSocketsLock); |
| idx = CFArrayGetFirstIndexOfValue(__CFWriteSockets, CFRangeMake(0, CFArrayGetCount(__CFWriteSockets)), s); |
| if (0 <= idx) { |
| CFArrayRemoveValueAtIndex(__CFWriteSockets, idx); |
| __CFSocketClearFDForWrite(s); |
| #if DEPLOYMENT_TARGET_WINDOWS |
| __CFSocketFdClr(s->_socket, __CFExceptSocketsFds); |
| #endif |
| } |
| idx = CFArrayGetFirstIndexOfValue(__CFReadSockets, CFRangeMake(0, CFArrayGetCount(__CFReadSockets)), s); |
| if (0 <= idx) { |
| CFArrayRemoveValueAtIndex(__CFReadSockets, idx); |
| __CFSocketClearFDForRead(s); |
| } |
| __CFSpinUnlock(&__CFActiveSocketsLock); |
| } |
| if (NULL != s->_runLoops) { |
| idx = CFArrayGetFirstIndexOfValue(s->_runLoops, CFRangeMake(0, CFArrayGetCount(s->_runLoops)), rl); |
| if (0 <= idx) CFArrayRemoveValueAtIndex(s->_runLoops, idx); |
| } |
| __CFSocketUnlock(s); |
| } |
| |
| // Note: must be called with socket lock held, then returns with it released |
| // Used by both the v0 and v1 RunLoopSource perform routines |
| static void __CFSocketDoCallback(CFSocketRef s, CFDataRef data, CFDataRef address, CFSocketNativeHandle sock) { |
| CFSocketCallBack callout = NULL; |
| void *contextInfo = NULL; |
| SInt32 errorCode = 0; |
| Boolean readSignalled = false, writeSignalled = false, connectSignalled = false, calledOut = false; |
| uint8_t readCallBackType, callBackTypes; |
| |
| callBackTypes = __CFSocketCallBackTypes(s); |
| readCallBackType = __CFSocketReadCallBackType(s); |
| readSignalled = __CFSocketIsReadSignalled(s); |
| writeSignalled = __CFSocketIsWriteSignalled(s); |
| connectSignalled = writeSignalled && !s->_f.connected; |
| __CFSocketUnsetReadSignalled(s); |
| __CFSocketUnsetWriteSignalled(s); |
| callout = s->_callout; |
| contextInfo = s->_context.info; |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "entering perform for socket %d with read signalled %d write signalled %d connect signalled %d callback types %d\n", s->_socket, readSignalled, writeSignalled, connectSignalled, callBackTypes); |
| #endif |
| if (writeSignalled) { |
| errorCode = s->_errorCode; |
| s->_f.connected = TRUE; |
| } |
| __CFSocketUnlock(s); |
| if ((callBackTypes & kCFSocketConnectCallBack) != 0) { |
| if (connectSignalled && (!calledOut || CFSocketIsValid(s))) { |
| if (errorCode) { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "perform calling out error %ld to socket %d\n", errorCode, s->_socket); |
| #endif |
| if (callout) callout(s, kCFSocketConnectCallBack, NULL, &errorCode, contextInfo); |
| calledOut = true; |
| } else { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "perform calling out connect to socket %d\n", s->_socket); |
| #endif |
| if (callout) callout(s, kCFSocketConnectCallBack, NULL, NULL, contextInfo); |
| calledOut = true; |
| } |
| } |
| } |
| if (kCFSocketDataCallBack == readCallBackType) { |
| if (NULL != data && (!calledOut || CFSocketIsValid(s))) { |
| SInt32 datalen = CFDataGetLength(data); |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "perform calling out data of length %ld to socket %d\n", datalen, s->_socket); |
| #endif |
| if (callout) callout(s, kCFSocketDataCallBack, address, data, contextInfo); |
| calledOut = true; |
| if (0 == datalen) CFSocketInvalidate(s); |
| } |
| } else if (kCFSocketAcceptCallBack == readCallBackType) { |
| if (INVALID_SOCKET != sock && (!calledOut || CFSocketIsValid(s))) { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "perform calling out accept of socket %d to socket %d\n", sock, s->_socket); |
| #endif |
| if (callout) callout(s, kCFSocketAcceptCallBack, address, &sock, contextInfo); |
| calledOut = true; |
| } |
| } else if (kCFSocketReadCallBack == readCallBackType) { |
| if (readSignalled && (!calledOut || CFSocketIsValid(s))) { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "perform calling out read to socket %d\n", s->_socket); |
| #endif |
| if (callout) callout(s, kCFSocketReadCallBack, NULL, NULL, contextInfo); |
| calledOut = true; |
| } |
| } |
| if ((callBackTypes & kCFSocketWriteCallBack) != 0) { |
| if (writeSignalled && !errorCode && (!calledOut || CFSocketIsValid(s))) { |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "perform calling out write to socket %d\n", s->_socket); |
| #endif |
| if (callout) callout(s, kCFSocketWriteCallBack, NULL, NULL, contextInfo); |
| calledOut = true; |
| } |
| } |
| } |
| |
| static void __CFSocketPerformV0(void *info) { |
| CFSocketRef s = (CFSocketRef)info; |
| CFDataRef data = NULL; |
| CFDataRef address = NULL; |
| CFSocketNativeHandle sock = INVALID_SOCKET; |
| uint8_t readCallBackType, callBackTypes; |
| CFRunLoopRef rl = NULL; |
| |
| __CFSocketLock(s); |
| if (!__CFSocketIsValid(s)) { |
| __CFSocketUnlock(s); |
| return; |
| } |
| callBackTypes = __CFSocketCallBackTypes(s); |
| readCallBackType = __CFSocketReadCallBackType(s); |
| CFOptionFlags callBacksSignalled = 0; |
| if (__CFSocketIsReadSignalled(s)) callBacksSignalled |= readCallBackType; |
| if (__CFSocketIsWriteSignalled(s)) callBacksSignalled |= kCFSocketWriteCallBack; |
| |
| if (kCFSocketDataCallBack == readCallBackType) { |
| if (NULL != s->_dataQueue && 0 < CFArrayGetCount(s->_dataQueue)) { |
| data = (CFDataRef)CFArrayGetValueAtIndex(s->_dataQueue, 0); |
| CFRetain(data); |
| CFArrayRemoveValueAtIndex(s->_dataQueue, 0); |
| address = (CFDataRef)CFArrayGetValueAtIndex(s->_addressQueue, 0); |
| CFRetain(address); |
| CFArrayRemoveValueAtIndex(s->_addressQueue, 0); |
| } |
| } else if (kCFSocketAcceptCallBack == readCallBackType) { |
| if (NULL != s->_dataQueue && 0 < CFArrayGetCount(s->_dataQueue)) { |
| sock = (CFSocketNativeHandle)(uintptr_t)CFArrayGetValueAtIndex(s->_dataQueue, 0); |
| CFArrayRemoveValueAtIndex(s->_dataQueue, 0); |
| address = (CFDataRef)CFArrayGetValueAtIndex(s->_addressQueue, 0); |
| CFRetain(address); |
| CFArrayRemoveValueAtIndex(s->_addressQueue, 0); |
| } |
| } |
| |
| __CFSocketDoCallback(s, data, address, sock); // does __CFSocketUnlock(s) |
| if (NULL != data) CFRelease(data); |
| if (NULL != address) CFRelease(address); |
| |
| __CFSocketLock(s); |
| if (__CFSocketIsValid(s) && kCFSocketNoCallBack != readCallBackType) { |
| // if there's still more data, we want to wake back up right away |
| if ((kCFSocketDataCallBack == readCallBackType || kCFSocketAcceptCallBack == readCallBackType) && NULL != s->_dataQueue && 0 < CFArrayGetCount(s->_dataQueue)) { |
| CFRunLoopSourceSignal(s->_source0); |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "perform short-circuit signaling source for socket %d with flags 0x%x disabled 0x%x connected 0x%x\n", s->_socket, s->_f.client, s->_f.disabled, s->_f.connected); |
| #endif |
| rl = __CFSocketCopyRunLoopToWakeUp(s); |
| } |
| } |
| // Only reenable callbacks that are auto-reenabled |
| __CFSocketEnableCallBacks(s, callBacksSignalled & s->_f.client, FALSE, 'p'); // unlocks s |
| |
| if (NULL != rl) { |
| CFRunLoopWakeUp(rl); |
| CFRelease(rl); |
| } |
| } |
| |
| CFRunLoopSourceRef CFSocketCreateRunLoopSource(CFAllocatorRef allocator, CFSocketRef s, CFIndex order) { |
| CHECK_FOR_FORK(); |
| CFRunLoopSourceRef result = NULL; |
| __CFGenericValidateType(s, __kCFSocketTypeID); |
| __CFSocketLock(s); |
| if (__CFSocketIsValid(s)) { |
| if (NULL == s->_source0) { |
| CFRunLoopSourceContext context; |
| context.version = 0; |
| context.info = s; |
| context.retain = CFRetain; |
| context.release = CFRelease; |
| context.copyDescription = CFCopyDescription; |
| context.equal = CFEqual; |
| context.hash = CFHash; |
| context.schedule = __CFSocketSchedule; |
| context.cancel = __CFSocketCancel; |
| context.perform = __CFSocketPerformV0; |
| s->_source0 = CFRunLoopSourceCreate(allocator, order, &context); |
| } |
| CFRetain(s->_source0); /* This retain is for the receiver */ |
| result = s->_source0; |
| } |
| __CFSocketUnlock(s); |
| return result; |
| } |
| |
| #endif /* NEW_SOCKET */ |
| |
| static CFSpinLock_t __CFSocketWriteLock_ = CFSpinLockInit; |
| //#warning can only send on one socket at a time now |
| |
| CF_INLINE void __CFSocketWriteLock(CFSocketRef s) { |
| __CFSpinLock(& __CFSocketWriteLock_); |
| } |
| |
| CF_INLINE void __CFSocketWriteUnlock(CFSocketRef s) { |
| __CFSpinUnlock(& __CFSocketWriteLock_); |
| } |
| |
| //??? need timeout, error handling, retries |
| CFSocketError CFSocketSendData(CFSocketRef s, CFDataRef address, CFDataRef data, CFTimeInterval timeout) { |
| CHECK_FOR_FORK(); |
| const uint8_t *dataptr, *addrptr = NULL; |
| SInt32 datalen, addrlen = 0, size = 0; |
| CFSocketNativeHandle sock = INVALID_SOCKET; |
| struct timeval tv; |
| __CFGenericValidateType(s, CFSocketGetTypeID()); |
| if (address) { |
| addrptr = CFDataGetBytePtr(address); |
| addrlen = CFDataGetLength(address); |
| } |
| dataptr = CFDataGetBytePtr(data); |
| datalen = CFDataGetLength(data); |
| if (CFSocketIsValid(s)) sock = CFSocketGetNative(s); |
| if (INVALID_SOCKET != sock) { |
| CFRetain(s); |
| __CFSocketWriteLock(s); |
| tv.tv_sec = (0 >= timeout || INT_MAX <= timeout) ? INT_MAX : (int)(float)floor(timeout); |
| tv.tv_usec = (int)((timeout - floor(timeout)) * 1.0E6); |
| setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (SOCK_CONST_DATA)&tv, sizeof(tv)); // cast for WinSock bad API |
| if (NULL != addrptr && 0 < addrlen) { |
| size = sendto(sock, (SOCK_CONST_DATA)dataptr, datalen, 0, (struct sockaddr *)addrptr, addrlen); |
| } else { |
| size = send(sock, (SOCK_CONST_DATA)dataptr, datalen, 0); |
| } |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "wrote %ld bytes to socket %d\n", size, sock); |
| #endif |
| __CFSocketWriteUnlock(s); |
| CFRelease(s); |
| } |
| return (size > 0) ? kCFSocketSuccess : kCFSocketError; |
| } |
| |
| CFSocketError CFSocketSetAddress(CFSocketRef s, CFDataRef address) { |
| CHECK_FOR_FORK(); |
| const uint8_t *name; |
| SInt32 namelen, result = 0; |
| __CFGenericValidateType(s, CFSocketGetTypeID()); |
| if (NULL == address) return -1; |
| if (!CFSocketIsValid(s)) return 0; |
| name = CFDataGetBytePtr(address); |
| namelen = CFDataGetLength(address); |
| if (!name || namelen <= 0) return 0; |
| CFSocketNativeHandle sock = CFSocketGetNative(s); |
| result = bind(sock, (struct sockaddr *)name, namelen); |
| if (0 == result) { |
| listen(sock, 256); |
| } |
| //??? should return errno |
| return result; |
| } |
| |
| CFSocketError CFSocketConnectToAddress(CFSocketRef s, CFDataRef address, CFTimeInterval timeout) { |
| CHECK_FOR_FORK(); |
| //??? need error handling, retries |
| const uint8_t *name; |
| SInt32 namelen, result = -1, connect_err = 0, select_err = 0; |
| #if DEPLOYMENT_TARGET_WINDOWS |
| u_long yes = 1, no = 0; |
| #else |
| UInt32 yes = 1, no = 0; |
| #endif |
| Boolean wasBlocking = true; |
| |
| __CFGenericValidateType(s, CFSocketGetTypeID()); |
| if (!CFSocketIsValid(s)) return 0; |
| name = CFDataGetBytePtr(address); |
| namelen = CFDataGetLength(address); |
| if (!name || namelen <= 0) return 0; |
| CFSocketNativeHandle sock = CFSocketGetNative(s); |
| { |
| #if !DEPLOYMENT_TARGET_WINDOWS |
| SInt32 flags = fcntl(sock, F_GETFL, 0); |
| if (flags >= 0) wasBlocking = ((flags & O_NONBLOCK) == 0); |
| #endif |
| if (wasBlocking && (timeout > 0.0 || timeout < 0.0)) ioctlsocket(sock, FIONBIO, &yes); |
| result = connect(sock, (struct sockaddr *)name, namelen); |
| if (result != 0) { |
| connect_err = __CFSocketLastError(); |
| #if DEPLOYMENT_TARGET_WINDOWS |
| if (connect_err == WSAEWOULDBLOCK) connect_err = EINPROGRESS; |
| #endif |
| } |
| #if defined(LOG_CFSOCKET) |
| #if !DEPLOYMENT_TARGET_WINDOWS |
| fprintf(stdout, "connection attempt returns %ld error %ld on socket %d (flags 0x%lx blocking %d)\n", result, connect_err, sock, flags, wasBlocking); |
| #else |
| fprintf(stdout, "connection attempt returns %ld error %ld on socket %d\n", result, connect_err, s->_socket); |
| #endif |
| |
| #endif |
| if (EINPROGRESS == connect_err && timeout >= 0.0) { |
| /* select on socket */ |
| SInt32 nrfds; |
| int error_size = sizeof(select_err); |
| struct timeval tv; |
| CFMutableDataRef fds = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); |
| #if DEPLOYMENT_TARGET_WINDOWS |
| CFDataIncreaseLength(fds , sizeof(u_int) + sizeof(SOCKET)); |
| #endif |
| __CFSocketFdSet(sock, fds); |
| tv.tv_sec = (0 >= timeout || INT_MAX <= timeout) ? INT_MAX : (int)(float)floor(timeout); |
| tv.tv_usec = (int)((timeout - floor(timeout)) * 1.0E6); |
| nrfds = select(__CFSocketFdGetSize(fds), NULL, (fd_set *)CFDataGetMutableBytePtr(fds), NULL, &tv); |
| if (nrfds < 0) { |
| select_err = __CFSocketLastError(); |
| result = -1; |
| } else if (nrfds == 0) { |
| result = -2; |
| } else { |
| if (0 != getsockopt(sock, SOL_SOCKET, SO_ERROR, (SOCK_DATA)&select_err, (socklen_t *)&error_size)) select_err = 0; |
| result = (select_err == 0) ? 0 : -1; |
| } |
| CFRelease(fds); |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "timed connection attempt %s on socket %d, result %ld, select returns %ld error %ld\n", (result == 0) ? "succeeds" : "fails", sock, result, nrfds, select_err); |
| #endif |
| } |
| if (wasBlocking && (timeout > 0.0 || timeout < 0.0)) ioctlsocket(sock, FIONBIO, &no); |
| if (EINPROGRESS == connect_err && timeout < 0.0) { |
| result = 0; |
| #if defined(LOG_CFSOCKET) |
| fprintf(stdout, "connection attempt continues in background on socket %d\n", sock); |
| #endif |
| } |
| } |
| //??? should return errno |
| return result; |
| } |
| |
| CFSocketRef CFSocketCreate(CFAllocatorRef allocator, SInt32 protocolFamily, SInt32 socketType, SInt32 protocol, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context) { |
| CHECK_FOR_FORK(); |
| CFSocketNativeHandle sock = INVALID_SOCKET; |
| CFSocketRef s = NULL; |
| if (0 >= protocolFamily) protocolFamily = PF_INET; |
| if (PF_INET == protocolFamily) { |
| if (0 >= socketType) socketType = SOCK_STREAM; |
| if (0 >= protocol && SOCK_STREAM == socketType) protocol = IPPROTO_TCP; |
| if (0 >= protocol && SOCK_DGRAM == socketType) protocol = IPPROTO_UDP; |
| } |
| #if !DEPLOYMENT_TARGET_WINDOWS |
| if (PF_LOCAL == protocolFamily && 0 >= socketType) socketType = SOCK_STREAM; |
| #else |
| __CFSocketInitializeWinSock(); |
| #endif |
| sock = (CFSocketNativeHandle)socket(protocolFamily, socketType, protocol); |
| if (INVALID_SOCKET != sock) { |
| s = _CFSocketCreateWithNative(allocator, sock, callBackTypes, callout, context, FALSE); |
| } |
| return s; |
| } |
| |
| CFSocketRef CFSocketCreateWithSocketSignature(CFAllocatorRef allocator, const CFSocketSignature *signature, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context) { |
| CHECK_FOR_FORK(); |
| CFSocketRef s = CFSocketCreate(allocator, signature->protocolFamily, signature->socketType, signature->protocol, callBackTypes, callout, context); |
| if (NULL != s && (!CFSocketIsValid(s) || kCFSocketSuccess != CFSocketSetAddress(s, signature->address))) { |
| CFSocketInvalidate(s); |
| CFRelease(s); |
| s = NULL; |
| } |
| return s; |
| } |
| |
| CFSocketRef CFSocketCreateConnectedToSocketSignature(CFAllocatorRef allocator, const CFSocketSignature *signature, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context, CFTimeInterval timeout) { |
| CHECK_FOR_FORK(); |
| CFSocketRef s = CFSocketCreate(allocator, signature->protocolFamily, signature->socketType, signature->protocol, callBackTypes, callout, context); |
| if (NULL != s && (!CFSocketIsValid(s) || kCFSocketSuccess != CFSocketConnectToAddress(s, signature->address, timeout))) { |
| CFSocketInvalidate(s); |
| CFRelease(s); |
| s = NULL; |
| } |
| return s; |
| } |
| |
| typedef struct { |
| CFSocketError *error; |
| CFPropertyListRef *value; |
| CFDataRef *address; |
| } __CFSocketNameRegistryResponse; |
| |
| static void __CFSocketHandleNameRegistryReply(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) { |
| CFDataRef replyData = (CFDataRef)data; |
| __CFSocketNameRegistryResponse *response = (__CFSocketNameRegistryResponse *)info; |
| CFDictionaryRef replyDictionary = NULL; |
| CFPropertyListRef value; |
| replyDictionary = (CFDictionaryRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, replyData, kCFPropertyListImmutable, NULL); |
| if (NULL != response->error) *(response->error) = kCFSocketError; |
| if (NULL != replyDictionary) { |
| if (CFGetTypeID((CFTypeRef)replyDictionary) == CFDictionaryGetTypeID() && NULL != (value = CFDictionaryGetValue(replyDictionary, kCFSocketResultKey))) { |
| if (NULL != response->error) *(response->error) = kCFSocketSuccess; |
| if (NULL != response->value) *(response->value) = CFRetain(value); |
| if (NULL != response->address) *(response->address) = address ? CFDataCreateCopy(kCFAllocatorSystemDefault, address) : NULL; |
| } |
| CFRelease(replyDictionary); |
| } |
| CFSocketInvalidate(s); |
| } |
| |
| static void __CFSocketSendNameRegistryRequest(CFSocketSignature *signature, CFDictionaryRef requestDictionary, __CFSocketNameRegistryResponse *response, CFTimeInterval timeout) { |
| CFDataRef requestData = NULL; |
| CFSocketContext context = {0, response, NULL, NULL, NULL}; |
| CFSocketRef s = NULL; |
| CFRunLoopSourceRef source = NULL; |
| if (NULL != response->error) *(response->error) = kCFSocketError; |
| requestData = CFPropertyListCreateXMLData(kCFAllocatorSystemDefault, requestDictionary); |
| if (NULL != requestData) { |
| if (NULL != response->error) *(response->error) = kCFSocketTimeout; |
| s = CFSocketCreateConnectedToSocketSignature(kCFAllocatorSystemDefault, signature, kCFSocketDataCallBack, __CFSocketHandleNameRegistryReply, &context, timeout); |
| if (NULL != s) { |
| if (kCFSocketSuccess == CFSocketSendData(s, NULL, requestData, timeout)) { |
| source = CFSocketCreateRunLoopSource(kCFAllocatorSystemDefault, s, 0); |
| CFRunLoopAddSource(CFRunLoopGetCurrent(), source, __kCFSocketRegistryRequestRunLoopMode); |
| CFRunLoopRunInMode(__kCFSocketRegistryRequestRunLoopMode, timeout, false); |
| CFRelease(source); |
| } |
| CFSocketInvalidate(s); |
| CFRelease(s); |
| } |
| CFRelease(requestData); |
| } |
| } |
| |
| static void __CFSocketValidateSignature(const CFSocketSignature *providedSignature, CFSocketSignature *signature, uint16_t defaultPortNumber) { |
| struct sockaddr_in sain, *sainp; |
| memset(&sain, 0, sizeof(sain)); |
| #if DEPLOYMENT_TARGET_MACOSX |
| sain.sin_len = sizeof(sain); |
| #endif |
| sain.sin_family = AF_INET; |
| sain.sin_port = htons(__CFSocketDefaultNameRegistryPortNumber); |
| sain.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
| if (NULL == providedSignature) { |
| signature->protocolFamily = PF_INET; |
| signature->socketType = SOCK_STREAM; |
| signature->protocol = IPPROTO_TCP; |
| signature->address = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)&sain, sizeof(sain)); |
| } else { |
| signature->protocolFamily = providedSignature->protocolFamily; |
| signature->socketType = providedSignature->socketType; |
| signature->protocol = providedSignature->protocol; |
| if (0 >= signature->protocolFamily) signature->protocolFamily = PF_INET; |
| if (PF_INET == signature->protocolFamily) { |
| if (0 >= signature->socketType) signature->socketType = SOCK_STREAM; |
| if (0 >= signature->protocol && SOCK_STREAM == signature->socketType) signature->protocol = IPPROTO_TCP; |
| if (0 >= signature->protocol && SOCK_DGRAM == signature->socketType) signature->protocol = IPPROTO_UDP; |
| } |
| if (NULL == providedSignature->address) { |
| signature->address = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)&sain, sizeof(sain)); |
| } else { |
| sainp = (struct sockaddr_in *)CFDataGetBytePtr(providedSignature->address); |
| if ((int)sizeof(struct sockaddr_in) <= CFDataGetLength(providedSignature->address) && (AF_INET == sainp->sin_family || 0 == sainp->sin_family)) { |
| #if DEPLOYMENT_TARGET_MACOSX |
| sain.sin_len = sizeof(sain); |
| #endif |
| sain.sin_family = AF_INET; |
| sain.sin_port = sainp->sin_port; |
| if (0 == sain.sin_port) sain.sin_port = htons(defaultPortNumber); |
| sain.sin_addr.s_addr = sainp->sin_addr.s_addr; |
| if (htonl(INADDR_ANY) == sain.sin_addr.s_addr) sain.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
| signature->address = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)&sain, sizeof(sain)); |
| } else { |
| signature->address = (CFDataRef)CFRetain(providedSignature->address); |
| } |
| } |
| } |
| } |
| |
| CFSocketError CFSocketRegisterValue(const CFSocketSignature *nameServerSignature, CFTimeInterval timeout, CFStringRef name, CFPropertyListRef value) { |
| CFSocketSignature signature; |
| CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); |
| CFSocketError retval = kCFSocketError; |
| __CFSocketNameRegistryResponse response = {&retval, NULL, NULL}; |
| CFDictionaryAddValue(dictionary, kCFSocketCommandKey, kCFSocketRegisterCommand); |
| CFDictionaryAddValue(dictionary, kCFSocketNameKey, name); |
| if (NULL != value) CFDictionaryAddValue(dictionary, kCFSocketValueKey, value); |
| __CFSocketValidateSignature(nameServerSignature, &signature, __CFSocketDefaultNameRegistryPortNumber); |
| __CFSocketSendNameRegistryRequest(&signature, dictionary, &response, timeout); |
| CFRelease(dictionary); |
| CFRelease(signature.address); |
| return retval; |
| } |
| |
| CFSocketError CFSocketCopyRegisteredValue(const CFSocketSignature *nameServerSignature, CFTimeInterval timeout, CFStringRef name, CFPropertyListRef *value, CFDataRef *serverAddress) { |
| CFSocketSignature signature; |
| CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); |
| CFSocketError retval = kCFSocketError; |
| __CFSocketNameRegistryResponse response = {&retval, value, serverAddress}; |
| CFDictionaryAddValue(dictionary, kCFSocketCommandKey, kCFSocketRetrieveCommand); |
| CFDictionaryAddValue(dictionary, kCFSocketNameKey, name); |
| __CFSocketValidateSignature(nameServerSignature, &signature, __CFSocketDefaultNameRegistryPortNumber); |
| __CFSocketSendNameRegistryRequest(&signature, dictionary, &response, timeout); |
| CFRelease(dictionary); |
| CFRelease(signature.address); |
| return retval; |
| } |
| |
| CFSocketError CFSocketRegisterSocketSignature(const CFSocketSignature *nameServerSignature, CFTimeInterval timeout, CFStringRef name, const CFSocketSignature *signature) { |
| CFSocketSignature validatedSignature; |
| CFMutableDataRef data = NULL; |
| CFSocketError retval; |
| CFIndex length; |
| uint8_t bytes[4]; |
| if (NULL == signature) { |
| retval = CFSocketUnregister(nameServerSignature, timeout, name); |
| } else { |
| __CFSocketValidateSignature(signature, &validatedSignature, 0); |
| if (NULL == validatedSignature.address || 0 > validatedSignature.protocolFamily || 255 < validatedSignature.protocolFamily || 0 > validatedSignature.socketType || 255 < validatedSignature.socketType || 0 > validatedSignature.protocol || 255 < validatedSignature.protocol || 0 >= (length = CFDataGetLength(validatedSignature.address)) || 255 < length) { |
| retval = kCFSocketError; |
| } else { |
| data = CFDataCreateMutable(kCFAllocatorSystemDefault, sizeof(bytes) + length); |
| bytes[0] = validatedSignature.protocolFamily; |
| bytes[1] = validatedSignature.socketType; |
| bytes[2] = validatedSignature.protocol; |
| bytes[3] = (uint8_t)length; |
| CFDataAppendBytes(data, bytes, sizeof(bytes)); |
| CFDataAppendBytes(data, CFDataGetBytePtr(validatedSignature.address), length); |
| retval = CFSocketRegisterValue(nameServerSignature, timeout, name, data); |
| CFRelease(data); |
| } |
| CFRelease(validatedSignature.address); |
| } |
| return retval; |
| } |
| |
| CFSocketError CFSocketCopyRegisteredSocketSignature(const CFSocketSignature *nameServerSignature, CFTimeInterval timeout, CFStringRef name, CFSocketSignature *signature, CFDataRef *nameServerAddress) { |
| CFDataRef data = NULL; |
| CFSocketSignature returnedSignature; |
| const uint8_t *ptr = NULL, *aptr = NULL; |
| uint8_t *mptr; |
| CFIndex length = 0; |
| CFDataRef serverAddress = NULL; |
| CFSocketError retval = CFSocketCopyRegisteredValue(nameServerSignature, timeout, name, (CFPropertyListRef *)&data, &serverAddress); |
| if (NULL == data || CFGetTypeID(data) != CFDataGetTypeID() || NULL == (ptr = CFDataGetBytePtr(data)) || (length = CFDataGetLength(data)) < 4) retval = kCFSocketError; |
| if (kCFSocketSuccess == retval && NULL != signature) { |
| returnedSignature.protocolFamily = (SInt32)*ptr++; |
| returnedSignature.socketType = (SInt32)*ptr++; |
| returnedSignature.protocol = (SInt32)*ptr++; |
| ptr++; |
| returnedSignature.address = CFDataCreate(kCFAllocatorSystemDefault, ptr, length - 4); |
| __CFSocketValidateSignature(&returnedSignature, signature, 0); |
| CFRelease(returnedSignature.address); |
| ptr = CFDataGetBytePtr(signature->address); |
| if (CFDataGetLength(signature->address) >= (int)sizeof(struct sockaddr_in) && AF_INET == ((struct sockaddr *)ptr)->sa_family && NULL != serverAddress && CFDataGetLength(serverAddress) >= (int)sizeof(struct sockaddr_in) && NULL != (aptr = CFDataGetBytePtr(serverAddress)) && AF_INET == ((struct sockaddr *)aptr)->sa_family) { |
| CFMutableDataRef address = CFDataCreateMutableCopy(kCFAllocatorSystemDefault, CFDataGetLength(signature->address), signature->address); |
| mptr = CFDataGetMutableBytePtr(address); |
| ((struct sockaddr_in *)mptr)->sin_addr = ((struct sockaddr_in *)aptr)->sin_addr; |
| CFRelease(signature->address); |
| signature->address = address; |
| } |
| if (NULL != nameServerAddress) *nameServerAddress = serverAddress ? (CFDataRef)CFRetain(serverAddress) : NULL; |
| } |
| if (NULL != data) CFRelease(data); |
| if (NULL != serverAddress) CFRelease(serverAddress); |
| return retval; |
| } |
| |
| CFSocketError CFSocketUnregister(const CFSocketSignature *nameServerSignature, CFTimeInterval timeout, CFStringRef name) { |
| return CFSocketRegisterValue(nameServerSignature, timeout, name, NULL); |
| } |
| |
| CF_EXPORT void CFSocketSetDefaultNameRegistryPortNumber(uint16_t port) { |
| __CFSocketDefaultNameRegistryPortNumber = port; |
| } |
| |
| CF_EXPORT uint16_t CFSocketGetDefaultNameRegistryPortNumber(void) { |
| return __CFSocketDefaultNameRegistryPortNumber; |
| } |
| |
| |