#if 0
#pragma mark Includes
#endif
#include "CFNetworkInternal.h"
#include "CFNetworkSchedule.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <CoreFoundation/CFStreamPriv.h>
#include <CFNetwork/CFSocketStreamPriv.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <Security/Security.h>
#include <Security/SecureTransportPriv.h>
#if 0
#pragma mark -
#pragma mark Constants
#endif
#define kSocketEvents ((CFOptionFlags)(kCFSocketReadCallBack | kCFSocketConnectCallBack | kCFSocketWriteCallBack))
#define kReadWriteTimeoutInterval ((CFTimeInterval)75.0)
#define kRecvBufferSize ((CFIndex)(32768L));
#define kSecurityBufferSize ((CFIndex)(32768L));
#ifndef __MACH__
const int kCFStreamErrorDomainSOCKS = 5;
#endif
#if 0
#pragma mark *Constant Strings
#pragma mark **Stream Property Keys
#endif
CONST_STRING_DECL(kCFStreamPropertySocketRemoteHost, "kCFStreamPropertySocketRemoteHost")
CONST_STRING_DECL(kCFStreamPropertySocketRemoteNetService, "kCFStreamPropertySocketRemoteNetService")
CONST_STRING_DECL(kCFStreamPropertyShouldCloseNativeSocket, "kCFStreamPropertyShouldCloseNativeSocket")
CONST_STRING_DECL(_kCFStreamPropertySocketPeerName, "_kCFStreamPropertySocketPeerName")
CONST_STRING_DECL(kCFStreamPropertySSLPeerCertificates, "kCFStreamPropertySSLPeerCertificates")
CONST_STRING_DECL(_kCFStreamPropertySSLClientCertificates, "_kCFStreamPropertySSLClientCertificates")
CONST_STRING_DECL(_kCFStreamPropertySSLClientCertificateState, "_kCFStreamPropertySSLClientCertificateState")
CONST_STRING_DECL(kCFStreamPropertySSLSettings, "kCFStreamPropertySSLSettings")
CONST_STRING_DECL(kCFStreamSSLAllowsAnyRoot, "kCFStreamSSLAllowsAnyRoot")
CONST_STRING_DECL(kCFStreamSSLAllowsExpiredCertificates, "kCFStreamSSLAllowsExpiredCertificates")
CONST_STRING_DECL(kCFStreamSSLAllowsExpiredRoots, "kCFStreamSSLAllowsExpiredRoots")
CONST_STRING_DECL(kCFStreamSSLCertificates, "kCFStreamSSLCertificates")
CONST_STRING_DECL(kCFStreamSSLIsServer, "kCFStreamSSLIsServer")
CONST_STRING_DECL(kCFStreamSSLLevel, "kCFStreamSSLLevel")
CONST_STRING_DECL(kCFStreamSSLPeerName, "kCFStreamSSLPeerName")
CONST_STRING_DECL(kCFStreamSSLValidatesCertificateChain, "kCFStreamSSLValidatesCertificateChain")
CONST_STRING_DECL(kCFStreamSocketSecurityLevelTLSv1SSLv3, "kCFStreamSocketSecurityLevelTLSv1SSLv3")
CONST_STRING_DECL(kCFStreamPropertyUseAddressCache, "kCFStreamPropertyUseAddressCache")
CONST_STRING_DECL(_kCFStreamSocketIChatWantsSubNet, "_kCFStreamSocketIChatWantsSubNet")
CONST_STRING_DECL(_kCFStreamSocketCreatedCallBack, "_kCFStreamSocketCreatedCallBack")
CONST_STRING_DECL(kCFStreamPropertyProxyExceptionsList, "ExceptionsList")
CONST_STRING_DECL(kCFStreamPropertyProxyLocalBypass, "ExcludeSimpleHostnames");
CONST_STRING_DECL(kCFStreamPropertyCONNECTProxy, "kCFStreamPropertyCONNECTProxy")
CONST_STRING_DECL(kCFStreamPropertyCONNECTProxyHost, "kCFStreamPropertyCONNECTProxyHost")
CONST_STRING_DECL(kCFStreamPropertyCONNECTProxyPort, "kCFStreamPropertyCONNECTProxyPort")
CONST_STRING_DECL(kCFStreamPropertyCONNECTVersion, "kCFStreamPropertyCONNECTVersion")
CONST_STRING_DECL(kCFStreamPropertyCONNECTAdditionalHeaders, "kCFStreamPropertyCONNECTAdditionalHeaders")
CONST_STRING_DECL(kCFStreamPropertyCONNECTResponse, "kCFStreamPropertyCONNECTResponse")
CONST_STRING_DECL(kCFStreamPropertyPreviousCONNECTResponse, "kCFStreamPropertyPreviousCONNECTResponse")
#ifdef __CONSTANT_CFSTRINGS__
#define _kCFStreamProxySettingSOCKSEnable CFSTR("SOCKSEnable")
#define _kCFStreamPropertySocketRemotePort CFSTR("_kCFStreamPropertySocketRemotePort")
#define _kCFStreamPropertySocketAddressAttempt CFSTR("_kCFStreamPropertySocketAddressAttempt")
#define _kCFStreamPropertySocketFamilyTypeProtocol CFSTR("_kCFStreamPropertySocketFamilyTypeProtocol")
#define _kCFStreamPropertyHostForOpen CFSTR("_kCFStreamPropertyHostForOpen")
#define _kCFStreamPropertyNetworkReachability CFSTR("_kCFStreamPropertyNetworkReachability")
#define _kCFStreamPropertyRecvBuffer CFSTR("_kCFStreamPropertyRecvBuffer")
#define _kCFStreamPropertyRecvBufferCount CFSTR("_kCFStreamPropertyRecvBufferCount")
#define _kCFStreamPropertyRecvBufferSize CFSTR("_kCFStreamPropertyRecvBufferSize")
#define _kCFStreamPropertySecurityRecvBuffer CFSTR("_kCFStreamPropertySecurityRecvBuffer")
#define _kCFStreamPropertySecurityRecvBufferSize CFSTR("_kCFStreamPropertySecurityRecvBufferSize")
#define _kCFStreamPropertySecurityRecvBufferCount CFSTR("_kCFStreamPropertySecurityRecvBufferCount")
#define _kCFStreamPropertyHandshakes CFSTR("_kCFStreamPropertyHandshakes")
#define _kCFStreamPropertyCONNECTSendBuffer CFSTR("_kCFStreamPropertyCONNECTSendBuffer")
#define _kCFStreamPropertySOCKSSendBuffer CFSTR("_kCFStreamPropertySOCKSSendBuffer")
#define _kCFStreamPropertySOCKSRecvBuffer CFSTR("_kCFStreamPropertySOCKSRecvBuffer")
#define _kCFStreamPropertyReadTimeout CFSTR("_kCFStreamPropertyReadTimeout")
#define _kCFStreamPropertyWriteTimeout CFSTR("_kCFStreamPropertyWriteTimeout")
#define _kCFStreamPropertyReadCancel CFSTR("_kCFStreamPropertyReadCancel")
#define _kCFStreamPropertyWriteCancel CFSTR("_kCFStreamPropertyWriteCancel")
#else
static CONST_STRING_DECL(_kCFStreamProxySettingSOCKSEnable, "SOCKSEnable")
static CONST_STRING_DECL(_kCFStreamPropertySocketRemotePort, "_kCFStreamPropertySocketRemotePort")
static CONST_STRING_DECL(_kCFStreamPropertySocketAddressAttempt, "_kCFStreamPropertySocketAddressAttempt")
static CONST_STRING_DECL(_kCFStreamPropertySocketFamilyTypeProtocol, "_kCFStreamPropertySocketFamilyTypeProtocol")
static CONST_STRING_DECL(_kCFStreamPropertyHostForOpen, "_kCFStreamPropertyHostForOpen")
static CONST_STRING_DECL(_kCFStreamPropertyNetworkReachability, "_kCFStreamPropertyNetworkReachability")
static CONST_STRING_DECL(_kCFStreamPropertyRecvBuffer, "_kCFStreamPropertyRecvBuffer")
static CONST_STRING_DECL(_kCFStreamPropertyRecvBufferCount, "_kCFStreamPropertyRecvBufferCount")
static CONST_STRING_DECL(_kCFStreamPropertyRecvBufferSize, "_kCFStreamPropertyRecvBufferSize")
static CONST_STRING_DECL(_kCFStreamPropertySecurityRecvBuffer, "_kCFStreamPropertySecurityRecvBuffer")
static CONST_STRING_DECL(_kCFStreamPropertySecurityRecvBufferSize, "_kCFStreamPropertySecurityRecvBufferSize")
static CONST_STRING_DECL(_kCFStreamPropertySecurityRecvBufferCount, "_kCFStreamPropertySecurityRecvBufferCount")
static CONST_STRING_DECL(_kCFStreamPropertyHandshakes, "_kCFStreamPropertyHandshakes")
static CONST_STRING_DECL(_kCFStreamPropertyCONNECTSendBuffer, "_kCFStreamPropertyCONNECTSendBuffer")
static CONST_STRING_DECL(_kCFStreamPropertySOCKSSendBuffer, "_kCFStreamPropertySOCKSSendBuffer")
static CONST_STRING_DECL(_kCFStreamPropertySOCKSRecvBuffer, "_kCFStreamPropertySOCKSRecvBuffer")
static CONST_STRING_DECL(_kCFStreamPropertyReadCancel, "_kCFStreamPropertyReadCancel")
static CONST_STRING_DECL(_kCFStreamPropertyWriteCancel, "_kCFStreamPropertyWriteCancel")
#endif
#ifdef __MACH__
extern const CFStringRef kCFStreamPropertyAutoErrorOnSystemChange;
CONST_STRING_DECL(kCFStreamPropertySocketSSLContext, "kCFStreamPropertySocketSSLContext")
CONST_STRING_DECL(_kCFStreamPropertySocketSecurityAuthenticatesServerCertificate, "_kCFStreamPropertySocketSecurityAuthenticatesServerCertificate")
#else
CONST_STRING_DECL(kCFStreamPropertySOCKSProxy, "kCFStreamPropertySOCKSProxy")
CONST_STRING_DECL(kCFStreamPropertySOCKSProxyHost, "SOCKSProxy")
CONST_STRING_DECL(kCFStreamPropertySOCKSProxyPort, "SOCKSPort")
CONST_STRING_DECL(kCFStreamPropertySOCKSVersion, "kCFStreamPropertySOCKSVersion")
CONST_STRING_DECL(kCFStreamSocketSOCKSVersion4, "kCFStreamSocketSOCKSVersion4")
CONST_STRING_DECL(kCFStreamSocketSOCKSVersion5, "kCFStreamSocketSOCKSVersion5")
CONST_STRING_DECL(kCFStreamPropertySOCKSUser, "kCFStreamPropertySOCKSUser")
CONST_STRING_DECL(kCFStreamPropertySOCKSPassword, "kCFStreamPropertySOCKSPassword")
CONST_STRING_DECL(kCFStreamPropertyAutoErrorOnSystemChange, "kCFStreamPropertyAutoErrorOnSystemChange")
#endif
#if 0
#pragma mark **Other Strings
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define _kCFStreamSocketFamily CFSTR("_kCFStreamSocketFamily")
#define _kCFStreamSocketType CFSTR("_kCFStreamSocketType")
#define _kCFStreamSocketProtocol CFSTR("_kCFStreamSocketProtocol")
#else
static CONST_STRING_DECL(_kCFStreamSocketFamily, "_kCFStreamSocketFamily")
static CONST_STRING_DECL(_kCFStreamSocketType, "_kCFStreamSocketType")
static CONST_STRING_DECL(_kCFStreamSocketProtocol, "_kCFStreamSocketProtocol")
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define _kCFStreamSocketOpenCompletedPrivateMode CFSTR("_kCFStreamSocketOpenCompletedPrivateMode")
#define _kCFStreamSocketReadPrivateMode CFSTR("_kCFStreamSocketReadPrivateMode")
#define _kCFStreamSocketCanReadPrivateMode CFSTR("_kCFStreamSocketCanReadPrivateMode")
#define _kCFStreamSocketWritePrivateMode CFSTR("_kCFStreamSocketWritePrivateMode")
#define _kCFStreamSocketCanWritePrivateMode CFSTR("_kCFStreamSocketCanWritePrivateMode")
#define _kCFStreamSocketSecurityClosePrivateMode CFSTR("_kCFStreamSocketSecurityClosePrivateMode")
#define _kCFStreamSocketBogusPrivateMode CFSTR("_kCFStreamSocketBogusPrivateMode")
#define _kCFStreamPropertyBogusRunLoop CFSTR("_kCFStreamPropertyBogusRunLoop")
#else
static CONST_STRING_DECL(_kCFStreamSocketOpenCompletedPrivateMode, "_kCFStreamSocketOpenCompletedPrivateMode")
static CONST_STRING_DECL(_kCFStreamSocketReadPrivateMode, "_kCFStreamSocketReadPrivateMode")
static CONST_STRING_DECL(_kCFStreamSocketCanReadPrivateMode, "_kCFStreamSocketCanReadPrivateMode")
static CONST_STRING_DECL(_kCFStreamSocketWritePrivateMode, "_kCFStreamSocketWritePrivateMode")
static CONST_STRING_DECL(_kCFStreamSocketCanWritePrivateMode, "_kCFStreamSocketCanWritePrivateMode")
static CONST_STRING_DECL(_kCFStreamSocketSecurityClosePrivateMode, "_kCFStreamSocketSecurityClosePrivateMode")
static CONST_STRING_DECL(_kCFStreamSocketBogusPrivateMode, "_kCFStreamSocketBogusPrivateMode")
static CONST_STRING_DECL(_kCFStreamPropertyBogusRunLoop, "_kCFStreamPropertyBogusRunLoop")
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define _kCFStreamCONNECTURLFormat CFSTR("%@:%d")
#define _kCFStreamCONNECTMethod CFSTR("CONNECT")
#define _kCFStreamUserAgentHeader CFSTR("User-Agent")
#define _kCFStreamUserAgentValue CFSTR("CFNetwork/1.1")
#define _kCFStreamHostHeader CFSTR("Host")
#else
static CONST_STRING_DECL(_kCFStreamCONNECTURLFormat, "%@:%d")
static CONST_STRING_DECL(_kCFStreamCONNECTMethod, "CONNECT")
static CONST_STRING_DECL(_kCFStreamUserAgentHeader, "User-Agent")
static CONST_STRING_DECL(_kCFStreamUserAgentValue, "CFNetwork/1.1")
static CONST_STRING_DECL(_kCFStreamHostHeader, "Host")
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define _kCFStreamAutoHostName CFSTR("OnDemandHostName")
#define _kCFStreamPropertyAutoConnectPriority CFSTR("OnDemandPriority")
#define _kCFStreamAutoVPNPriorityDefault CFSTR("Default")
#else
static CONST_STRING_DECL(_kCFStreamAutoHostName, "OnDemandHostName")
static CONST_STRING_DECL(_kCFStreamPropertyAutoConnectPriority, "OnDemandPriority")
static CONST_STRING_DECL(_kCFStreamAutoVPNPriorityDefault, "Default")
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define kCFSocketStreamDescriptionFormat CFSTR("<SocketStream %p>{flags = 0x%08x, read = %p, write = %p, socket = %@, properties = %p }")
#else
static CONST_STRING_DECL(kCFSocketStreamDescriptionFormat, "<SocketStream %p>{flags = 0x%08x, read = %p, write = %p, socket = %@, properties = %p }");
#endif
#if 0
#pragma mark -
#pragma mark Enum Values
#endif
enum {
kFlagBitOpenStarted = 0,
kFlagBitOpenComplete,
kFlagBitCanRead,
kFlagBitCanWrite,
kFlagBitPollOpen,
kFlagBitPollRead,
kFlagBitPollWrite,
kFlagBitShared,
kFlagBitCreatedNative,
kFlagBitReadStreamOpened,
kFlagBitWriteStreamOpened,
kFlagBitUseSSL,
kFlagBitClosed,
kFlagBitTriedVPN,
kFlagBitHasHandshakes,
kFlagBitIsBuffered,
kFlagBitRecvdRead,
kFlagBitReadHasCancel,
kFlagBitWriteHasCancel,
kFlagBitMinLoops,
kFlagBitMaxLoops = kFlagBitMinLoops + 3,
kMaximumNumberLoopAttempts = (1 << (kFlagBitMaxLoops - kFlagBitMinLoops)),
kSelectModeRead = 1,
kSelectModeWrite = 2,
kSelectModeExcept = 4
};
#if 0
#pragma mark -
#pragma mark Type Declarations
#pragma mark *CFStream Context
#endif
typedef struct {
CFSpinLock_t _lock;
UInt32 _flags;
CFStreamError _error;
CFReadStreamRef _clientReadStream;
CFWriteStreamRef _clientWriteStream;
CFSocketRef _socket;
CFMutableArrayRef _readloops;
CFMutableArrayRef _writeloops;
CFMutableArrayRef _sharedloops;
CFMutableArrayRef _schedulables;
CFMutableDictionaryRef _properties;
} _CFSocketStreamContext;
#if 0
#pragma mark *Other Types
#endif
typedef void (*_CFSocketStreamSocketCreatedCallBack)(CFSocketNativeHandle s, void* info);
typedef void (*_CFSocketStreamPerformHandshakeCallBack)(_CFSocketStreamContext* ctxt);
#if 0
#pragma mark -
#pragma mark Static Function Declarations
#pragma mark *Stream Callbacks
#endif
static void _SocketStreamFinalize(CFTypeRef stream, _CFSocketStreamContext* ctxt);
static CFStringRef _SocketStreamCopyDescription(CFTypeRef stream, _CFSocketStreamContext* ctxt);
static Boolean _SocketStreamOpen(CFTypeRef stream, CFStreamError* error, Boolean* openComplete, _CFSocketStreamContext* ctxt);
static Boolean _SocketStreamOpenCompleted(CFTypeRef stream, CFStreamError* error, _CFSocketStreamContext* ctxt);
static CFIndex _SocketStreamRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, _CFSocketStreamContext* ctxt);
static Boolean _SocketStreamCanRead(CFReadStreamRef stream, _CFSocketStreamContext* ctxt);
static CFIndex _SocketStreamWrite(CFWriteStreamRef stream, const UInt8* buffer, CFIndex bufferLength, CFStreamError* error, _CFSocketStreamContext* ctxt);
static Boolean _SocketStreamCanWrite(CFWriteStreamRef stream, _CFSocketStreamContext* ctxt);
static void _SocketStreamClose(CFTypeRef stream, _CFSocketStreamContext* ctxt);
static CFTypeRef _SocketStreamCopyProperty(CFTypeRef stream, CFStringRef propertyName, _CFSocketStreamContext* ctxt);
static Boolean _SocketStreamSetProperty(CFTypeRef stream, CFStringRef propertyName, CFTypeRef propertyValue, _CFSocketStreamContext* ctxt);
static void _SocketStreamSchedule(CFTypeRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, _CFSocketStreamContext* ctxt);
static void _SocketStreamUnschedule(CFTypeRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, _CFSocketStreamContext* ctxt);
#if 0
#pragma mark *Utility Functions
#endif
static void _SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, _CFSocketStreamContext*info);
static void _HostCallBack(CFHostRef theHost, CFHostInfoType typeInfo, const CFStreamError* error, _CFSocketStreamContext* info);
static void _NetServiceCallBack(CFNetServiceRef theService, CFStreamError* error, _CFSocketStreamContext* info);
static void _SocksHostCallBack(CFHostRef theHost, CFHostInfoType typeInfo, const CFStreamError* error, _CFSocketStreamContext* info);
static void _ReachabilityCallBack(SCNetworkReachabilityRef target, const SCNetworkConnectionFlags flags, _CFSocketStreamContext* ctxt);
static void _NetworkConnectionCallBack(SCNetworkConnectionRef conn, SCNetworkConnectionStatus status, _CFSocketStreamContext* ctxt);
static Boolean _SchedulablesAdd(CFMutableArrayRef schedulables, CFTypeRef addition);
static Boolean _SchedulablesRemove(CFMutableArrayRef schedulables, CFTypeRef removal);
static void _SchedulablesScheduleApplierFunction(CFTypeRef obj, CFTypeRef loopAndMode[]);
static void _SchedulablesUnscheduleApplierFunction(CFTypeRef obj, CFTypeRef loopAndMode[]);
static void _SchedulablesUnscheduleFromAllApplierFunction(CFTypeRef obj, CFArrayRef schedules);
static void _SchedulablesInvalidateApplierFunction(CFTypeRef obj, void* context);
static void _SocketStreamSchedule_NoLock(CFTypeRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, _CFSocketStreamContext* ctxt);
static void _SocketStreamUnschedule_NoLock(CFTypeRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, _CFSocketStreamContext* ctxt);
static CFNumberRef _CFNumberCopyPortForOpen(CFDictionaryRef properties);
static CFDataRef _CFDataCopyAddressByInjectingPort(CFDataRef address, CFNumberRef port);
static Boolean _ScheduleAndStartLookup(CFTypeRef lookup, CFArrayRef* schedules, CFStreamError* error, const void* cb, void* info);
static CFIndex _CFSocketRecv(CFSocketRef s, UInt8* buffer, CFIndex length, CFStreamError* error);
static CFIndex _CFSocketSend(CFSocketRef s, const UInt8* buffer, CFIndex length, CFStreamError* error);
static Boolean _CFSocketCan(CFSocketRef s, int mode);
static _CFSocketStreamContext* _SocketStreamCreateContext(CFAllocatorRef alloc);
static void _SocketStreamDestroyContext_NoLock(CFAllocatorRef alloc, _CFSocketStreamContext* ctxt);
static Boolean _SocketStreamStartLookupForOpen_NoLock(_CFSocketStreamContext* ctxt);
static Boolean _SocketStreamCreateSocket_NoLock(_CFSocketStreamContext* ctxt, CFDataRef address);
static Boolean _SocketStreamConnect_NoLock(_CFSocketStreamContext* ctxt, CFDataRef address);
static Boolean _SocketStreamAttemptNextConnection_NoLock(_CFSocketStreamContext* ctxt);
static Boolean _SocketStreamCan(_CFSocketStreamContext* ctxt, CFTypeRef stream, int test, CFStringRef mode, CFStreamError* error);
static void _SocketStreamAddReachability_NoLock(_CFSocketStreamContext* ctxt);
static void _SocketStreamRemoveReachability_NoLock(_CFSocketStreamContext* ctxt);
static CFComparisonResult _OrderHandshakes(_CFSocketStreamPerformHandshakeCallBack fn1, _CFSocketStreamPerformHandshakeCallBack fn2, void* context);
static Boolean _SocketStreamAddHandshake_NoLock(_CFSocketStreamContext* ctxt, _CFSocketStreamPerformHandshakeCallBack fn);
static void _SocketStreamRemoveHandshake_NoLock(_CFSocketStreamContext* ctxt, _CFSocketStreamPerformHandshakeCallBack fn);
static void _SocketStreamAttemptAutoVPN_NoLock(_CFSocketStreamContext* ctxt, CFStringRef name);
static CFIndex _SocketStreamBufferedRead_NoLock(_CFSocketStreamContext* ctxt, UInt8* buffer, CFIndex length);
static void _SocketStreamBufferedSocketRead_NoLock(_CFSocketStreamContext* ctxt);
static void _SocketStreamPerformCancel(void* info);
CF_INLINE SInt32 _LastError(CFStreamError* error) {
error->domain = _kCFStreamErrorDomainNativeSockets;
#if defined(__WIN32__)
error->error = WSAGetLastError();
if (!error->error) {
error->error = errno;
error->domain = kCFStreamErrorDomainPOSIX;
}
#else
error->error = errno;
#endif
return error->error;
}
#if 0
#pragma mark *SOCKS Support
#endif
static void _PerformSOCKSv5Handshake_NoLock(_CFSocketStreamContext* ctxt);
static void _PerformSOCKSv5PostambleHandshake_NoLock(_CFSocketStreamContext* ctxt);
static void _PerformSOCKSv5UserPassHandshake_NoLock(_CFSocketStreamContext* ctxt);
static void _PerformSOCKSv4Handshake_NoLock(_CFSocketStreamContext* ctxt);
static Boolean _SOCKSSetInfo_NoLock(_CFSocketStreamContext* ctxt, CFDictionaryRef settings);
static void _SocketStreamSOCKSHandleLookup_NoLock(_CFSocketStreamContext* ctxt, CFHostRef lookup);
CF_INLINE CFStringRef _GetSOCKSVersion(CFDictionaryRef settings) {
CFStringRef result = (CFStringRef)CFDictionaryGetValue(settings, kCFStreamPropertySOCKSVersion);
if (!result)
result = kCFStreamSocketSOCKSVersion5;
return result;
}
#if 0
#pragma mark *CONNECT Support
#endif
static void _CreateNameAndPortForCONNECTProxy(CFDictionaryRef properties, CFStringRef* name, CFNumberRef* port, CFStreamError* error);
static void _PerformCONNECTHandshake_NoLock(_CFSocketStreamContext* ctxt);
static void _PerformCONNECTHaltHandshake_NoLock(_CFSocketStreamContext* ctxt);
static void _CONNECTHeaderApplier(CFStringRef key, CFStringRef value, CFHTTPMessageRef request);
static Boolean _CONNECTSetInfo_NoLock(_CFSocketStreamContext* ctxt, CFDictionaryRef settings);
#if 0
#pragma mark *SSL Support
#endif
static OSStatus _SecurityReadFunc_NoLock(_CFSocketStreamContext* ctxt, void* data, UInt32* dataLength);
static OSStatus _SecurityWriteFunc_NoLock(_CFSocketStreamContext* ctxt, const void* data, UInt32* dataLength);
static CFIndex _SocketStreamSecuritySend_NoLock(_CFSocketStreamContext* ctxt, const UInt8* buffer, CFIndex length);
static void _SocketStreamSecurityBufferedRead_NoLock(_CFSocketStreamContext* ctxt);
static void _PerformSecurityHandshake_NoLock(_CFSocketStreamContext* ctxt);
static void _PerformSecuritySendHandshake_NoLock(_CFSocketStreamContext* ctxt);
static void _SocketStreamSecurityClose_NoLock(_CFSocketStreamContext* ctxt);
static Boolean _SocketStreamSecuritySetContext_NoLock(_CFSocketStreamContext *ctxt, CFDataRef value);
static Boolean _SocketStreamSecuritySetInfo_NoLock(_CFSocketStreamContext* ctxt, CFDictionaryRef settings);
static Boolean _SocketStreamSecuritySetAuthenticatesServerCertificates_NoLock(_CFSocketStreamContext* ctxt, CFBooleanRef authenticates);
static CFStringRef _SecurityGetProtocol(SSLContextRef security);
static SSLSessionState _SocketStreamSecurityGetSessionState_NoLock(_CFSocketStreamContext* ctxt);
#if 0
#pragma mark -
#pragma mark Extern Function Declarations
#endif
extern void _CFStreamCreatePairWithCFSocketSignaturePieces(CFAllocatorRef alloc, SInt32 protocolFamily, SInt32 socketType,
SInt32 protocol, CFDataRef address, CFReadStreamRef* readStream,
CFWriteStreamRef* writeStream);
extern void _CFSocketStreamCreatePair(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFSocketNativeHandle s,
const CFSocketSignature* sig, CFReadStreamRef* readStream, CFWriteStreamRef* writeStream);
extern CFDataRef _CFHTTPMessageCopySerializedHeaders(CFHTTPMessageRef msg, Boolean forProxy);
#if 0
#pragma mark -
#pragma mark Callback Structs
#pragma mark *CFReadStreamCallBacks
#endif
static const CFReadStreamCallBacks
kSocketReadStreamCallBacks = {
1,
NULL,
(void (*)(CFReadStreamRef, void*))_SocketStreamFinalize,
(CFStringRef (*)(CFReadStreamRef, void*))_SocketStreamCopyDescription,
(Boolean (*)(CFReadStreamRef, CFStreamError*, Boolean*, void*))_SocketStreamOpen,
(Boolean (*)(CFReadStreamRef, CFStreamError*, void*))_SocketStreamOpenCompleted,
(CFIndex (*)(CFReadStreamRef, UInt8*, CFIndex, CFStreamError*, Boolean*, void*))_SocketStreamRead,
NULL,
(Boolean (*)(CFReadStreamRef, void*))_SocketStreamCanRead,
(void (*)(CFReadStreamRef, void*))_SocketStreamClose,
(CFTypeRef (*)(CFReadStreamRef, CFStringRef, void*))_SocketStreamCopyProperty,
(Boolean (*)(CFReadStreamRef, CFStringRef, CFTypeRef, void*))_SocketStreamSetProperty,
NULL,
(void (*)(CFReadStreamRef, CFRunLoopRef, CFStringRef, void*))_SocketStreamSchedule,
(void (*)(CFReadStreamRef, CFRunLoopRef, CFStringRef, void*))_SocketStreamUnschedule
};
#if 0
#pragma mark *CFWriteStreamCallBacks
#endif
static const CFWriteStreamCallBacks
kSocketWriteStreamCallBacks = {
1,
NULL,
(void (*)(CFWriteStreamRef, void*))_SocketStreamFinalize,
(CFStringRef (*)(CFWriteStreamRef, void*))_SocketStreamCopyDescription,
(Boolean (*)(CFWriteStreamRef, CFStreamError*, Boolean*, void*))_SocketStreamOpen,
(Boolean (*)(CFWriteStreamRef, CFStreamError*, void*))_SocketStreamOpenCompleted,
(CFIndex (*)(CFWriteStreamRef, const UInt8*, CFIndex, CFStreamError*, void*))_SocketStreamWrite,
(Boolean (*)(CFWriteStreamRef, void*))_SocketStreamCanWrite,
(void (*)(CFWriteStreamRef, void*))_SocketStreamClose,
(CFTypeRef (*)(CFWriteStreamRef, CFStringRef, void*))_SocketStreamCopyProperty,
(Boolean (*)(CFWriteStreamRef, CFStringRef, CFTypeRef, void*))_SocketStreamSetProperty,
NULL,
(void (*)(CFWriteStreamRef, CFRunLoopRef, CFStringRef, void*))_SocketStreamSchedule,
(void (*)(CFWriteStreamRef, CFRunLoopRef, CFStringRef, void*))_SocketStreamUnschedule
};
#if 0
#pragma mark -
#pragma mark CFStream Callback Functions
#endif
void
_SocketStreamFinalize(CFTypeRef stream, _CFSocketStreamContext* ctxt) {
_SocketStreamClose(stream, ctxt);
__CFSpinLock(&ctxt->_lock);
if (__CFBitIsSet(ctxt->_flags, kFlagBitShared)) {
__CFBitClear(ctxt->_flags, kFlagBitShared);
__CFSpinUnlock(&ctxt->_lock);
}
else {
_SocketStreamDestroyContext_NoLock(CFGetAllocator(stream), ctxt);
}
}
CFStringRef
_SocketStreamCopyDescription(CFTypeRef stream, _CFSocketStreamContext* ctxt) {
return CFStringCreateWithFormat(CFGetAllocator(stream),
NULL,
kCFSocketStreamDescriptionFormat,
stream,
ctxt->_flags,
ctxt->_clientReadStream,
ctxt->_clientWriteStream,
ctxt->_socket,
ctxt->_properties);
}
Boolean
_SocketStreamOpen(CFTypeRef stream, CFStreamError* error, Boolean* openComplete,
_CFSocketStreamContext* ctxt)
{
Boolean result = TRUE;
CFRunLoopRef rl = NULL;
memset(error, 0, sizeof(error[0]));
*openComplete = FALSE;
__CFSpinLock(&ctxt->_lock);
__CFBitSet(ctxt->_flags, (stream == ctxt->_clientReadStream) ? kFlagBitReadStreamOpened : kFlagBitWriteStreamOpened);
rl = (CFRunLoopRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyBogusRunLoop);
if (!rl) {
rl = CFRunLoopGetCurrent();
CFDictionaryAddValue(ctxt->_properties, _kCFStreamPropertyBogusRunLoop, rl);
}
if (__CFBitIsSet(ctxt->_flags, kFlagBitOpenComplete)) {
memmove(error, &ctxt->_error, sizeof(error[0]));
*openComplete = TRUE;
}
else if (!__CFBitIsSet(ctxt->_flags, kFlagBitOpenStarted)) {
__CFBitSet(ctxt->_flags, kFlagBitOpenStarted);
if (ctxt->_error.error) {
__CFBitSet(ctxt->_flags, kFlagBitOpenComplete);
__CFBitClear(ctxt->_flags, kFlagBitPollOpen);
}
else if (CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyHostForOpen)) {
_SocketStreamAttemptNextConnection_NoLock(ctxt);
}
else if (!_SocketStreamStartLookupForOpen_NoLock(ctxt)) {
if (!ctxt->_error.error)
_SocketStreamAttemptNextConnection_NoLock(ctxt);
}
if (__CFBitIsSet(ctxt->_flags, kFlagBitOpenComplete)) {
__CFBitClear(ctxt->_flags, kFlagBitOpenStarted);
*openComplete = TRUE;
}
if (ctxt->_error.error)
memmove(error, &ctxt->_error, sizeof(error[0]));
}
if (error->error) {
__CFBitSet(ctxt->_flags, kFlagBitOpenComplete);
__CFBitClear(ctxt->_flags, kFlagBitPollOpen);
*openComplete = TRUE;
result = FALSE;
}
if (result && rl)
_SocketStreamSchedule_NoLock(stream, rl, _kCFStreamSocketBogusPrivateMode, ctxt);
__CFSpinUnlock(&ctxt->_lock);
return result;
}
Boolean
_SocketStreamOpenCompleted(CFTypeRef stream, CFStreamError* error, _CFSocketStreamContext* ctxt) {
return _SocketStreamCan(ctxt, stream, kFlagBitOpenComplete, _kCFStreamSocketOpenCompletedPrivateMode, error);
}
CFIndex
_SocketStreamRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength,
CFStreamError* error, Boolean* atEOF, _CFSocketStreamContext* ctxt)
{
CFIndex result = 0;
CFStreamEventType event = kCFStreamEventNone;
memset(error, 0, sizeof(error[0]));
*atEOF = FALSE;
__CFSpinLock(&ctxt->_lock);
while (1) {
if (!ctxt->_error.error && !__CFBitIsSet(ctxt->_flags, kFlagBitCanRead)) {
CFRunLoopRef rl = CFRunLoopGetCurrent();
CFRunLoopSourceContext src = {0, rl, NULL, NULL, NULL, NULL, NULL, NULL, NULL, _SocketStreamPerformCancel};
CFRunLoopSourceRef cancel = CFRunLoopSourceCreate(CFGetAllocator(stream), 0, &src);
if (cancel) {
CFAbsoluteTime later;
CFTimeInterval interval;
CFNumberRef value = (CFNumberRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyReadTimeout);
CFTypeRef loopAndMode[2] = {rl, _kCFStreamSocketReadPrivateMode};
CFDictionaryAddValue(ctxt->_properties, _kCFStreamPropertyReadCancel, cancel);
CFRelease(cancel);
if (!value || !CFNumberGetValue(value, kCFNumberDoubleType, &interval))
interval = kReadWriteTimeoutInterval;
later = (interval == 0.0) ? DBL_MAX : CFAbsoluteTimeGetCurrent() + interval;
_SchedulesAddRunLoopAndMode(ctxt->_readloops, (CFRunLoopRef)loopAndMode[0], (CFStringRef)loopAndMode[1]);
CFArrayApplyFunction(ctxt->_schedulables,
CFRangeMake(0, CFArrayGetCount(ctxt->_schedulables)),
(CFArrayApplierFunction)_SchedulablesScheduleApplierFunction,
loopAndMode);
CFRunLoopAddSource((CFRunLoopRef)loopAndMode[0], cancel, (CFStringRef)loopAndMode[1]);
__CFBitSet(ctxt->_flags, kFlagBitReadHasCancel);
do {
__CFSpinUnlock(&ctxt->_lock);
CFRunLoopRunInMode(_kCFStreamSocketReadPrivateMode, interval, TRUE);
__CFSpinLock(&ctxt->_lock);
} while (!ctxt->_error.error &&
!__CFBitIsSet(ctxt->_flags, kFlagBitCanRead) &&
(0 < (interval = (later - CFAbsoluteTimeGetCurrent()))));
__CFBitClear(ctxt->_flags, kFlagBitReadHasCancel);
CFRunLoopRemoveSource((CFRunLoopRef)loopAndMode[0], cancel, (CFStringRef)loopAndMode[1]);
CFArrayApplyFunction(ctxt->_schedulables,
CFRangeMake(0, CFArrayGetCount(ctxt->_schedulables)),
(CFArrayApplierFunction)_SchedulablesUnscheduleApplierFunction,
loopAndMode);
_SchedulesRemoveRunLoopAndMode(ctxt->_readloops, (CFRunLoopRef)loopAndMode[0], (CFStringRef)loopAndMode[1]);
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertyReadCancel);
if (!__CFBitIsSet(ctxt->_flags, kFlagBitCanRead) && !ctxt->_error.error) {
ctxt->_error.error = ETIMEDOUT;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
}
}
else {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
}
}
if (__CFBitIsSet(ctxt->_flags, kFlagBitIsBuffered)) {
result = _SocketStreamBufferedRead_NoLock(ctxt, buffer, bufferLength);
}
else if (!ctxt->_error.error) {
result = _CFSocketRecv(ctxt->_socket, buffer, bufferLength, &ctxt->_error);
}
__CFBitClear(ctxt->_flags, kFlagBitCanRead);
if ((ctxt->_error.error == EAGAIN) && (ctxt->_error.domain == _kCFStreamErrorDomainNativeSockets)) {
memset(&ctxt->_error, 0, sizeof(ctxt->_error));
continue;
}
break;
}
if (result > 0) {
if (!__CFBitIsSet(ctxt->_flags, kFlagBitHasHandshakes)) {
if (__CFBitIsSet(ctxt->_flags, kFlagBitIsBuffered)) {
CFDataRef c = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferCount);
if (__CFBitIsSet(ctxt->_flags, kFlagBitClosed) || (c && *((CFIndex*)CFDataGetBytePtr(c)))) {
__CFBitSet(ctxt->_flags, kFlagBitCanRead);
__CFBitClear(ctxt->_flags, kFlagBitPollRead);
event = kCFStreamEventHasBytesAvailable;
}
}
else if (!_CFSocketCan(ctxt->_socket, kSelectModeRead))
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketReadCallBack);
else {
event = kCFStreamEventHasBytesAvailable;
__CFBitSet(ctxt->_flags, kFlagBitCanRead);
__CFBitClear(ctxt->_flags, kFlagBitPollRead);
}
}
else {
CFMutableArrayRef handshakes = (CFMutableArrayRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyHandshakes);
if (handshakes &&
CFArrayGetCount(handshakes) &&
(_PerformCONNECTHaltHandshake_NoLock == CFArrayGetValueAtIndex(handshakes, 0)))
{
if (!_CFSocketCan(ctxt->_socket, kSelectModeRead))
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketReadCallBack);
else {
event = kCFStreamEventHasBytesAvailable;
__CFBitSet(ctxt->_flags, kFlagBitCanRead);
__CFBitClear(ctxt->_flags, kFlagBitPollRead);
}
}
}
}
else if (ctxt->_error.error) {
memmove(error, &ctxt->_error, sizeof(error[0]));
if (ctxt->_clientWriteStream && __CFBitIsSet(ctxt->_flags, kFlagBitWriteStreamOpened))
_CFWriteStreamSignalEventDelayed(ctxt->_clientWriteStream, kCFStreamEventErrorOccurred, error);
*atEOF = TRUE;
result = -1;
CFSocketDisableCallBacks(ctxt->_socket, kCFSocketReadCallBack | kCFSocketWriteCallBack);
}
else if (!result) {
*atEOF = TRUE;
CFSocketDisableCallBacks(ctxt->_socket, kCFSocketReadCallBack);
}
__CFSpinUnlock(&ctxt->_lock);
if (event != kCFStreamEventNone)
CFReadStreamSignalEvent(stream, event, NULL);
return result;
}
Boolean
_SocketStreamCanRead(CFReadStreamRef stream, _CFSocketStreamContext* ctxt) {
CFStreamError error;
Boolean result = FALSE;
__CFSpinLock(&ctxt->_lock);
if (!__CFBitIsSet(ctxt->_flags, kFlagBitHasHandshakes) && __CFBitIsSet(ctxt->_flags, kFlagBitIsBuffered)) {
CFDataRef c = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferCount);
if (__CFBitIsSet(ctxt->_flags, kFlagBitClosed) || (c && *((CFIndex*)CFDataGetBytePtr(c)))) {
result = TRUE;
__CFSpinUnlock(&ctxt->_lock);
}
else if (__CFBitIsSet(ctxt->_flags, kFlagBitUseSSL)) {
_SocketStreamSecurityBufferedRead_NoLock(ctxt);
result = __CFBitIsSet(ctxt->_flags, kFlagBitCanRead) || __CFBitIsSet(ctxt->_flags, kFlagBitClosed);
__CFSpinUnlock(&ctxt->_lock);
}
else {
__CFSpinUnlock(&ctxt->_lock);
result = _SocketStreamCan(ctxt, stream, kFlagBitCanRead, _kCFStreamSocketCanReadPrivateMode, &error);
}
}
else {
__CFSpinUnlock(&ctxt->_lock);
result = _SocketStreamCan(ctxt, stream, kFlagBitCanRead, _kCFStreamSocketCanReadPrivateMode, &error);
}
return result;
}
CFIndex
_SocketStreamWrite(CFWriteStreamRef stream, const UInt8* buffer, CFIndex bufferLength,
CFStreamError* error, _CFSocketStreamContext* ctxt)
{
CFIndex result = 0;
CFStreamEventType event = kCFStreamEventNone;
memset(error, 0, sizeof(error[0]));
__CFSpinLock(&ctxt->_lock);
while (1) {
if (!ctxt->_error.error && !__CFBitIsSet(ctxt->_flags, kFlagBitCanWrite)) {
CFRunLoopRef rl = CFRunLoopGetCurrent();
CFRunLoopSourceContext src = {0, rl, NULL, NULL, NULL, NULL, NULL, NULL, NULL, _SocketStreamPerformCancel};
CFRunLoopSourceRef cancel = CFRunLoopSourceCreate(CFGetAllocator(stream), 0, &src);
if (cancel) {
CFAbsoluteTime later;
CFTimeInterval interval;
CFNumberRef value = (CFNumberRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyWriteTimeout);
CFTypeRef loopAndMode[2] = {rl, _kCFStreamSocketWritePrivateMode};
CFDictionaryAddValue(ctxt->_properties, _kCFStreamPropertyWriteCancel, cancel);
CFRelease(cancel);
if (!value || !CFNumberGetValue(value, kCFNumberDoubleType, &interval))
interval = kReadWriteTimeoutInterval;
later = (interval == 0.0) ? DBL_MAX : CFAbsoluteTimeGetCurrent() + interval;
_SchedulesAddRunLoopAndMode(ctxt->_writeloops, (CFRunLoopRef)loopAndMode[0], (CFStringRef)loopAndMode[1]);
__CFBitSet(ctxt->_flags, kFlagBitWriteHasCancel);
CFArrayApplyFunction(ctxt->_schedulables,
CFRangeMake(0, CFArrayGetCount(ctxt->_schedulables)),
(CFArrayApplierFunction)_SchedulablesScheduleApplierFunction,
loopAndMode);
CFRunLoopAddSource((CFRunLoopRef)loopAndMode[0], cancel, (CFStringRef)loopAndMode[1]);
do {
__CFSpinUnlock(&ctxt->_lock);
CFRunLoopRunInMode(_kCFStreamSocketWritePrivateMode, interval, FALSE);
__CFSpinLock(&ctxt->_lock);
} while (!ctxt->_error.error &&
!__CFBitIsSet(ctxt->_flags, kFlagBitCanWrite) &&
(0 < (interval = (later - CFAbsoluteTimeGetCurrent()))));
__CFBitClear(ctxt->_flags, kFlagBitWriteHasCancel);
CFRunLoopRemoveSource((CFRunLoopRef)loopAndMode[0], cancel, (CFStringRef)loopAndMode[1]);
CFArrayApplyFunction(ctxt->_schedulables,
CFRangeMake(0, CFArrayGetCount(ctxt->_schedulables)),
(CFArrayApplierFunction)_SchedulablesUnscheduleApplierFunction,
loopAndMode);
_SchedulesRemoveRunLoopAndMode(ctxt->_writeloops, (CFRunLoopRef)loopAndMode[0], (CFStringRef)loopAndMode[1]);
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertyWriteCancel);
if (!__CFBitIsSet(ctxt->_flags, kFlagBitCanWrite) && !ctxt->_error.error) {
ctxt->_error.error = ETIMEDOUT;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
}
}
else {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
}
}
if (!ctxt->_error.error) {
if (__CFBitIsSet(ctxt->_flags, kFlagBitUseSSL))
result = _SocketStreamSecuritySend_NoLock(ctxt, buffer, bufferLength);
else
result = _CFSocketSend(ctxt->_socket, buffer, bufferLength, &ctxt->_error);
}
__CFBitClear(ctxt->_flags, kFlagBitCanWrite);
if ((ctxt->_error.error == EAGAIN) && (ctxt->_error.domain == _kCFStreamErrorDomainNativeSockets)) {
memset(&ctxt->_error, 0, sizeof(ctxt->_error));
continue;
}
break;
}
if (ctxt->_error.error) {
CFDataRef c = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferCount);
if (!c || !*((CFIndex*)CFDataGetBytePtr(c))) {
memmove(error, &ctxt->_error, sizeof(error[0]));
if (ctxt->_clientReadStream && __CFBitIsSet(ctxt->_flags, kFlagBitReadStreamOpened))
_CFReadStreamSignalEventDelayed(ctxt->_clientReadStream, kCFStreamEventErrorOccurred, error);
}
result = -1;
CFSocketDisableCallBacks(ctxt->_socket, kCFSocketWriteCallBack | kCFSocketReadCallBack);
}
else if (!result) {
CFSocketDisableCallBacks(ctxt->_socket, kCFSocketWriteCallBack);
}
else {
if (!__CFBitIsSet(ctxt->_flags, kFlagBitHasHandshakes)) {
if (__CFBitIsSet(ctxt->_flags, kFlagBitClosed)) {
event = kCFStreamEventEndEncountered;
__CFBitSet(ctxt->_flags, kFlagBitCanWrite);
__CFBitClear(ctxt->_flags, kFlagBitPollWrite);
}
else if (!_CFSocketCan(ctxt->_socket, kSelectModeWrite))
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketWriteCallBack);
else {
event = kCFStreamEventCanAcceptBytes;
__CFBitSet(ctxt->_flags, kFlagBitCanWrite);
__CFBitClear(ctxt->_flags, kFlagBitPollWrite);
}
}
}
__CFSpinUnlock(&ctxt->_lock);
if (event != kCFStreamEventNone)
CFWriteStreamSignalEvent(stream, event, NULL);
return result;
}
Boolean
_SocketStreamCanWrite(CFWriteStreamRef stream, _CFSocketStreamContext* ctxt) {
CFStreamError error;
return _SocketStreamCan(ctxt, stream, kFlagBitCanWrite, _kCFStreamSocketCanWritePrivateMode, &error);
}
void
_SocketStreamClose(CFTypeRef stream, _CFSocketStreamContext* ctxt) {
CFMutableArrayRef loops, otherloops;
CFIndex count;
CFRunLoopRef rl;
__CFSpinLock(&ctxt->_lock);
rl = (CFRunLoopRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyBogusRunLoop);
if (rl) {
_SocketStreamUnschedule_NoLock(stream, rl, _kCFStreamSocketBogusPrivateMode, ctxt);
}
if (CFGetTypeID(stream) == CFReadStreamGetTypeID()) {
ctxt->_clientReadStream = NULL;
loops = ctxt->_readloops;
otherloops = ctxt->_writeloops;
}
else {
ctxt->_clientWriteStream = NULL;
loops = ctxt->_writeloops;
otherloops = ctxt->_readloops;
}
CFArrayApplyFunction(ctxt->_schedulables,
CFRangeMake(0, CFArrayGetCount(ctxt->_schedulables)),
(CFArrayApplierFunction)_SchedulablesUnscheduleFromAllApplierFunction,
loops);
CFArrayRemoveAllValues(loops);
if ((count = CFArrayGetCount(ctxt->_sharedloops))) {
CFArrayAppendArray(otherloops, ctxt->_sharedloops, CFRangeMake(0, count));
CFArrayRemoveAllValues(ctxt->_sharedloops);
}
if (!ctxt->_clientReadStream && !ctxt->_clientWriteStream) {
CFRange r;
if (CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketSSLContext))
_SocketStreamSecurityClose_NoLock(ctxt);
r = CFRangeMake(0, CFArrayGetCount(ctxt->_schedulables));
CFArrayApplyFunction(ctxt->_schedulables, r, (CFArrayApplierFunction)_SchedulablesUnscheduleFromAllApplierFunction, otherloops);
CFArrayRemoveAllValues(otherloops);
CFArrayApplyFunction(ctxt->_schedulables, r, (CFArrayApplierFunction)_SchedulablesInvalidateApplierFunction, NULL);
CFArrayRemoveAllValues(ctxt->_schedulables);
if (ctxt->_socket) {
CFSocketInvalidate(ctxt->_socket);
CFRelease(ctxt->_socket);
ctxt->_socket = NULL;
}
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertyBogusRunLoop);
}
__CFSpinUnlock(&ctxt->_lock);
}
CFTypeRef
_SocketStreamCopyProperty(CFTypeRef stream, CFStringRef propertyName, _CFSocketStreamContext* ctxt) {
CFTypeRef result = NULL;
CFTypeRef property;
__CFSpinLock(&ctxt->_lock);
property = CFDictionaryGetValue(ctxt->_properties, propertyName);
if (!property) {
if (CFEqual(kCFStreamPropertySocketRemoteHostName, propertyName)) {
CFTypeRef host = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketRemoteHost);
if (host) {
CFArrayRef list = CFHostGetNames((CFHostRef)host, NULL);
if (list && CFArrayGetCount(list))
property = CFArrayGetValueAtIndex(list, 0);
}
else {
host = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketRemoteNetService);
if (host)
property = CFNetServiceGetTargetHost((CFNetServiceRef)host);
}
}
else if (CFEqual(kCFStreamPropertySocketNativeHandle, propertyName) && ctxt->_socket) {
CFSocketNativeHandle s = CFSocketGetNative(ctxt->_socket);
result = CFDataCreate(CFGetAllocator(stream), (const void*)(&s), sizeof(s));
}
else if (CFEqual(kCFStreamPropertyCONNECTResponse, propertyName)) {
if (CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyCONNECTProxy))
result = CFHTTPMessageCreateEmpty(CFGetAllocator(stream), FALSE);
}
else if (CFEqual(kCFStreamPropertySSLPeerCertificates, propertyName)) {
CFDataRef wrapper = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketSSLContext);
if (wrapper) {
if (SSLGetPeerCertificates(*((SSLContextRef*)CFDataGetBytePtr(wrapper)), (CFArrayRef*)&result) && result) {
CFRelease(result);
result = NULL;
}
}
}
else if (CFEqual(_kCFStreamPropertySSLClientCertificates, propertyName)) {
CFDataRef wrapper = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketSSLContext);
if (wrapper) {
if (SSLGetCertificate(*((SSLContextRef*)CFDataGetBytePtr(wrapper)), (CFArrayRef*)&result) && result) {
result = NULL;
} else if (result) {
CFRetain(result);
}
}
}
else if (CFEqual(_kCFStreamPropertySSLClientCertificateState, propertyName)) {
CFDataRef wrapper = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketSSLContext);
if (wrapper) {
SSLClientCertificateState clientState = kSSLClientCertNone;
if (SSLGetClientCertificateState(*((SSLContextRef*)CFDataGetBytePtr(wrapper)), &clientState)) {
result = NULL;
} else {
result = CFNumberCreate(CFGetAllocator(ctxt->_properties), kCFNumberIntType, &clientState);
}
}
}
}
if (CFEqual(propertyName, kCFStreamPropertySocketSecurityLevel)) {
CFDataRef wrapper = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketSSLContext);
if (wrapper)
property = _SecurityGetProtocol(*((SSLContextRef*)CFDataGetBytePtr(wrapper)));
}
if (property) {
CFTypeID type = CFGetTypeID(property);
if (CFHostGetTypeID() == type)
result = CFHostCreateCopy(CFGetAllocator(stream), (CFHostRef)property);
else if (CFNetServiceGetTypeID() == type)
result = CFNetServiceCreateCopy(CFGetAllocator(stream), (CFNetServiceRef)property);
else if (CFDictionaryGetTypeID() == type)
result = CFDictionaryCreateCopy(CFGetAllocator(stream), (CFDictionaryRef)property);
else if (CFArrayGetTypeID() == type)
result = CFArrayCreateCopy(CFGetAllocator(stream), (CFArrayRef)property);
else if (CFHTTPMessageGetTypeID() == type)
result = CFHTTPMessageCreateCopy(CFGetAllocator(stream), (CFHTTPMessageRef)property);
else
result = CFRetain(property);
}
__CFSpinUnlock(&ctxt->_lock);
return result;
}
Boolean
_SocketStreamSetProperty(CFTypeRef stream, CFStringRef propertyName, CFTypeRef propertyValue,
_CFSocketStreamContext* ctxt)
{
Boolean result = FALSE;
__CFSpinLock(&ctxt->_lock);
if (CFEqual(propertyName, kCFStreamPropertyUseAddressCache) ||
CFEqual(propertyName, _kCFStreamSocketIChatWantsSubNet))
{
if (propertyValue)
CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
else
CFDictionaryRemoveValue(ctxt->_properties, propertyName);
result = TRUE;
}
else if (CFEqual(propertyName, kCFStreamPropertyAutoErrorOnSystemChange)) {
if (!propertyValue) {
CFDictionaryRemoveValue(ctxt->_properties, propertyName);
_SocketStreamAddReachability_NoLock(ctxt);
}
else {
CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
if (CFEqual(propertyValue, kCFBooleanFalse))
_SocketStreamRemoveReachability_NoLock(ctxt);
else
_SocketStreamAddReachability_NoLock(ctxt);
}
result = TRUE;
}
else if (CFEqual(propertyName, _kCFStreamSocketCreatedCallBack)) {
if (!propertyValue)
CFDictionaryRemoveValue(ctxt->_properties, propertyName);
else {
CFArrayRef old = (CFArrayRef)CFDictionaryGetValue(ctxt->_properties, propertyName);
if (!old || !CFEqual(old, propertyValue))
CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
}
result = TRUE;
}
else if (CFEqual(propertyName, kCFStreamPropertyShouldCloseNativeSocket)) {
if (propertyValue)
CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
else
CFDictionaryRemoveValue(ctxt->_properties, propertyName);
if (ctxt->_socket) {
CFOptionFlags flags = CFSocketGetSocketFlags(ctxt->_socket);
if (!propertyValue) {
if (__CFBitIsSet(ctxt->_flags, kFlagBitCreatedNative))
flags &= ~kCFSocketCloseOnInvalidate;
else
flags |= kCFSocketCloseOnInvalidate;
}
else if (propertyValue != kCFBooleanFalse)
flags |= kCFSocketCloseOnInvalidate;
else
flags &= ~kCFSocketCloseOnInvalidate;
CFSocketSetSocketFlags(ctxt->_socket, flags);
}
result = TRUE;
}
else if (CFEqual(propertyName, kCFStreamPropertyCONNECTProxy))
result = _CONNECTSetInfo_NoLock(ctxt, propertyValue);
else if (CFEqual(propertyName, kCFStreamPropertySocketSSLContext))
result = _SocketStreamSecuritySetContext_NoLock(ctxt, propertyValue);
else if (CFEqual(propertyName, kCFStreamPropertySSLSettings)) {
result = _SocketStreamSecuritySetInfo_NoLock(ctxt, propertyValue);
if (result) {
if (propertyValue)
CFDictionarySetValue(ctxt->_properties, kCFStreamPropertySSLSettings, propertyValue);
else
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertySSLSettings);
}
}
else if (CFEqual(propertyName, _kCFStreamPropertySocketSecurityAuthenticatesServerCertificate)) {
result = TRUE;
if (CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketSSLContext) &&
(_SocketStreamSecurityGetSessionState_NoLock(ctxt) == kSSLIdle))
{
result = _SocketStreamSecuritySetAuthenticatesServerCertificates_NoLock(ctxt, propertyValue ? propertyValue : kCFBooleanTrue);
}
if (result) {
if (propertyValue)
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertySocketSecurityAuthenticatesServerCertificate, propertyValue);
else
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertySocketSecurityAuthenticatesServerCertificate);
}
}
else if (CFEqual(propertyName, kCFStreamPropertySocketSecurityLevel)) {
CFMutableDictionaryRef settings = CFDictionaryCreateMutable(CFGetAllocator(ctxt->_properties),
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (settings) {
CFDictionaryAddValue(settings, kCFStreamSSLLevel, propertyValue);
result = _SocketStreamSecuritySetInfo_NoLock(ctxt, settings);
CFRelease(settings);
if (result) {
if (propertyValue)
CFDictionarySetValue(ctxt->_properties, kCFStreamPropertySocketSecurityLevel, propertyValue);
else
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertySocketSecurityLevel);
}
}
}
else if (CFEqual(propertyName, _kCFStreamPropertySocketPeerName)) {
if (propertyValue)
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertySocketPeerName, propertyValue);
else
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertySSLSettings);
result = TRUE;
}
else if (CFEqual(propertyName, kCFStreamPropertySOCKSProxy))
result = _SOCKSSetInfo_NoLock(ctxt, (CFDictionaryRef)propertyValue);
else if (CFEqual(propertyName, _kCFStreamPropertyHostForOpen) ||
CFEqual(propertyName, _kCFStreamPropertyReadTimeout) ||
CFEqual(propertyName, _kCFStreamPropertyWriteTimeout) ||
CFEqual(propertyName, _kCFStreamPropertyAutoConnectPriority))
{
if (propertyValue)
CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
else
CFDictionaryRemoveValue(ctxt->_properties, propertyName);
result = TRUE;
}
else if (CFEqual(propertyName, _kCFStreamPropertyRecvBufferSize) &&
!__CFBitIsSet(ctxt->_flags, kFlagBitOpenStarted) &&
!__CFBitIsSet(ctxt->_flags, kFlagBitOpenComplete))
{
if (!propertyValue) {
CFDictionaryRemoveValue(ctxt->_properties, propertyName);
__CFBitClear(ctxt->_flags, kFlagBitIsBuffered);
}
else if (CFNumberGetByteSize(propertyValue) == sizeof(CFIndex)) {
CFDictionarySetValue(ctxt->_properties, propertyName, propertyValue);
__CFBitSet(ctxt->_flags, kFlagBitIsBuffered);
}
result = TRUE;
}
if (ctxt->_error.error) {
CFDataRef c = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferCount);
if ((!c || !*((CFIndex*)CFDataGetBytePtr(c))) &&
(ctxt->_clientReadStream && __CFBitIsSet(ctxt->_flags, kFlagBitReadStreamOpened)))
{
_CFReadStreamSignalEventDelayed(ctxt->_clientReadStream, kCFStreamEventErrorOccurred, &ctxt->_error);
}
if (ctxt->_clientWriteStream && __CFBitIsSet(ctxt->_flags, kFlagBitWriteStreamOpened))
_CFWriteStreamSignalEventDelayed(ctxt->_clientWriteStream, kCFStreamEventErrorOccurred, &ctxt->_error);
}
__CFSpinUnlock(&ctxt->_lock);
return result;
}
void
_SocketStreamSchedule(CFTypeRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode,
_CFSocketStreamContext* ctxt)
{
__CFSpinLock(&ctxt->_lock);
_SocketStreamSchedule_NoLock(stream, runLoop, runLoopMode, ctxt);
__CFSpinUnlock(&ctxt->_lock);
}
void
_SocketStreamUnschedule(CFTypeRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode,
_CFSocketStreamContext* ctxt)
{
__CFSpinLock(&ctxt->_lock);
_SocketStreamUnschedule_NoLock(stream, runLoop, runLoopMode, ctxt);
__CFSpinUnlock(&ctxt->_lock);
}
#if 0
#pragma mark -
#pragma mark Utility Functions
#endif
void
_SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, _CFSocketStreamContext* ctxt) {
CFReadStreamRef rStream = NULL;
CFWriteStreamRef wStream = NULL;
CFStreamEventType event = kCFStreamEventNone;
CFStreamError error = {0, 0};
__CFSpinLock(&ctxt->_lock);
if (!ctxt->_error.error) {
switch (type) {
case kCFSocketConnectCallBack:
if (!data) {
CFBooleanRef reach = (CFBooleanRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyAutoErrorOnSystemChange);
__CFBitClear(ctxt->_flags, kFlagBitOpenStarted);
__CFBitSet(ctxt->_flags, kFlagBitOpenComplete);
__CFBitClear(ctxt->_flags, kFlagBitPollOpen);
event = kCFStreamEventOpenCompleted;
rStream = ctxt->_clientReadStream;
wStream = ctxt->_clientWriteStream;
if (!reach || (reach != kCFBooleanFalse))
_SocketStreamAddReachability_NoLock(ctxt);
}
else {
int i;
CFArrayRef loops[3] = {ctxt->_readloops, ctxt->_writeloops, ctxt->_sharedloops};
ctxt->_error.error = *((SInt32 *)data);
ctxt->_error.domain = _kCFStreamErrorDomainNativeSockets;
_SchedulablesRemove(ctxt->_schedulables, s);
for (i = 0; i < (sizeof(loops) / sizeof(loops[0])); i++)
_CFTypeUnscheduleFromMultipleRunLoops(s, loops[i]);
_CFTypeInvalidate(s);
CFRelease(s);
ctxt->_socket = NULL;
if (_SocketStreamAttemptNextConnection_NoLock(ctxt)) {
memset(&ctxt->_error, 0, sizeof(ctxt->_error));
}
}
break;
case kCFSocketReadCallBack:
if (__CFBitIsSet(ctxt->_flags, kFlagBitHasHandshakes)) {
CFArrayRef handshakes = (CFArrayRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyHandshakes);
((_CFSocketStreamPerformHandshakeCallBack)CFArrayGetValueAtIndex(handshakes, 0))(ctxt);
}
else if (__CFBitIsSet(ctxt->_flags, kFlagBitIsBuffered)) {
if (__CFBitIsSet(ctxt->_flags, kFlagBitUseSSL))
_SocketStreamSecurityBufferedRead_NoLock(ctxt);
else
_SocketStreamBufferedSocketRead_NoLock(ctxt);
if (__CFBitIsSet(ctxt->_flags, kFlagBitCanRead)) {
event = kCFStreamEventHasBytesAvailable;
rStream = ctxt->_clientReadStream;
}
}
else {
__CFBitSet(ctxt->_flags, kFlagBitCanRead);
__CFBitClear(ctxt->_flags, kFlagBitPollRead);
event = kCFStreamEventHasBytesAvailable;
rStream = ctxt->_clientReadStream;
}
break;
case kCFSocketWriteCallBack:
if (__CFBitIsSet(ctxt->_flags, kFlagBitHasHandshakes)) {
CFArrayRef handshakes = (CFArrayRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyHandshakes);
((_CFSocketStreamPerformHandshakeCallBack)CFArrayGetValueAtIndex(handshakes, 0))(ctxt);
}
else {
__CFBitSet(ctxt->_flags, kFlagBitCanWrite);
__CFBitClear(ctxt->_flags, kFlagBitPollWrite);
event = kCFStreamEventCanAcceptBytes;
wStream = ctxt->_clientWriteStream;
}
break;
default:
break;
}
}
if (ctxt->_error.error) {
memmove(&error, &ctxt->_error, sizeof(error));
event = kCFStreamEventErrorOccurred;
rStream = ctxt->_clientReadStream;
wStream = ctxt->_clientWriteStream;
}
if (rStream && __CFBitIsSet(ctxt->_flags, kFlagBitReadStreamOpened))
CFRetain(rStream);
else
rStream = NULL;
if (wStream && __CFBitIsSet(ctxt->_flags, kFlagBitWriteStreamOpened))
CFRetain(wStream);
else
wStream = NULL;
if (event != kCFStreamEventNone) {
CFRunLoopRef rrl = NULL, wrl = NULL;
CFRunLoopSourceRef rsrc = __CFBitIsSet(ctxt->_flags, kFlagBitReadHasCancel) ?
(CFRunLoopSourceRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyReadCancel) :
NULL;
CFRunLoopSourceRef wsrc = __CFBitIsSet(ctxt->_flags, kFlagBitWriteHasCancel) ?
(CFRunLoopSourceRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyWriteCancel) :
NULL;
if (rsrc) {
CFRunLoopSourceContext c = {0};
CFRetain(rsrc);
CFRunLoopSourceGetContext(rsrc, &c);
rrl = (CFRunLoopRef)(c.info);
}
if (wsrc) {
CFRunLoopSourceContext c = {0};
CFRetain(wsrc);
CFRunLoopSourceGetContext(wsrc, &c);
wrl = (CFRunLoopRef)(c.info);
}
if (rStream && (event == kCFStreamEventErrorOccurred)) {
CFDataRef c = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferCount);
if (c && *((CFIndex*)CFDataGetBytePtr(c))) {
event = kCFStreamEventHasBytesAvailable;
memset(&error, 0, sizeof(error));
}
}
__CFSpinUnlock(&ctxt->_lock);
if (rStream) {
if (!rsrc)
CFReadStreamSignalEvent(rStream, event, &error);
else {
CFRunLoopSourceSignal(rsrc);
CFRunLoopWakeUp(rrl);
}
}
if (wStream) {
if (!wsrc)
CFWriteStreamSignalEvent(wStream, event, &error);
else {
CFRunLoopSourceSignal(wsrc);
CFRunLoopWakeUp(wrl);
}
}
if (rsrc) CFRelease(rsrc);
if (wsrc) CFRelease(wsrc);
}
else
__CFSpinUnlock(&ctxt->_lock);
if (rStream) CFRelease(rStream);
if (wStream) CFRelease(wStream);
}
void
_HostCallBack(CFHostRef theHost, CFHostInfoType typeInfo, const CFStreamError* error, _CFSocketStreamContext* ctxt) {
int i;
CFArrayRef addresses;
CFMutableArrayRef loops[3];
CFStreamError err;
CFReadStreamRef rStream = NULL;
CFWriteStreamRef wStream = NULL;
if (typeInfo != kCFHostAddresses) return;
__CFSpinLock(&ctxt->_lock);
if (error->error)
memmove(&(ctxt->_error), error, sizeof(error[0]));
_SchedulablesRemove(ctxt->_schedulables, theHost);
_CFTypeInvalidate(theHost);
loops[0] = ctxt->_readloops;
loops[1] = ctxt->_writeloops;
loops[2] = ctxt->_sharedloops;
for (i = 0; i < (sizeof(loops) / sizeof(loops[0])); i++)
_CFTypeUnscheduleFromMultipleRunLoops(theHost, loops[i]);
CFHostCancelInfoResolution(theHost, kCFHostAddresses);
if (!error->error) {
addresses = CFHostGetAddressing(theHost, NULL);
if (addresses && CFArrayGetCount(addresses))
_SocketStreamAttemptNextConnection_NoLock(ctxt);
else {
ctxt->_error.error = EAI_NODATA;
ctxt->_error.domain = kCFStreamErrorDomainNetDB;
}
}
if (ctxt->_error.error) {
CFTypeRef extra = (CFTypeRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketRemoteHost);
if (!extra || (extra != theHost)) {
if (!extra)
extra = (CFTypeRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketRemoteNetService);
if (extra && _SchedulablesRemove(ctxt->_schedulables, extra)) {
for (i = 0; i < (sizeof(loops) / sizeof(loops[0])); i++)
_CFTypeUnscheduleFromMultipleRunLoops(theHost, loops[i]);
_CFTypeInvalidate(theHost);
if (CFGetTypeID(extra) == CFHostGetTypeID())
CFHostCancelInfoResolution((CFHostRef)extra, kCFHostAddresses);
else
CFNetServiceCancel((CFNetServiceRef)extra);
}
}
if (ctxt->_error.domain == kCFStreamErrorDomainNetDB) {
switch (ctxt->_error.error) {
case EAI_NODATA:
_SocketStreamAttemptAutoVPN_NoLock(ctxt, (CFStringRef)CFArrayGetValueAtIndex(CFHostGetNames(theHost, NULL), 0));
break;
default:
break;
}
}
if (ctxt->_error.error) {
__CFBitSet(ctxt->_flags, kFlagBitOpenComplete);
__CFBitClear(ctxt->_flags, kFlagBitOpenStarted);
__CFBitClear(ctxt->_flags, kFlagBitPollOpen);
memmove(&err, &ctxt->_error, sizeof(err));
if (ctxt->_clientReadStream && __CFBitIsSet(ctxt->_flags, kFlagBitReadStreamOpened))
rStream = (CFReadStreamRef)CFRetain(ctxt->_clientReadStream);
if (ctxt->_clientWriteStream && __CFBitIsSet(ctxt->_flags, kFlagBitWriteStreamOpened))
wStream = (CFWriteStreamRef)CFRetain(ctxt->_clientWriteStream);
}
}
__CFSpinUnlock(&ctxt->_lock);
if (rStream) {
CFReadStreamSignalEvent(rStream, kCFStreamEventErrorOccurred, &err);
CFRelease(rStream);
}
if (wStream) {
CFWriteStreamSignalEvent(wStream, kCFStreamEventErrorOccurred, &err);
CFRelease(wStream);
}
}
void
_NetServiceCallBack(CFNetServiceRef theService, CFStreamError* error, _CFSocketStreamContext* ctxt) {
int i;
CFMutableArrayRef loops[3];
CFArrayRef addresses;
CFReadStreamRef rStream = NULL;
CFWriteStreamRef wStream = NULL;
__CFSpinLock(&ctxt->_lock);
if (error->error)
memmove(&(ctxt->_error), error, sizeof(error[0]));
_SchedulablesRemove(ctxt->_schedulables, theService);
_CFTypeInvalidate(theService);
loops[0] = ctxt->_readloops;
loops[1] = ctxt->_writeloops;
loops[2] = ctxt->_sharedloops;
for (i = 0; i < (sizeof(loops) / sizeof(loops[0])); i++)
_CFTypeUnscheduleFromMultipleRunLoops(theService, loops[i]);
CFNetServiceCancel(theService);
if (!error->error) {
addresses = CFNetServiceGetAddressing(theService);
if (addresses && CFArrayGetCount(addresses))
_SocketStreamAttemptNextConnection_NoLock(ctxt);
else {
ctxt->_error.error = EAI_NODATA;
ctxt->_error.domain = kCFStreamErrorDomainNetDB;
}
}
if (ctxt->_error.error) {
memmove(&ctxt->_error, error, sizeof(error));
if (ctxt->_clientReadStream && __CFBitIsSet(ctxt->_flags, kFlagBitReadStreamOpened))
rStream = (CFReadStreamRef)CFRetain(ctxt->_clientReadStream);
if (ctxt->_clientWriteStream && __CFBitIsSet(ctxt->_flags, kFlagBitWriteStreamOpened))
wStream = (CFWriteStreamRef)CFRetain(ctxt->_clientWriteStream);
}
__CFSpinUnlock(&ctxt->_lock);
if (rStream) {
CFReadStreamSignalEvent(rStream, kCFStreamEventErrorOccurred, (CFStreamError*)(&error));
CFRelease(rStream);
}
if (wStream) {
CFWriteStreamSignalEvent(wStream, kCFStreamEventErrorOccurred, (CFStreamError*)(&error));
CFRelease(wStream);
}
}
void
_SocksHostCallBack(CFHostRef theHost, CFHostInfoType typeInfo, const CFStreamError* error, _CFSocketStreamContext* ctxt) {
CFStreamError err;
CFReadStreamRef rStream = NULL;
CFWriteStreamRef wStream = NULL;
if (typeInfo != kCFHostAddresses) return;
__CFSpinLock(&ctxt->_lock);
_SocketStreamSOCKSHandleLookup_NoLock(ctxt, theHost);
CFHostCancelInfoResolution(theHost, kCFHostAddresses);
if (ctxt->_error.error) {
__CFBitSet(ctxt->_flags, kFlagBitOpenComplete);
__CFBitClear(ctxt->_flags, kFlagBitOpenStarted);
__CFBitClear(ctxt->_flags, kFlagBitPollOpen);
memmove(&err, &ctxt->_error, sizeof(err));
if (ctxt->_clientReadStream && __CFBitIsSet(ctxt->_flags, kFlagBitReadStreamOpened))
rStream = (CFReadStreamRef)CFRetain(ctxt->_clientReadStream);
if (ctxt->_clientWriteStream && __CFBitIsSet(ctxt->_flags, kFlagBitWriteStreamOpened))
wStream = (CFWriteStreamRef)CFRetain(ctxt->_clientWriteStream);
}
__CFSpinUnlock(&ctxt->_lock);
if (rStream) {
CFReadStreamSignalEvent(rStream, kCFStreamEventErrorOccurred, &err);
CFRelease(rStream);
}
if (wStream) {
CFWriteStreamSignalEvent(wStream, kCFStreamEventErrorOccurred, &err);
CFRelease(wStream);
}
}
void
_ReachabilityCallBack(SCNetworkReachabilityRef target, const SCNetworkConnectionFlags flags, _CFSocketStreamContext* ctxt) {
CFDataRef c;
__CFSpinLock(&ctxt->_lock);
ctxt->_error.error = ENOTCONN;
ctxt->_error.domain = _kCFStreamErrorDomainNativeSockets;
c = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferCount);
if ((!c || !*((CFIndex*)CFDataGetBytePtr(c))) &&
(ctxt->_clientReadStream && __CFBitIsSet(ctxt->_flags, kFlagBitReadStreamOpened)))
{
_CFReadStreamSignalEventDelayed(ctxt->_clientReadStream, kCFStreamEventErrorOccurred, &ctxt->_error);
}
if (ctxt->_clientWriteStream && __CFBitIsSet(ctxt->_flags, kFlagBitWriteStreamOpened))
_CFWriteStreamSignalEventDelayed(ctxt->_clientWriteStream, kCFStreamEventErrorOccurred, &ctxt->_error);
__CFSpinUnlock(&ctxt->_lock);
}
void
_NetworkConnectionCallBack(SCNetworkConnectionRef conn, SCNetworkConnectionStatus status, _CFSocketStreamContext* ctxt) {
CFReadStreamRef rStream = NULL;
CFWriteStreamRef wStream = NULL;
CFStreamEventType event = kCFStreamEventNone;
CFStreamError error = {0, 0};
switch (status) {
case kSCNetworkConnectionConnected:
case kSCNetworkConnectionDisconnected:
case kSCNetworkConnectionInvalid:
{
int i;
CFArrayRef loops[3] = {ctxt->_readloops, ctxt->_writeloops, ctxt->_sharedloops};
__CFSpinLock(&ctxt->_lock);
for (i = 0; i < (sizeof(loops) / sizeof(loops[0])); i++)
_CFTypeUnscheduleFromMultipleRunLoops(conn, loops[i]);
_CFTypeInvalidate(conn);
_SchedulablesRemove(ctxt->_schedulables, conn);
if (!_SocketStreamStartLookupForOpen_NoLock(ctxt)) {
if (!ctxt->_error.error)
_SocketStreamAttemptNextConnection_NoLock(ctxt);
}
if (__CFBitIsSet(ctxt->_flags, kFlagBitOpenComplete)) {
__CFBitClear(ctxt->_flags, kFlagBitOpenStarted);
event = kCFStreamEventOpenCompleted;
if (ctxt->_clientReadStream && __CFBitIsSet(ctxt->_flags, kFlagBitReadStreamOpened))
rStream = ctxt->_clientReadStream;
if (ctxt->_clientWriteStream && __CFBitIsSet(ctxt->_flags, kFlagBitWriteStreamOpened))
wStream = ctxt->_clientWriteStream;
}
if (ctxt->_error.error) {
memmove(&error, &ctxt->_error, sizeof(error));
event = kCFStreamEventErrorOccurred;
if (ctxt->_clientReadStream && __CFBitIsSet(ctxt->_flags, kFlagBitReadStreamOpened))
rStream = ctxt->_clientReadStream;
if (ctxt->_clientWriteStream && __CFBitIsSet(ctxt->_flags, kFlagBitWriteStreamOpened))
wStream = ctxt->_clientWriteStream;
}
if (rStream) CFRetain(rStream);
if (wStream) CFRetain(wStream);
__CFSpinUnlock(&ctxt->_lock);
break;
}
default:
break;
}
if (event != kCFStreamEventNone) {
if (rStream)
CFReadStreamSignalEvent(rStream, event, &error);
if (wStream)
CFWriteStreamSignalEvent(wStream, event, &error);
}
if (rStream) CFRelease(rStream);
if (wStream) CFRelease(wStream);
}
_CFSocketStreamContext*
_SocketStreamCreateContext(CFAllocatorRef alloc) {
_CFSocketStreamContext* ctxt = (_CFSocketStreamContext*)CFAllocatorAllocate(alloc,
sizeof(ctxt[0]),
0);
if (ctxt) {
memset(ctxt, 0, sizeof(ctxt[0]));
ctxt->_readloops = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
ctxt->_writeloops = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
ctxt->_sharedloops = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
ctxt->_schedulables = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
ctxt->_properties = CFDictionaryCreateMutable(alloc,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!ctxt->_readloops || !ctxt->_writeloops || !ctxt->_sharedloops ||
!ctxt->_schedulables || !ctxt->_properties)
{
ctxt = NULL;
}
}
return ctxt;
}
void
_SocketStreamDestroyContext_NoLock(CFAllocatorRef alloc, _CFSocketStreamContext* ctxt) {
int i;
CFMutableArrayRef loops[] = {ctxt->_readloops, ctxt->_writeloops, ctxt->_sharedloops};
if (ctxt->_schedulables) {
CFRange r = CFRangeMake(0, CFArrayGetCount(ctxt->_schedulables));
for (i = 0; i < (sizeof(loops) / sizeof(loops[0])); i++) {
if (loops[i])
CFArrayApplyFunction(ctxt->_schedulables, r, (CFArrayApplierFunction)_SchedulablesUnscheduleFromAllApplierFunction, loops[i]);
}
CFArrayApplyFunction(ctxt->_schedulables, r, (CFArrayApplierFunction)_SchedulablesInvalidateApplierFunction, NULL);
CFRelease(ctxt->_schedulables);
}
if (ctxt->_socket) {
CFSocketInvalidate(ctxt->_socket);
CFRelease(ctxt->_socket);
}
for (i = 0; i < (sizeof(loops) / sizeof(loops[0])); i++)
if (loops[i]) CFRelease(loops[i]);
if (ctxt->_properties)
CFRelease(ctxt->_properties);
CFAllocatorDeallocate(alloc, ctxt);
}
Boolean
_SchedulablesAdd(CFMutableArrayRef schedulables, CFTypeRef addition) {
if (!CFArrayContainsValue(schedulables, CFRangeMake(0, CFArrayGetCount(schedulables)), addition)) {
CFArrayAppendValue(schedulables, addition);
return TRUE;
}
return FALSE;
}
Boolean
_SchedulablesRemove(CFMutableArrayRef schedulables, CFTypeRef removal) {
CFIndex i = CFArrayGetFirstIndexOfValue(schedulables, CFRangeMake(0, CFArrayGetCount(schedulables)), removal);
if (i != kCFNotFound) {
CFArrayRemoveValueAtIndex(schedulables, i);
return TRUE;
}
return FALSE;
}
void
_SchedulablesScheduleApplierFunction(CFTypeRef obj, CFTypeRef loopAndMode[]) {
_CFTypeScheduleOnRunLoop(obj, (CFRunLoopRef)loopAndMode[0], (CFStringRef)loopAndMode[1]);
}
void
_SchedulablesUnscheduleApplierFunction(CFTypeRef obj, CFTypeRef loopAndMode[]) {
_CFTypeUnscheduleFromRunLoop(obj, (CFRunLoopRef)loopAndMode[0], (CFStringRef)loopAndMode[1]);
}
void
_SchedulablesUnscheduleFromAllApplierFunction(CFTypeRef obj, CFArrayRef schedules) {
_CFTypeUnscheduleFromMultipleRunLoops(obj, schedules);
}
void
_SchedulablesInvalidateApplierFunction(CFTypeRef obj, void* context) {
(void)context;
CFTypeID type = CFGetTypeID(obj);
_CFTypeInvalidate(obj);
if (CFHostGetTypeID() == type)
CFHostCancelInfoResolution((CFHostRef)obj, kCFHostAddresses);
else if (CFNetServiceGetTypeID() == type)
CFNetServiceCancel((CFNetServiceRef)obj);
}
void
_SocketStreamSchedule_NoLock(CFTypeRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode,
_CFSocketStreamContext* ctxt)
{
CFMutableArrayRef loops, otherloops;
Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID());
if (isReadStream) {
loops = ctxt->_readloops;
otherloops = ctxt->_writeloops;
}
else {
loops = ctxt->_writeloops;
otherloops = ctxt->_readloops;
}
if ((kCFNotFound == _SchedulesFind(ctxt->_sharedloops, runLoop, runLoopMode)) &&
(kCFNotFound == _SchedulesFind(loops, runLoop, runLoopMode)))
{
if (kCFNotFound == _SchedulesFind(otherloops, runLoop, runLoopMode)) {
CFTypeRef loopAndMode[2] = {runLoop, runLoopMode};
_SchedulesAddRunLoopAndMode(loops, runLoop, runLoopMode);
CFArrayApplyFunction(ctxt->_schedulables,
CFRangeMake(0, CFArrayGetCount(ctxt->_schedulables)),
(CFArrayApplierFunction)_SchedulablesScheduleApplierFunction,
loopAndMode);
}
else {
_SchedulesRemoveRunLoopAndMode(otherloops, runLoop, runLoopMode);
_SchedulesAddRunLoopAndMode(ctxt->_sharedloops, runLoop, runLoopMode);
}
if (isReadStream) {
if (__CFBitIsSet(ctxt->_flags, kFlagBitCanRead) &&
(CFArrayGetCount(loops) + CFArrayGetCount(ctxt->_sharedloops) == 4))
{
CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL);
}
}
else {
if (__CFBitIsSet(ctxt->_flags, kFlagBitCanWrite) &&
(CFArrayGetCount(loops) + CFArrayGetCount(ctxt->_sharedloops) == 4))
{
CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL);
}
}
}
}
void
_SocketStreamUnschedule_NoLock(CFTypeRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode,
_CFSocketStreamContext* ctxt)
{
CFMutableArrayRef loops, otherloops;
if (CFGetTypeID(stream) == CFReadStreamGetTypeID()) {
loops = ctxt->_readloops;
otherloops = ctxt->_writeloops;
}
else {
loops = ctxt->_writeloops;
otherloops = ctxt->_readloops;
}
if (_SchedulesRemoveRunLoopAndMode(ctxt->_sharedloops, runLoop, runLoopMode)) {
_SchedulesAddRunLoopAndMode(otherloops, runLoop, runLoopMode);
}
else if (_SchedulesRemoveRunLoopAndMode(loops, runLoop, runLoopMode)) {
CFTypeRef loopAndMode[2] = {runLoop, runLoopMode};
CFArrayApplyFunction(ctxt->_schedulables,
CFRangeMake(0, CFArrayGetCount(ctxt->_schedulables)),
(CFArrayApplierFunction)_SchedulablesUnscheduleApplierFunction,
loopAndMode);
}
}
CFNumberRef
_CFNumberCopyPortForOpen(CFDictionaryRef properties) {
CFNumberRef result = NULL;
CFDictionaryRef proxy = (CFDictionaryRef)CFDictionaryGetValue(properties, kCFStreamPropertySOCKSProxy);
if (proxy) {
result = (CFNumberRef)CFDictionaryGetValue(proxy, kCFStreamPropertySOCKSProxyPort);
if (result)
CFRetain(result);
else {
SInt32 default_port = 1080;
result = CFNumberCreate(CFGetAllocator(properties), kCFNumberSInt32Type, &default_port);
}
}
else if ((proxy = (CFDictionaryRef)CFDictionaryGetValue(properties, kCFStreamPropertyCONNECTProxy))) {
result = (CFNumberRef)CFDictionaryGetValue(proxy, kCFStreamPropertyCONNECTProxyPort);
if (result) CFRetain(result);
}
else {
result = (CFNumberRef)CFDictionaryGetValue(properties, _kCFStreamPropertySocketRemotePort);
if (result) CFRetain(result);
}
return result;
}
CFDataRef
_CFDataCopyAddressByInjectingPort(CFDataRef address, CFNumberRef port) {
if (!port)
CFRetain(address);
else {
SInt32 p;
if (!CFNumberGetValue(port, kCFNumberSInt32Type, &p))
address = NULL;
else {
switch (((struct sockaddr*)CFDataGetBytePtr(address))->sa_family) {
case AF_INET:
address = CFDataCreateMutableCopy(CFGetAllocator(address),
0,
address);
if (address)
((struct sockaddr_in*)(CFDataGetMutableBytePtr((CFMutableDataRef)address)))->sin_port = htons(0x0000FFFF & p);
break;
case AF_INET6:
address = CFDataCreateMutableCopy(CFGetAllocator(address),
0,
address);
if (address)
((struct sockaddr_in6*)(CFDataGetMutableBytePtr((CFMutableDataRef)address)))->sin6_port = htons(0x0000FFFF & p);
break;
default:
address = NULL;
break;
}
}
}
return address;
}
Boolean
_ScheduleAndStartLookup(CFTypeRef lookup, CFArrayRef* schedules, CFStreamError* error, const void* cb, void* info) {
do {
int i;
CFArrayRef addresses = NULL;
CFTypeID lookup_type = CFGetTypeID(lookup);
CFTypeID host_type = CFHostGetTypeID();
memset(error, 0, sizeof(error[0]));
if (lookup_type == host_type)
addresses = CFHostGetAddressing((CFHostRef)lookup, NULL);
else
addresses = CFNetServiceGetAddressing((CFNetServiceRef)lookup);
if (addresses) {
if (CFArrayGetCount(addresses))
break;
else {
error->error = EAI_NODATA;
error->domain = kCFStreamErrorDomainNetDB;
break;
}
}
if (lookup_type == host_type) {
CFHostClientContext c = {0, info, NULL, NULL, NULL};
CFHostSetClient((CFHostRef)lookup, (CFHostClientCallBack)cb, &c);
}
else {
CFNetServiceClientContext c = {0, info, NULL, NULL, NULL};
CFNetServiceSetClient((CFNetServiceRef)lookup, (CFNetServiceClientCallBack)cb, &c);
}
for (i = 0; schedules[i]; i++)
_CFTypeScheduleOnMultipleRunLoops(lookup, schedules[i]);
if (lookup_type == host_type)
CFHostStartInfoResolution((CFHostRef)lookup, kCFHostAddresses, error);
else
CFNetServiceResolveWithTimeout((CFNetServiceRef)lookup, 0.0, error);
if (error->error) {
for (i = 0; schedules[i]; i++)
_CFTypeUnscheduleFromMultipleRunLoops(lookup, schedules[i]);
_CFTypeInvalidate(lookup);
break;
}
return TRUE;
}
while (0);
return FALSE;
}
CFIndex
_CFSocketRecv(CFSocketRef s, UInt8* buffer, CFIndex length, CFStreamError* error) {
CFIndex result = -1;
memset(error, 0, sizeof(error[0]));
if (!s || !CFSocketIsValid(s)) {
error->error = EINVAL;
error->domain = kCFStreamErrorDomainPOSIX;
}
else {
result = read(CFSocketGetNative(s), buffer, length);
if (result < 0) {
_LastError(error);
result = -1;
}
}
return result;
}
CFIndex
_CFSocketSend(CFSocketRef s, const UInt8* buffer, CFIndex length, CFStreamError* error) {
CFIndex result = -1;
memset(error, 0, sizeof(error[0]));
if (!s || !CFSocketIsValid(s)) {
error->error = EINVAL;
error->domain = kCFStreamErrorDomainPOSIX;
}
else {
result = write(CFSocketGetNative(s), buffer, length);
if (result < 0) {
_LastError(error);
result = -1;
}
}
return result;
}
Boolean
_CFSocketCan(CFSocketRef s, int mode) {
int val;
fd_set set;
fd_set* setptr = &set;
struct timeval timeout = {0, 0};
int fd = CFSocketGetNative(s);
FD_ZERO(setptr);
#if !defined __WIN32__
if (fd >= FD_SETSIZE) {
val = howmany(fd + 1, NFDBITS) * sizeof(fd_mask);
setptr = (fd_set*)malloc(val);
bzero(setptr, val);
}
#endif
FD_SET(fd, setptr);
val = select(fd + 1,
(mode & kSelectModeRead ? setptr : NULL),
(mode & kSelectModeWrite ? setptr : NULL),
(mode & kSelectModeExcept ? setptr : NULL),
&timeout);
if (setptr != &set)
free(setptr);
return (val > 0) ? TRUE : FALSE;
}
Boolean
_SocketStreamStartLookupForOpen_NoLock(_CFSocketStreamContext* ctxt) {
Boolean result = FALSE;
do {
CFTypeRef lookup = NULL;
CFHostRef extra = NULL;
CFTypeID lookup_type, host_type = CFHostGetTypeID();
CFArrayRef loops[4] = {ctxt->_readloops, ctxt->_writeloops, ctxt->_sharedloops, NULL};
CFDictionaryRef proxy = (CFDictionaryRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
if (proxy) {
lookup = CFHostCreateWithName(CFGetAllocator(ctxt->_properties),
(CFStringRef)CFDictionaryGetValue(proxy, kCFStreamPropertySOCKSProxyHost));
if (lookup && CFEqual(_GetSOCKSVersion(proxy), kCFStreamSocketSOCKSVersion4)) {
extra = (CFHostRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketRemoteHost);
if (!extra) {
ctxt->_error.error = EINVAL;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
break;
}
else if (_ScheduleAndStartLookup(extra,
loops,
&ctxt->_error,
(const void*)_SocksHostCallBack,
ctxt))
{
_SchedulablesAdd(ctxt->_schedulables, extra);
}
else if (ctxt->_error.error) {
CFRelease(lookup);
break;
}
}
}
else if ((proxy = (CFDictionaryRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyCONNECTProxy))) {
lookup = CFHostCreateWithName(CFGetAllocator(ctxt->_properties),
(CFStringRef)CFDictionaryGetValue(proxy, kCFStreamPropertyCONNECTProxyHost));
}
else {
lookup = (CFTypeRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketRemoteHost);
if (!lookup)
lookup = (CFTypeRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketRemoteNetService);
if (lookup) CFRetain(lookup);
}
if (!lookup) {
if (!ctxt->_socket && !CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketNativeHandle)) {
ctxt->_error.error = errno;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
if (!ctxt->_error.error)
ctxt->_error.error = ENOMEM;
}
break;
}
lookup_type = CFGetTypeID(lookup);
result = _ScheduleAndStartLookup(lookup,
loops,
&ctxt->_error,
((lookup_type == host_type) ? (const void*)_HostCallBack : (const void*)_NetServiceCallBack),
ctxt);
if (result)
_SchedulablesAdd(ctxt->_schedulables, lookup);
else if (ctxt->_error.error) {
CFRelease(lookup);
if (extra) {
int i;
_SchedulablesRemove(ctxt->_schedulables, extra);
for (i = 0; loops[i]; i++)
_CFTypeUnscheduleFromMultipleRunLoops(extra, loops[i]);
_CFTypeInvalidate(extra);
CFHostCancelInfoResolution((CFHostRef)extra, kCFHostAddresses);
}
break;
}
CFDictionaryAddValue(ctxt->_properties, _kCFStreamPropertyHostForOpen, lookup);
CFRelease(lookup);
} while (0);
return result;
}
Boolean
_SocketStreamCreateSocket_NoLock(_CFSocketStreamContext* ctxt, CFDataRef address) {
do {
SInt32 protocolFamily = PF_INET;
SInt32 socketType = SOCK_STREAM;
SInt32 protocol = IPPROTO_TCP;
int yes = 1;
CFOptionFlags flags;
CFSocketNativeHandle s;
CFSocketContext c = {0, ctxt, NULL, NULL, NULL};
CFArrayRef callback;
CFBooleanRef boolean;
CFDictionaryRef info = (CFDictionaryRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertySocketFamilyTypeProtocol);
if (info) {
if (CFDictionaryContainsValue(info, _kCFStreamSocketFamily)) {
const void* tmp = CFDictionaryGetValue(info, _kCFStreamSocketFamily);
protocolFamily = (SInt32)tmp;
}
if (CFDictionaryContainsValue(info, _kCFStreamSocketType)) {
const void* tmp = CFDictionaryGetValue(info, _kCFStreamSocketType);
socketType = (SInt32)tmp;
}
if (CFDictionaryContainsValue(info, _kCFStreamSocketProtocol)) {
const void* tmp = CFDictionaryGetValue(info, _kCFStreamSocketProtocol);
protocol = (SInt32)tmp;
}
}
if (address)
protocolFamily = ((struct sockaddr*)CFDataGetBytePtr(address))->sa_family;
ctxt->_socket = CFSocketCreate(CFGetAllocator(ctxt->_properties),
protocolFamily,
socketType,
protocol,
kSocketEvents,
(CFSocketCallBack)_SocketCallBack,
&c);
if (!ctxt->_socket) {
if (!_LastError(&ctxt->_error)) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
}
break;
}
s = CFSocketGetNative(ctxt->_socket);
callback = (CFArrayRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamSocketCreatedCallBack);
if (callback)
((_CFSocketStreamSocketCreatedCallBack)CFArrayGetValueAtIndex(callback, 0))(s, (void*)CFArrayGetValueAtIndex(callback, 1));
boolean = (CFBooleanRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamSocketIChatWantsSubNet);
if (boolean == kCFBooleanTrue) {
int ttl = 255;
setsockopt(s, IPPROTO_IP, IP_TTL, (void*)&ttl, sizeof(ttl));
}
#if !defined(__WIN32)
setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void*)&yes, sizeof(yes));
#endif
ioctl(s, FIONBIO, (void*)&yes);
flags = CFSocketGetSocketFlags(ctxt->_socket) &
~kCFSocketAutomaticallyReenableReadCallBack &
~kCFSocketAutomaticallyReenableWriteCallBack;
boolean = (CFBooleanRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyShouldCloseNativeSocket);
if (!boolean || (boolean != kCFBooleanFalse))
flags |= kCFSocketCloseOnInvalidate;
else
flags &= ~kCFSocketCloseOnInvalidate;
CFSocketSetSocketFlags(ctxt->_socket, flags);
return TRUE;
} while (0);
return FALSE;
}
Boolean
_SocketStreamConnect_NoLock(_CFSocketStreamContext* ctxt, CFDataRef address) {
int i;
Boolean result = FALSE;
CFArrayRef loops[3] = {ctxt->_readloops, ctxt->_writeloops, ctxt->_sharedloops};
for (i = 0; i < (sizeof(loops) / sizeof(loops[0])); i++)
_CFTypeScheduleOnMultipleRunLoops(ctxt->_socket, loops[i]);
if ((result = (CFSocketConnectToAddress(ctxt->_socket, address, -1.0) == kCFSocketSuccess))) {
memset(&ctxt->_error, 0, sizeof(ctxt->_error));
_SchedulablesAdd(ctxt->_schedulables, ctxt->_socket);
}
else {
if (!_LastError(&ctxt->_error)) {
ctxt->_error.error = EINVAL;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
}
for (i = 0; i < (sizeof(loops) / sizeof(loops[0])); i++)
_CFTypeUnscheduleFromMultipleRunLoops(ctxt->_socket, loops[i]);
_CFTypeInvalidate(ctxt->_socket);
CFRelease(ctxt->_socket);
ctxt->_socket = NULL;
}
return result;
}
Boolean
_SocketStreamAttemptNextConnection_NoLock(_CFSocketStreamContext* ctxt) {
do {
CFTypeRef lookup = (CFTypeRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyHostForOpen);
SInt32* attempt = NULL;
if (lookup) {
CFIndex count;
CFArrayRef list = NULL;
CFDataRef address = NULL;
CFNumberRef port = _CFNumberCopyPortForOpen(ctxt->_properties);
CFMutableDataRef a = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertySocketAddressAttempt);
if (a)
attempt = (SInt32*)CFDataGetMutableBytePtr(a);
else {
SInt32 i = 0;
a = CFDataCreateMutable(CFGetAllocator(ctxt->_properties), sizeof(i));
if (!a) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
if (port) CFRelease(port);
break;
}
CFDictionaryAddValue(ctxt->_properties, _kCFStreamPropertySocketAddressAttempt, a);
CFRelease(a);
attempt = (SInt32*)CFDataGetMutableBytePtr(a);
*attempt = 0;
}
if (CFGetTypeID(lookup) == CFHostGetTypeID())
list = CFHostGetAddressing((CFHostRef)lookup, NULL);
else
list = CFNetServiceGetAddressing((CFNetServiceRef)lookup);
if (!list || (*attempt >= (count = CFArrayGetCount(list)))) {
if (!ctxt->_error.error) {
ctxt->_error.error = EAI_NODATA;
ctxt->_error.domain = kCFStreamErrorDomainNetDB;
}
if (port) CFRelease(port);
break;
}
do {
address = _CFDataCopyAddressByInjectingPort((CFDataRef)CFArrayGetValueAtIndex(list, *attempt), port);
*attempt = *attempt + 1;
if (address) {
if (ctxt->_socket) {
*attempt = count;
_SocketStreamConnect_NoLock(ctxt, address);
}
else if (_SocketStreamCreateSocket_NoLock(ctxt, address))
_SocketStreamConnect_NoLock(ctxt, address);
CFRelease(address);
if (!ctxt->_error.error) {
if (port) CFRelease(port);
return TRUE;
}
}
} while (*attempt < count);
if (port) CFRelease(port);
if (!ctxt->_error.error) {
ctxt->_error.error = EINVAL;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
}
break;
}
else {
int i;
int yes = 1;
CFOptionFlags flags;
CFBooleanRef boolean;
CFSocketNativeHandle s;
CFSocketContext c = {0, ctxt, NULL, NULL, NULL};
CFArrayRef loops[3] = {ctxt->_readloops, ctxt->_writeloops, ctxt->_sharedloops};
if (!ctxt->_socket) {
CFDataRef wrapper = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketNativeHandle);
if (!wrapper) {
ctxt->_error.error = EINVAL;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
break;
}
ctxt->_socket = CFSocketCreateWithNative(CFGetAllocator(ctxt->_properties),
*((CFSocketNativeHandle*)CFDataGetBytePtr(wrapper)),
kSocketEvents,
(CFSocketCallBack)_SocketCallBack,
&c);
if (!ctxt->_socket) {
if (!_LastError(&ctxt->_error)) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
}
break;
}
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertySocketNativeHandle);
}
__CFBitSet(ctxt->_flags, kFlagBitOpenComplete);
__CFBitClear(ctxt->_flags, kFlagBitPollOpen);
s = CFSocketGetNative(ctxt->_socket);
#if !defined(__WIN32)
setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void*)&yes, sizeof(yes));
#endif
ioctl(s, FIONBIO, (void*)&yes);
flags = CFSocketGetSocketFlags(ctxt->_socket) &
~kCFSocketAutomaticallyReenableReadCallBack &
~kCFSocketAutomaticallyReenableWriteCallBack;
boolean = (CFBooleanRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyShouldCloseNativeSocket);
if (!boolean || (boolean != kCFBooleanFalse))
flags |= kCFSocketCloseOnInvalidate;
else
flags &= ~kCFSocketCloseOnInvalidate;
CFSocketSetSocketFlags(ctxt->_socket, flags);
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketReadCallBack | kCFSocketWriteCallBack);
for (i = 0; i < (sizeof(loops) / sizeof(loops[0])); i++)
_CFTypeScheduleOnMultipleRunLoops(ctxt->_socket, loops[i]);
_SchedulablesAdd(ctxt->_schedulables, ctxt->_socket);
}
return TRUE;
} while (0);
return FALSE;
}
Boolean
_SocketStreamCan(_CFSocketStreamContext* ctxt, CFTypeRef stream, int test, CFStringRef mode, CFStreamError* error) {
Boolean result = TRUE;
Boolean isRead;
memset(error, 0, sizeof(error[0]));
__CFSpinLock(&ctxt->_lock);
isRead = CFReadStreamGetTypeID() == CFGetTypeID(stream);
result = __CFBitIsSet(ctxt->_flags, test);
if (!ctxt->_error.error && !result) {
CFMutableArrayRef loops = isRead ? ctxt->_readloops : ctxt->_writeloops;
if (!__CFBitIsSet(ctxt->_flags, test + kFlagBitPollOpen) &&
((CFArrayGetCount(ctxt->_sharedloops) + CFArrayGetCount(loops)) > 2))
{
__CFBitSet(ctxt->_flags, test + kFlagBitPollOpen);
}
else {
CFTypeRef loopAndMode[2] = {CFRunLoopGetCurrent(), mode};
_SchedulesAddRunLoopAndMode(loops, (CFRunLoopRef)loopAndMode[0], (CFStringRef)loopAndMode[1]);
if (ctxt->_socket &&
(CFArrayGetCount(ctxt->_schedulables) == 1) &&
(ctxt->_socket == CFArrayGetValueAtIndex(ctxt->_schedulables, 0)))
{
CFRunLoopSourceRef src = CFSocketCreateRunLoopSource(CFGetAllocator(ctxt->_schedulables), ctxt->_socket, 0);
if (src) {
CFRunLoopAddSource((CFRunLoopRef)loopAndMode[0], src, (CFStringRef)loopAndMode[1]);
CFRelease(src);
}
}
else {
CFArrayApplyFunction(ctxt->_schedulables,
CFRangeMake(0, CFArrayGetCount(ctxt->_schedulables)),
(CFArrayApplierFunction)_SchedulablesScheduleApplierFunction,
loopAndMode);
}
__CFSpinUnlock(&ctxt->_lock);
CFRunLoopRunInMode(mode, 0.0, FALSE);
__CFSpinLock(&ctxt->_lock);
if (ctxt->_socket &&
(CFArrayGetCount(ctxt->_schedulables) == 1) &&
(ctxt->_socket == CFArrayGetValueAtIndex(ctxt->_schedulables, 0)))
{
CFRunLoopSourceRef src = CFSocketCreateRunLoopSource(CFGetAllocator(ctxt->_schedulables), ctxt->_socket, 0);
if (src) {
CFRunLoopRemoveSource((CFRunLoopRef)loopAndMode[0], src, (CFStringRef)loopAndMode[1]);
CFRelease(src);
}
}
else {
CFArrayApplyFunction(ctxt->_schedulables,
CFRangeMake(0, CFArrayGetCount(ctxt->_schedulables)),
(CFArrayApplierFunction)_SchedulablesUnscheduleApplierFunction,
loopAndMode);
}
_SchedulesRemoveRunLoopAndMode(loops, (CFRunLoopRef)loopAndMode[0], (CFStringRef)loopAndMode[1]);
result = __CFBitIsSet(ctxt->_flags, test);
}
}
if (ctxt->_error.error) {
CFDataRef c = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferCount);
memmove(error, &ctxt->_error, sizeof(error[0]));
__CFBitSet(ctxt->_flags, test);
result = TRUE;
if ((!c || !*((CFIndex*)CFDataGetBytePtr(c))) &&
(ctxt->_clientReadStream && __CFBitIsSet(ctxt->_flags, kFlagBitReadStreamOpened)))
{
_CFReadStreamSignalEventDelayed(ctxt->_clientReadStream, kCFStreamEventErrorOccurred, error);
}
if (ctxt->_clientWriteStream && __CFBitIsSet(ctxt->_flags, kFlagBitWriteStreamOpened))
_CFWriteStreamSignalEventDelayed(ctxt->_clientWriteStream, kCFStreamEventErrorOccurred, error);
}
__CFSpinUnlock(&ctxt->_lock);
return result;
}
void
_SocketStreamAddReachability_NoLock(_CFSocketStreamContext* ctxt) {
SCNetworkReachabilityRef reachability = (SCNetworkReachabilityRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyNetworkReachability);
if (ctxt->_socket && __CFBitIsSet(ctxt->_flags, kFlagBitOpenComplete) && !reachability) {
CFDataRef localAddr = CFSocketCopyAddress(ctxt->_socket);
CFDataRef peerAddr = CFSocketCopyPeerAddress(ctxt->_socket);
if (localAddr && peerAddr) {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddressPair(CFGetAllocator(ctxt->_properties),
(const struct sockaddr*)CFDataGetBytePtr(localAddr),
(const struct sockaddr*)CFDataGetBytePtr(peerAddr));
if (reachability) {
int i;
SCNetworkReachabilityContext c = {0, ctxt, NULL, NULL, NULL};
CFArrayRef loops[3] = {ctxt->_readloops, ctxt->_writeloops, ctxt->_sharedloops};
CFDictionaryAddValue(ctxt->_properties, _kCFStreamPropertyNetworkReachability, reachability);
SCNetworkReachabilitySetCallback(reachability, (SCNetworkReachabilityCallBack)_ReachabilityCallBack, &c);
for (i = 0; i < (sizeof(loops) / sizeof(loops[0])); i++)
_CFTypeScheduleOnMultipleRunLoops(reachability, loops[i]);
_SchedulablesAdd(ctxt->_schedulables, reachability);
CFRelease(reachability);
}
}
if (localAddr) CFRelease(localAddr);
if (peerAddr) CFRelease(peerAddr);
}
}
void
_SocketStreamRemoveReachability_NoLock(_CFSocketStreamContext* ctxt) {
SCNetworkReachabilityRef reachability = (SCNetworkReachabilityRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyNetworkReachability);
if (reachability) {
int i;
CFArrayRef loops[3] = {ctxt->_readloops, ctxt->_writeloops, ctxt->_sharedloops};
_CFTypeInvalidate(reachability);
for (i = 0; i < (sizeof(loops) / sizeof(loops[0])); i++)
_CFTypeUnscheduleFromMultipleRunLoops(reachability, loops[i]);
_SchedulablesRemove(ctxt->_schedulables, reachability);
CFDictionaryRemoveValue(ctxt->_properties, reachability);
}
}
CFIndex
_SocketStreamBufferedRead_NoLock(_CFSocketStreamContext* ctxt, UInt8* buffer, CFIndex length) {
CFIndex result = 0;
CFNumberRef s = (CFNumberRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferSize);
CFMutableDataRef b = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBuffer);
CFMutableDataRef c = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferCount);
if (b && c && s) {
CFIndex* i = (CFIndex*)CFDataGetMutableBytePtr(c);
UInt8* ptr = (UInt8*)CFDataGetMutableBytePtr(b);
CFIndex max;
CFNumberGetValue(s, kCFNumberCFIndexType, &max);
result = (*i < length) ? *i : length;
*i = *i - result;
memmove(buffer, ptr, result);
memmove(ptr, ptr + result, *i);
memset(ptr + *i, 0, max - *i);
if (__CFBitIsSet(ctxt->_flags, kFlagBitUseSSL) && (*i == 0)) {
_SocketStreamSecurityBufferedRead_NoLock(ctxt);
}
}
if (!result) {
if (!ctxt->_error.error && !__CFBitIsSet(ctxt->_flags, kFlagBitClosed)) {
ctxt->_error.error = EAGAIN;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
}
}
else if (__CFBitIsSet(ctxt->_flags, kFlagBitRecvdRead)) {
__CFBitClear(ctxt->_flags, kFlagBitRecvdRead);
if (ctxt->_socket)
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketReadCallBack);
}
return result;
}
void
_SocketStreamBufferedSocketRead_NoLock(_CFSocketStreamContext* ctxt) {
CFIndex* i;
CFIndex s = kRecvBufferSize;
CFNumberRef size = (CFNumberRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferSize);
CFMutableDataRef buffer = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBuffer);
CFMutableDataRef count = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferCount);
if (!buffer) {
CFAllocatorRef alloc = CFGetAllocator(ctxt->_properties);
if (!size)
size = CFNumberCreate(alloc, kCFNumberCFIndexType, &s);
else
CFNumberGetValue(size, kCFNumberCFIndexType, &s);
if (size) {
buffer = CFDataCreateMutable(alloc, s);
count = CFDataCreateMutable(alloc, sizeof(CFIndex));
}
if (!buffer || !count || !size) {
if (buffer) CFRelease(buffer);
if (count) CFRelease(count);
if (size) CFRelease(size);
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
return;
}
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferSize, size);
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertyRecvBuffer, buffer);
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferCount, count);
CFRelease(size);
CFRelease(buffer);
CFRelease(count);
*((CFIndex*)CFDataGetMutableBytePtr(count)) = 0;
}
i = (CFIndex*)CFDataGetMutableBytePtr(count);
CFNumberGetValue(size, kCFNumberCFIndexType, &s);
if (*i < s) {
UInt8* ptr = (UInt8*)CFDataGetMutableBytePtr(buffer);
CFIndex bytesRead = _CFSocketRecv(ctxt->_socket, ptr + *i, s - *i, &ctxt->_error);
__CFBitClear(ctxt->_flags, kFlagBitRecvdRead);
if (bytesRead > 0) {
*i = *i + bytesRead;
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketReadCallBack);
__CFBitSet(ctxt->_flags, kFlagBitCanRead);
__CFBitClear(ctxt->_flags, kFlagBitPollRead);
}
else if (bytesRead == 0) {
__CFBitSet(ctxt->_flags, kFlagBitClosed);
__CFBitSet(ctxt->_flags, kFlagBitCanRead);
__CFBitClear(ctxt->_flags, kFlagBitPollRead);
}
}
else
__CFBitSet(ctxt->_flags, kFlagBitRecvdRead);
}
CFComparisonResult
_OrderHandshakes(_CFSocketStreamPerformHandshakeCallBack fn1, _CFSocketStreamPerformHandshakeCallBack fn2, void* context) {
if (!fn1) return kCFCompareGreaterThan;
if (!fn2) return kCFCompareLessThan;
if (*fn1 == _PerformSOCKSv5Handshake_NoLock) return kCFCompareLessThan;
if (*fn2 == _PerformSOCKSv5Handshake_NoLock) return kCFCompareGreaterThan;
if (*fn1 == _PerformSOCKSv5UserPassHandshake_NoLock) return kCFCompareLessThan;
if (*fn2 == _PerformSOCKSv5UserPassHandshake_NoLock) return kCFCompareGreaterThan;
if (*fn1 == _PerformSOCKSv5PostambleHandshake_NoLock) return kCFCompareLessThan;
if (*fn2 == _PerformSOCKSv5PostambleHandshake_NoLock) return kCFCompareGreaterThan;
if (*fn1 == _PerformSOCKSv4Handshake_NoLock) return kCFCompareLessThan;
if (*fn2 == _PerformSOCKSv4Handshake_NoLock) return kCFCompareGreaterThan;
if (*fn1 == _PerformCONNECTHaltHandshake_NoLock) return kCFCompareLessThan;
if (*fn2 == _PerformCONNECTHaltHandshake_NoLock) return kCFCompareGreaterThan;
if (*fn1 == _PerformCONNECTHandshake_NoLock) return kCFCompareLessThan;
if (*fn2 == _PerformCONNECTHandshake_NoLock) return kCFCompareGreaterThan;
if (*fn1 == _PerformSecuritySendHandshake_NoLock) return kCFCompareLessThan;
if (*fn2 == _PerformSecuritySendHandshake_NoLock) return kCFCompareGreaterThan;
if (*fn1 == _PerformSecurityHandshake_NoLock) return kCFCompareLessThan;
if (*fn2 == _PerformSecurityHandshake_NoLock) return kCFCompareGreaterThan;
return kCFCompareEqualTo;
}
Boolean
_SocketStreamAddHandshake_NoLock(_CFSocketStreamContext* ctxt, _CFSocketStreamPerformHandshakeCallBack fn) {
CFIndex i;
CFMutableArrayRef handshakes = (CFMutableArrayRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyHandshakes);
if (!handshakes) {
CFArrayCallBacks cb = {0, NULL, NULL, NULL, NULL};
handshakes = CFArrayCreateMutable(CFGetAllocator(ctxt->_properties), 0, &cb);
if (!handshakes)
return FALSE;
CFDictionaryAddValue(ctxt->_properties, _kCFStreamPropertyHandshakes, handshakes);
__CFBitSet(ctxt->_flags, kFlagBitHasHandshakes);
CFRelease(handshakes);
}
i = CFArrayGetFirstIndexOfValue(handshakes, CFRangeMake(0, CFArrayGetCount(handshakes)), fn);
if (i == kCFNotFound) {
CFRange r;
CFArrayAppendValue(handshakes, fn);
r = CFRangeMake(0, CFArrayGetCount(handshakes));
CFArraySortValues(handshakes, r, (CFComparatorFunction)_OrderHandshakes, NULL);
i = CFArrayGetFirstIndexOfValue(handshakes, r, fn);
}
if (!i) {
__CFBitClear(ctxt->_flags, kFlagBitCanRead);
__CFBitClear(ctxt->_flags, kFlagBitCanWrite);
__CFBitClear(ctxt->_flags, kFlagBitRecvdRead);
if (ctxt->_socket && __CFBitIsSet(ctxt->_flags, kFlagBitOpenComplete))
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketReadCallBack | kCFSocketWriteCallBack);
}
return TRUE;
}
void
_SocketStreamRemoveHandshake_NoLock(_CFSocketStreamContext* ctxt, _CFSocketStreamPerformHandshakeCallBack fn) {
CFMutableArrayRef handshakes = (CFMutableArrayRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyHandshakes);
if (handshakes) {
CFIndex i = CFArrayGetFirstIndexOfValue(handshakes, CFRangeMake(0, CFArrayGetCount(handshakes)), fn);
if (i != kCFNotFound)
CFArrayRemoveValueAtIndex(handshakes, i);
if (!CFArrayGetCount(handshakes)) {
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertyHandshakes);
__CFBitClear(ctxt->_flags, kFlagBitHasHandshakes);
}
}
__CFBitClear(ctxt->_flags, kFlagBitCanRead);
__CFBitClear(ctxt->_flags, kFlagBitCanWrite);
__CFBitClear(ctxt->_flags, kFlagBitRecvdRead);
if (ctxt->_socket && __CFBitIsSet(ctxt->_flags, kFlagBitOpenComplete)) {
if (!__CFBitIsSet(ctxt->_flags, kFlagBitHasHandshakes) && __CFBitIsSet(ctxt->_flags, kFlagBitIsBuffered)) {
CFStreamError error = ctxt->_error;
CFDataRef c = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferCount);
Boolean buffered = (c && *((CFIndex*)CFDataGetBytePtr(c)));
if (buffered)
memset(&error, 0, sizeof(error));
if (__CFBitIsSet(ctxt->_flags, kFlagBitClosed) || buffered) {
if (ctxt->_clientReadStream && __CFBitIsSet(ctxt->_flags, kFlagBitReadStreamOpened))
_CFReadStreamSignalEventDelayed(ctxt->_clientReadStream, kCFStreamEventHasBytesAvailable, &error);
}
else if (__CFBitIsSet(ctxt->_flags, kFlagBitUseSSL)) {
_SocketStreamSecurityBufferedRead_NoLock(ctxt);
if (__CFBitIsSet(ctxt->_flags, kFlagBitCanRead) || __CFBitIsSet(ctxt->_flags, kFlagBitClosed)) {
if (ctxt->_clientReadStream && __CFBitIsSet(ctxt->_flags, kFlagBitReadStreamOpened)) {
if (c && *((CFIndex*)CFDataGetBytePtr(c)))
memset(&error, 0, sizeof(error));
_CFReadStreamSignalEventDelayed(ctxt->_clientReadStream, kCFStreamEventHasBytesAvailable, &error);
}
}
}
}
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketReadCallBack | kCFSocketWriteCallBack);
}
}
void
_SocketStreamAttemptAutoVPN_NoLock(_CFSocketStreamContext* ctxt, CFStringRef name) {
if (!__CFBitIsSet(ctxt->_flags, kFlagBitTriedVPN)) {
CFTypeRef values[2] = {name, NULL};
const CFStringRef keys[2] = {_kCFStreamAutoHostName, _kCFStreamPropertyAutoConnectPriority};
CFAllocatorRef alloc = CFGetAllocator(ctxt->_properties);
CFDictionaryRef options;
values[1] = (CFStringRef)CFDictionaryGetValue(ctxt->_properties, keys[1]);
if (!values[1])
values[1] = _kCFStreamAutoVPNPriorityDefault;
options = CFDictionaryCreate(alloc,
(const void **)keys,
(const void **)values,
sizeof(values) / sizeof(values[0]),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
__CFBitSet(ctxt->_flags, kFlagBitTriedVPN);
if (!options) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
}
else {
CFStringRef service_id = NULL;
CFDictionaryRef user_options = NULL;
if (SCNetworkConnectionCopyUserPreferences(options, &service_id, &user_options)) {
SCNetworkConnectionContext c = {0, ctxt, NULL, NULL, NULL};
SCNetworkConnectionRef conn = SCNetworkConnectionCreateWithServiceID(alloc,
service_id,
(SCNetworkConnectionCallBack)_NetworkConnectionCallBack,
&c);
if (conn) {
int i;
CFArrayRef loops[3] = {ctxt->_readloops, ctxt->_writeloops, ctxt->_sharedloops};
for (i = 0; i < (sizeof(loops) / sizeof(loops[0])); i++)
_CFTypeScheduleOnMultipleRunLoops(conn, loops[i]);
if (SCNetworkConnectionStart(conn, user_options, TRUE)) {
memset(&ctxt->_error, 0, sizeof(ctxt->_error));
_SchedulablesAdd(ctxt->_schedulables, conn);
}
else {
for (i = 0; i < (sizeof(loops) / sizeof(loops[0])); i++)
_CFTypeUnscheduleFromMultipleRunLoops(conn, loops[i]);
_CFTypeInvalidate(conn);
}
}
if (conn) CFRelease(conn);
}
CFRelease(options);
if (service_id) CFRelease(service_id);
if (user_options) CFRelease(user_options);
}
}
}
void
_SocketStreamPerformCancel(void* info) {
(void)info;
}
#if 0
#pragma mark *SOCKS Support
#endif
#define kSOCKSv4BufferMaximum ((CFIndex)(8L))
#define kSOCKSv5BufferMaximum ((CFIndex)(2L))
void
_PerformSOCKSv5Handshake_NoLock(_CFSocketStreamContext* ctxt) {
do {
CFMutableDataRef to_send = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties,
_kCFStreamPropertySOCKSSendBuffer);
CFMutableDataRef to_recv = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties,
_kCFStreamPropertySOCKSRecvBuffer);
if (!to_recv) {
CFStreamError error = {0, 0};
CFIndex length, sent;
if (!to_send) {
UInt8* ptr;
CFDictionaryRef proxy = (CFDictionaryRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
CFStringRef user = (CFStringRef)CFDictionaryGetValue(proxy, kCFStreamPropertySOCKSUser);
CFStringRef pass = (CFStringRef)CFDictionaryGetValue(proxy, kCFStreamPropertySOCKSPassword);
to_send = CFDataCreateMutable(CFGetAllocator(ctxt->_properties), 4);
if (!to_send) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
break;
}
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertySOCKSSendBuffer, to_send);
CFRelease(to_send);
ptr = CFDataGetMutableBytePtr(to_send);
CFDataSetLength(to_send, 4);
ptr[0] = 0x05; ptr[1] = 0x01;
ptr[2] = 0x00; ptr[3] = 0x02;
if (user && CFStringGetLength(user) && pass && CFStringGetLength(pass))
ptr[1] = 0x02;
else
CFDataSetLength(to_send, 3);
}
length = CFDataGetLength(to_send);
sent = _CFSocketSend(ctxt->_socket, CFDataGetBytePtr(to_send), length, &error);
if (sent == length) {
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertySOCKSSendBuffer);
to_recv = CFDataCreateMutable(CFGetAllocator(ctxt->_properties), 2);
if (!to_recv) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
break;
}
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertySOCKSRecvBuffer, to_recv);
CFRelease(to_recv);
}
else if (sent > 0) {
UInt8* ptr = CFDataGetMutableBytePtr(to_send);
length -= sent;
memmove(ptr, ptr + sent, length);
CFDataSetLength(to_send, length);
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketWriteCallBack);
}
else if ((error.error != EAGAIN) || (error.domain != kCFStreamErrorDomainPOSIX))
memmove(&ctxt->_error, &error, sizeof(error));
}
else {
UInt8* ptr = CFDataGetMutableBytePtr(to_recv);
CFIndex length = CFDataGetLength(to_recv);
if (length != kSOCKSv5BufferMaximum) {
CFStreamError error = {0, 0};
CFIndex recvd = _CFSocketRecv(ctxt->_socket, ptr + length, kSOCKSv5BufferMaximum - length, &error);
if (recvd == 0) {
ctxt->_error.error = ENOTCONN;
ctxt->_error.domain = _kCFStreamErrorDomainNativeSockets;
}
else if (recvd > 0) {
UInt8 tmp[kSOCKSv5BufferMaximum];
length += recvd;
memmove(tmp, ptr, length);
CFDataSetLength(to_recv, length);
memmove(ptr, tmp, length);
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketReadCallBack);
}
else if ((error.error != EAGAIN) || (error.domain != kCFStreamErrorDomainPOSIX))
memmove(&ctxt->_error, &error, sizeof(error));
}
if (length == kSOCKSv5BufferMaximum) {
switch (ptr[1]) {
case 0x00:
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertySOCKSRecvBuffer);
_SocketStreamAddHandshake_NoLock(ctxt, _PerformSOCKSv5PostambleHandshake_NoLock);
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformSOCKSv5Handshake_NoLock);
break;
case 0x02:
{
CFDictionaryRef proxy = (CFDictionaryRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
CFStringRef user = (CFStringRef)CFDictionaryGetValue(proxy, kCFStreamPropertySOCKSUser);
CFStringRef pass = (CFStringRef)CFDictionaryGetValue(proxy, kCFStreamPropertySOCKSPassword);
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertySOCKSRecvBuffer);
if (user && pass) {
_SocketStreamAddHandshake_NoLock(ctxt, _PerformSOCKSv5UserPassHandshake_NoLock);
_SocketStreamAddHandshake_NoLock(ctxt, _PerformSOCKSv5PostambleHandshake_NoLock);
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformSOCKSv5Handshake_NoLock);
}
else {
ctxt->_error.domain = kCFStreamErrorDomainSOCKS;
ctxt->_error.error = ((kCFStreamErrorSOCKS5SubDomainMethod << 16) | (ptr[1] & 0x000000FF));
}
break;
}
default:
ctxt->_error.domain = kCFStreamErrorDomainSOCKS;
ctxt->_error.error = ((kCFStreamErrorSOCKS5SubDomainMethod << 16) | (ptr[1] & 0x000000FF));
break;
}
}
}
} while (0);
if (ctxt->_error.error)
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformSOCKSv5Handshake_NoLock);
}
void
_PerformSOCKSv5PostambleHandshake_NoLock(_CFSocketStreamContext* ctxt) {
do {
CFMutableDataRef to_send = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties,
_kCFStreamPropertySOCKSSendBuffer);
CFMutableDataRef to_recv = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties,
_kCFStreamPropertySOCKSRecvBuffer);
if (!to_recv) {
CFStreamError error = {0, 0};
CFIndex length, sent;
if (!to_send) {
UInt8* ptr;
SInt32 value = 0;
CFHostRef host = (CFHostRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketRemoteHost);
CFNumberRef port = (CFNumberRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertySocketRemotePort);
CFArrayRef list = CFHostGetNames(host, NULL);
CFStringRef name = (list && CFArrayGetCount(list)) ? (CFStringRef)CFArrayGetValueAtIndex(list, 0) : NULL;
if (name)
CFRetain(name);
else {
list = CFHostGetAddressing(host, NULL);
if (list && CFArrayGetCount(list)) {
name = _CFNetworkCFStringCreateWithCFDataAddress(CFGetAllocator(list),
(CFDataRef)CFArrayGetValueAtIndex(list, 0));
}
if (!name) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
break;
}
}
to_send = CFDataCreateMutable(CFGetAllocator(ctxt->_properties), 262);
if (!to_send) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
break;
}
CFDataSetLength(to_send, 262);
ptr = CFDataGetMutableBytePtr(to_send);
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertySOCKSSendBuffer, to_send);
CFRelease(to_send);
CFStringGetPascalString(name, &(ptr[4]), 256, kCFStringEncodingUTF8);
CFRelease(name);
ptr[0] = 0x05; ptr[1] = 0x01;
ptr[2] = 0x00; ptr[3] = 0x03;
CFNumberGetValue(port, kCFNumberSInt32Type, &value);
value = htons(value & 0x0000FFFF);
ptr[ptr[4] + 5] = ((value & 0x0000FF00) >> 8);
ptr[ptr[4] + 6] = (value & 0x000000FF);
CFDataSetLength(to_send, 7 + ptr[4]);
}
length = CFDataGetLength(to_send);
sent = _CFSocketSend(ctxt->_socket, CFDataGetBytePtr(to_send), length, &error);
if (sent == length) {
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertySOCKSSendBuffer);
to_recv = CFDataCreateMutable(CFGetAllocator(ctxt->_properties), 262);
if (!to_recv) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
break;
}
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertySOCKSRecvBuffer, to_recv);
CFRelease(to_recv);
}
else if (sent > 0) {
UInt8* ptr = CFDataGetMutableBytePtr(to_send);
length -= sent;
memmove(ptr, ptr + sent, length);
CFDataSetLength(to_send, length);
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketWriteCallBack);
}
else if ((error.error != EAGAIN) || (error.domain != kCFStreamErrorDomainPOSIX))
memmove(&ctxt->_error, &error, sizeof(error));
}
else {
CFStreamError error = {0, 0};
UInt8* ptr = CFDataGetMutableBytePtr(to_recv);
CFIndex length = CFDataGetLength(to_recv);
CFIndex recvd = 0;
if (length < 2)
recvd = _CFSocketRecv(ctxt->_socket, ptr + length, 2 - length, &error);
length += (recvd > 0) ? recvd : 0;
if (!error.error && (length >= 2)) {
if ((ptr[0] != 5) || ptr[1]) {
ctxt->_error.domain = kCFStreamErrorDomainSOCKS;
ctxt->_error.error = ((kCFStreamErrorSOCKS5SubDomainResponse << 16) | (((ptr[0] != 5) ? -1 : ptr[1]) & 0x000000FF));
break;
}
else {
if (length < 8)
recvd = _CFSocketRecv(ctxt->_socket, ptr + length, 8 - length, &error);
length += (recvd > 0) ? recvd : 0;
if (!error.error && (length >= 5)) {
CFIndex intended = 0;
switch (ptr[3]) {
case 0x01:
intended = 10;
break;
case 0x03:
intended = 7 + ptr[4];
break;
case 0x04:
intended = 22;
break;
default:
ctxt->_error.domain = kCFStreamErrorDomainSOCKS;
ctxt->_error.error = ((kCFStreamErrorSOCKS5SubDomainResponse << 16) | kCFStreamErrorSOCKS5BadResponseAddr);
break;
}
if (ctxt->_error.error)
break;
if (length < intended)
recvd = _CFSocketRecv(ctxt->_socket, ptr + length, intended - length, &error);
if (!error.error) {
length += recvd;
if (length == intended) {
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertySOCKSRecvBuffer);
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformSOCKSv5PostambleHandshake_NoLock);
return;
}
}
}
}
}
if (recvd == 0) {
ctxt->_error.error = ENOTCONN;
ctxt->_error.domain = _kCFStreamErrorDomainNativeSockets;
}
else if (!error.error)
break;
else if ((error.error == EAGAIN) || (error.domain == kCFStreamErrorDomainPOSIX)) {
UInt8 tmp[262];
length += recvd;
memmove(tmp, ptr, length);
CFDataSetLength(to_recv, length);
memmove(ptr, tmp, length);
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketReadCallBack);
}
else
memmove(&ctxt->_error, &error, sizeof(error));
}
} while (0);
if (ctxt->_error.error)
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformSOCKSv5Handshake_NoLock);
}
void
_PerformSOCKSv5UserPassHandshake_NoLock(_CFSocketStreamContext* ctxt) {
do {
CFMutableDataRef to_send = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties,
_kCFStreamPropertySOCKSSendBuffer);
CFMutableDataRef to_recv = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties,
_kCFStreamPropertySOCKSRecvBuffer);
if (!to_recv) {
CFStreamError error = {0, 0};
CFIndex length, sent;
if (!to_send) {
UInt8* ptr;
CFDictionaryRef proxy = (CFDictionaryRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
CFStringRef user = (CFStringRef)CFDictionaryGetValue(proxy, kCFStreamPropertySOCKSUser);
CFStringRef pass = (CFStringRef)CFDictionaryGetValue(proxy, kCFStreamPropertySOCKSPassword);
to_send = CFDataCreateMutable(CFGetAllocator(ctxt->_properties), 513);
if (!to_send) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
break;
}
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertySOCKSSendBuffer, to_send);
CFRelease(to_send);
ptr = CFDataGetMutableBytePtr(to_send);
CFDataSetLength(to_send, 513);
ptr[0] = 0x01;
CFStringGetPascalString(user, &(ptr[1]), 256, kCFStringEncodingUTF8);
CFStringGetPascalString(pass, &(ptr[2 + ptr[1]]), 256, kCFStringEncodingUTF8);
CFDataSetLength(to_send, 3 + ptr[1] + ptr[2 + ptr[1]]);
}
length = CFDataGetLength(to_send);
sent = _CFSocketSend(ctxt->_socket, CFDataGetBytePtr(to_send), length, &error);
if (sent == length) {
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertySOCKSSendBuffer);
to_recv = CFDataCreateMutable(CFGetAllocator(ctxt->_properties), 2);
if (!to_recv) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
break;
}
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertySOCKSRecvBuffer, to_recv);
CFRelease(to_recv);
}
else if (sent > 0) {
UInt8* ptr = CFDataGetMutableBytePtr(to_send);
length -= sent;
memmove(ptr, ptr + sent, length);
CFDataSetLength(to_send, length);
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketWriteCallBack);
}
else if ((error.error != EAGAIN) || (error.domain != kCFStreamErrorDomainPOSIX))
memmove(&ctxt->_error, &error, sizeof(error));
}
else {
UInt8* ptr = CFDataGetMutableBytePtr(to_recv);
CFIndex length = CFDataGetLength(to_recv);
if (length != kSOCKSv5BufferMaximum) {
CFStreamError error = {0, 0};
CFIndex recvd = _CFSocketRecv(ctxt->_socket, ptr + length, kSOCKSv5BufferMaximum - length, &error);
if (recvd == 0) {
ctxt->_error.error = ENOTCONN;
ctxt->_error.domain = _kCFStreamErrorDomainNativeSockets;
}
else if (recvd > 0) {
UInt8 tmp[kSOCKSv5BufferMaximum];
length += recvd;
memmove(tmp, ptr, length);
CFDataSetLength(to_recv, length);
memmove(ptr, tmp, length);
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketReadCallBack);
}
else if ((error.error != EAGAIN) || (error.domain != kCFStreamErrorDomainPOSIX))
memmove(&ctxt->_error, &error, sizeof(error));
}
if (length == kSOCKSv5BufferMaximum) {
if (ptr[1]) {
ctxt->_error.domain = kCFStreamErrorDomainSOCKS;
ctxt->_error.error = ((kCFStreamErrorSOCKS5SubDomainUserPass << 16) | (ptr[1] & 0x000000FF));
break;
}
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertySOCKSRecvBuffer);
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformSOCKSv5UserPassHandshake_NoLock);
}
}
} while (0);
if (ctxt->_error.error)
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformSOCKSv5Handshake_NoLock);
}
void
_PerformSOCKSv4Handshake_NoLock(_CFSocketStreamContext* ctxt) {
do {
CFMutableDataRef to_send = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties,
_kCFStreamPropertySOCKSSendBuffer);
CFMutableDataRef to_recv = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties,
_kCFStreamPropertySOCKSRecvBuffer);
if (to_send) {
CFStreamError error = {0, 0};
CFIndex length = CFDataGetLength(to_send);
CFIndex sent = _CFSocketSend(ctxt->_socket, CFDataGetBytePtr(to_send), length, &error);
if (sent == length)
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertySOCKSSendBuffer);
else if (sent > 0) {
UInt8* ptr = CFDataGetMutableBytePtr(to_send);
length -= sent;
memmove(ptr, ptr + sent, length);
CFDataSetLength(to_send, length);
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketWriteCallBack);
}
else if ((error.error != EAGAIN) || (error.domain != kCFStreamErrorDomainPOSIX))
memmove(&ctxt->_error, &error, sizeof(error));
}
else {
UInt8* ptr;
CFIndex length;
if (!to_recv) {
to_recv = CFDataCreateMutable(CFGetAllocator(ctxt->_properties), kSOCKSv4BufferMaximum);
if (!to_recv) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
break;
}
CFDictionaryAddValue(ctxt->_properties, _kCFStreamPropertySOCKSRecvBuffer, to_recv);
CFRelease(to_recv);
}
ptr = CFDataGetMutableBytePtr(to_recv);
length = CFDataGetLength(to_recv);
if (length != kSOCKSv4BufferMaximum) {
CFStreamError error = {0, 0};
CFIndex recvd = _CFSocketRecv(ctxt->_socket, ptr + length, kSOCKSv4BufferMaximum - length, &error);
if (recvd == 0) {
ctxt->_error.error = ENOTCONN;
ctxt->_error.domain = _kCFStreamErrorDomainNativeSockets;
}
else if (recvd > 0) {
UInt8 tmp[kSOCKSv4BufferMaximum];
length += recvd;
memmove(tmp, ptr, length);
CFDataSetLength(to_recv, length);
memmove(ptr, tmp, length);
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketReadCallBack);
}
else if ((error.error != EAGAIN) || (error.domain != kCFStreamErrorDomainPOSIX))
memmove(&ctxt->_error, &error, sizeof(error));
}
if (length == kSOCKSv4BufferMaximum) {
if ((ptr[0] == 0) && (ptr[1] == 90))
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformSOCKSv4Handshake_NoLock);
else {
ctxt->_error.error = (kCFStreamErrorSOCKS4SubDomainResponse << 16) | (((ptr[0] == 0) ? ptr[1] : -1) & 0x0000FFFF);
ctxt->_error.domain = kCFStreamErrorDomainSOCKS;
}
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertySOCKSRecvBuffer);
}
}
} while (0);
if (ctxt->_error.error)
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformSOCKSv4Handshake_NoLock);
}
Boolean
_SOCKSSetInfo_NoLock(_CFSocketStreamContext* ctxt, CFDictionaryRef settings) {
CFDictionaryRef old = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
if ((settings && (CFDictionaryGetTypeID() != CFGetTypeID(settings))) ||
__CFBitIsSet(ctxt->_flags, kFlagBitOpenComplete) ||
__CFBitIsSet(ctxt->_flags, kFlagBitOpenStarted) ||
__CFBitIsSet(ctxt->_flags, kFlagBitCreatedNative))
{
return FALSE;
}
if (!old || !CFEqual(old, settings)) {
if (!settings) {
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformSOCKSv4Handshake_NoLock);
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformSOCKSv5Handshake_NoLock);
}
else {
CFStringRef name = NULL;
CFStringRef user = (CFStringRef)CFDictionaryGetValue(settings, kCFStreamPropertySOCKSUser);
CFStringRef pass = (CFStringRef)CFDictionaryGetValue(settings, kCFStreamPropertySOCKSPassword);
CFStringRef version = _GetSOCKSVersion(settings);
CFTypeRef lookup = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketRemoteHost);
CFNumberRef enabled = CFDictionaryGetValue(settings, _kCFStreamProxySettingSOCKSEnable);
SInt32 enabled_value = 0;
if (!CFEqual(version, kCFStreamSocketSOCKSVersion4) && !CFEqual(version, kCFStreamSocketSOCKSVersion5))
return FALSE;
if (enabled && CFNumberGetValue(enabled, kCFNumberSInt32Type, &enabled_value) && !enabled_value) {
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
return TRUE;
}
if (lookup) {
CFArrayRef list = CFHostGetNames((CFHostRef)lookup, NULL);
if (list && CFArrayGetCount(list))
name = (CFStringRef)CFRetain((CFTypeRef)CFArrayGetValueAtIndex(list, 0));
else {
list = CFHostGetAddressing((CFHostRef)lookup, NULL);
if (list && CFArrayGetCount(list)) {
name = _CFNetworkCFStringCreateWithCFDataAddress(CFGetAllocator(list),
(CFDataRef)CFArrayGetValueAtIndex(list, 0));
}
}
}
else if ((lookup = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketRemoteNetService))) {
return FALSE;
}
if (!name) {
if (!CFEqual(version, kCFStreamSocketSOCKSVersion4))
return FALSE;
}
else {
CFIndex length = CFStringGetLength(name);
if (!_CFNetworkDoesNeedProxy(name,
CFDictionaryGetValue(settings, kCFStreamPropertyProxyExceptionsList),
CFDictionaryGetValue(settings, kCFStreamPropertyProxyLocalBypass)))
{
CFRelease(name);
return FALSE;
}
CFRelease(name);
if (CFEqual(version, kCFStreamSocketSOCKSVersion5) && ((length <= 0) || (length > 255)))
return FALSE;
}
if (CFEqual(version, kCFStreamSocketSOCKSVersion5) && pass && (CFStringGetLength(pass) > 255))
return FALSE;
if (user) {
if (CFEqual(version, kCFStreamSocketSOCKSVersion4) && (CFStringGetLength(user) > 512))
return FALSE;
else if (CFEqual(version, kCFStreamSocketSOCKSVersion5) && (CFStringGetLength(user) > 255))
return FALSE;
}
if (!_SocketStreamAddHandshake_NoLock(ctxt, CFEqual(version, kCFStreamSocketSOCKSVersion4) ? _PerformSOCKSv4Handshake_NoLock : _PerformSOCKSv5Handshake_NoLock))
return FALSE;
CFDictionarySetValue(ctxt->_properties, kCFStreamPropertySOCKSProxy, settings);
}
}
return TRUE;
}
void
_SocketStreamSOCKSHandleLookup_NoLock(_CFSocketStreamContext* ctxt, CFHostRef lookup) {
int i;
CFArrayRef addresses;
CFStringRef name = NULL;
CFMutableArrayRef loops[3];
CFIndex user_len = 0;
CFIndex extra = 0;
CFMutableDataRef buffer;
CFDictionaryRef settings = (CFDictionaryRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
CFStringRef user = (CFStringRef)CFDictionaryGetValue(settings, kCFStreamPropertySOCKSUser);
UInt8* ptr = NULL;
CFNumberRef port = CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertySocketRemotePort);
_SchedulablesRemove(ctxt->_schedulables, lookup);
_CFTypeInvalidate(lookup);
loops[0] = ctxt->_readloops;
loops[1] = ctxt->_writeloops;
loops[2] = ctxt->_sharedloops;
for (i = 0; i < (sizeof(loops) / sizeof(loops[0])); i++)
_CFTypeUnscheduleFromMultipleRunLoops(lookup, loops[i]);
addresses = CFHostGetAddressing((CFHostRef)lookup, NULL);
if (!addresses || !CFArrayGetCount(addresses))
name = (CFStringRef)CFArrayGetValueAtIndex(CFHostGetNames((CFHostRef)lookup, NULL), 0);
if (user) {
user_len = CFStringGetBytes(user, CFRangeMake(0, CFStringGetLength(user)),
kCFStringEncodingUTF8, 0, FALSE, NULL, 0, NULL);
}
user_len += 1;
if (name) {
extra = CFStringGetBytes(name, CFRangeMake(0, CFStringGetLength(name)), kCFStringEncodingUTF8, 0, FALSE, NULL, 0, NULL);
extra += 1;
}
buffer = CFDataCreateMutable(CFGetAllocator(ctxt->_properties), 8 + extra + user_len);
if (!buffer) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
return;
}
CFDataSetLength(buffer, 8 + extra + user_len);
CFDictionaryAddValue(ctxt->_properties, _kCFStreamPropertySOCKSSendBuffer, buffer);
CFRelease(buffer);
ptr = CFDataGetMutableBytePtr(buffer);
memset(ptr, 0, CFDataGetLength(buffer));
if (name) {
CFStringGetBytes(name, CFRangeMake(0, CFStringGetLength(name)),
kCFStringEncodingUTF8, 0, FALSE, ptr + 8 + user_len - 1, extra, NULL);
ptr[8 + user_len + extra - 1] = '\0';
}
else {
CFIndex i, count = CFArrayGetCount(addresses);
for (i = 0; i < count; i++) {
struct sockaddr_in* sin = (struct sockaddr_in*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i));
if (sin->sin_family == AF_INET) {
memmove(&ptr[4], &sin->sin_addr, sizeof(sin->sin_addr));
if (!port)
memmove(&ptr[2], &sin->sin_port, sizeof(sin->sin_port));
break;
}
}
if (i == count) {
ctxt->_error.error = EINVAL;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertySOCKSSendBuffer);
return;
}
}
ptr[0] = 0x04;
ptr[1] = 0x01;
if (port) {
SInt32 value;
CFNumberGetValue(port, kCFNumberSInt32Type, &value);
*((UInt16*)(&ptr[2])) = htons(value & 0x0000FFFF);
}
if (user) {
CFStringGetBytes(user, CFRangeMake(0, CFStringGetLength(user)),
kCFStringEncodingUTF8, 0, FALSE, ptr + 8, user_len - 1, NULL);
}
if (__CFBitIsSet(ctxt->_flags, kFlagBitOpenComplete)) {
CFArrayRef handshakes = (CFArrayRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyHandshakes);
if (handshakes && (CFArrayGetValueAtIndex(handshakes, 0) == _PerformSOCKSv4Handshake_NoLock))
_PerformSOCKSv4Handshake_NoLock(ctxt);
}
}
#if 0
#pragma mark *CONNECT Support
#endif
void
_CreateNameAndPortForCONNECTProxy(CFDictionaryRef properties, CFStringRef* name, CFNumberRef* port, CFStreamError* error) {
CFTypeRef lookup;
CFDataRef addr = NULL;
CFAllocatorRef alloc = CFGetAllocator(properties);
*name = NULL;
*port = NULL;
memset(error, 0, sizeof(error[0]));
*port = (CFNumberRef)CFDictionaryGetValue(properties, _kCFStreamPropertySocketRemotePort);
if ((lookup = (CFTypeRef)CFDictionaryGetValue(properties, kCFStreamPropertySocketRemoteNetService))) {
*name = CFNetServiceGetTargetHost((CFNetServiceRef)lookup);
if (!*port) {
CFArrayRef list = CFNetServiceGetAddressing((CFNetServiceRef)lookup);
if (list && CFArrayGetCount(list))
addr = CFArrayGetValueAtIndex(list, 0);
}
}
else if ((lookup = (CFTypeRef)CFDictionaryGetValue(properties, kCFStreamPropertySocketRemoteHost))) {
CFArrayRef list = CFHostGetNames((CFHostRef)lookup, NULL);
if (list && CFArrayGetCount(list))
*name = (CFStringRef)CFArrayGetValueAtIndex(list, 0);
else {
list = CFHostGetAddressing((CFHostRef)lookup, NULL);
if (list && CFArrayGetCount(list))
addr = CFArrayGetValueAtIndex(list, 0);
}
}
if (!*port && !*name && !addr) {
error->error = EINVAL;
error->domain = kCFStreamErrorDomainPOSIX;
return;
}
if (*port)
CFRetain(*port);
else {
SInt32 p;
struct sockaddr* sa = (struct sockaddr*)CFDataGetBytePtr(addr);
switch (sa->sa_family) {
case AF_INET:
p = ((struct sockaddr_in*)sa)->sin_port;
*port = CFNumberCreate(alloc, kCFNumberSInt32Type, &p);
break;
case AF_INET6:
p = ((struct sockaddr_in6*)sa)->sin6_port;
*port = CFNumberCreate(alloc, kCFNumberSInt32Type, &p);
break;
default:
error->error = EINVAL;
error->domain = kCFStreamErrorDomainPOSIX;
return;
}
}
if (*name)
CFRetain(*name);
else
*name = _CFNetworkCFStringCreateWithCFDataAddress(alloc, addr);
if (!*name || !*port) {
if (*name) CFRelease(*name);
if (*port) CFRelease(*port);
error->error = ENOMEM;
error->domain = kCFStreamErrorDomainPOSIX;
}
}
void
_PerformCONNECTHandshake_NoLock(_CFSocketStreamContext* ctxt) {
UInt8 buffer[2048];
CFIndex count;
CFStreamError error = {0, 0};
CFAllocatorRef alloc = CFGetAllocator(ctxt->_properties);
CFHTTPMessageRef response = (CFHTTPMessageRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyCONNECTResponse);
if (!response) {
CFIndex length;
CFDataRef left = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyCONNECTSendBuffer);
if (!left) {
CFDictionaryRef headers;
CFHTTPMessageRef request = NULL;
CFStringRef version, name = NULL;
CFDictionaryRef proxy = (CFDictionaryRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyCONNECTProxy);
version = (CFStringRef)CFDictionaryGetValue(proxy, kCFStreamPropertyCONNECTVersion);
do {
SInt32 p;
CFURLRef url;
CFNumberRef port;
CFStringRef urlstr;
_CreateNameAndPortForCONNECTProxy(ctxt->_properties, &name, &port, &ctxt->_error);
if (ctxt->_error.error) break;
CFNumberGetValue(port, kCFNumberSInt32Type, &p);
urlstr = CFStringCreateWithFormat(alloc, NULL, _kCFStreamCONNECTURLFormat, name, p & 0x0000FFFF);
CFRelease(port);
if (!urlstr) break;
url = CFURLCreateWithString(alloc, urlstr, NULL);
CFRelease(urlstr);
if (!url) break;
request = CFHTTPMessageCreateRequest(alloc, _kCFStreamCONNECTMethod, url, version ? version : kCFHTTPVersion1_0);
CFRelease(url);
} while (0);
if (!request) {
if (name) CFRelease(name);
if (!ctxt->_error.error) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
}
return;
}
headers = CFDictionaryGetValue(proxy, kCFStreamPropertyCONNECTAdditionalHeaders);
if (headers) {
CFStringRef value = (CFStringRef)CFDictionaryGetValue(headers, _kCFStreamHostHeader);
if (!value)
CFHTTPMessageSetHeaderFieldValue(request, _kCFStreamHostHeader, name);
value = (CFStringRef)CFDictionaryGetValue(headers, _kCFStreamUserAgentHeader);
if (!value)
CFHTTPMessageSetHeaderFieldValue(request, _kCFStreamUserAgentHeader, _kCFStreamUserAgentValue);
CFDictionaryApplyFunction(headers, (CFDictionaryApplierFunction)_CONNECTHeaderApplier, request);
}
else {
CFHTTPMessageSetHeaderFieldValue(request, _kCFStreamHostHeader, name);
CFHTTPMessageSetHeaderFieldValue(request, _kCFStreamUserAgentHeader, _kCFStreamUserAgentValue);
}
CFRelease(name);
left = _CFHTTPMessageCopySerializedHeaders(request, TRUE);
CFRelease(request);
if (!left) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
return;
}
CFDictionaryAddValue(ctxt->_properties, _kCFStreamPropertyCONNECTSendBuffer, left);
CFRelease(left);
}
length = CFDataGetLength(left);
count = _CFSocketSend(ctxt->_socket, CFDataGetBytePtr(left), length, &error);
if (error.error) {
if ((error.error == EAGAIN) && (error.domain == _kCFStreamErrorDomainNativeSockets))
count = 0;
else {
memmove(&ctxt->_error, &error, sizeof(error));
return;
}
}
length -= count;
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketWriteCallBack);
if (!length) {
CFDictionaryRemoveValue(ctxt->_properties, _kCFStreamPropertyCONNECTSendBuffer);
response = CFHTTPMessageCreateEmpty(alloc, FALSE);
if (!response) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
return;
}
CFDictionaryAddValue(ctxt->_properties, kCFStreamPropertyCONNECTResponse, response);
CFRelease(response);
}
else if (count) {
left = CFDataCreate(alloc, ((UInt8*)CFDataGetBytePtr(left)) + count, length);
if (!left) {
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
return;
}
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertyCONNECTSendBuffer, left);
CFRelease(left);
}
else
return;
}
if (-1 == (count = recv(CFSocketGetNative(ctxt->_socket), buffer, sizeof(buffer), MSG_PEEK))) {
if ((_LastError(&error) == EAGAIN) && (error.domain == _kCFStreamErrorDomainNativeSockets)) {
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketReadCallBack);
return;
}
else if (error.error) {
memmove(&ctxt->_error, &error, sizeof(error));
return;
}
}
if (CFHTTPMessageAppendBytes(response, buffer, count)) {
if (CFHTTPMessageIsHeaderComplete(response)) {
UInt32 code = CFHTTPMessageGetResponseStatusCode(response);
CFDataRef body = CFHTTPMessageCopyBody(response);
if (body) {
count -= CFDataGetLength(body);
CFRelease(body);
CFHTTPMessageSetBody(response, NULL);
}
if ((code < 200) || (code > 299))
_SocketStreamAddHandshake_NoLock(ctxt, _PerformCONNECTHaltHandshake_NoLock);
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformCONNECTHandshake_NoLock);
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertyPreviousCONNECTResponse);
}
if (-1 == recv(CFSocketGetNative(ctxt->_socket), buffer, count, 0)) {
_LastError(&ctxt->_error);
return;
}
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketReadCallBack);
}
else {
ctxt->_error.error = kCFStreamErrorHTTPParseFailure;
ctxt->_error.domain = kCFStreamErrorDomainHTTP;
}
}
void
_PerformCONNECTHaltHandshake_NoLock(_CFSocketStreamContext* ctxt) {
(void)ctxt;
if (ctxt->_clientWriteStream && __CFBitIsSet(ctxt->_flags, kFlagBitWriteStreamOpened)) {
__CFBitSet(ctxt->_flags, kFlagBitCanWrite);
_CFWriteStreamSignalEventDelayed(ctxt->_clientWriteStream, kCFStreamEventCanAcceptBytes, NULL);
}
if (ctxt->_clientReadStream && __CFBitIsSet(ctxt->_flags, kFlagBitReadStreamOpened)) {
__CFBitSet(ctxt->_flags, kFlagBitCanRead);
_CFReadStreamSignalEventDelayed(ctxt->_clientReadStream, kCFStreamEventHasBytesAvailable, NULL);
}
}
void
_CONNECTHeaderApplier(CFStringRef key, CFStringRef value, CFHTTPMessageRef request) {
CFHTTPMessageSetHeaderFieldValue(request, key, value);
}
Boolean
_CONNECTSetInfo_NoLock(_CFSocketStreamContext* ctxt, CFDictionaryRef settings) {
Boolean resume = FALSE;
CFStringRef server = settings ? CFDictionaryGetValue(settings, kCFStreamPropertyCONNECTProxyHost) : NULL;
CFNumberRef port = settings ? CFDictionaryGetValue(settings, kCFStreamPropertyCONNECTProxyPort) : NULL;
CFDictionaryRef old = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyCONNECTProxy);
if ((settings && (!server || !port)) ||
__CFBitIsSet(ctxt->_flags, kFlagBitCreatedNative))
{
return FALSE;
}
if (__CFBitIsSet(ctxt->_flags, kFlagBitOpenComplete) || __CFBitIsSet(ctxt->_flags, kFlagBitOpenStarted)) {
CFMutableArrayRef handshakes = (CFMutableArrayRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyHandshakes);
if (handshakes &&
CFArrayGetCount(handshakes) &&
(_PerformCONNECTHaltHandshake_NoLock == CFArrayGetValueAtIndex(handshakes, 0)))
{
resume = TRUE;
}
else
return FALSE;
}
if (!old || !CFEqual(old, settings)) {
if (!settings) {
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertyCONNECTProxy);
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformCONNECTHandshake_NoLock);
}
else {
Boolean hasName = FALSE;
CFTypeRef lookup = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketRemoteHost);
if (lookup) {
CFArrayRef list = CFHostGetNames((CFHostRef)lookup, NULL);
if (list && CFArrayGetCount(list))
hasName = TRUE;
else {
list = CFHostGetAddressing((CFHostRef)lookup, NULL);
if (list && CFArrayGetCount(list))
hasName = TRUE;
}
}
else if ((lookup = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketRemoteNetService))) {
if (CFNetServiceGetTargetHost((CFNetServiceRef)lookup))
hasName = TRUE;
}
if (!hasName)
return FALSE;
if (!_SocketStreamAddHandshake_NoLock(ctxt, _PerformCONNECTHandshake_NoLock)) {
if (resume) {
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertyCONNECTResponse);
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformCONNECTHaltHandshake_NoLock);
}
return FALSE;
}
if (resume) {
CFTypeRef last = CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyCONNECTResponse);
if (last)
CFDictionarySetValue(ctxt->_properties, kCFStreamPropertyPreviousCONNECTResponse, last);
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertyCONNECTResponse);
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformCONNECTHaltHandshake_NoLock);
}
CFDictionarySetValue(ctxt->_properties, kCFStreamPropertyCONNECTProxy, settings);
}
}
return TRUE;
}
#if 0
#pragma mark *SSL Support
#endif
OSStatus
_SecurityReadFunc_NoLock(_CFSocketStreamContext* ctxt, void* data, UInt32* dataLength) {
CFIndex i, s;
UInt32 try = *dataLength;
CFStreamError error = {0, 0};
CFDataRef size = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertySecurityRecvBufferSize);
CFMutableDataRef buffer = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertySecurityRecvBuffer);
CFMutableDataRef count = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertySecurityRecvBufferCount);
if (!buffer) {
CFAllocatorRef alloc = CFGetAllocator(ctxt->_properties);
if (!size) {
s = kSecurityBufferSize;
size = CFDataCreate(alloc, (const UInt8*)&s, sizeof(s));
}
if (size) {
buffer = CFDataCreateMutable(alloc, *((CFIndex*)CFDataGetBytePtr(size)));
count = CFDataCreateMutable(alloc, sizeof(CFIndex));
}
if (!buffer || !count || !size) {
if (buffer) CFRelease(buffer);
if (count) CFRelease(count);
if (size) CFRelease(size);
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
return errSSLInternal;
}
CFDictionaryAddValue(ctxt->_properties, _kCFStreamPropertySecurityRecvBufferSize, size);
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertySecurityRecvBuffer, buffer);
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertySecurityRecvBufferCount, count);
CFRelease(size);
CFRelease(buffer);
CFRelease(count);
*((CFIndex*)CFDataGetMutableBytePtr(count)) = 0;
}
i = *((CFIndex*)CFDataGetMutableBytePtr(count));
s = *((CFIndex*)CFDataGetBytePtr(size));
if (i < *dataLength) {
CFIndex r = _CFSocketRecv(ctxt->_socket, ((UInt8*)CFDataGetMutableBytePtr(buffer)) + i, s - i, &error);
__CFBitClear(ctxt->_flags, kFlagBitRecvdRead);
if (!error.error)
i += r;
}
else
__CFBitSet(ctxt->_flags, kFlagBitRecvdRead);
if (!error.error) {
UInt8* ptr = (UInt8*)CFDataGetMutableBytePtr(buffer);
*dataLength = (*dataLength <= i) ? *dataLength : i;
i -= *dataLength;
memmove(data, ptr, *dataLength);
memmove(ptr, ptr + *dataLength, i);
memset(ptr + i, 0, s - i);
*((CFIndex*)CFDataGetMutableBytePtr(count)) = i;
if (*dataLength)
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketReadCallBack);
return (!*dataLength ? errSSLClosedAbort : (try == *dataLength) ? 0 : errSSLWouldBlock);
}
*dataLength = 0;
if ((error.domain == _kCFStreamErrorDomainNativeSockets) && (EAGAIN == error.error))
return errSSLWouldBlock;
memmove(&ctxt->_error, &error, sizeof(error));
return errSSLInternal;
}
OSStatus
_SecurityWriteFunc_NoLock(_CFSocketStreamContext* ctxt, const void* data, UInt32* dataLength) {
CFStreamError error = {0, 0};
UInt32 try = *dataLength;
*dataLength = _CFSocketSend(ctxt->_socket, data, *dataLength, &error);
if (!error.error)
return ((try == *dataLength) ? 0 : errSSLWouldBlock);
*dataLength = 0;
if ((error.domain == _kCFStreamErrorDomainNativeSockets) && (EAGAIN == error.error))
return errSSLWouldBlock;
memmove(&ctxt->_error, &error, sizeof(error));
return errSSLInternal;
}
CFIndex
_SocketStreamSecuritySend_NoLock(_CFSocketStreamContext* ctxt, const UInt8* buffer, CFIndex length) {
CFIndex bytesWritten = 0;
SSLContextRef ssl = *((SSLContextRef*)CFDataGetBytePtr((CFDataRef)CFDictionaryGetValue(ctxt->_properties,
kCFStreamPropertySocketSSLContext)));
OSStatus result = SSLWrite(ssl, buffer, length, &bytesWritten);
if (ctxt->_error.error)
return -1;
if ((result == errSSLWouldBlock) && bytesWritten) {
_SocketStreamAddHandshake_NoLock(ctxt, _PerformSecuritySendHandshake_NoLock);
result = noErr;
}
switch (result) {
case errSSLClosedGraceful:
case errSSLClosedAbort:
__CFBitSet(ctxt->_flags, kFlagBitClosed);
case noErr:
break;
default:
ctxt->_error.error = result;
ctxt->_error.domain = kCFStreamErrorDomainSSL;
return -1;
}
return bytesWritten;
}
void
_SocketStreamSecurityBufferedRead_NoLock(_CFSocketStreamContext* ctxt) {
CFIndex* i;
CFIndex s = kRecvBufferSize;
OSStatus status = noErr;
SSLContextRef ssl = *((SSLContextRef*)CFDataGetBytePtr((CFDataRef)CFDictionaryGetValue(ctxt->_properties,
kCFStreamPropertySocketSSLContext)));
CFNumberRef size = (CFNumberRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferSize);
CFMutableDataRef buffer = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBuffer);
CFMutableDataRef count = (CFMutableDataRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferCount);
if (!buffer) {
CFAllocatorRef alloc = CFGetAllocator(ctxt->_properties);
if (!size)
size = CFNumberCreate(alloc, kCFNumberCFIndexType, &s);
else
CFNumberGetValue(size, kCFNumberCFIndexType, &s);
if (size) {
buffer = CFDataCreateMutable(alloc, s);
count = CFDataCreateMutable(alloc, sizeof(CFIndex));
}
if (!buffer || !count || !size) {
if (buffer) CFRelease(buffer);
if (count) CFRelease(count);
if (size) CFRelease(size);
ctxt->_error.error = ENOMEM;
ctxt->_error.domain = kCFStreamErrorDomainPOSIX;
return;
}
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferSize, size);
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertyRecvBuffer, buffer);
CFDictionarySetValue(ctxt->_properties, _kCFStreamPropertyRecvBufferCount, count);
CFRelease(size);
CFRelease(buffer);
CFRelease(count);
*((CFIndex*)CFDataGetMutableBytePtr(count)) = 0;
}
i = (CFIndex*)CFDataGetMutableBytePtr(count);
CFNumberGetValue(size, kCFNumberCFIndexType, &s);
if (*i < s) {
CFIndex start = *i;
UInt8* ptr = (UInt8*)CFDataGetMutableBytePtr(buffer);
while (!status && (*i < s)) {
CFIndex bytesRead = 0;
status = SSLRead(ssl, ptr + *i, s - *i, &bytesRead);
if (bytesRead > 0)
*i = *i + bytesRead;
}
if ((*i == start) && (*i == 0) && !__CFBitIsSet(ctxt->_flags, kFlagBitClosed))
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketReadCallBack);
}
switch (status) {
case errSSLClosedGraceful:
case errSSLClosedAbort:
__CFBitSet(ctxt->_flags, kFlagBitClosed);
__CFBitSet(ctxt->_flags, kFlagBitCanRead);
__CFBitClear(ctxt->_flags, kFlagBitPollRead);
case noErr:
case errSSLWouldBlock:
if (*i) {
__CFBitSet(ctxt->_flags, kFlagBitCanRead);
__CFBitClear(ctxt->_flags, kFlagBitPollRead);
}
break;
default:
if (!ctxt->_error.error) {
ctxt->_error.error = status;
ctxt->_error.domain = kCFStreamErrorDomainSSL;
}
break;
}
}
void
_PerformSecurityHandshake_NoLock(_CFSocketStreamContext* ctxt) {
OSStatus result;
const void* peerid = NULL;
size_t peeridlen;
SSLContextRef ssl = *((SSLContextRef*)CFDataGetBytePtr((CFDataRef)CFDictionaryGetValue(ctxt->_properties,
kCFStreamPropertySocketSSLContext)));
result = SSLGetPeerID(ssl, &peerid, &peeridlen);
if (!result && !peerid) {
Boolean set = FALSE;
CFTypeRef value = (CFTypeRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertyCONNECTProxy);
if (!value)
value = (CFTypeRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySOCKSProxy);
if (value) {
CFStringRef host = NULL;
CFNumberRef port = NULL;
CFStreamError error;
_CreateNameAndPortForCONNECTProxy(ctxt->_properties, &host, &port, &error);
if (host && port) {
SInt32 p;
CFStringRef peer;
CFAllocatorRef alloc = CFGetAllocator(ctxt->_properties);
CFNumberGetValue(port, kCFNumberSInt32Type, &p);
peer = CFStringCreateWithFormat(alloc, NULL, _kCFStreamCONNECTURLFormat, host, p & 0x0000FFFF);
if (peer) {
UInt8 static_buffer[1024];
UInt8* buffer = &static_buffer[0];
CFIndex buffer_size = sizeof(static_buffer);
buffer = _CFStringGetOrCreateCString(alloc, peer, static_buffer, &buffer_size, kCFStringEncodingUTF8);
CFRelease(peer);
SSLSetPeerID(ssl, buffer, buffer_size);
set = TRUE;
if (buffer != &static_buffer[0])
CFAllocatorDeallocate(alloc, buffer);
}
}
if (host) CFRelease(host);
if (port) CFRelease(port);
}
if (!set) {
UInt8 static_buffer[SOCK_MAXADDRLEN];
struct sockaddr* sa = (struct sockaddr*)&static_buffer[0];
socklen_t addrlen = sizeof(static_buffer);
if (!getpeername(CFSocketGetNative(ctxt->_socket), sa, &addrlen)) {
if (sa->sa_family == AF_INET) {
in_port_t port = ((struct sockaddr_in*)sa)->sin_port;
memmove(static_buffer, &(((struct sockaddr_in*)sa)->sin_addr), sizeof(((struct sockaddr_in*)sa)->sin_addr));
memmove(static_buffer + sizeof(((struct sockaddr_in*)sa)->sin_addr), &port, sizeof(port));
SSLSetPeerID(ssl, static_buffer, sizeof(((struct sockaddr_in*)sa)->sin_addr) + sizeof(port));
}
else if (sa->sa_family == AF_INET6) {
in_port_t port = ((struct sockaddr_in6*)sa)->sin6_port;
memmove(static_buffer, &(((struct sockaddr_in6*)sa)->sin6_addr), sizeof(((struct sockaddr_in6*)sa)->sin6_addr));
memmove(static_buffer + sizeof(((struct sockaddr_in6*)sa)->sin6_addr), &port, sizeof(port));
SSLSetPeerID(ssl, static_buffer, sizeof(((struct sockaddr_in6*)sa)->sin6_addr) + sizeof(port));
}
}
}
}
result = SSLHandshake(ssl);
if (result != errSSLWouldBlock) {
if (result) {
ctxt->_error.error = result;
ctxt->_error.domain = kCFStreamErrorDomainSSL;
}
__CFBitSet(ctxt->_flags, kFlagBitUseSSL);
__CFBitSet(ctxt->_flags, kFlagBitIsBuffered);
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformSecurityHandshake_NoLock);
}
}
void
_PerformSecuritySendHandshake_NoLock(_CFSocketStreamContext* ctxt) {
OSStatus result;
CFIndex bytesWritten;
SSLContextRef ssl = *((SSLContextRef*)CFDataGetBytePtr((CFDataRef)CFDictionaryGetValue(ctxt->_properties,
kCFStreamPropertySocketSSLContext)));
result = SSLWrite(ssl, NULL, 0, &bytesWritten);
if (result == errSSLWouldBlock)
CFSocketEnableCallBacks(ctxt->_socket, kCFSocketWriteCallBack);
else {
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformSecuritySendHandshake_NoLock);
if (result) {
ctxt->_error.error = result;
ctxt->_error.domain = kCFStreamErrorDomainSSL;
}
}
}
void
_SocketStreamSecurityClose_NoLock(_CFSocketStreamContext* ctxt) {
SSLContextRef ssl = *((SSLContextRef*)CFDataGetBytePtr((CFDataRef)CFDictionaryGetValue(ctxt->_properties,
kCFStreamPropertySocketSSLContext)));
while (!ctxt->_error.error && (errSSLWouldBlock == SSLClose(ssl))) {
CFTypeRef loopAndMode[2] = {CFRunLoopGetCurrent(), _kCFStreamSocketSecurityClosePrivateMode};
_SchedulesAddRunLoopAndMode(ctxt->_sharedloops, (CFRunLoopRef)loopAndMode[0], (CFStringRef)loopAndMode[1]);
CFArrayApplyFunction(ctxt->_schedulables,
CFRangeMake(0, CFArrayGetCount(ctxt->_schedulables)),
(CFArrayApplierFunction)_SchedulablesScheduleApplierFunction,
loopAndMode);
__CFSpinUnlock(&ctxt->_lock);
CFRunLoopRunInMode(_kCFStreamSocketSecurityClosePrivateMode, 1e+20, TRUE);
__CFSpinLock(&ctxt->_lock);
CFArrayApplyFunction(ctxt->_schedulables,
CFRangeMake(0, CFArrayGetCount(ctxt->_schedulables)),
(CFArrayApplierFunction)_SchedulablesUnscheduleApplierFunction,
loopAndMode);
_SchedulesRemoveRunLoopAndMode(ctxt->_sharedloops, (CFRunLoopRef)loopAndMode[0], (CFStringRef)loopAndMode[1]);
}
SSLDisposeContext(ssl);
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertySocketSSLContext);
}
Boolean
_SocketStreamSecuritySetContext_NoLock(_CFSocketStreamContext *ctxt, CFDataRef value) {
CFDataRef wrapper = (CFDataRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketSSLContext);
SSLContextRef old = wrapper ? *((SSLContextRef*)CFDataGetBytePtr(wrapper)) : NULL;
SSLContextRef security = value ? *((SSLContextRef*)CFDataGetBytePtr(value)) : NULL;
if (old) {
if (_SocketStreamSecurityGetSessionState_NoLock(ctxt) != kSSLIdle)
return FALSE;
if (security != old)
SSLDisposeContext(old);
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertySocketSSLContext);
}
if (!security) {
CFDictionaryRemoveValue(ctxt->_properties, kCFStreamPropertySocketSSLContext);
_SocketStreamRemoveHandshake_NoLock(ctxt, _PerformSecurityHandshake_NoLock);
return TRUE;
}
if ((!(ctxt->_error.error = SSLSetIOFuncs(security, (SSLReadFunc)_SecurityReadFunc_NoLock, (SSLWriteFunc)_SecurityWriteFunc_NoLock))) &&
(!(ctxt->_error.error = SSLSetConnection(security, (SSLConnectionRef)ctxt))))
{
Boolean result = _SocketStreamAddHandshake_NoLock(ctxt, _PerformSecurityHandshake_NoLock);
if (result)
CFDictionarySetValue(ctxt->_properties, kCFStreamPropertySocketSSLContext, value);
return result;
}
ctxt->_error.domain = kCFStreamErrorDomainSSL;
return FALSE;
}
Boolean
_SocketStreamSecuritySetInfo_NoLock(_CFSocketStreamContext* ctxt, CFDictionaryRef settings) {
SSLContextRef security = NULL;
CFDataRef wrapper = NULL;
if (!_SocketStreamSecuritySetContext_NoLock(ctxt, NULL))
return FALSE;
if (!settings)
return TRUE;
do {
CFTypeRef value = CFDictionaryGetValue(settings, kCFStreamSSLIsServer);
CFBooleanRef check = (CFBooleanRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertySocketSecurityAuthenticatesServerCertificate);
if (SSLNewContext((value && CFEqual(value, kCFBooleanTrue)), &security))
break;
value = CFDictionaryGetValue(settings, kCFStreamSSLLevel);
if ((!value || CFEqual(value, kCFStreamSocketSecurityLevelNegotiatedSSL)) && SSLSetProtocolVersion(security, kTLSProtocol1))
break;
else if (value) {
if (CFEqual(value, kCFStreamSocketSecurityLevelNone)) {
SSLDisposeContext(security);
return TRUE;
}
else if (CFEqual(value, kCFStreamSocketSecurityLevelSSLv2) && SSLSetProtocolVersion(security, kSSLProtocol2))
break;
else if (CFEqual(value, kCFStreamSocketSecurityLevelSSLv3) && SSLSetProtocolVersion(security, kSSLProtocol3Only))
break;
else if (CFEqual(value, kCFStreamSocketSecurityLevelTLSv1) && SSLSetProtocolVersion(security, kTLSProtocol1Only))
break;
else if (CFEqual(value, kCFStreamSocketSecurityLevelTLSv1SSLv3) &&
(SSLSetProtocolVersion(security, kTLSProtocol1) || SSLSetProtocolVersionEnabled(security, kSSLProtocol2, FALSE)))
{
break;
}
}
if (check && (check == kCFBooleanFalse)) {
if (SSLSetAllowsExpiredRoots(security, TRUE))
break;
if (SSLSetAllowsAnyRoot(security, TRUE))
break;
}
value = CFDictionaryGetValue(settings, kCFStreamSSLAllowsExpiredCertificates);
if (value && CFEqual(value, kCFBooleanTrue) && SSLSetAllowsExpiredCerts(security, TRUE))
break;
value = CFDictionaryGetValue(settings, kCFStreamSSLAllowsExpiredRoots);
if (value && CFEqual(value, kCFBooleanTrue) && SSLSetAllowsExpiredRoots(security, TRUE))
break;
value = CFDictionaryGetValue(settings, kCFStreamSSLAllowsAnyRoot);
if (value && CFEqual(value, kCFBooleanTrue) && SSLSetAllowsAnyRoot(security, TRUE))
break;
value = CFDictionaryGetValue(settings, kCFStreamSSLValidatesCertificateChain);
if (value && CFEqual(value, kCFBooleanFalse) && SSLSetEnableCertVerify(security, FALSE))
break;
value = CFDictionaryGetValue(settings, kCFStreamSSLCertificates);
if (value && SSLSetCertificate(security, value))
break;
value = CFDictionaryGetValue(settings, kCFStreamSSLPeerName);
if (!value) {
CFStringRef name = (CFStringRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertySocketPeerName);
CFTypeRef lookup = (CFTypeRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketRemoteHost);
if (check && (check == kCFBooleanFalse))
value = kCFNull;
else if (name)
value = name;
else if (lookup) {
CFArrayRef names = CFHostGetNames((CFHostRef)lookup, NULL);
if (names)
value = CFArrayGetValueAtIndex(names, 0);
}
else if ((lookup = (CFTypeRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketRemoteNetService)))
value = CFNetServiceGetTargetHost((CFNetServiceRef)lookup);
}
if (value) {
if (CFEqual(value, kCFNull)) {
if (SSLSetPeerDomainName(security, NULL, 0))
break;
}
else {
OSStatus err;
UInt8 static_buffer[1024];
UInt8* buffer = &static_buffer[0];
CFIndex buffer_size = sizeof(static_buffer);
CFAllocatorRef alloc = CFGetAllocator(ctxt->_properties);
buffer = _CFStringGetOrCreateCString(alloc, value, static_buffer, &buffer_size, kCFStringEncodingUTF8);
err = SSLSetPeerDomainName(security, buffer, buffer_size);
if (buffer != &static_buffer[0])
CFAllocatorDeallocate(alloc, buffer);
if (err)
break;
}
}
wrapper = CFDataCreate(CFGetAllocator(ctxt->_properties), (void*)&security, sizeof(security));
if (!wrapper || !_SocketStreamSecuritySetContext_NoLock(ctxt, wrapper))
break;
CFRelease(wrapper);
return TRUE;
} while(1);
if (security)
SSLDisposeContext(security);
if (wrapper)
CFRelease(wrapper);
return FALSE;
}
Boolean
_SocketStreamSecuritySetAuthenticatesServerCertificates_NoLock(_CFSocketStreamContext* ctxt, CFBooleanRef authenticates) {
SSLContextRef ssl = *((SSLContextRef*)CFDataGetBytePtr((CFDataRef)CFDictionaryGetValue(ctxt->_properties,
kCFStreamPropertySocketSSLContext)));
do {
CFTypeRef value = NULL;
CFStringRef name = (CFStringRef)CFDictionaryGetValue(ctxt->_properties, _kCFStreamPropertySocketPeerName);
CFTypeRef lookup = (CFTypeRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketRemoteHost);
if (SSLSetAllowsExpiredRoots(ssl, authenticates ? FALSE : TRUE))
break;
if (SSLSetAllowsAnyRoot(ssl, authenticates ? FALSE : TRUE))
break;
if (authenticates == kCFBooleanFalse)
value = kCFNull;
else if (name)
value = name;
else if (lookup) {
CFArrayRef names = CFHostGetNames((CFHostRef)lookup, NULL);
if (names)
value = CFArrayGetValueAtIndex(names, 0);
}
else if ((lookup = (CFTypeRef)CFDictionaryGetValue(ctxt->_properties, kCFStreamPropertySocketRemoteNetService)))
value = CFNetServiceGetTargetHost((CFNetServiceRef)lookup);
if (!value) break;
if (CFEqual(value, kCFNull)) {
if (SSLSetPeerDomainName(ssl, NULL, 0))
break;
}
else {
OSStatus err;
UInt8 static_buffer[1024];
UInt8* buffer = &static_buffer[0];
CFIndex buffer_size = sizeof(static_buffer);
CFAllocatorRef alloc = CFGetAllocator(ctxt->_properties);
buffer = _CFStringGetOrCreateCString(alloc, value, static_buffer, &buffer_size, kCFStringEncodingUTF8);
err = SSLSetPeerDomainName(ssl, buffer, buffer_size);
if (err)
break;
}
return TRUE;
} while (1);
return FALSE;
}
CFStringRef
_SecurityGetProtocol(SSLContextRef security) {
SSLProtocol version = kSSLProtocolUnknown;
SSLGetNegotiatedProtocolVersion(security, &version);
if (kSSLProtocolUnknown == version)
SSLGetProtocolVersion(security, &version);
switch (version) {
case kSSLProtocol2:
return kCFStreamSocketSecurityLevelSSLv2;
case kSSLProtocol3Only:
return kCFStreamSocketSecurityLevelSSLv3;
case kTLSProtocol1Only:
return kCFStreamSocketSecurityLevelTLSv1;
case kSSLProtocol3:
case kTLSProtocol1:
default:
{
Boolean enabled;
if (!SSLGetProtocolVersionEnabled(security, kSSLProtocol2, &enabled) && !enabled)
return kCFStreamSocketSecurityLevelTLSv1SSLv3;
return kCFStreamSocketSecurityLevelNegotiatedSSL;
}
}
}
SSLSessionState
_SocketStreamSecurityGetSessionState_NoLock(_CFSocketStreamContext* ctxt) {
SSLContextRef ssl = *((SSLContextRef*)CFDataGetBytePtr((CFDataRef)CFDictionaryGetValue(ctxt->_properties,
kCFStreamPropertySocketSSLContext)));
SSLSessionState state;
return !SSLGetSessionState(ssl, &state) ? state : kSSLAborted;
}
#if 0
#pragma mark -
#pragma mark Extern Function Definitions (API)
#endif
void
CFStreamCreatePairWithSocketToCFHost(CFAllocatorRef alloc, CFHostRef host, UInt32 port,
CFReadStreamRef* readStream, CFWriteStreamRef* writeStream)
{
_CFSocketStreamContext* ctxt;
if (readStream) *readStream = NULL;
if (writeStream) *writeStream = NULL;
ctxt = _SocketStreamCreateContext(alloc);
if (ctxt) {
CFNumberRef num;
port = port & 0x0000FFFF;
num = CFNumberCreate(alloc, kCFNumberSInt32Type, &port);
if (!num)
_SocketStreamDestroyContext_NoLock(alloc, ctxt);
else {
CFDictionaryAddValue(ctxt->_properties, kCFStreamPropertySocketRemoteHost, host);
CFDictionaryAddValue(ctxt->_properties, _kCFStreamPropertySocketRemotePort, num);
if (readStream) {
*readStream = CFReadStreamCreate(alloc, &kSocketReadStreamCallBacks, ctxt);
ctxt->_clientReadStream = *readStream;
}
if (writeStream) {
*writeStream = CFWriteStreamCreate(alloc, &kSocketWriteStreamCallBacks, ctxt);
ctxt->_clientWriteStream = *writeStream;
}
if (readStream && *readStream && writeStream && *writeStream)
__CFBitSet(ctxt->_flags, kFlagBitShared);
}
if (num) CFRelease(num);
}
}
void
CFStreamCreatePairWithSocketToNetService(CFAllocatorRef alloc, CFNetServiceRef service,
CFReadStreamRef* readStream, CFWriteStreamRef* writeStream)
{
_CFSocketStreamContext* ctxt;
if (readStream) *readStream = NULL;
if (writeStream) *writeStream = NULL;
ctxt = _SocketStreamCreateContext(alloc);
if (ctxt) {
CFDictionaryAddValue(ctxt->_properties, kCFStreamPropertySocketRemoteNetService, service);
if (readStream) {
*readStream = CFReadStreamCreate(alloc, &kSocketReadStreamCallBacks, ctxt);
ctxt->_clientReadStream = *readStream;
}
if (writeStream) {
*writeStream = CFWriteStreamCreate(alloc, &kSocketWriteStreamCallBacks, ctxt);
ctxt->_clientWriteStream = *writeStream;
}
if (readStream && *readStream && writeStream && *writeStream)
__CFBitSet(ctxt->_flags, kFlagBitShared);
}
}
Boolean
CFSocketStreamPairSetSecurityProtocol(CFReadStreamRef socketReadStream, CFWriteStreamRef socketWriteStream,
CFStreamSocketSecurityProtocol securityProtocol)
{
Boolean result = FALSE;
CFStringRef value = NULL;
switch (securityProtocol) {
case kCFStreamSocketSecurityNone:
value = kCFStreamSocketSecurityLevelNone;
break;
case kCFStreamSocketSecuritySSLv2:
value = kCFStreamSocketSecurityLevelSSLv2;
break;
case kCFStreamSocketSecuritySSLv3:
value = kCFStreamSocketSecurityLevelSSLv3;
break;
case kCFStreamSocketSecuritySSLv23:
value = kCFStreamSocketSecurityLevelNegotiatedSSL;
break;
case kCFStreamSocketSecurityTLSv1:
value = kCFStreamSocketSecurityLevelTLSv1;
break;
default:
return result;
}
if (socketReadStream) {
result = CFReadStreamSetProperty(socketReadStream,
kCFStreamPropertySocketSecurityLevel,
value);
}
else if (socketWriteStream) {
result = CFWriteStreamSetProperty(socketWriteStream,
kCFStreamPropertySocketSecurityLevel,
value);
}
return result;
}
#if 0
#pragma mark -
#pragma mark Extern Function Definitions (SPI)
#endif
extern void
_CFStreamCreatePairWithCFSocketSignaturePieces(CFAllocatorRef alloc, SInt32 protocolFamily, SInt32 socketType,
SInt32 protocol, CFDataRef address, CFReadStreamRef* readStream,
CFWriteStreamRef* writeStream)
{
_CFSocketStreamContext* ctxt;
if (readStream) *readStream = NULL;
if (writeStream) *writeStream = NULL;
ctxt = _SocketStreamCreateContext(alloc);
if (ctxt) {
CFDictionaryValueCallBacks cb = {0, NULL, NULL, NULL, NULL};
CFHostRef h = CFHostCreateWithAddress(alloc, address);
CFStringRef keys[] = {
_kCFStreamSocketFamily,
_kCFStreamSocketType,
_kCFStreamSocketProtocol
};
SInt32 values[] = {
protocolFamily,
socketType,
protocol
};
CFDictionaryRef props = CFDictionaryCreate(alloc,
(const void**)keys,
(const void**)values,
(sizeof(values) / sizeof(values[0])),
&kCFTypeDictionaryKeyCallBacks,
&cb);
if (!props || !h)
_SocketStreamDestroyContext_NoLock(alloc, ctxt);
else {
CFDictionaryAddValue(ctxt->_properties, _kCFStreamPropertySocketFamilyTypeProtocol, props);
CFDictionaryAddValue(ctxt->_properties, kCFStreamPropertySocketRemoteHost, h);
if (readStream) {
*readStream = CFReadStreamCreate(alloc, &kSocketReadStreamCallBacks, ctxt);
ctxt->_clientReadStream = *readStream;
}
if (writeStream) {
*writeStream = CFWriteStreamCreate(alloc, &kSocketWriteStreamCallBacks, ctxt);
ctxt->_clientWriteStream = *writeStream;
}
if (readStream && *readStream && writeStream && *writeStream)
__CFBitSet(ctxt->_flags, kFlagBitShared);
}
if (h) CFRelease(h);
if (props) CFRelease(props);
}
}
void
CFStreamCreatePairWithNetServicePieces(CFAllocatorRef alloc, CFStringRef domain, CFStringRef serviceType,
CFStringRef name, CFReadStreamRef* readStream, CFWriteStreamRef* writeStream)
{
CFNetServiceRef service = CFNetServiceCreate(alloc, domain, serviceType, name, 0);
if (readStream) *readStream = NULL;
if (writeStream) *writeStream = NULL;
if (service) {
CFStreamCreatePairWithSocketToNetService(alloc, service, readStream, writeStream);
CFRelease(service);
}
}
void
_CFSocketStreamCreatePair(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFSocketNativeHandle s,
const CFSocketSignature* sig, CFReadStreamRef* readStream, CFWriteStreamRef* writeStream)
{
if (!readStream && !writeStream) return;
if (readStream) *readStream = NULL;
if (writeStream) *writeStream = NULL;
if (host) {
CFHostRef h = CFHostCreateWithName(alloc, host);
if (h) {
CFStreamCreatePairWithSocketToCFHost(alloc, h, port, readStream, writeStream);
CFRelease(h);
}
}
else if (sig) {
_CFStreamCreatePairWithCFSocketSignaturePieces(alloc,
sig->protocolFamily,
sig->socketType,
sig->protocol,
sig->address,
readStream,
writeStream);
}
else {
_CFSocketStreamContext* ctxt = _SocketStreamCreateContext(alloc);
if (ctxt) {
CFDataRef wrapper = CFDataCreate(alloc, (const void*)(&s), sizeof(s));
__CFBitSet(ctxt->_flags, kFlagBitCreatedNative);
if (!wrapper)
_SocketStreamDestroyContext_NoLock(alloc, ctxt);
else {
CFDictionaryAddValue(ctxt->_properties, kCFStreamPropertySocketNativeHandle, wrapper);
CFRelease(wrapper);
CFDictionaryAddValue(ctxt->_properties, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse);
if (readStream) {
*readStream = CFReadStreamCreate(alloc, &kSocketReadStreamCallBacks, ctxt);
ctxt->_clientReadStream = *readStream;
}
if (writeStream) {
*writeStream = CFWriteStreamCreate(alloc, &kSocketWriteStreamCallBacks, ctxt);
ctxt->_clientWriteStream = *writeStream;
}
if (readStream && *readStream && writeStream && *writeStream)
__CFBitSet(ctxt->_flags, kFlagBitShared);
}
}
}
}