diff options
author | Benoit Foucher <benoit@zeroc.com> | 2016-05-31 08:59:20 +0200 |
---|---|---|
committer | Benoit Foucher <benoit@zeroc.com> | 2016-05-31 08:59:20 +0200 |
commit | 9f99bad130508d529814c778852621f6a1b814cc (patch) | |
tree | 4fa8328539c80f719cca38a0fb7f67b2995618fc /cpp/src/Ice/ios | |
parent | PHP 7 fix for printing Exception info (diff) | |
download | ice-9f99bad130508d529814c778852621f6a1b814cc.tar.bz2 ice-9f99bad130508d529814c778852621f6a1b814cc.tar.xz ice-9f99bad130508d529814c778852621f6a1b814cc.zip |
Added support for building Xcode SDK, added iAP transport
Diffstat (limited to 'cpp/src/Ice/ios')
-rw-r--r-- | cpp/src/Ice/ios/Notifications.mm | 138 | ||||
-rw-r--r-- | cpp/src/Ice/ios/RegisterPluginsInit.cpp | 30 | ||||
-rw-r--r-- | cpp/src/Ice/ios/StreamAcceptor.cpp | 160 | ||||
-rw-r--r-- | cpp/src/Ice/ios/StreamAcceptor.h | 54 | ||||
-rw-r--r-- | cpp/src/Ice/ios/StreamConnector.cpp | 169 | ||||
-rw-r--r-- | cpp/src/Ice/ios/StreamConnector.h | 52 | ||||
-rw-r--r-- | cpp/src/Ice/ios/StreamEndpointI.cpp | 820 | ||||
-rw-r--r-- | cpp/src/Ice/ios/StreamEndpointI.h | 170 | ||||
-rw-r--r-- | cpp/src/Ice/ios/StreamTransceiver.cpp | 793 | ||||
-rw-r--r-- | cpp/src/Ice/ios/StreamTransceiver.h | 102 | ||||
-rw-r--r-- | cpp/src/Ice/ios/iAPConnector.h | 58 | ||||
-rw-r--r-- | cpp/src/Ice/ios/iAPConnector.mm | 153 | ||||
-rw-r--r-- | cpp/src/Ice/ios/iAPEndpointI.h | 109 | ||||
-rw-r--r-- | cpp/src/Ice/ios/iAPEndpointI.mm | 640 | ||||
-rw-r--r-- | cpp/src/Ice/ios/iAPTransceiver.h | 85 | ||||
-rw-r--r-- | cpp/src/Ice/ios/iAPTransceiver.mm | 490 |
16 files changed, 4023 insertions, 0 deletions
diff --git a/cpp/src/Ice/ios/Notifications.mm b/cpp/src/Ice/ios/Notifications.mm new file mode 100644 index 00000000000..bab9a73824c --- /dev/null +++ b/cpp/src/Ice/ios/Notifications.mm @@ -0,0 +1,138 @@ +// ********************************************************************** +// +// 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. +// +// ********************************************************************** + +#import <Ice/Config.h> + +#if TARGET_OS_IPHONE != 0 + +#import <Foundation/NSObject.h> +#import <Foundation/NSNotification.h> +#import <UIKit/UIApplication.h> + +#include <Ice/ConnectionFactory.h> + +#include <set> + +using namespace std; +using namespace IceInternal; + +namespace IceInternal +{ + +bool registerForBackgroundNotification(const IncomingConnectionFactoryPtr&); +void unregisterForBackgroundNotification(const IncomingConnectionFactoryPtr&); + +} + +namespace +{ + +class Observer +{ +public: + + Observer() : _background(false) + { + [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification + object:nil + queue:nil + usingBlock:^(NSNotification*) + { + didEnterBackground(); + }]; + + [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification + object:nil + queue:nil + usingBlock:^(NSNotification*) + { + willEnterForeground(); + }]; + } + + bool + add(const IncomingConnectionFactoryPtr& factory) + { + IceUtil::Mutex::Lock sync(_mutex); + _factories.insert(factory); + if(_background) + { + factory->stopAcceptor(); + } + else + { + factory->startAcceptor(); + } + return _background; + } + + void + remove(const IncomingConnectionFactoryPtr& factory) + { + IceUtil::Mutex::Lock sync(_mutex); + _factories.erase(factory); + } + + void + didEnterBackground() + { + IceUtil::Mutex::Lock sync(_mutex); + + // + // Notify all the incoming connection factories that we are + // entering the background mode. + // + for(set<IncomingConnectionFactoryPtr>::const_iterator p = _factories.begin(); p != _factories.end(); ++p) + { + (*p)->stopAcceptor(); + } + _background = true; + } + + void + willEnterForeground() + { + IceUtil::Mutex::Lock sync(_mutex); + + // + // Notify all the incoming connection factories that we are + // entering the foreground mode. + // + _background = false; + for(set<IncomingConnectionFactoryPtr>::const_iterator p = _factories.begin(); p != _factories.end(); ++p) + { + (*p)->startAcceptor(); + } + } + +private: + + IceUtil::Mutex _mutex; + bool _background; + set<IncomingConnectionFactoryPtr> _factories; +}; + + +} + +static Observer* observer = new Observer(); + +bool +IceInternal::registerForBackgroundNotification(const IncomingConnectionFactoryPtr& factory) +{ + return observer->add(factory); +} + +void +IceInternal::unregisterForBackgroundNotification(const IncomingConnectionFactoryPtr& factory) +{ + observer->remove(factory); +} + +#endif diff --git a/cpp/src/Ice/ios/RegisterPluginsInit.cpp b/cpp/src/Ice/ios/RegisterPluginsInit.cpp new file mode 100644 index 00000000000..712f4dcc765 --- /dev/null +++ b/cpp/src/Ice/ios/RegisterPluginsInit.cpp @@ -0,0 +1,30 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <Ice/RegisterPluginsInit.h> +#include <Ice/CommunicatorF.h> +#include <Ice/Initialize.h> + +extern "C" +{ + +Ice::Plugin* createIceUDP(const Ice::CommunicatorPtr&, const std::string&, const Ice::StringSeq&); +Ice::Plugin* createIceTCP(const Ice::CommunicatorPtr&, const std::string&, const Ice::StringSeq&); +Ice::Plugin* createIceSSL(const Ice::CommunicatorPtr&, const std::string&, const Ice::StringSeq&); +Ice::Plugin* createIceIAP(const Ice::CommunicatorPtr&, const std::string&, const Ice::StringSeq&); + +}; + +IceInternal::RegisterPluginsInit::RegisterPluginsInit() +{ + Ice::registerPluginFactory("IceUDP", createIceUDP, true); + Ice::registerPluginFactory("IceTCP", createIceTCP, true); + Ice::registerPluginFactory("IceSSL", createIceSSL, true); + Ice::registerPluginFactory("IceIAP", createIceIAP, true); +} diff --git a/cpp/src/Ice/ios/StreamAcceptor.cpp b/cpp/src/Ice/ios/StreamAcceptor.cpp new file mode 100644 index 00000000000..3d1a4a956b0 --- /dev/null +++ b/cpp/src/Ice/ios/StreamAcceptor.cpp @@ -0,0 +1,160 @@ +// ********************************************************************** +// +// 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 "StreamTransceiver.h" +#include "StreamEndpointI.h" +#include "StreamAcceptor.h" + +#include <IceUtil/StringUtil.h> + +#include <Ice/Instance.h> +#include <Ice/Network.h> +#include <Ice/Exception.h> +#include <Ice/Properties.h> + +#include <CoreFoundation/CoreFoundation.h> + +using namespace std; +using namespace Ice; +using namespace IceInternal; + +NativeInfoPtr +IceObjC::StreamAcceptor::getNativeInfo() +{ + return this; +} + +void +IceObjC::StreamAcceptor::close() +{ + if(_fd != INVALID_SOCKET) + { + closeSocketNoThrow(_fd); + _fd = INVALID_SOCKET; + } +} + +EndpointIPtr +IceObjC::StreamAcceptor::listen() +{ + try + { + const_cast<Address&>(_addr) = doBind(_fd, _addr); + doListen(_fd, _backlog); + } + catch(...) + { + _fd = INVALID_SOCKET; + throw; + } + + _endpoint = _endpoint->endpoint(this); + return _endpoint; +} + +TransceiverPtr +IceObjC::StreamAcceptor::accept() +{ + SOCKET fd = doAccept(_fd); + setBlock(fd, false); + setTcpBufSize(fd, _instance); + + // + // Create the read/write streams + // + CFReadStreamRef readStream = nil; + CFWriteStreamRef writeStream = nil; + try + { + CFStreamCreatePairWithSocket(NULL, fd, &readStream, &writeStream); + _instance->setupStreams(readStream, writeStream, true, ""); + return new StreamTransceiver(_instance, readStream, writeStream, fd); + } + catch(const Ice::LocalException& ex) + { + if(fd != INVALID_SOCKET) + { + closeSocketNoThrow(fd); + } + if(readStream) + { + CFRelease(readStream); + } + if(writeStream) + { + CFRelease(writeStream); + } + throw; + } +} + +string +IceObjC::StreamAcceptor::protocol() const +{ + return _instance->protocol(); +} + +string +IceObjC::StreamAcceptor::toString() const +{ + return addrToString(_addr); +} + +string +IceObjC::StreamAcceptor::toDetailedString() const +{ + ostringstream os; + os << "local address = " << toString(); + vector<string> intfs = getHostsForEndpointExpand(inetAddrToString(_addr), _instance->protocolSupport(), true); + if(!intfs.empty()) + { + os << "\nlocal interfaces = "; + os << IceUtilInternal::joinString(intfs, ", "); + } + return os.str(); +} + +int +IceObjC::StreamAcceptor::effectivePort() const +{ + return getPort(_addr); +} + +IceObjC::StreamAcceptor::StreamAcceptor(const StreamEndpointIPtr& endpoint, + const InstancePtr& instance, + const string& host, + int port) : + _endpoint(endpoint), + _instance(instance), + _addr(getAddressForServer(host, port, instance->protocolSupport(), instance->preferIPv6())) +{ +#ifdef SOMAXCONN + _backlog = instance->properties()->getPropertyAsIntWithDefault("Ice.TCP.Backlog", SOMAXCONN); +#else + _backlog = instance->properties()->getPropertyAsIntWithDefault("Ice.TCP.Backlog", 511); +#endif + + try + { + _fd = createSocket(false, _addr); + setBlock(_fd, false); + setTcpBufSize(_fd, _instance); + setReuseAddress(_fd, true); + } + catch(...) + { + _fd = INVALID_SOCKET; + throw; + } +} + +IceObjC::StreamAcceptor::~StreamAcceptor() +{ + assert(_fd == INVALID_SOCKET); +} diff --git a/cpp/src/Ice/ios/StreamAcceptor.h b/cpp/src/Ice/ios/StreamAcceptor.h new file mode 100644 index 00000000000..5997ac7c6db --- /dev/null +++ b/cpp/src/Ice/ios/StreamAcceptor.h @@ -0,0 +1,54 @@ +// ********************************************************************** +// +// 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. +// +// ********************************************************************** + +#ifndef ICE_STREAM_ACCEPTOR_H +#define ICE_STREAM_ACCEPTOR_H + +#include <Ice/TransceiverF.h> +#include <Ice/Acceptor.h> + +namespace IceObjC +{ + +class StreamEndpointI; +#ifdef ICE_CPP11_MAPPING // C++11 mapping +typedef ::std::shared_ptr<StreamEndpointI> StreamEndpointIPtr; +#else +typedef IceUtil::Handle<StreamEndpointI> StreamEndpointIPtr; +#endif + +class StreamAcceptor : public IceInternal::Acceptor, public IceInternal::NativeInfo +{ +public: + + virtual IceInternal::NativeInfoPtr getNativeInfo(); + virtual void close(); + virtual IceInternal::EndpointIPtr listen(); + virtual IceInternal::TransceiverPtr accept(); + virtual std::string protocol() const; + virtual std::string toString() const; + virtual std::string toDetailedString() const; + + int effectivePort() const; + +private: + + StreamAcceptor(const StreamEndpointIPtr&, const InstancePtr&, const std::string&, int); + virtual ~StreamAcceptor(); + friend class StreamEndpointI; + + StreamEndpointIPtr _endpoint; + InstancePtr _instance; + int _backlog; + IceInternal::Address _addr; +}; + +} + +#endif diff --git a/cpp/src/Ice/ios/StreamConnector.cpp b/cpp/src/Ice/ios/StreamConnector.cpp new file mode 100644 index 00000000000..a5b87b07db0 --- /dev/null +++ b/cpp/src/Ice/ios/StreamConnector.cpp @@ -0,0 +1,169 @@ +// ********************************************************************** +// +// 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 "StreamTransceiver.h" +#include "StreamEndpointI.h" +#include "StreamConnector.h" + +#include <Ice/Network.h> +#include <Ice/Exception.h> +#include <Ice/Properties.h> +#include <Ice/NetworkProxy.h> + +#include <CoreFoundation/CoreFoundation.h> + +using namespace std; +using namespace Ice; +using namespace IceInternal; + +TransceiverPtr +IceObjC::StreamConnector::connect() +{ + CFReadStreamRef readStream = nil; + CFWriteStreamRef writeStream = nil; + try + { + CFStringRef h = CFStringCreateWithCString(NULL, _host.c_str(), kCFStringEncodingUTF8); + CFHostRef host = CFHostCreateWithName(NULL, h); + CFRelease(h); + CFStreamCreatePairWithSocketToCFHost(NULL, host, _port, &readStream, &writeStream); + CFRelease(host); + + _instance->setupStreams(readStream, writeStream, false, _host); + return new StreamTransceiver(_instance, readStream, writeStream, _host, _port); + } + catch(const Ice::LocalException& ex) + { + if(readStream) + { + CFRelease(readStream); + } + if(writeStream) + { + CFRelease(writeStream); + } + throw; + } +} + +Short +IceObjC::StreamConnector::type() const +{ + return _instance->type(); +} + +string +IceObjC::StreamConnector::toString() const +{ + string proxyHost = _instance->proxyHost(); + ostringstream os; + if(!proxyHost.empty()) + { + os << proxyHost << ":" << _instance->proxyPort(); + } + else + { + os << _host << ":" << _port; + } + return os.str(); +} + +bool +IceObjC::StreamConnector::operator==(const IceInternal::Connector& r) const +{ + const StreamConnector* p = dynamic_cast<const StreamConnector*>(&r); + if(!p) + { + return false; + } + + if(_timeout != p->_timeout) + { + return false; + } + + if(_connectionId != p->_connectionId) + { + return false; + } + + if(_host != p->_host) + { + return false; + } + + if(_port != p->_port) + { + return false; + } + + return true; +} + +bool +IceObjC::StreamConnector::operator!=(const IceInternal::Connector& r) const +{ + return !operator==(r); +} + +bool +IceObjC::StreamConnector::operator<(const IceInternal::Connector& r) const +{ + const StreamConnector* p = dynamic_cast<const StreamConnector*>(&r); + if(!p) + { + return type() < r.type(); + } + + if(_timeout < p->_timeout) + { + return true; + } + else if(p->_timeout < _timeout) + { + return false; + } + + if(_connectionId < p->_connectionId) + { + return true; + } + else if(p->_connectionId < _connectionId) + { + return false; + } + + if(_host < p->_host) + { + return true; + } + else if(p->_host < _host) + { + return false; + } + + return _port < p->_port; +} + +IceObjC::StreamConnector::StreamConnector(const InstancePtr& instance, + const string& host, + Ice::Int port, + Ice::Int timeout, + const string& connectionId) : + _instance(instance), + _host(host.empty() ? string("127.0.0.1") : host), + _port(port), + _timeout(timeout), + _connectionId(connectionId) +{ +} + +IceObjC::StreamConnector::~StreamConnector() +{ +} diff --git a/cpp/src/Ice/ios/StreamConnector.h b/cpp/src/Ice/ios/StreamConnector.h new file mode 100644 index 00000000000..91d806fd707 --- /dev/null +++ b/cpp/src/Ice/ios/StreamConnector.h @@ -0,0 +1,52 @@ +// ********************************************************************** +// +// 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. +// +// ********************************************************************** + +#ifndef ICE_STREAM_CONNECTOR_H +#define ICE_STREAM_CONNECTOR_H + +#include <Ice/TransceiverF.h> +#include <Ice/Connector.h> + +namespace IceObjC +{ + +class StreamEndpointI; + +class Instance; +typedef IceUtil::Handle<Instance> InstancePtr; + +class StreamConnector : public IceInternal::Connector +{ +public: + + virtual IceInternal::TransceiverPtr connect(); + + virtual Ice::Short type() const; + virtual std::string toString() const; + + virtual bool operator==(const IceInternal::Connector&) const; + virtual bool operator!=(const IceInternal::Connector&) const; + virtual bool operator<(const IceInternal::Connector&) const; + +private: + + StreamConnector(const InstancePtr&, const std::string&, Ice::Int, Ice::Int, const std::string&); + virtual ~StreamConnector(); + friend class StreamEndpointI; + + const InstancePtr _instance; + const std::string _host; + const Ice::Int _port; + const Ice::Int _timeout; + const std::string _connectionId; +}; + +} + +#endif 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)); +} diff --git a/cpp/src/Ice/ios/StreamEndpointI.h b/cpp/src/Ice/ios/StreamEndpointI.h new file mode 100644 index 00000000000..8e45febc64b --- /dev/null +++ b/cpp/src/Ice/ios/StreamEndpointI.h @@ -0,0 +1,170 @@ +// ********************************************************************** +// +// 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. +// +// ********************************************************************** + +#ifndef ICE_STREAM_ENDPOINT_I_H +#define ICE_STREAM_ENDPOINT_I_H + +#include <Ice/ProtocolInstance.h> +#include <Ice/IPEndpointI.h> +#include <Ice/WSEndpoint.h> +#include <Ice/EndpointFactory.h> +#include <Ice/InstanceF.h> + +#include <CoreFoundation/CFDictionary.h> +#include <CFNetwork/CFNetwork.h> + +namespace Ice +{ + +class OutputStream; +class InputStream; + +} + +namespace IceObjC +{ + +class Instance : public IceInternal::ProtocolInstance +{ +public: + + Instance(const Ice::CommunicatorPtr&, Ice::Short, const std::string&, bool); + virtual ~Instance(); + + CFArrayRef certificateAuthorities() const + { + return _certificateAuthorities; + } + + CFDataRef trustOnlyKeyID() const + { + return _trustOnlyKeyID; + } + + const std::string& proxyHost() const + { + return _proxyHost; + } + + int proxyPort() const + { + return _proxyPort; + } + + void setupStreams(CFReadStreamRef, CFWriteStreamRef, bool, const std::string&) const; + + Instance* clone(const IceInternal::ProtocolInstancePtr&); + +private: + + const bool _voip; + const Ice::CommunicatorPtr _communicator; + CFMutableDictionaryRef _serverSettings; + CFMutableDictionaryRef _clientSettings; + CFMutableDictionaryRef _proxySettings; + CFArrayRef _certificateAuthorities; + CFDataRef _trustOnlyKeyID; + + std::string _proxyHost; + int _proxyPort; +}; +typedef IceUtil::Handle<Instance> InstancePtr; + +class StreamAcceptor; +typedef IceUtil::Handle<StreamAcceptor> StreamAcceptorPtr; + +class StreamEndpointI; +#ifdef ICE_CPP11_MAPPING // C++11 mapping +typedef ::std::shared_ptr<StreamEndpointI> StreamEndpointIPtr; +#else +typedef IceUtil::Handle<StreamEndpointI> StreamEndpointIPtr; +#endif + +class StreamEndpointI : public IceInternal::IPEndpointI, public IceInternal::WSEndpointDelegate +{ +public: + + StreamEndpointI(const InstancePtr&, const std::string&, Ice::Int, const IceInternal::Address&, Ice::Int, + const std::string&, bool); + StreamEndpointI(const InstancePtr&); + StreamEndpointI(const InstancePtr&, Ice::InputStream*); + + virtual Ice::EndpointInfoPtr getInfo() const; + virtual Ice::EndpointInfoPtr getWSInfo(const std::string&) const; + + virtual Ice::Int timeout() const; + virtual IceInternal::EndpointIPtr timeout(Ice::Int) const; + virtual bool compress() const; + virtual IceInternal::EndpointIPtr compress(bool) const; + virtual bool datagram() const; + virtual bool secure() const; + + virtual void connectors_async(Ice::EndpointSelectionType, const IceInternal::EndpointI_connectorsPtr&) const; + virtual IceInternal::TransceiverPtr transceiver() const; + virtual IceInternal::AcceptorPtr acceptor(const std::string&) const; + virtual std::string options() const; + +#ifdef ICE_CPP11_MAPPING + virtual bool operator==(const Ice::Endpoint&) const; + virtual bool operator<(const Ice::Endpoint&) const; +#else + virtual bool operator==(const Ice::LocalObject&) const; + virtual bool operator<(const Ice::LocalObject&) const; +#endif + + StreamEndpointIPtr endpoint(const StreamAcceptorPtr&) const; + + using IPEndpointI::connectionId; + +protected: + + virtual void streamWriteImpl(Ice::OutputStream*) const; + virtual void hashInit(Ice::Int&) const; + virtual void fillEndpointInfo(Ice::IPEndpointInfo*) const; + virtual bool checkOption(const std::string&, const std::string&, const std::string&); + + virtual IceInternal::ConnectorPtr createConnector(const IceInternal::Address&, + const IceInternal::NetworkProxyPtr&) const; + virtual IceInternal::IPEndpointIPtr createEndpoint(const std::string&, int, const std::string&) const; + +private: + + const InstancePtr _instance; + + // + // All members are const, because endpoints are immutable. + // + const Ice::Int _timeout; + const bool _compress; +}; + +class StreamEndpointFactory : public IceInternal::EndpointFactory +{ +public: + + StreamEndpointFactory(const InstancePtr&); + + virtual ~StreamEndpointFactory(); + + virtual Ice::Short type() const; + virtual std::string protocol() const; + virtual IceInternal::EndpointIPtr create(std::vector<std::string>&, bool) const; + virtual IceInternal::EndpointIPtr read(Ice::InputStream*) const; + virtual void destroy(); + + virtual IceInternal::EndpointFactoryPtr clone(const IceInternal::ProtocolInstancePtr&) const; + +private: + + InstancePtr _instance; +}; + +} + +#endif diff --git a/cpp/src/Ice/ios/StreamTransceiver.cpp b/cpp/src/Ice/ios/StreamTransceiver.cpp new file mode 100644 index 00000000000..90e500eed85 --- /dev/null +++ b/cpp/src/Ice/ios/StreamTransceiver.cpp @@ -0,0 +1,793 @@ +// ********************************************************************** +// +// 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 "StreamTransceiver.h" +#include "StreamEndpointI.h" + +#include <Ice/Properties.h> +#include <Ice/TraceLevels.h> +#include <Ice/Connection.h> +#include <Ice/LoggerUtil.h> +#include <Ice/Buffer.h> +#include <Ice/Network.h> +#include <IceSSL/ConnectionInfo.h> + +#include <CoreFoundation/CoreFoundation.h> +#include <Security/Security.h> + +using namespace std; +using namespace Ice; +using namespace IceInternal; + +namespace +{ + +void selectorReadCallback(CFReadStreamRef, CFStreamEventType event, void* info) +{ + SelectorReadyCallback* callback = reinterpret_cast<SelectorReadyCallback*>(info); + switch(event) + { + case kCFStreamEventOpenCompleted: + callback->readyCallback(static_cast<SocketOperation>(SocketOperationConnect | SocketOperationRead)); + break; + case kCFStreamEventHasBytesAvailable: + callback->readyCallback(SocketOperationRead); + break; + default: + callback->readyCallback(SocketOperationRead, -1); // Error + break; + } +} + +void selectorWriteCallback(CFWriteStreamRef, CFStreamEventType event, void* info) +{ + SelectorReadyCallback* callback = reinterpret_cast<SelectorReadyCallback*>(info); + switch(event) + { + case kCFStreamEventOpenCompleted: + callback->readyCallback(static_cast<SocketOperation>(SocketOperationConnect | SocketOperationWrite)); + break; + case kCFStreamEventCanAcceptBytes: + callback->readyCallback(SocketOperationWrite); + break; + default: + callback->readyCallback(SocketOperationWrite, -1); // Error + break; + } +} + +} + +static inline string +fromCFString(CFStringRef ref) +{ + const char* s = CFStringGetCStringPtr(ref, kCFStringEncodingUTF8); + if(s) + { + return string(s); + } + + // Not great, but is good enough for this purpose. + char buf[1024]; + CFStringGetCString(ref, buf, sizeof(buf), kCFStringEncodingUTF8); + return string(buf); +} + +IceInternal::NativeInfoPtr +IceObjC::StreamTransceiver::getNativeInfo() +{ + return this; +} + +void +IceObjC::StreamTransceiver::initStreams(SelectorReadyCallback* callback) +{ + CFOptionFlags events; + CFStreamClientContext ctx = { 0, callback, 0, 0, 0 }; + events = kCFStreamEventOpenCompleted | kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred | + kCFStreamEventEndEncountered; + CFWriteStreamSetClient(_writeStream, events, selectorWriteCallback, &ctx); + + events = kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | + kCFStreamEventEndEncountered; + CFReadStreamSetClient(_readStream, events, selectorReadCallback, &ctx); +} + +SocketOperation +IceObjC::StreamTransceiver::registerWithRunLoop(SocketOperation op) +{ + IceUtil::Mutex::Lock sync(_mutex); + SocketOperation readyOp = SocketOperationNone; + if(op & SocketOperationConnect) + { + if(CFWriteStreamGetStatus(_writeStream) != kCFStreamStatusNotOpen || + CFReadStreamGetStatus(_readStream) != kCFStreamStatusNotOpen) + { + return SocketOperationConnect; + } + + _opening = true; + + CFWriteStreamScheduleWithRunLoop(_writeStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + CFReadStreamScheduleWithRunLoop(_readStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + + _writeStreamRegistered = true; // Note: this must be set after the schedule call + _readStreamRegistered = true; // Note: this must be set after the schedule call + + CFReadStreamOpen(_readStream); + CFWriteStreamOpen(_writeStream); + } + else + { + if(op & SocketOperationWrite) + { + if(CFWriteStreamCanAcceptBytes(_writeStream)) + { + readyOp = static_cast<SocketOperation>(readyOp | SocketOperationWrite); + } + else if(!_writeStreamRegistered) + { + CFWriteStreamScheduleWithRunLoop(_writeStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + _writeStreamRegistered = true; // Note: this must be set after the schedule call + if(CFWriteStreamCanAcceptBytes(_writeStream)) + { + readyOp = static_cast<SocketOperation>(readyOp | SocketOperationWrite); + } + } + } + + if(op & SocketOperationRead) + { + if(CFReadStreamHasBytesAvailable(_readStream)) + { + readyOp = static_cast<SocketOperation>(readyOp | SocketOperationRead); + } + else if(!_readStreamRegistered) + { + CFReadStreamScheduleWithRunLoop(_readStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + _readStreamRegistered = true; // Note: this must be set after the schedule call + if(CFReadStreamHasBytesAvailable(_readStream)) + { + readyOp = static_cast<SocketOperation>(readyOp | SocketOperationRead); + } + } + } + } + return readyOp; +} + +SocketOperation +IceObjC::StreamTransceiver::unregisterFromRunLoop(SocketOperation op, bool error) +{ + IceUtil::Mutex::Lock sync(_mutex); + _error |= error; + + if(_opening) + { + // Wait for the stream to be ready for write + if(op == SocketOperationWrite) + { + _writeStreamRegistered = false; + } + + // + // We don't wait for the stream to be ready for read (even if + // it's a client connection) because there's no guarantees that + // the server might actually send data right away. If we use + // the WebSocket transport, the server actually waits for the + // client to write the HTTP upgrade request. + // + //if(op & SocketOperationRead && (_fd != INVALID_SOCKET || !(op & SocketOperationConnect))) + if(op == (SocketOperationRead | SocketOperationConnect)) + { + _readStreamRegistered = false; + } + + if(error || (!_readStreamRegistered && !_writeStreamRegistered)) + { + CFWriteStreamUnscheduleFromRunLoop(_writeStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + CFReadStreamUnscheduleFromRunLoop(_readStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + _opening = false; + return SocketOperationConnect; + } + else + { + return SocketOperationNone; + } + } + else + { + if(op & SocketOperationWrite && _writeStreamRegistered) + { + CFWriteStreamUnscheduleFromRunLoop(_writeStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + _writeStreamRegistered = false; + } + + if(op & SocketOperationRead && _readStreamRegistered) + { + CFReadStreamUnscheduleFromRunLoop(_readStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + _readStreamRegistered = false; + } + } + return op; +} + +void +IceObjC::StreamTransceiver::closeStreams() +{ + CFReadStreamSetClient(_readStream, kCFStreamEventNone, 0, 0); + CFWriteStreamSetClient(_writeStream, kCFStreamEventNone, 0, 0); + + CFReadStreamClose(_readStream); + CFWriteStreamClose(_writeStream); +} + +SocketOperation +IceObjC::StreamTransceiver::initialize(Buffer& readBuffer, Buffer& writeBuffer) +{ + IceUtil::Mutex::Lock sync(_mutex); + if(_state == StateNeedConnect) + { + _state = StateConnectPending; + return SocketOperationConnect; + } + + if(_state <= StateConnectPending) + { + if(_error) + { + CFErrorRef err = NULL; + if(CFWriteStreamGetStatus(_writeStream) == kCFStreamStatusError) + { + err = CFWriteStreamCopyError(_writeStream); + } + else if(CFReadStreamGetStatus(_readStream) == kCFStreamStatusError) + { + err = CFReadStreamCopyError(_readStream); + } + checkError(err, __FILE__, __LINE__); + } + + _state = StateConnected; + + if(_fd == INVALID_SOCKET) + { + if(!CFReadStreamSetProperty(_readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse) || + !CFWriteStreamSetProperty(_writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse)) + { + throw Ice::SocketException(__FILE__, __LINE__, 0); + } + + CFDataRef d = (CFDataRef)CFReadStreamCopyProperty(_readStream, kCFStreamPropertySocketNativeHandle); + CFDataGetBytes(d, CFRangeMake(0, sizeof(SOCKET)), reinterpret_cast<UInt8*>(&_fd)); + CFRelease(d); + } + + ostringstream s; + Address localAddr; + fdToLocalAddress(_fd, localAddr); + s << "local address = " << addrToString(localAddr); + string proxyHost = _instance->proxyHost(); + if(!proxyHost.empty()) + { + s << "\nSOCKS proxy address = " << proxyHost << ":" << _instance->proxyPort(); + } + Address remoteAddr; + bool peerConnected = fdToRemoteAddress(_fd, remoteAddr); + if(peerConnected) + { + s << "\nremote address = " << addrToString(remoteAddr); + } + else + { + s << "\nremote address = " << _host << ":" << _port; + } + _desc = s.str(); + + setBlock(_fd, false); + setTcpBufSize(_fd, _instance); + + // + // Limit the size of packets passed to SSLWrite/SSLRead to avoid + // blocking and holding too much memory. + // + _maxSendPacketSize = std::max(512, getSendBufferSize(_fd)); + _maxRecvPacketSize = std::max(512, getRecvBufferSize(_fd)); + } + assert(_state == StateConnected); + return SocketOperationNone; +} + +SocketOperation +#ifdef ICE_CPP11_MAPPING +IceObjC::StreamTransceiver::closing(bool initiator, exception_ptr) +#else +IceObjC::StreamTransceiver::closing(bool initiator, const Ice::LocalException&) +#endif +{ + // If we are initiating the connection closure, wait for the peer + // to close the TCP/IP connection. Otherwise, close immediately. + return initiator ? SocketOperationRead : SocketOperationNone; +} + +void +IceObjC::StreamTransceiver::close() +{ + if(_fd != INVALID_SOCKET) + { + try + { + closeSocket(_fd); + _fd = INVALID_SOCKET; + } + catch(const SocketException&) + { + _fd = INVALID_SOCKET; + throw; + } + } +} + +SocketOperation +IceObjC::StreamTransceiver::write(Buffer& buf) +{ + IceUtil::Mutex::Lock sync(_mutex); + if(_error) + { + assert(CFWriteStreamGetStatus(_writeStream) == kCFStreamStatusError); + checkError(CFWriteStreamCopyError(_writeStream), __FILE__, __LINE__); + } + + // Its impossible for the packetSize to be more than an Int. + size_t packetSize = std::min(static_cast<size_t>(buf.b.end() - buf.i), _maxSendPacketSize); + while(buf.i != buf.b.end()) + { + if(!CFWriteStreamCanAcceptBytes(_writeStream)) + { + return SocketOperationWrite; + } + + if(_checkCertificates) + { + _checkCertificates = false; + checkCertificates(); + } + + assert(_fd != INVALID_SOCKET); + CFIndex ret = CFWriteStreamWrite(_writeStream, reinterpret_cast<const UInt8*>(&*buf.i), packetSize); + + if(ret == SOCKET_ERROR) + { + if(CFWriteStreamGetStatus(_writeStream) == kCFStreamStatusAtEnd) + { + ConnectionLostException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + + assert(CFWriteStreamGetStatus(_writeStream) == kCFStreamStatusError); + checkError(CFWriteStreamCopyError(_writeStream), __FILE__, __LINE__); + if(noBuffers() && packetSize > 1024) + { + packetSize /= 2; + } + continue; + } + + buf.i += ret; + + if(packetSize > buf.b.end() - buf.i) + { + packetSize = static_cast<int>(buf.b.end() - buf.i); + } + } + return SocketOperationNone; +} + +SocketOperation +IceObjC::StreamTransceiver::read(Buffer& buf) +{ + IceUtil::Mutex::Lock sync(_mutex); + if(_error) + { + assert(CFReadStreamGetStatus(_readStream) == kCFStreamStatusError); + checkError(CFReadStreamCopyError(_readStream), __FILE__, __LINE__); + } + + // Its impossible for the packetSize to be more than an Int. + size_t packetSize = std::min(static_cast<size_t>(buf.b.end() - buf.i), _maxRecvPacketSize); + while(buf.i != buf.b.end()) + { + if(!CFReadStreamHasBytesAvailable(_readStream)) + { + return SocketOperationRead; + } + + if(_checkCertificates) + { + _checkCertificates = false; + checkCertificates(); + } + + assert(_fd != INVALID_SOCKET); + CFIndex ret = CFReadStreamRead(_readStream, reinterpret_cast<UInt8*>(&*buf.i), packetSize); + + if(ret == 0) + { + ConnectionLostException ex(__FILE__, __LINE__); + ex.error = 0; + throw ex; + } + + if(ret == SOCKET_ERROR) + { + if(CFReadStreamGetStatus(_readStream) == kCFStreamStatusAtEnd) + { + ConnectionLostException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + + assert(CFReadStreamGetStatus(_readStream) == kCFStreamStatusError); + checkError(CFReadStreamCopyError(_readStream), __FILE__, __LINE__); + if(noBuffers() && packetSize > 1024) + { + packetSize /= 2; + } + continue; + } + + buf.i += ret; + + if(packetSize > buf.b.end() - buf.i) + { + packetSize = static_cast<int>(buf.b.end() - buf.i); + } + } + + return SocketOperationNone; +} + +string +IceObjC::StreamTransceiver::protocol() const +{ + return _instance->protocol(); +} + +string +IceObjC::StreamTransceiver::toString() const +{ + return _desc; +} + +string +IceObjC::StreamTransceiver::toDetailedString() const +{ + return _desc; +} + +Ice::ConnectionInfoPtr +IceObjC::StreamTransceiver::getInfo() const +{ + if(_instance->secure()) + { + IceSSL::ConnectionInfoPtr info = ICE_MAKE_SHARED(IceSSL::ConnectionInfo); + fillConnectionInfo(info); + info->verified = _state == StateConnected; + return info; + } + else + { + Ice::TCPConnectionInfoPtr info = ICE_MAKE_SHARED(Ice::TCPConnectionInfo); + fillConnectionInfo(info); + return info; + } +} + +Ice::ConnectionInfoPtr +IceObjC::StreamTransceiver::getWSInfo(const Ice::HeaderDict& headers) const +{ + if(_instance->secure()) + { + IceSSL::WSSConnectionInfoPtr info = ICE_MAKE_SHARED(IceSSL::WSSConnectionInfo); + fillConnectionInfo(info); + info->verified = _state == StateConnected; + info->headers = headers; + return info; + } + else + { + Ice::WSConnectionInfoPtr info = ICE_MAKE_SHARED(Ice::WSConnectionInfo); + fillConnectionInfo(info); + info->headers = headers; + return info; + } +} + +void +IceObjC::StreamTransceiver::checkSendSize(const Buffer& buf) +{ +} + +void +IceObjC::StreamTransceiver::setBufferSize(int rcvSize, int sndSize) +{ + setTcpBufSize(_fd, rcvSize, sndSize, _instance); +} + +IceObjC::StreamTransceiver::StreamTransceiver(const InstancePtr& instance, + CFReadStreamRef readStream, + CFWriteStreamRef writeStream, + const string& host, + Ice::Int port) : + StreamNativeInfo(INVALID_SOCKET), + _instance(instance), + _host(host), + _port(port), + _readStream(readStream), + _writeStream(writeStream), + _readStreamRegistered(false), + _writeStreamRegistered(false), + _opening(false), + _checkCertificates(instance->secure()), + _error(false), + _state(StateNeedConnect) +{ + ostringstream s; + s << "local address = <not available>"; + string proxyHost = instance->proxyHost(); + if(!proxyHost.empty()) + { + s << "\nSOCKS proxy address = " << proxyHost << ":" << instance->proxyPort(); + } + s << "\nremote address = " << host << ":" << port; + _desc = s.str(); +} + +IceObjC::StreamTransceiver::StreamTransceiver(const InstancePtr& instance, + CFReadStreamRef readStream, + CFWriteStreamRef writeStream, + SOCKET fd) : + StreamNativeInfo(fd), + _instance(instance), + _port(0), + _readStream(readStream), + _writeStream(writeStream), + _readStreamRegistered(false), + _writeStreamRegistered(false), + _opening(false), + _checkCertificates(false), + _error(false), + _state(StateNeedConnect), + _desc(fdToString(fd)) +{ +} + +IceObjC::StreamTransceiver::~StreamTransceiver() +{ + assert(_fd == INVALID_SOCKET); + CFRelease(_readStream); + CFRelease(_writeStream); +} + +void +IceObjC::StreamTransceiver::checkCertificates() +{ + SecTrustRef trust = (SecTrustRef)CFWriteStreamCopyProperty(_writeStream, kCFStreamPropertySSLPeerTrust); + if(!trust) + { + throw Ice::SecurityException(__FILE__, __LINE__, "unable to obtain trust object"); + } + + try + { + SecPolicyRef policy = 0; + if(_host.empty() || _instance->properties()->getPropertyAsIntWithDefault("IceSSL.CheckCertName", 1) == 0) + { + policy = SecPolicyCreateBasicX509(); + } + else + { + CFStringRef h = CFStringCreateWithCString(NULL, _host.c_str(), kCFStringEncodingUTF8); + policy = SecPolicyCreateSSL(false, h); + CFRelease(h); + } + + OSStatus err = SecTrustSetPolicies(trust, policy); + CFRelease(policy); + if(err != noErr) + { + ostringstream os; + os << "unable to set trust object policy (error = " << err << ")"; + throw Ice::SecurityException(__FILE__, __LINE__, os.str()); + } + + // + // If IceSSL.CertAuthFile is set, we use the certificate authorities from this file + // instead of the ones from the keychain. + // + if((err = SecTrustSetAnchorCertificates(trust, _instance->certificateAuthorities())) != noErr) + { + ostringstream os; + os << "couldn't set root CA certificates with trust object (error = " << err << ")"; + throw Ice::SecurityException(__FILE__, __LINE__, os.str()); + } + + SecTrustResultType result = kSecTrustResultInvalid; + if((err = SecTrustEvaluate(trust, &result)) != noErr) + { + ostringstream os; + os << "unable to evaluate the peer certificate trust (error = " << err << ")"; + throw Ice::SecurityException(__FILE__, __LINE__, os.str()); + } + + // + // The kSecTrustResultUnspecified result indicates that the user didn't set any trust + // settings for the root CA. This is expected if the root CA is provided by the user + // with IceSSL.CertAuthFile or if the user didn't explicitly set any trust settings + // for the certificate. + // + if(result != kSecTrustResultProceed && result != kSecTrustResultUnspecified) + { + ostringstream os; + os << "certificate validation failed (result = " << result << ")"; + throw Ice::SecurityException(__FILE__, __LINE__, os.str()); + } + + if(_instance->trustOnlyKeyID()) + { + if(SecTrustGetCertificateCount(trust) < 0) + { + throw Ice::SecurityException(__FILE__, __LINE__, "unable to obtain peer certificate"); + } + + SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, 0); + + // + // To check the subject key ID, we add the peer certificate to the keychain with SetItemAdd, + // then we lookup for the cert using the kSecAttrSubjectKeyID. Then we remove the cert from + // the keychain. NOTE: according to the Apple documentation, it should in theory be possible + // to not add/remove the item to the keychain by specifying the kSecMatchItemList key (or + // kSecUseItemList?) when calling SecItemCopyMatching. Unfortunately this doesn't appear to + // work. Similarly, it should be possible to get back the attributes of the certificate + // once it added by setting kSecReturnAttributes in the add query, again this doesn't seem + // to work. + // + CFMutableDictionaryRef query; + query = CFDictionaryCreateMutable(0, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(query, kSecClass, kSecClassCertificate); + CFDictionarySetValue(query, kSecValueRef, cert); + err = SecItemAdd(query, 0); + if(err != noErr && err != errSecDuplicateItem) + { + CFRelease(query); + ostringstream os; + os << "unable to add peer certificate to keychain (error = " << err << ")"; + throw Ice::SecurityException(__FILE__, __LINE__, os.str()); + } + CFRelease(query); + + query = CFDictionaryCreateMutable(0, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(query, kSecClass, kSecClassCertificate); + CFDictionarySetValue(query, kSecValueRef, cert); + CFDictionarySetValue(query, kSecAttrSubjectKeyID, _instance->trustOnlyKeyID()); + err = SecItemCopyMatching(query, 0); + OSStatus foundErr = err; + CFRelease(query); + + query = CFDictionaryCreateMutable(0, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(query, kSecClass, kSecClassCertificate); + CFDictionarySetValue(query, kSecValueRef, cert); + err = SecItemDelete(query); + if(err != noErr) + { + CFRelease(query); + ostringstream os; + os << "unable to remove peer certificate from keychain (error = " << err << ")"; + throw Ice::SecurityException(__FILE__, __LINE__, os.str()); + } + CFRelease(query); + + if(foundErr != noErr) + { + ostringstream os; + os << "the certificate subject key ID doesn't match the `IceSSL.TrustOnly.Client' property "; + os << "(error = " << foundErr << ")"; + throw Ice::SecurityException(__FILE__, __LINE__, os.str()); + } + } + CFRelease(trust); + } + catch(...) + { + if(trust) + { + CFRelease(trust); + } + throw; + } +} + +void +IceObjC::StreamTransceiver::checkError(CFErrorRef err, const char* file, int line) +{ + assert(err); + CFStringRef domain = CFErrorGetDomain(err); + if(CFStringCompare(domain, kCFErrorDomainPOSIX, 0) == kCFCompareEqualTo) + { + errno = CFErrorGetCode(err); + CFRelease(err); + if(interrupted() || noBuffers()) + { + return; + } + + if(connectionLost()) + { + ConnectionLostException ex(file, line); + ex.error = getSocketErrno(); + throw ex; + } + else if(connectionRefused()) + { + ConnectionRefusedException ex(file, line); + ex.error = getSocketErrno(); + throw ex; + } + else if(connectFailed()) + { + ConnectFailedException ex(file, line); + ex.error = getSocketErrno(); + throw ex; + } + else + { + SocketException ex(file, line); + ex.error = getSocketErrno(); + throw ex; + } + } + + int error = CFErrorGetCode(err); + if(error == kCFHostErrorHostNotFound || error == kCFHostErrorUnknown) + { + int rs = 0; + if(error == kCFHostErrorUnknown) + { + CFDictionaryRef dict = CFErrorCopyUserInfo(err); + CFNumberRef d = (CFNumberRef)CFDictionaryGetValue(dict, kCFGetAddrInfoFailureKey); + if(d != 0) + { + CFNumberGetValue(d, kCFNumberSInt32Type, &rs); + } + CFRelease(dict); + } + + CFRelease(err); + + DNSException ex(file, line); + ex.error = rs; + ex.host = _host; + throw ex; + } + + CFNetworkException ex(file, line); + ex.domain = fromCFString(domain); + ex.error = CFErrorGetCode(err); + CFRelease(err); + throw ex; +} + +void +IceObjC::StreamTransceiver::fillConnectionInfo(const Ice::IPConnectionInfoPtr& info) const +{ + fdToAddressAndPort(_fd, info->localAddress, info->localPort, info->remoteAddress, info->remotePort); + info->rcvSize = getRecvBufferSize(_fd); + info->sndSize = getSendBufferSize(_fd); +} diff --git a/cpp/src/Ice/ios/StreamTransceiver.h b/cpp/src/Ice/ios/StreamTransceiver.h new file mode 100644 index 00000000000..088001887e2 --- /dev/null +++ b/cpp/src/Ice/ios/StreamTransceiver.h @@ -0,0 +1,102 @@ +// ********************************************************************** +// +// 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. +// +// ********************************************************************** + +#ifndef ICE_STREAM_TRANSCEIVER_H +#define ICE_STREAM_TRANSCEIVER_H + +#include <Ice/WSTransceiver.h> +#include <Ice/Network.h> +#include <Ice/Selector.h> + +struct __CFError; +typedef struct __CFError * CFErrorRef; + +struct __CFWriteStream; +typedef struct __CFWriteStream * CFWriteStreamRef; + +struct __CFReadStream; +typedef struct __CFReadStream * CFReadStreamRef; + +namespace IceObjC +{ + +class Instance; +typedef IceUtil::Handle<Instance> InstancePtr; + +class StreamTransceiver : public IceInternal::Transceiver, public IceInternal::StreamNativeInfo, + public IceInternal::WSTransceiverDelegate +{ + enum State + { + StateNeedConnect, + StateConnectPending, + StateConnected + }; + +public: + + StreamTransceiver(const InstancePtr&, CFReadStreamRef, CFWriteStreamRef, const std::string&, Ice::Int); + StreamTransceiver(const InstancePtr&, CFReadStreamRef, CFWriteStreamRef, SOCKET); + virtual ~StreamTransceiver(); + + virtual IceInternal::NativeInfoPtr getNativeInfo(); + + virtual void initStreams(IceInternal::SelectorReadyCallback*); + virtual IceInternal::SocketOperation registerWithRunLoop(IceInternal::SocketOperation); + virtual IceInternal::SocketOperation unregisterFromRunLoop(IceInternal::SocketOperation, bool); + virtual void closeStreams(); + + virtual IceInternal::SocketOperation initialize(IceInternal::Buffer&, IceInternal::Buffer&); +#ifdef ICE_CPP11_MAPPING + virtual IceInternal::SocketOperation closing(bool initiator, std::exception_ptr); +#else + virtual IceInternal::SocketOperation closing(bool, const Ice::LocalException&); +#endif + virtual void close(); + + virtual IceInternal::SocketOperation write(IceInternal::Buffer&); + virtual IceInternal::SocketOperation read(IceInternal::Buffer&); + + virtual std::string protocol() const; + virtual std::string toString() const; + virtual std::string toDetailedString() const; + virtual Ice::ConnectionInfoPtr getInfo() const; + virtual Ice::ConnectionInfoPtr getWSInfo(const Ice::HeaderDict&) const; + virtual void checkSendSize(const IceInternal::Buffer&); + virtual void setBufferSize(int, int); + +private: + + void checkCertificates(); + void checkError(CFErrorRef, const char*, int); + void fillConnectionInfo(const Ice::IPConnectionInfoPtr&) const; + + const InstancePtr _instance; + const std::string _host; + const Ice::Int _port; + CFReadStreamRef _readStream; + CFWriteStreamRef _writeStream; + bool _readStreamRegistered; + bool _writeStreamRegistered; + bool _opening; + bool _checkCertificates; + + IceUtil::Mutex _mutex; + bool _error; + + State _state; + std::string _desc; + + size_t _maxSendPacketSize; + size_t _maxRecvPacketSize; +}; + +} + +#endif diff --git a/cpp/src/Ice/ios/iAPConnector.h b/cpp/src/Ice/ios/iAPConnector.h new file mode 100644 index 00000000000..42fe9f9029e --- /dev/null +++ b/cpp/src/Ice/ios/iAPConnector.h @@ -0,0 +1,58 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice Touch is licensed to you under the terms described in the +// ICE_TOUCH_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICE_IAP_CONNECTOR_H +#define ICE_IAP_CONNECTOR_H + +#include <Ice/TransceiverF.h> +#include <Ice/ProtocolInstanceF.h> +#include <Ice/TraceLevelsF.h> +#include <Ice/LoggerF.h> +#include <Ice/Connector.h> + +#import <Foundation/Foundation.h> +#import <ExternalAccessory/ExternalAccessory.h> + +namespace IceObjC +{ + +class iAPEndpointI; + +class Instance; +typedef IceUtil::Handle<Instance> InstancePtr; + +class iAPConnector : public IceInternal::Connector +{ +public: + + virtual IceInternal::TransceiverPtr connect(); + + virtual Ice::Short type() const; + virtual std::string toString() const; + + virtual bool operator==(const IceInternal::Connector&) const; + virtual bool operator!=(const IceInternal::Connector&) const; + virtual bool operator<(const IceInternal::Connector&) const; + +private: + + iAPConnector(const IceInternal::ProtocolInstancePtr&, Ice::Int, const std::string&, NSString*, EAAccessory*); + virtual ~iAPConnector(); + friend class iAPEndpointI; + + const IceInternal::ProtocolInstancePtr _instance; + const Ice::Int _timeout; + const std::string _connectionId; + NSString* _protocol; + EAAccessory* _accessory; +}; + +} + +#endif diff --git a/cpp/src/Ice/ios/iAPConnector.mm b/cpp/src/Ice/ios/iAPConnector.mm new file mode 100644 index 00000000000..37b2b7fc2dc --- /dev/null +++ b/cpp/src/Ice/ios/iAPConnector.mm @@ -0,0 +1,153 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice Touch is licensed to you under the terms described in the +// ICE_TOUCH_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include "iAPTransceiver.h" +#include "iAPEndpointI.h" +#include "iAPConnector.h" + +#include <Ice/ProtocolInstance.h> +#include <Ice/Exception.h> + +using namespace std; +using namespace Ice; +using namespace IceInternal; + +TransceiverPtr +IceObjC::iAPConnector::connect() +{ + EASession* session = [[EASession alloc] initWithAccessory:_accessory forProtocol:_protocol]; + if(!session) + { + throw Ice::ConnectFailedException(__FILE__, __LINE__, 0); + } + return new iAPTransceiver(_instance, session); +} + +Short +IceObjC::iAPConnector::type() const +{ + return _instance->type(); +} + +string +IceObjC::iAPConnector::toString() const +{ + ostringstream os; + os << [_accessory.name UTF8String]; + os << " model `" << [_accessory.modelNumber UTF8String] << "'"; + os << " made by `" << [_accessory.manufacturer UTF8String] << "'"; + os << " protocol `" << [_protocol UTF8String] << "'"; + return os.str(); +} + +bool +IceObjC::iAPConnector::operator==(const IceInternal::Connector& r) const +{ + const iAPConnector* p = dynamic_cast<const iAPConnector*>(&r); + if(!p) + { + return false; + } + + if(_timeout != p->_timeout) + { + return false; + } + + if(_connectionId != p->_connectionId) + { + return false; + } + + if(![_accessory isEqual:p->_accessory]) + { + return false; + } + + if(![_protocol isEqual:p->_protocol]) + { + return false; + } + + return true; +} + +bool +IceObjC::iAPConnector::operator!=(const IceInternal::Connector& r) const +{ + return !operator==(r); +} + +bool +IceObjC::iAPConnector::operator<(const IceInternal::Connector& r) const +{ + const iAPConnector* p = dynamic_cast<const iAPConnector*>(&r); + if(!p) + { + return type() < r.type(); + } + + if(_timeout < p->_timeout) + { + return true; + } + else if(p->_timeout < _timeout) + { + return false; + } + + if(_connectionId < p->_connectionId) + { + return true; + } + else if(p->_connectionId < _connectionId) + { + return false; + } + + if([_accessory hash] < [p->_accessory hash]) + { + return true; + } + else if([p->_accessory hash] < [_accessory hash]) + { + return false; + } + + NSInteger order = [_protocol compare:p->_protocol]; + if(order == NSOrderedAscending) + { + return true; + } + else if(order == NSOrderedDescending) + { + return false; + } + + return false; +} + +IceObjC::iAPConnector::iAPConnector(const ProtocolInstancePtr& instance, + Ice::Int timeout, + const string& connectionId, + NSString* protocol, + EAAccessory* accessory) : + _instance(instance), + _timeout(timeout), + _connectionId(connectionId), + _protocol([protocol retain]), + _accessory([accessory retain]) +{ +} + +IceObjC::iAPConnector::~iAPConnector() +{ + [_protocol release]; + [_accessory release]; +} diff --git a/cpp/src/Ice/ios/iAPEndpointI.h b/cpp/src/Ice/ios/iAPEndpointI.h new file mode 100644 index 00000000000..242574d0edb --- /dev/null +++ b/cpp/src/Ice/ios/iAPEndpointI.h @@ -0,0 +1,109 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice Touch is licensed to you under the terms described in the +// ICE_TOUCH_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICE_IAP_ENDPOINT_I_H +#define ICE_IAP_ENDPOINT_I_H + +#include <Ice/ProtocolInstanceF.h> +#include <Ice/EndpointI.h> +#include <Ice/EndpointFactory.h> + +namespace IceObjC +{ + +class iAPEndpointI; +#ifdef ICE_CPP11_MAPPING // C++11 mapping +typedef ::std::shared_ptr<iAPEndpointI> iAPEndpointIPtr; +#else +typedef IceUtil::Handle<iAPEndpointI> iAPEndpointIPtr; +#endif + +class iAPEndpointI : public IceInternal::EndpointI, public Ice::EnableSharedFromThis<iAPEndpointI> +{ +public: + + iAPEndpointI(const IceInternal::ProtocolInstancePtr&, const std::string&, const std::string&, const std::string&, + const std::string&, Ice::Int, const std::string&, bool); + iAPEndpointI(const IceInternal::ProtocolInstancePtr&); + iAPEndpointI(const IceInternal::ProtocolInstancePtr&, Ice::InputStream*); + + virtual void streamWrite(Ice::OutputStream*) const; + + virtual Ice::EndpointInfoPtr getInfo() const; + virtual Ice::Short type() const; + virtual const std::string& protocol() const; + virtual bool datagram() const; + virtual bool secure() const; + + virtual Ice::Int timeout() const; + virtual IceInternal::EndpointIPtr timeout(Ice::Int) const; + virtual const std::string& connectionId() const; + virtual IceInternal::EndpointIPtr connectionId(const std::string&) const; + virtual bool compress() const; + virtual IceInternal::EndpointIPtr compress(bool) const; + + virtual IceInternal::TransceiverPtr transceiver() const; + virtual void connectors_async(Ice::EndpointSelectionType, const IceInternal::EndpointI_connectorsPtr&) const; + virtual IceInternal::AcceptorPtr acceptor(const std::string&) const; + + virtual std::vector<IceInternal::EndpointIPtr> expand() const; + virtual bool equivalent(const IceInternal::EndpointIPtr&) const; + +#ifdef ICE_CPP11_MAPPING + virtual bool operator==(const Ice::Endpoint&) const; + virtual bool operator<(const Ice::Endpoint&) const; +#else + virtual bool operator==(const Ice::LocalObject&) const; + virtual bool operator<(const Ice::LocalObject&) const; +#endif + + virtual std::string options() const; + virtual ::Ice::Int hash() const; + +private: + + virtual bool checkOption(const std::string&, const std::string&, const std::string&); + + // + // All members are const, because endpoints are immutable. + // + const IceInternal::ProtocolInstancePtr _instance; + const std::string _manufacturer; + const std::string _modelNumber; + const std::string _name; + const std::string _protocol; + const Ice::Int _timeout; + const std::string _connectionId; + const bool _compress; +}; + +class iAPEndpointFactory : public IceInternal::EndpointFactory +{ +public: + + iAPEndpointFactory(const IceInternal::ProtocolInstancePtr&); + + virtual ~iAPEndpointFactory(); + + virtual Ice::Short type() const; + virtual std::string protocol() const; + virtual IceInternal::EndpointIPtr create(std::vector<std::string>&, bool) const; + virtual IceInternal::EndpointIPtr read(Ice::InputStream*) const; + virtual void destroy(); + + virtual IceInternal::EndpointFactoryPtr clone(const IceInternal::ProtocolInstancePtr&) const; + +private: + + IceInternal::ProtocolInstancePtr _instance; +}; + +} + +#endif diff --git a/cpp/src/Ice/ios/iAPEndpointI.mm b/cpp/src/Ice/ios/iAPEndpointI.mm new file mode 100644 index 00000000000..c6bc8dc66fb --- /dev/null +++ b/cpp/src/Ice/ios/iAPEndpointI.mm @@ -0,0 +1,640 @@ +// ********************************************************************** +// +// 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 "iAPEndpointI.h" +#include "iAPConnector.h" + +#include <Ice/Network.h> +#include <Ice/InputStream.h> +#include <Ice/OutputStream.h> +#include <Ice/LocalException.h> +#include <Ice/ProtocolInstance.h> +#include <Ice/DefaultsAndOverrides.h> +#include <Ice/Initialize.h> +#include <Ice/EndpointFactoryManager.h> +#include <Ice/Properties.h> +#include <Ice/HashUtil.h> + +#include <CoreFoundation/CoreFoundation.h> + +#include <fstream> + +using namespace std; +using namespace Ice; +using namespace IceInternal; + +extern "C" +{ + +Plugin* +createIceIAP(const CommunicatorPtr& com, const string&, const StringSeq&) +{ + ProtocolInstancePtr instance = new ProtocolInstance(com, iAPEndpointType, "iap", false); + return new EndpointFactoryPlugin(com, new IceObjC::iAPEndpointFactory(instance)); +} + +} + +IceObjC::iAPEndpointI::iAPEndpointI(const ProtocolInstancePtr& instance, const string& m, + const string& o, const string& n, const string& p, Int ti, + const string& conId, bool co) : + _instance(instance), + _manufacturer(m), + _modelNumber(o), + _name(n), + _protocol(p), + _timeout(ti), + _connectionId(conId), + _compress(co) +{ +} + +IceObjC::iAPEndpointI::iAPEndpointI(const ProtocolInstancePtr& instance) : + _instance(instance), + _timeout(-1), + _compress(false) +{ +} + +IceObjC::iAPEndpointI::iAPEndpointI(const ProtocolInstancePtr& instance, InputStream* s) : + _instance(instance), + _timeout(-1), + _compress(false) +{ + s->read(const_cast<string&>(_manufacturer), false); + s->read(const_cast<string&>(_modelNumber), false); + s->read(const_cast<string&>(_name), false); + s->read(const_cast<Int&>(_timeout)); + s->read(const_cast<bool&>(_compress)); +} + +void +IceObjC::iAPEndpointI::streamWrite(OutputStream* s) const +{ + s->startEncapsulation(); + s->write(_manufacturer, false); + s->write(_modelNumber, false); + s->write(_name, false); + s->write(_timeout); + s->write(_compress); + s->endEncapsulation(); +} + +EndpointInfoPtr +IceObjC::iAPEndpointI::getInfo() const +{ + return 0; +} + +Short +IceObjC::iAPEndpointI::type() const +{ + return _instance->type(); +} + +const string& +IceObjC::iAPEndpointI::protocol() const +{ + return _instance->protocol(); +} + +bool +IceObjC::iAPEndpointI::datagram() const +{ + return false; +} + +bool +IceObjC::iAPEndpointI::secure() const +{ + return _instance->secure(); +} + +Int +IceObjC::iAPEndpointI::timeout() const +{ + return _timeout; +} + +EndpointIPtr +IceObjC::iAPEndpointI::timeout(Int t) const +{ + if(t == _timeout) + { + return shared_from_this(); + } + else + { + return ICE_MAKE_SHARED(iAPEndpointI, _instance, _manufacturer, _modelNumber, _name, _protocol, t, _connectionId, _compress); + } +} + +const string& +IceObjC::iAPEndpointI::connectionId() const +{ + return _connectionId; +} + +EndpointIPtr +IceObjC::iAPEndpointI::connectionId(const string& cId) const +{ + if(cId == _connectionId) + { + return shared_from_this(); + } + else + { + return ICE_MAKE_SHARED(iAPEndpointI, _instance, _manufacturer, _modelNumber, _name, _protocol, _timeout, cId, _compress); + } +} + +bool +IceObjC::iAPEndpointI::compress() const +{ + return _compress; +} + +EndpointIPtr +IceObjC::iAPEndpointI::compress(bool c) const +{ + if(c == _compress) + { + return shared_from_this(); + } + else + { + return ICE_MAKE_SHARED(iAPEndpointI, _instance, _manufacturer, _modelNumber, _name, _protocol, _timeout, _connectionId, c); + } +} + +TransceiverPtr +IceObjC::iAPEndpointI::transceiver() const +{ + return 0; +} + +void +IceObjC::iAPEndpointI::connectors_async(Ice::EndpointSelectionType selType, + const EndpointI_connectorsPtr& callback) const +{ + try + { + vector<ConnectorPtr> c; + + EAAccessoryManager* manager = [EAAccessoryManager sharedAccessoryManager]; + if(manager == nil) + { + throw Ice::ConnectFailedException(__FILE__, __LINE__, 0); + } + + NSString* protocol = _protocol.empty() ? @"com.zeroc.ice" : [[NSString alloc] initWithUTF8String:_protocol.c_str()]; + NSArray* array = [manager connectedAccessories]; + NSEnumerator* enumerator = [array objectEnumerator]; + EAAccessory* accessory = nil; + int lastError = 0; + while((accessory = [enumerator nextObject])) + { + if(!accessory.connected) + { + lastError = 1; + continue; + } + + if(!_manufacturer.empty() && _manufacturer != [accessory.manufacturer UTF8String]) + { + lastError = 2; + continue; + } + if(!_modelNumber.empty() && _modelNumber != [accessory.modelNumber UTF8String]) + { + lastError = 3; + continue; + } + if(!_name.empty() && _name != [accessory.name UTF8String]) + { + lastError = 4; + continue; + } + + if(![accessory.protocolStrings containsObject:protocol]) + { + lastError = 5; + continue; + } + + c.push_back(new iAPConnector(_instance, _timeout, _connectionId, protocol, accessory)); + } + [protocol release]; + if(c.empty()) + { + throw Ice::ConnectFailedException(__FILE__, __LINE__, 0); + } + callback->connectors(c); + } + catch(const Ice::LocalException& ex) + { + callback->exception(ex); + } +} + +AcceptorPtr +IceObjC::iAPEndpointI::acceptor(const string&) const +{ + assert(false); + return 0; +} + +vector<EndpointIPtr> +IceObjC::iAPEndpointI::expand() const +{ + vector<EndpointIPtr> endps; + endps.push_back(shared_from_this()); + return endps; +} + +bool +IceObjC::iAPEndpointI::equivalent(const EndpointIPtr& endpoint) const +{ + const iAPEndpointI* endpointI = dynamic_cast<const iAPEndpointI*>(endpoint.get()); + if(!endpointI) + { + return false; + } + return endpointI->_manufacturer == _manufacturer && + endpointI->_modelNumber == _modelNumber && + endpointI->_name == _name; +} + +bool +#ifdef ICE_CPP11_MAPPING +IceObjC::iAPEndpointI::operator==(const Ice::Endpoint& r) const +#else +IceObjC::iAPEndpointI::operator==(const Ice::LocalObject& r) const +#endif +{ + const iAPEndpointI* p = dynamic_cast<const iAPEndpointI*>(&r); + if(!p) + { + return false; + } + + if(this == p) + { + return true; + } + + if(_manufacturer != p->_manufacturer) + { + return false; + } + + if(_modelNumber != p->_modelNumber) + { + return false; + } + + if(_name != p->_name) + { + return false; + } + + if(_timeout != p->_timeout) + { + return false; + } + + if(_connectionId != p->_connectionId) + { + return false; + } + + if(_compress != p->_compress) + { + return false; + } + + return true; +} + +bool +#ifdef ICE_CPP11_MAPPING +IceObjC::iAPEndpointI::operator<(const Ice::Endpoint& r) const +#else +IceObjC::iAPEndpointI::operator<(const Ice::LocalObject& r) const +#endif +{ + const iAPEndpointI* p = dynamic_cast<const iAPEndpointI*>(&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(_manufacturer < p->_manufacturer) + { + return true; + } + else if(p->_manufacturer < _manufacturer) + { + return false; + } + + if(_modelNumber < p->_modelNumber) + { + return true; + } + else if(p->_modelNumber < _modelNumber) + { + return false; + } + + if(_name < p->_name) + { + return true; + } + else if(p->_name < _name) + { + return false; + } + + if(_timeout < p->_timeout) + { + return true; + } + else if(p->_timeout < _timeout) + { + return false; + } + + if(_connectionId < p->_connectionId) + { + return true; + } + else if(p->_connectionId < _connectionId) + { + return false; + } + + if(!_compress && p->_compress) + { + return true; + } + else if(p->_compress < _compress) + { + return false; + } + + return false; +} + +string +IceObjC::iAPEndpointI::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; + if(!_manufacturer.empty()) + { + s << " -m "; + bool addQuote = _manufacturer.find(':') != string::npos; + if(addQuote) + { + s << "\""; + } + s << _manufacturer; + if(addQuote) + { + s << "\""; + } + } + + if(!_modelNumber.empty()) + { + s << " -o "; + bool addQuote = _modelNumber.find(':') != string::npos; + if(addQuote) + { + s << "\""; + } + s << _modelNumber; + if(addQuote) + { + s << "\""; + } + } + + if(!_name.empty()) + { + s << " -n "; + bool addQuote = _name.find(':') != string::npos; + if(addQuote) + { + s << "\""; + } + s << _name; + if(addQuote) + { + s << "\""; + } + } + + if(!_protocol.empty()) + { + s << " -p "; + bool addQuote = _protocol.find(':') != string::npos; + if(addQuote) + { + s << "\""; + } + s << _protocol; + if(addQuote) + { + s << "\""; + } + } + + if(_timeout != -1) + { + s << " -t " << _timeout; + } + if(_compress) + { + s << " -z"; + } + return s.str(); +} + +Ice::Int +IceObjC::iAPEndpointI::hash() const +{ + Ice::Int h = 5381; + hashAdd(h, _manufacturer); + hashAdd(h, _modelNumber); + hashAdd(h, _name); + hashAdd(h, _timeout); + hashAdd(h, _connectionId); + return h; +} + +bool +IceObjC::iAPEndpointI::checkOption(const string& option, const string& argument, const string& endpoint) +{ + switch(option[1]) + { + case 'm': + { + if(argument.empty()) + { + EndpointParseException ex(__FILE__, __LINE__); + ex.str = "no argument provided for -h option in endpoint " + endpoint; + throw ex; + } + const_cast<string&>(_manufacturer) = argument; + break; + } + + case 'o': + { + if(argument.empty()) + { + EndpointParseException ex(__FILE__, __LINE__); + ex.str = "no argument provided for -h option in endpoint " + endpoint; + throw ex; + } + const_cast<string&>(_modelNumber) = argument; + break; + } + + case 'n': + { + if(argument.empty()) + { + EndpointParseException ex(__FILE__, __LINE__); + ex.str = "no argument provided for -h option in endpoint " + endpoint; + throw ex; + } + const_cast<string&>(_name) = argument; + break; + } + + case 'p': + { + if(argument.empty()) + { + EndpointParseException ex(__FILE__, __LINE__); + ex.str = "no argument provided for -h option in endpoint " + endpoint; + throw ex; + } + const_cast<string&>(_protocol) = argument; + break; + } + + case 't': + { + 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; + } + } + break; + } + + case 'z': + { + if(!argument.empty()) + { + EndpointParseException ex(__FILE__, __LINE__); + ex.str = "no argument provided for -h option in endpoint " + endpoint; + throw ex; + } + const_cast<bool&>(_compress) = true; + break; + } + + default: + { + return false; + } + } + return true; +} + +IceObjC::iAPEndpointFactory::iAPEndpointFactory(const ProtocolInstancePtr& instance) : + _instance(instance) +{ +} + +IceObjC::iAPEndpointFactory::~iAPEndpointFactory() +{ +} + +Short +IceObjC::iAPEndpointFactory::type() const +{ + return _instance->type(); +} + +string +IceObjC::iAPEndpointFactory::protocol() const +{ + return _instance->protocol(); +} + +EndpointIPtr +IceObjC::iAPEndpointFactory::create(vector<string>& args, bool oaEndpoint) const +{ + if(oaEndpoint) + { + return 0; + } + EndpointIPtr endpt = ICE_MAKE_SHARED(iAPEndpointI, _instance); + endpt->initWithOptions(args); + return endpt; +} + +EndpointIPtr +IceObjC::iAPEndpointFactory::read(InputStream* s) const +{ + return ICE_MAKE_SHARED(iAPEndpointI, _instance, s); +} + +void +IceObjC::iAPEndpointFactory::destroy() +{ + _instance = 0; +} + +EndpointFactoryPtr +IceObjC::iAPEndpointFactory::clone(const ProtocolInstancePtr& instance) const +{ + return new iAPEndpointFactory(instance); +} diff --git a/cpp/src/Ice/ios/iAPTransceiver.h b/cpp/src/Ice/ios/iAPTransceiver.h new file mode 100644 index 00000000000..b5e10c80f1e --- /dev/null +++ b/cpp/src/Ice/ios/iAPTransceiver.h @@ -0,0 +1,85 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice Touch is licensed to you under the terms described in the +// ICE_TOUCH_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICE_IAP_TRANSCEIVER_H +#define ICE_IAP_TRANSCEIVER_H + +#include <Ice/ProtocolInstanceF.h> +#include <Ice/Transceiver.h> +#include <Ice/Network.h> +#include <Ice/Selector.h> + +#import <Foundation/Foundation.h> +#import <ExternalAccessory/ExternalAccessory.h> + +@class iAPTransceiverCallback; + +namespace IceObjC +{ + +class iAPTransceiver : public IceInternal::Transceiver, public IceInternal::StreamNativeInfo +{ + enum State + { + StateNeedConnect, + StateConnectPending, + StateConnected + }; + +public: + + iAPTransceiver(const IceInternal::ProtocolInstancePtr&, EASession*); + virtual ~iAPTransceiver(); + + virtual void initStreams(IceInternal::SelectorReadyCallback*); + virtual IceInternal::SocketOperation registerWithRunLoop(IceInternal::SocketOperation); + virtual IceInternal::SocketOperation unregisterFromRunLoop(IceInternal::SocketOperation, bool); + virtual void closeStreams(); + + virtual IceInternal::NativeInfoPtr getNativeInfo(); + + virtual IceInternal::SocketOperation initialize(IceInternal::Buffer&, IceInternal::Buffer&); +#ifdef ICE_CPP11_MAPPING + virtual IceInternal::SocketOperation closing(bool, std::exception_ptr); +#else + virtual IceInternal::SocketOperation closing(bool, const Ice::LocalException&); +#endif + virtual void close(); + virtual IceInternal::SocketOperation write(IceInternal::Buffer&); + virtual IceInternal::SocketOperation read(IceInternal::Buffer&); + + virtual std::string protocol() const; + virtual std::string toString() const; + virtual std::string toDetailedString() const; + virtual Ice::ConnectionInfoPtr getInfo() const; + virtual void checkSendSize(const IceInternal::Buffer&); + virtual void setBufferSize(int, int); + +private: + + void checkError(NSError*, const char*, int); + + IceInternal::ProtocolInstancePtr _instance; + NSInputStream* _readStream; + NSOutputStream* _writeStream; + iAPTransceiverCallback* _callback; + bool _readStreamRegistered; + bool _writeStreamRegistered; + bool _opening; + + IceUtil::Mutex _mutex; + bool _error; + + State _state; + std::string _desc; +}; + +} + +#endif diff --git a/cpp/src/Ice/ios/iAPTransceiver.mm b/cpp/src/Ice/ios/iAPTransceiver.mm new file mode 100644 index 00000000000..1e7b989f8e1 --- /dev/null +++ b/cpp/src/Ice/ios/iAPTransceiver.mm @@ -0,0 +1,490 @@ +// ********************************************************************** +// +// 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 "iAPTransceiver.h" +#include "iAPEndpointI.h" + +#include <Ice/LocalException.h> +#include <Ice/ProtocolInstance.h> +#include <Ice/Buffer.h> + +#import <Foundation/NSRunLoop.h> +#import <Foundation/NSError.h> +#import <Foundation/NSString.h> + +using namespace std; +using namespace Ice; +using namespace IceInternal; + +@interface iAPTransceiverCallback : NSObject<NSStreamDelegate> +{ +@private + + SelectorReadyCallback* callback; +} +-(id) init:(SelectorReadyCallback*)cb; +@end + +@implementation iAPTransceiverCallback +-(id) init:(SelectorReadyCallback*)cb; +{ + if(![super init]) + { + return nil; + } + callback = cb; + return self; +} + +- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode +{ + switch(eventCode) + { + case NSStreamEventHasBytesAvailable: + callback->readyCallback(SocketOperationRead); + break; + case NSStreamEventHasSpaceAvailable: + callback->readyCallback(SocketOperationWrite); + break; + case NSStreamEventOpenCompleted: + if([[stream class] isSubclassOfClass:[NSInputStream class]]) + { + callback->readyCallback(static_cast<SocketOperation>(SocketOperationConnect | SocketOperationRead)); + } + else + { + callback->readyCallback(static_cast<SocketOperation>(SocketOperationConnect | SocketOperationWrite)); + } + break; + case NSStreamEventEndEncountered: + case NSStreamEventErrorOccurred: + if([[stream class] isSubclassOfClass:[NSInputStream class]]) + { + callback->readyCallback(SocketOperationRead, -1); // Error + } + else + { + callback->readyCallback(SocketOperationWrite, -1); // Error + } + } +} +@end + +void +IceObjC::iAPTransceiver::initStreams(SelectorReadyCallback* callback) +{ + _callback = [[iAPTransceiverCallback alloc] init:callback]; + [_writeStream setDelegate:_callback]; + [_readStream setDelegate:_callback]; +} + +SocketOperation +IceObjC::iAPTransceiver::registerWithRunLoop(SocketOperation op) +{ + IceUtil::Mutex::Lock sync(_mutex); + + SocketOperation readyOp = SocketOperationNone; + if(op & SocketOperationConnect) + { + if([_writeStream streamStatus] != NSStreamStatusNotOpen || [_readStream streamStatus] != NSStreamStatusNotOpen) + { + return SocketOperationConnect; + } + + _opening = true; + + [_writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [_readStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + + _writeStreamRegistered = true; // Note: this must be set after the schedule call + _readStreamRegistered = true; // Note: this must be set after the schedule call + + [_writeStream open]; + [_readStream open]; + } + else + { + if(op & SocketOperationWrite) + { + if([_writeStream hasSpaceAvailable]) + { + readyOp = static_cast<SocketOperation>(readyOp | SocketOperationWrite); + } + else if(!_writeStreamRegistered) + { + [_writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + _writeStreamRegistered = true; // Note: this must be set after the schedule call + if([_writeStream hasSpaceAvailable]) + { + readyOp = static_cast<SocketOperation>(readyOp | SocketOperationWrite); + } + } + } + + if(op & SocketOperationRead) + { + if([_readStream hasBytesAvailable]) + { + readyOp = static_cast<SocketOperation>(readyOp | SocketOperationRead); + } + else if(!_readStreamRegistered) + { + [_readStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + _readStreamRegistered = true; // Note: this must be set after the schedule call + if([_readStream hasBytesAvailable]) + { + readyOp = static_cast<SocketOperation>(readyOp | SocketOperationRead); + } + } + } + } + return readyOp; +} + +SocketOperation +IceObjC::iAPTransceiver::unregisterFromRunLoop(SocketOperation op, bool error) +{ + IceUtil::Mutex::Lock sync(_mutex); + _error |= error; + + if(_opening) + { + // Wait for the stream to be ready for write + if(op == SocketOperationWrite) + { + _writeStreamRegistered = false; + } + + // + // We don't wait for the stream to be ready for read (even if + // it's a client connection) because there's no guarantees that + // the server might actually send data right away. If we use + // the WebSocket transport, the server actually waits for the + // client to write the HTTP upgrade request. + // + //if(op & SocketOperationRead && (_fd != INVALID_SOCKET || !(op & SocketOperationConnect))) + if(op == (SocketOperationRead | SocketOperationConnect)) + { + _readStreamRegistered = false; + } + + if(error || (!_readStreamRegistered && !_writeStreamRegistered)) + { + [_writeStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [_readStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + _opening = false; + return SocketOperationConnect; + } + else + { + return SocketOperationNone; + } + } + else + { + if(op & SocketOperationWrite && _writeStreamRegistered) + { + [_writeStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + _writeStreamRegistered = false; + } + + if(op & SocketOperationRead && _readStreamRegistered) + { + [_readStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + _readStreamRegistered = false; + } + } + return op; +} + +void +IceObjC::iAPTransceiver::closeStreams() +{ + [_writeStream setDelegate:nil]; + [_readStream setDelegate:nil]; + + [_callback release]; + _callback = 0; + + [_writeStream close]; + [_readStream close]; +} + +IceInternal::NativeInfoPtr +IceObjC::iAPTransceiver::getNativeInfo() +{ + return this; +} + +SocketOperation +IceObjC::iAPTransceiver::initialize(Buffer& readBuffer, Buffer& writeBuffer) +{ + IceUtil::Mutex::Lock sync(_mutex); + if(_state == StateNeedConnect) + { + _state = StateConnectPending; + return SocketOperationConnect; + } + + if(_state <= StateConnectPending) + { + if(_error) + { + assert([_writeStream streamStatus] == NSStreamStatusError); + NSError* err = [_writeStream streamError]; + NSString* domain = [err domain]; + if([domain compare:NSPOSIXErrorDomain] == NSOrderedSame) + { + errno = [err code]; + [err release]; + if(connectionRefused()) + { + ConnectionRefusedException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + else if(connectFailed()) + { + ConnectFailedException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + else + { + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + } + + // Otherwise throw a generic exception. + CFNetworkException ex(__FILE__, __LINE__); + ex.domain = [domain UTF8String]; + ex.error = [err code]; + [err release]; + throw ex; + } + _state = StateConnected; + } + assert(_state == StateConnected); + return SocketOperationNone; +} + +SocketOperation +#ifdef ICE_CPP11_MAPPING +IceObjC::iAPTransceiver::closing(bool initiator, exception_ptr) +#else +IceObjC::iAPTransceiver::closing(bool initiator, const Ice::LocalException&) +#endif +{ + // If we are initiating the connection closure, wait for the peer + // to close the TCP/IP connection. Otherwise, close immediately. + return initiator ? SocketOperationRead : SocketOperationNone; +} + +void +IceObjC::iAPTransceiver::close() +{ +} + +SocketOperation +IceObjC::iAPTransceiver::write(Buffer& buf) +{ + IceUtil::Mutex::Lock sync(_mutex); + if(_error) + { + assert([_writeStream streamStatus] == NSStreamStatusError); + checkError([_writeStream streamError], __FILE__, __LINE__); + } + + // Its impossible for the packetSize to be more than an Int. + int packetSize = static_cast<int>(buf.b.end() - buf.i); + while(buf.i != buf.b.end()) + { + if(![_writeStream hasSpaceAvailable]) + { + return SocketOperationWrite; + } + assert([_writeStream streamStatus] >= NSStreamStatusOpen); + + NSInteger ret = [_writeStream write:reinterpret_cast<const UInt8*>(&*buf.i) maxLength:packetSize]; + if(ret == SOCKET_ERROR) + { + if([_writeStream streamStatus] == NSStreamStatusAtEnd) + { + ConnectionLostException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + + assert([_writeStream streamStatus] == NSStreamStatusError); + checkError([_writeStream streamError], __FILE__, __LINE__); + if(noBuffers() && packetSize > 1024) + { + packetSize /= 2; + } + continue; + } + + buf.i += ret; + + if(packetSize > buf.b.end() - buf.i) + { + packetSize = static_cast<int>(buf.b.end() - buf.i); + } + } + + return SocketOperationNone; +} + +SocketOperation +IceObjC::iAPTransceiver::read(Buffer& buf) +{ + IceUtil::Mutex::Lock sync(_mutex); + if(_error) + { + assert([_readStream streamStatus] == NSStreamStatusError); + checkError([_readStream streamError], __FILE__, __LINE__); + } + + // Its impossible for the packetSize to be more than an Int. + int packetSize = static_cast<int>(buf.b.end() - buf.i); + while(buf.i != buf.b.end()) + { + if(![_readStream hasBytesAvailable] && [_readStream streamStatus] != NSStreamStatusError) + { + return SocketOperationRead; + } + assert([_readStream streamStatus] >= NSStreamStatusOpen); + + NSInteger ret = [_readStream read:reinterpret_cast<UInt8*>(&*buf.i) maxLength:packetSize]; + if(ret == 0) + { + ConnectionLostException ex(__FILE__, __LINE__); + ex.error = 0; + throw ex; + } + + if(ret == SOCKET_ERROR) + { + if([_readStream streamStatus] == NSStreamStatusAtEnd) + { + ConnectionLostException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + + assert([_readStream streamStatus] == NSStreamStatusError); + checkError([_readStream streamError], __FILE__, __LINE__); + if(noBuffers() && packetSize > 1024) + { + packetSize /= 2; + } + continue; + } + + buf.i += ret; + + if(packetSize > buf.b.end() - buf.i) + { + packetSize = static_cast<int>(buf.b.end() - buf.i); + } + } + + return SocketOperationNone; +} + +string +IceObjC::iAPTransceiver::protocol() const +{ + return _instance->protocol(); +} + +string +IceObjC::iAPTransceiver::toString() const +{ + return _desc; +} + +string +IceObjC::iAPTransceiver::toDetailedString() const +{ + return toString(); +} + +Ice::ConnectionInfoPtr +IceObjC::iAPTransceiver::getInfo() const +{ + return 0; +} + +void +IceObjC::iAPTransceiver::checkSendSize(const Buffer& buf) +{ +} + +void +IceObjC::iAPTransceiver::setBufferSize(int, int) +{ +} + +IceObjC::iAPTransceiver::iAPTransceiver(const ProtocolInstancePtr& instance, EASession* session) : + StreamNativeInfo(INVALID_SOCKET), + _instance(instance), + _readStream([[session inputStream] retain]), + _writeStream([[session outputStream] retain]), + _readStreamRegistered(false), + _writeStreamRegistered(false), + _error(false), + _state(StateNeedConnect) +{ + _desc = string("name = ") + [session.accessory.name UTF8String]; +} + +IceObjC::iAPTransceiver::~iAPTransceiver() +{ + [_readStream release]; + [_writeStream release]; +} + +void +IceObjC::iAPTransceiver::checkError(NSError* err, const char* file, int line) +{ + NSString* domain = [err domain]; + if([domain compare:NSPOSIXErrorDomain] == NSOrderedSame) + { + errno = [err code]; + [err release]; + if(connectionRefused()) + { + ConnectionRefusedException ex(file, line); + ex.error = getSocketErrno(); + throw ex; + } + else if(connectFailed()) + { + ConnectFailedException ex(file, line); + ex.error = getSocketErrno(); + throw ex; + } + else + { + SocketException ex(file, line); + ex.error = getSocketErrno(); + throw ex; + } + } + + // Otherwise throw a generic exception. + CFNetworkException ex(file, line); + ex.domain = [domain UTF8String]; + ex.error = [err code]; + [err release]; + throw ex; +} |