diff options
Diffstat (limited to 'cpp/src/Ice/ios/StreamEndpointI.cpp')
-rw-r--r-- | cpp/src/Ice/ios/StreamEndpointI.cpp | 820 |
1 files changed, 820 insertions, 0 deletions
diff --git a/cpp/src/Ice/ios/StreamEndpointI.cpp b/cpp/src/Ice/ios/StreamEndpointI.cpp new file mode 100644 index 00000000000..0be2584140c --- /dev/null +++ b/cpp/src/Ice/ios/StreamEndpointI.cpp @@ -0,0 +1,820 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved. +// +// This copy of Ice Touch is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include "StreamEndpointI.h" +#include "StreamAcceptor.h" +#include "StreamConnector.h" + +#include <IceUtil/StringUtil.h> + +#include <Ice/Network.h> +#include <Ice/InputStream.h> +#include <Ice/OutputStream.h> +#include <Ice/LocalException.h> +#include <Ice/Communicator.h> +#include <Ice/EndpointFactoryManager.h> +#include <Ice/Properties.h> +#include <Ice/HashUtil.h> +#include <Ice/NetworkProxy.h> + +#include <IceSSL/EndpointInfo.h> + +#include <CoreFoundation/CoreFoundation.h> +#include <Security/Security.h> + +#include <fstream> + +using namespace std; +using namespace Ice; +using namespace IceInternal; + +extern "C" +{ + +Plugin* +createIceTCP(const CommunicatorPtr& com, const string&, const StringSeq&) +{ + IceObjC::InstancePtr tcpInstance = new IceObjC::Instance(com, TCPEndpointType, "tcp", false); + return new EndpointFactoryPlugin(com, new IceObjC::StreamEndpointFactory(tcpInstance)); +} + +Plugin* +createIceSSL(const CommunicatorPtr& com, const string&, const StringSeq&) +{ + IceObjC::InstancePtr sslInstance = new IceObjC::Instance(com, SSLEndpointType, "ssl", true); + return new EndpointFactoryPlugin(com, new IceObjC::StreamEndpointFactory(sslInstance)); +} + +} + +namespace Ice +{ + +void +registerIceSSL(bool) +{ + // Nothing to do, we always register IceSSL +} + +} + +inline CFStringRef +toCFString(const string& s) +{ + return CFStringCreateWithCString(NULL, s.c_str(), kCFStringEncodingUTF8); +} + +inline int +hexValue(char c) +{ + if(c >= '0' && c <= '9') + { + return c - '0'; + } + else if(c >= 'A' && c <= 'F') + { + return (c - 'A') + 10; + } + else if(c >= 'a' && c <= 'f') + { + return (c - 'a') + 10; + } + return -1; +} + +inline CFDataRef +parseKey(const string& keyStr) +{ + int i = 0, j = 0; + const char* m = keyStr.c_str(); + CFMutableDataRef data = CFDataCreateMutable(0, 160); + unsigned char buf[160]; + while(i < (int)keyStr.size()) + { + if(isspace(m[i]) || m[i] == ':') + { + ++i; + continue; + } + else if(i == (int)keyStr.size() - 1) + { + CFRelease(data); + return 0; // Not enough bytes. + } + + int vh = hexValue(m[i++]); + int vl = hexValue(m[i++]); + if(vh < 0 || vl < 0) + { + CFRelease(data); + return 0; + } + buf[j] = vh << 4; + buf[j++] += vl; + + if(j == sizeof(buf)) + { + CFDataAppendBytes(data, (UInt8*)buf, j); + j = 0; + } + } + + if(j > 0) + { + CFDataAppendBytes(data, buf, j); + } + + return data; +} + +namespace +{ + +CFDataRef +readCert(const string& defaultDir, const string& certFile) +{ + string path; + CFURLRef url = 0; + CFBundleRef bundle = CFBundleGetMainBundle(); + if(bundle) + { + CFStringRef resourceName = toCFString(certFile); + CFStringRef subDirName = toCFString(defaultDir); + url = CFBundleCopyResourceURL(bundle, resourceName, 0, subDirName); + CFRelease(resourceName); + CFRelease(subDirName); + + UInt8 filePath[PATH_MAX]; + if(CFURLGetFileSystemRepresentation(url, true, filePath, sizeof(filePath))) + { + path = string(reinterpret_cast<char*>(filePath)); + } + } + + if(!url || path.empty()) + { + path = defaultDir.empty() ? certFile : defaultDir + "/" + certFile; + } + + FILE *file = fopen(path.c_str(), "rb"); + if(!file) + { + ostringstream os; + os << "IceSSL: unable to open file " << certFile << " (error = " << IceUtilInternal::lastErrorToString() << ")"; + throw InitializationException(__FILE__, __LINE__, os.str()); + } + + fseek(file, 0, SEEK_END); + unsigned long size = ftell(file); + fseek(file, 0, SEEK_SET); + CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, size); + CFDataSetLength(data, size); + if(fread(CFDataGetMutableBytePtr(data), 1, size, file) != size) + { + CFRelease(data); + ostringstream os; + os << "IceSSL: error while reading file " << certFile; + throw InitializationException(__FILE__, __LINE__, os.str()); + } + fclose(file); + return data; +} + +} + +IceObjC::Instance::Instance(const Ice::CommunicatorPtr& com, Short type, const string& protocol, bool secure) : + ProtocolInstance(com, type, protocol, secure), + _voip(com->getProperties()->getPropertyAsIntWithDefault("Ice.Voip", 0) > 0), + _communicator(com), + _serverSettings(0), + _clientSettings(0), + _proxySettings(0), + _certificateAuthorities(0), + _trustOnlyKeyID(0) +{ + const Ice::PropertiesPtr properties = com->getProperties(); + if(secure) + { + _clientSettings = CFDictionaryCreateMutable(0, 1, &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + string defaultDir = properties->getProperty("IceSSL.DefaultDir"); + string certAuthFile = properties->getProperty("IceSSL.CAs"); + if(certAuthFile.empty()) + { + certAuthFile = properties->getProperty("IceSSL.CertAuthFile"); + } + string certFile = properties->getProperty("IceSSL.CertFile"); + + OSStatus err; + if(!certAuthFile.empty()) + { + CFDataRef cert = readCert(defaultDir, certAuthFile); + if(!cert) + { + InitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: unable to open file " + certAuthFile; + throw ex; + } + + SecCertificateRef result = SecCertificateCreateWithData(0, cert); + CFRelease(cert); + if(!result) + { + InitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: certificate " + certAuthFile + " is not a valid DER-encoded certificate"; + throw ex; + } + + SecCertificateRef certs[] = { result }; + _certificateAuthorities = CFArrayCreate(0, (const void**)certs, 1, &kCFTypeArrayCallBacks); + CFRelease(result); + + // The root CA will be validated by the transceiver. + // NOTE: on the iPhone, setting kCFStreamSSLAllowsAnyRoot = true isn't enough. + //CFDictionarySetValue(_clientSettings, kCFStreamSSLAllowsAnyRoot, kCFBooleanTrue); + CFDictionarySetValue(_clientSettings, kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse); + } + else if(properties->getPropertyAsInt("IceSSL.UsePlatformCAs") <= 0) + { + // Setup an empty list of Root CAs to not use the system root CAs. + _certificateAuthorities = CFArrayCreate(0, 0, 0, 0); + } + + if(!certFile.empty()) + { + CFDataRef cert = readCert(defaultDir, certFile); + if(!cert) + { + InitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: unable to open file " + certFile; + throw ex; + } + + CFMutableDictionaryRef settings = CFDictionaryCreateMutable(0, 1, &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFStringRef password = toCFString(properties->getProperty("IceSSL.Password")); + CFDictionarySetValue(settings, kSecImportExportPassphrase, password); + CFRelease(password); + + CFArrayRef items = 0; + err = SecPKCS12Import(cert, settings, &items); + CFRelease(cert); + CFRelease(settings); + if(err != noErr) + { + ostringstream os; + os << "IceSSL: unable to import certificate from file " << certFile << " (error = " << err << ")"; + throw InitializationException(__FILE__, __LINE__, os.str()); + } + + SecIdentityRef identity = 0; + if(CFArrayGetCount(items) > 0) + { + identity = (SecIdentityRef)CFDictionaryGetValue((CFDictionaryRef)CFArrayGetValueAtIndex(items, 0), + kSecImportItemIdentity); + } + if(identity == 0) + { + ostringstream os; + os << "IceSSL: couldn't find identity in file " << certFile << " (error = " << err << ")"; + throw InitializationException(__FILE__, __LINE__, os.str()); + } + CFRetain(identity); + CFRelease(items); + + SecIdentityRef identities[] = { identity }; + items = CFArrayCreate(0, (const void**)identities, 1, &kCFTypeArrayCallBacks); + CFDictionarySetValue(_clientSettings, kCFStreamSSLCertificates, items); + CFRelease(identity); + CFRelease(items); + } + + string trustOnly = properties->getProperty("IceSSL.TrustOnly.Client"); + if(!trustOnly.empty()) + { + _trustOnlyKeyID = parseKey(trustOnly); + if(!_trustOnlyKeyID) + { + ostringstream os; + os << "IceSSL: invalid `IceSSL.TrustOnly.Client' property value"; + throw InitializationException(__FILE__, __LINE__, os.str()); + } + } + + _serverSettings = CFDictionaryCreateMutableCopy(0, 0, _clientSettings); + CFDictionarySetValue(_serverSettings, kCFStreamSSLIsServer, kCFBooleanTrue); + } + + // + // Proxy settings + // + _proxyHost = properties->getProperty("Ice.SOCKSProxyHost"); + if(!_proxyHost.empty()) + { +#if TARGET_IPHONE_SIMULATOR != 0 + throw Ice::FeatureNotSupportedException(__FILE__, __LINE__, "SOCKS proxy not supported"); +#endif + _proxySettings = CFDictionaryCreateMutable(0, 3, &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + _proxyPort = properties->getPropertyAsIntWithDefault("Ice.SOCKSProxyPort", 1080); + + CFStringRef host = toCFString(_proxyHost); + CFDictionarySetValue(_proxySettings, kCFStreamPropertySOCKSProxyHost, host); + CFRelease(host); + + CFNumberRef port = CFNumberCreate(0, kCFNumberSInt32Type, &_proxyPort); + CFDictionarySetValue(_proxySettings, kCFStreamPropertySOCKSProxyPort, port); + CFRelease(port); + + CFDictionarySetValue(_proxySettings, kCFStreamPropertySOCKSVersion, kCFStreamSocketSOCKSVersion4); + } +} + +IceObjC::Instance::~Instance() +{ + if(_trustOnlyKeyID) + { + CFRelease(_trustOnlyKeyID); + } + if(_serverSettings) + { + CFRelease(_serverSettings); + } + if(_clientSettings) + { + CFRelease(_clientSettings); + } + if(_certificateAuthorities) + { + CFRelease(_certificateAuthorities); + } + if(_proxySettings) + { + CFRelease(_proxySettings); + } +} + +void +IceObjC::Instance::setupStreams(CFReadStreamRef readStream, + CFWriteStreamRef writeStream, + bool server, + const string& host) const +{ + if(_voip) + { +#if TARGET_IPHONE_SIMULATOR == 0 + if(!CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP) || + !CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP)) + { + throw Ice::SyscallException(__FILE__, __LINE__); + } +#endif + } + + if(!server && _proxySettings) + { + if(!CFReadStreamSetProperty(readStream, kCFStreamPropertySOCKSProxy, _proxySettings) || + !CFWriteStreamSetProperty(writeStream, kCFStreamPropertySOCKSProxy, _proxySettings)) + { + throw Ice::SyscallException(__FILE__, __LINE__); + } + } + + if(secure()) + { + CFDictionaryRef settings = server ? _serverSettings : _clientSettings; + + if(!CFReadStreamSetProperty(readStream, kCFStreamPropertySocketSecurityLevel, + kCFStreamSocketSecurityLevelNegotiatedSSL) || + !CFWriteStreamSetProperty(writeStream, kCFStreamPropertySocketSecurityLevel, + kCFStreamSocketSecurityLevelNegotiatedSSL)) + { + throw Ice::SecurityException(__FILE__, __LINE__, "couldn't set security level"); + } + + if(!server && properties()->getPropertyAsIntWithDefault("IceSSL.CheckCertName", 1)) + { + settings = CFDictionaryCreateMutableCopy(0, 0, settings); + + CFStringRef h = toCFString(host); + CFDictionarySetValue((CFMutableDictionaryRef)settings, kCFStreamSSLPeerName, h); + CFRelease(h); + } + else + { + CFRetain(settings); + } + + if(!CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, settings) || + !CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, settings)) + { + CFRelease(settings); + throw Ice::SecurityException(__FILE__, __LINE__, "couldn't set security options"); + } + CFRelease(settings); + } +} + +IceObjC::Instance* +IceObjC::Instance::clone(const ProtocolInstancePtr& instance) +{ + return new Instance(_communicator, instance->type(), instance->protocol(), instance->secure()); +} + +IceObjC::StreamEndpointI::StreamEndpointI(const InstancePtr& instance, const string& ho, Int po, + const Address& sourceAddr, Int ti, const string& conId, bool co) : + IceInternal::IPEndpointI(instance, ho, po, sourceAddr, conId), + _instance(instance), + _timeout(ti), + _compress(co) +{ +} + +IceObjC::StreamEndpointI::StreamEndpointI(const InstancePtr& instance) : + IceInternal::IPEndpointI(instance), + _instance(instance), + _timeout(instance->defaultTimeout()), + _compress(false) +{ +} + +IceObjC::StreamEndpointI::StreamEndpointI(const InstancePtr& instance, Ice::InputStream* s) : + IPEndpointI(instance, s), + _instance(instance), + _timeout(-1), + _compress(false) +{ + s->read(const_cast<Int&>(_timeout)); + s->read(const_cast<bool&>(_compress)); +} + +EndpointInfoPtr +IceObjC::StreamEndpointI::getInfo() const +{ + IPEndpointInfoPtr info; + if(_instance->secure()) + { + info = ICE_MAKE_SHARED(InfoI<IceSSL::EndpointInfo>, ICE_DYNAMIC_CAST(StreamEndpointI, shared_from_this())); + } + else + { + info = ICE_MAKE_SHARED(InfoI<Ice::TCPEndpointInfo>, ICE_DYNAMIC_CAST(StreamEndpointI, shared_from_this())); + } + fillEndpointInfo(info.get()); + return info; +} + +EndpointInfoPtr +IceObjC::StreamEndpointI::getWSInfo(const string& resource) const +{ + IPEndpointInfoPtr info; + if(_instance->secure()) + { + IceSSL::WSSEndpointInfoPtr i; + i = ICE_MAKE_SHARED(InfoI<IceSSL::WSSEndpointInfo>, ICE_DYNAMIC_CAST(StreamEndpointI, shared_from_this())); + i->resource = resource; + info = i; + } + else + { + Ice::WSEndpointInfoPtr i; + i = ICE_MAKE_SHARED(InfoI<Ice::WSEndpointInfo>, ICE_DYNAMIC_CAST(StreamEndpointI, shared_from_this())); + i->resource = resource; + info = i; + } + fillEndpointInfo(info.get()); + return info; +} + +Int +IceObjC::StreamEndpointI::timeout() const +{ + return _timeout; +} + +EndpointIPtr +IceObjC::StreamEndpointI::timeout(Int t) const +{ + if(t == _timeout) + { + return shared_from_this(); + } + else + { + return ICE_MAKE_SHARED(StreamEndpointI, _instance, _host, _port, _sourceAddr, t, _connectionId, _compress); + } +} + +bool +IceObjC::StreamEndpointI::compress() const +{ + return _compress; +} + +EndpointIPtr +IceObjC::StreamEndpointI::compress(bool c) const +{ + if(c == _compress) + { + return shared_from_this(); + } + else + { + return ICE_MAKE_SHARED(StreamEndpointI, _instance, _host, _port, _sourceAddr, _timeout, _connectionId, c); + } +} + +bool +IceObjC::StreamEndpointI::datagram() const +{ + return false; +} + +bool +IceObjC::StreamEndpointI::secure() const +{ + return _instance->secure(); +} + +void +IceObjC::StreamEndpointI::connectors_async(Ice::EndpointSelectionType selType, const EndpointI_connectorsPtr& cb) const +{ + vector<ConnectorPtr> connectors; + connectors.push_back(new StreamConnector(_instance, _host, _port, _timeout, _connectionId)); + cb->connectors(connectors); +} + +TransceiverPtr +IceObjC::StreamEndpointI::transceiver() const +{ + return 0; +} + +AcceptorPtr +IceObjC::StreamEndpointI::acceptor(const string&) const +{ + return new StreamAcceptor(ICE_DYNAMIC_CAST(StreamEndpointI, shared_from_this()), _instance, _host, _port); +} + +IceObjC::StreamEndpointIPtr +IceObjC::StreamEndpointI::endpoint(const StreamAcceptorPtr& a) const +{ + return ICE_MAKE_SHARED(StreamEndpointI, _instance, _host, a->effectivePort(), _sourceAddr, _timeout, _connectionId, + _compress); +} + +string +IceObjC::StreamEndpointI::options() const +{ + // + // WARNING: Certain features, such as proxy validation in Glacier2, + // depend on the format of proxy strings. Changes to toString() and + // methods called to generate parts of the reference string could break + // these features. Please review for all features that depend on the + // format of proxyToString() before changing this and related code. + // + ostringstream s; + + s << IPEndpointI::options(); + + if(_timeout == -1) + { + s << " -t infinite"; + } + else + { + s << " -t " << _timeout; + } + + if(_compress) + { + s << " -z"; + } + + return s.str(); +} + +bool +#ifdef ICE_CPP11_MAPPING +IceObjC::StreamEndpointI::operator==(const Endpoint& r) const +#else +IceObjC::StreamEndpointI::operator==(const LocalObject& r) const +#endif +{ + if(!IPEndpointI::operator==(r)) + { + return false; + } + + const StreamEndpointI* p = dynamic_cast<const StreamEndpointI*>(&r); + if(!p) + { + return false; + } + + if(this == p) + { + return true; + } + + if(_timeout != p->_timeout) + { + return false; + } + + if(_compress != p->_compress) + { + return false; + } + + return true; +} + +bool +#ifdef ICE_CPP11_MAPPING +IceObjC::StreamEndpointI::operator<(const Endpoint& r) const +#else +IceObjC::StreamEndpointI::operator<(const LocalObject& r) const +#endif +{ + const StreamEndpointI* p = dynamic_cast<const StreamEndpointI*>(&r); + if(!p) + { + const IceInternal::EndpointI* e = dynamic_cast<const IceInternal::EndpointI*>(&r); + if(!e) + { + return false; + } + return type() < e->type(); + } + + if(this == p) + { + return false; + } + + if(_timeout < p->_timeout) + { + return true; + } + else if(p->_timeout < _timeout) + { + return false; + } + + if(!_compress && p->_compress) + { + return true; + } + else if(p->_compress < _compress) + { + return false; + } + + return IPEndpointI::operator<(r); +} + +void +IceObjC::StreamEndpointI::streamWriteImpl(Ice::OutputStream* s) const +{ + IPEndpointI::streamWriteImpl(s); + s->write(_timeout); + s->write(_compress); +} + +void +IceObjC::StreamEndpointI::hashInit(Ice::Int& h) const +{ + IPEndpointI::hashInit(h); + hashAdd(h, _timeout); + hashAdd(h, _compress); +} + +void +IceObjC::StreamEndpointI::fillEndpointInfo(IPEndpointInfo* info) const +{ + IPEndpointI::fillEndpointInfo(info); + info->timeout = _timeout; + info->compress = _compress; +} + +bool +IceObjC::StreamEndpointI::checkOption(const string& option, const string& argument, const string& endpoint) +{ + if(IPEndpointI::checkOption(option, argument, endpoint)) + { + return true; + } + + switch(option[1]) + { + case 't': + { + if(argument.empty()) + { + EndpointParseException ex(__FILE__, __LINE__); + ex.str = "no argument provided for -t option in endpoint " + endpoint; + throw ex; + } + + if(argument == "infinite") + { + const_cast<Int&>(_timeout) = -1; + } + else + { + istringstream t(argument); + if(!(t >> const_cast<Int&>(_timeout)) || !t.eof() || _timeout < 1) + { + EndpointParseException ex(__FILE__, __LINE__); + ex.str = "invalid timeout value `" + argument + "' in endpoint " + endpoint; + throw ex; + } + } + return true; + } + + case 'z': + { + if(!argument.empty()) + { + EndpointParseException ex(__FILE__, __LINE__); + ex.str = "unexpected argument `" + argument + "' provided for -z option in " + endpoint; + throw ex; + } + const_cast<bool&>(_compress) = true; + return true; + } + + default: + { + return false; + } + } +} + +ConnectorPtr +IceObjC::StreamEndpointI::createConnector(const Address& address, const NetworkProxyPtr& proxy) const +{ + assert(false); +} + +IPEndpointIPtr +IceObjC::StreamEndpointI::createEndpoint(const string& host, int port, const string& connectionId) const +{ + return ICE_MAKE_SHARED(StreamEndpointI, _instance, host, port, _sourceAddr, _timeout, connectionId, _compress); +} + +IceObjC::StreamEndpointFactory::StreamEndpointFactory(const InstancePtr& instance) : _instance(instance) +{ +} + +IceObjC::StreamEndpointFactory::~StreamEndpointFactory() +{ +} + +Short +IceObjC::StreamEndpointFactory::type() const +{ + return _instance->type(); +} + +string +IceObjC::StreamEndpointFactory::protocol() const +{ + return _instance->protocol(); +} + +EndpointIPtr +IceObjC::StreamEndpointFactory::create(vector<string>& args, bool oaEndpoint) const +{ + IPEndpointIPtr endpt = ICE_MAKE_SHARED(StreamEndpointI, _instance); + endpt->initWithOptions(args, oaEndpoint); + return endpt; +} + +EndpointIPtr +IceObjC::StreamEndpointFactory::read(Ice::InputStream* s) const +{ + return ICE_MAKE_SHARED(StreamEndpointI, _instance, s); +} + +void +IceObjC::StreamEndpointFactory::destroy() +{ + _instance = 0; +} + +EndpointFactoryPtr +IceObjC::StreamEndpointFactory::clone(const ProtocolInstancePtr& instance) const +{ + return new StreamEndpointFactory(_instance->clone(instance)); +} |