1/* 2 * Copyright (c) 2014 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* CFSocketStream.c 25 Copyright (c) 2000-2013, Apple Inc. All rights reserved. 26 Responsibility: Jeremy Wyld 27*/ 28// Original Author: Becky Willrich 29#include <CoreFoundation/CFStream.h> 30#include <CoreFoundation/CFNumber.h> 31#include "CFInternal.h" 32#include "CFStreamInternal.h" 33#include "CFStreamPriv.h" 34 35#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 36// On Mach these live in CF for historical reasons, even though they are declared in CFNetwork 37 38const int kCFStreamErrorDomainSSL = 3; 39const int kCFStreamErrorDomainSOCKS = 5; 40 41CONST_STRING_DECL(kCFStreamPropertyShouldCloseNativeSocket, "kCFStreamPropertyShouldCloseNativeSocket") 42CONST_STRING_DECL(kCFStreamPropertyAutoErrorOnSystemChange, "kCFStreamPropertyAutoErrorOnSystemChange"); 43 44CONST_STRING_DECL(kCFStreamPropertySOCKSProxy, "kCFStreamPropertySOCKSProxy") 45CONST_STRING_DECL(kCFStreamPropertySOCKSProxyHost, "SOCKSProxy") 46CONST_STRING_DECL(kCFStreamPropertySOCKSProxyPort, "SOCKSPort") 47CONST_STRING_DECL(kCFStreamPropertySOCKSVersion, "kCFStreamPropertySOCKSVersion") 48CONST_STRING_DECL(kCFStreamSocketSOCKSVersion4, "kCFStreamSocketSOCKSVersion4") 49CONST_STRING_DECL(kCFStreamSocketSOCKSVersion5, "kCFStreamSocketSOCKSVersion5") 50CONST_STRING_DECL(kCFStreamPropertySOCKSUser, "kCFStreamPropertySOCKSUser") 51CONST_STRING_DECL(kCFStreamPropertySOCKSPassword, "kCFStreamPropertySOCKSPassword") 52 53CONST_STRING_DECL(kCFStreamPropertySocketSecurityLevel, "kCFStreamPropertySocketSecurityLevel"); 54CONST_STRING_DECL(kCFStreamSocketSecurityLevelNone, "kCFStreamSocketSecurityLevelNone"); 55CONST_STRING_DECL(kCFStreamSocketSecurityLevelSSLv2, "kCFStreamSocketSecurityLevelSSLv2"); 56CONST_STRING_DECL(kCFStreamSocketSecurityLevelSSLv3, "kCFStreamSocketSecurityLevelSSLv3"); 57CONST_STRING_DECL(kCFStreamSocketSecurityLevelTLSv1, "kCFStreamSocketSecurityLevelTLSv1"); 58CONST_STRING_DECL(kCFStreamSocketSecurityLevelNegotiatedSSL, "kCFStreamSocketSecurityLevelNegotiatedSSL"); 59 60#endif 61 62 63#if DEPLOYMENT_TARGET_WINDOWS 64typedef void (*CF_SOCKET_STREAM_PAIR)(CFAllocatorRef, CFStringRef, UInt32, CFSocketNativeHandle, const CFSocketSignature*, CFReadStreamRef*, CFWriteStreamRef*); 65#endif 66 67// These are duplicated in CFNetwork, who actually externs them in its headers 68CONST_STRING_DECL(kCFStreamPropertySocketSSLContext, "kCFStreamPropertySocketSSLContext") 69CONST_STRING_DECL(_kCFStreamPropertySocketSecurityAuthenticatesServerCertificate, "_kCFStreamPropertySocketSecurityAuthenticatesServerCertificate"); 70 71 72CF_EXPORT 73void _CFSocketStreamSetAuthenticatesServerCertificateDefault(Boolean shouldAuthenticate) { 74 CFLog(__kCFLogAssertion, CFSTR("_CFSocketStreamSetAuthenticatesServerCertificateDefault(): This call has been deprecated. Use SetProperty(_kCFStreamPropertySocketSecurityAuthenticatesServerCertificate, kCFBooleanTrue/False)\n")); 75} 76 77 78/* CF_EXPORT */ Boolean 79_CFSocketStreamGetAuthenticatesServerCertificateDefault(void) { 80 CFLog(__kCFLogAssertion, CFSTR("_CFSocketStreamGetAuthenticatesServerCertificateDefault(): This call has been removed as a security risk. Use security properties on individual streams instead.\n")); 81 return FALSE; 82} 83 84 85/* CF_EXPORT */ void 86_CFSocketStreamPairSetAuthenticatesServerCertificate(CFReadStreamRef rStream, CFWriteStreamRef wStream, Boolean authenticates) { 87 88 CFBooleanRef value = (!authenticates ? kCFBooleanFalse : kCFBooleanTrue); 89 90 if (rStream) 91 CFReadStreamSetProperty(rStream, _kCFStreamPropertySocketSecurityAuthenticatesServerCertificate, value); 92 else 93 CFWriteStreamSetProperty(wStream, _kCFStreamPropertySocketSecurityAuthenticatesServerCertificate, value); 94} 95 96 97// Flags for dyld loading of libraries. 98enum { 99 kTriedToLoad = 0, 100 kInitialized 101}; 102 103static struct { 104 CFSpinLock_t lock; 105 UInt32 flags; 106#if DEPLOYMENT_TARGET_WINDOWS 107 HMODULE image; 108#endif 109 void (*_CFSocketStreamCreatePair)(CFAllocatorRef, CFStringRef, UInt32, CFSocketNativeHandle, const CFSocketSignature*, CFReadStreamRef*, CFWriteStreamRef*); 110 CFErrorRef (*_CFErrorCreateWithStreamError)(CFAllocatorRef, CFStreamError*); 111 CFStreamError (*_CFStreamErrorFromCFError)(CFErrorRef); 112} CFNetworkSupport = { 113 CFSpinLockInit, 114 0x0, 115#if DEPLOYMENT_TARGET_WINDOWS 116 NULL, 117#endif 118 NULL, 119 NULL, 120 NULL 121}; 122 123#define CFNETWORK_CALL(sym, args) ((CFNetworkSupport.sym)args) 124 125#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 126#define CFNETWORK_LOAD_SYM(sym) __CFLookupCFNetworkFunction(#sym) 127#elif DEPLOYMENT_TARGET_WINDOWS 128#define CFNETWORK_LOAD_SYM(sym) (void *)GetProcAddress(CFNetworkSupport.image, #sym) 129#endif 130 131static void initializeCFNetworkSupport(void) { 132 __CFBitSet(CFNetworkSupport.flags, kTriedToLoad); 133 134#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 135 CFNetworkSupport._CFSocketStreamCreatePair = CFNETWORK_LOAD_SYM(_CFSocketStreamCreatePair); 136 CFNetworkSupport._CFErrorCreateWithStreamError = CFNETWORK_LOAD_SYM(_CFErrorCreateWithStreamError); 137 CFNetworkSupport._CFStreamErrorFromCFError = CFNETWORK_LOAD_SYM(_CFStreamErrorFromCFError); 138#elif DEPLOYMENT_TARGET_WINDOWS 139 if (!CFNetworkSupport.image) { 140#if _DEBUG 141 CFNetworkSupport.image = GetModuleHandleW(L"CFNetwork_debug.dll"); 142#else 143 CFNetworkSupport.image = GetModuleHandleW(L"CFNetwork.dll"); 144#endif 145 } 146 147 if (!CFNetworkSupport.image) { 148 // not loaded yet, try to load from the filesystem 149 char path[MAX_PATH+1]; 150 if (!CFNetworkSupport.image) { 151 strlcpy(path, (const char *)_CFDLLPath(), sizeof(path)); 152#if _DEBUG 153 strlcat(path, "\\CFNetwork_debug.dll", sizeof(path)); 154#else 155 strlcat(path, "\\CFNetwork.dll", sizeof(path)); 156#endif 157 CFNetworkSupport.image = LoadLibraryA(path); 158 } 159 } 160 161 if (!CFNetworkSupport.image) { 162 CFLog(__kCFLogAssertion, CFSTR("CoreFoundation: failed to dynamically load CFNetwork")); 163 } else { 164 CFNetworkSupport._CFSocketStreamCreatePair = (CF_SOCKET_STREAM_PAIR)CFNETWORK_LOAD_SYM(_CFSocketStreamCreatePair); 165 CFNetworkSupport._CFErrorCreateWithStreamError = (CFErrorRef(*)(CFAllocatorRef, CFStreamError *))CFNETWORK_LOAD_SYM(_CFErrorCreateWithStreamError); 166 CFNetworkSupport._CFStreamErrorFromCFError = (CFStreamError(*)(CFErrorRef))CFNETWORK_LOAD_SYM(_CFStreamErrorFromCFError); 167 } 168#endif 169 170 if (!CFNetworkSupport._CFSocketStreamCreatePair) CFLog(__kCFLogAssertion, CFSTR("CoreFoundation: failed to dynamically link symbol _CFSocketStreamCreatePair")); 171 if (!CFNetworkSupport._CFErrorCreateWithStreamError) CFLog(__kCFLogAssertion, CFSTR("CoreFoundation: failed to dynamically link symbol _CFErrorCreateWithStreamError")); 172 if (!CFNetworkSupport._CFStreamErrorFromCFError) CFLog(__kCFLogAssertion, CFSTR("CoreFoundation: failed to dynamically link symbol _CFStreamErrorFromCFError")); 173 174 __CFBitSet(CFNetworkSupport.flags, kInitialized); 175} 176 177static void 178createPair(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFSocketNativeHandle sock, const CFSocketSignature* sig, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream) 179{ 180 if (readStream) 181 *readStream = NULL; 182 183 if (writeStream) 184 *writeStream = NULL; 185 186 __CFSpinLock(&(CFNetworkSupport.lock)); 187 if (!__CFBitIsSet(CFNetworkSupport.flags, kTriedToLoad)) initializeCFNetworkSupport(); 188 __CFSpinUnlock(&(CFNetworkSupport.lock)); 189 190 CFNETWORK_CALL(_CFSocketStreamCreatePair, (alloc, host, port, sock, sig, readStream, writeStream)); 191} 192 193 194CF_EXPORT void CFStreamCreatePairWithSocket(CFAllocatorRef alloc, CFSocketNativeHandle sock, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream) { 195 createPair(alloc, NULL, 0, sock, NULL, readStream, writeStream); 196} 197 198CF_EXPORT void CFStreamCreatePairWithSocketToHost(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream) { 199 createPair(alloc, host, port, 0, NULL, readStream, writeStream); 200} 201 202CF_EXPORT void CFStreamCreatePairWithPeerSocketSignature(CFAllocatorRef alloc, const CFSocketSignature* sig, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream) { 203 createPair(alloc, NULL, 0, 0, sig, readStream, writeStream); 204} 205 206CF_PRIVATE CFStreamError _CFStreamErrorFromError(CFErrorRef error) { 207 CFStreamError result; 208 Boolean canUpCall; 209 210 __CFSpinLock(&(CFNetworkSupport.lock)); 211 if (!__CFBitIsSet(CFNetworkSupport.flags, kTriedToLoad)) initializeCFNetworkSupport(); 212 canUpCall = (CFNetworkSupport._CFStreamErrorFromCFError != NULL); 213 __CFSpinUnlock(&(CFNetworkSupport.lock)); 214 215 if (canUpCall) { 216 result = CFNETWORK_CALL(_CFStreamErrorFromCFError, (error)); 217 } else { 218 CFStringRef domain = CFErrorGetDomain(error); 219 if (CFEqual(domain, kCFErrorDomainPOSIX)) { 220 result.domain = kCFStreamErrorDomainPOSIX; 221 } else if (CFEqual(domain, kCFErrorDomainOSStatus)) { 222 result.domain = kCFStreamErrorDomainMacOSStatus; 223 } else if (CFEqual(domain, kCFErrorDomainMach)) { 224 result.domain = 11; // kCFStreamErrorDomainMach, but that symbol is in CFNetwork 225 } else { 226 result.domain = kCFStreamErrorDomainCustom; 227 } 228 result.error = CFErrorGetCode(error); 229 } 230 return result; 231} 232 233CF_PRIVATE CFErrorRef _CFErrorFromStreamError(CFAllocatorRef alloc, CFStreamError *streamError) { 234 CFErrorRef result; 235 Boolean canUpCall; 236 237 __CFSpinLock(&(CFNetworkSupport.lock)); 238 if (!__CFBitIsSet(CFNetworkSupport.flags, kTriedToLoad)) initializeCFNetworkSupport(); 239 canUpCall = (CFNetworkSupport._CFErrorCreateWithStreamError != NULL); 240 __CFSpinUnlock(&(CFNetworkSupport.lock)); 241 242 if (canUpCall) { 243 result = CFNETWORK_CALL(_CFErrorCreateWithStreamError, (alloc, streamError)); 244 } else { 245 if (streamError->domain == kCFStreamErrorDomainPOSIX) { 246 return CFErrorCreate(alloc, kCFErrorDomainPOSIX, streamError->error, NULL); 247 } else if (streamError->domain == kCFStreamErrorDomainMacOSStatus) { 248 return CFErrorCreate(alloc, kCFErrorDomainOSStatus, streamError->error, NULL); 249 } else { 250 CFStringRef key = CFSTR("CFStreamErrorDomainKey"); 251 CFNumberRef value = CFNumberCreate(alloc, kCFNumberCFIndexType, &streamError->domain); 252 CFDictionaryRef dict = CFDictionaryCreate(alloc, (const void **)(&key), (const void **)(&value), 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 253 result = CFErrorCreate(alloc, CFSTR("BogusCFStreamErrorCompatibilityDomain"), streamError->error, dict); 254 CFRelease(value); 255 CFRelease(dict); 256 } 257 } 258 return result; 259} 260